fractal/contrib/qr_code_scanner/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// SPDX-License-Identifier: GPL-3.0-or-later
use gtk::{gdk, glib, glib::closure_local, prelude::*, subclass::prelude::*};
use matrix_sdk::encryption::verification::QrVerificationData;

mod camera;
mod qr_code_detector;

pub use camera::{Camera, CameraExt};

mod imp {
    use std::{cell::RefCell, sync::LazyLock};

    use adw::subclass::prelude::*;
    use glib::subclass::{InitializingObject, Signal};
    use gtk::CompositeTemplate;

    use super::*;

    #[derive(Debug, CompositeTemplate, Default)]
    #[template(resource = "/org/gnome/Fractal/ui/contrib/qr_code_scanner/mod.ui")]
    pub struct QrCodeScanner {
        #[template_child]
        pub stack: TemplateChild<gtk::Stack>,
        #[template_child]
        pub picture: TemplateChild<gtk::Picture>,
        pub handler: RefCell<Option<glib::SignalHandlerId>>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for QrCodeScanner {
        const NAME: &'static str = "QrCodeScanner";
        type Type = super::QrCodeScanner;
        type ParentType = adw::Bin;

        fn class_init(klass: &mut Self::Class) {
            Self::bind_template(klass);
        }

        fn instance_init(obj: &InitializingObject<Self>) {
            obj.init_template();
        }
    }
    impl ObjectImpl for QrCodeScanner {
        fn signals() -> &'static [Signal] {
            static SIGNALS: LazyLock<Vec<Signal>> = LazyLock::new(|| {
                vec![Signal::builder("code-detected")
                    .param_types([QrVerificationDataBoxed::static_type()])
                    .run_first()
                    .build()]
            });
            SIGNALS.as_ref()
        }
    }
    impl WidgetImpl for QrCodeScanner {
        fn unmap(&self) {
            self.parent_unmap();
            self.obj().stop();
        }
    }
    impl BinImpl for QrCodeScanner {}
}

glib::wrapper! {
    pub struct QrCodeScanner(ObjectSubclass<imp::QrCodeScanner>) @extends gtk::Widget, adw::Bin;
}

impl QrCodeScanner {
    pub fn new() -> Self {
        glib::Object::new()
    }

    pub fn stop(&self) {
        let imp = self.imp();

        if let Some(paintable) = imp.picture.paintable() {
            imp.picture.set_paintable(gdk::Paintable::NONE);
            if let Some(handler) = imp.handler.take() {
                paintable.disconnect(handler);
            }
        }
    }

    pub async fn start(&self) {
        let imp = self.imp();
        let camera = camera::Camera::default();

        if let Some(paintable) = camera.paintable().await {
            self.stop();

            imp.picture.set_paintable(Some(&paintable));

            let callback = glib::clone!(
                #[weak(rename_to = obj)]
                self,
                #[upgrade_or]
                None,
                move |args: &[glib::Value]| {
                    let code = args
                        .get(1)
                        .unwrap()
                        .get::<QrVerificationDataBoxed>()
                        .unwrap();
                    obj.emit_by_name::<()>("code-detected", &[&code]);

                    None
                }
            );
            let handler = paintable.connect_local("code-detected", false, callback);

            imp.handler.replace(Some(handler));
            imp.stack.set_visible_child_name("camera");
        } else {
            imp.stack.set_visible_child_name("no-camera");
        }
    }

    pub fn connect_code_detected<F: Fn(&Self, QrVerificationData) + 'static>(
        &self,
        f: F,
    ) -> glib::SignalHandlerId {
        self.connect_closure(
            "code-detected",
            true,
            closure_local!(move |obj: Self, data: QrVerificationDataBoxed| {
                f(&obj, data.0);
            }),
        )
    }
}

#[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
#[boxed_type(name = "QrVerificationDataBoxed")]
struct QrVerificationDataBoxed(QrVerificationData);