fractal/components/camera/
viewfinder.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Camera viewfinder API.

use gettextrs::gettext;
use gtk::{glib, glib::closure_local, prelude::*, subclass::prelude::*};
use matrix_sdk::encryption::verification::QrVerificationData;

use super::QrVerificationDataBoxed;

/// The possible states of a [`CameraViewfinder`].
#[derive(Default, Debug, Copy, Clone, glib::Enum, PartialEq)]
#[enum_type(name = "CameraViewfinderState")]
pub enum CameraViewfinderState {
    /// The viewfinder is still loading.
    #[default]
    Loading,
    /// The viewfinder is ready for use.
    Ready,
    /// The viewfinder could not find any cameras to use.
    NoCameras,
    /// The viewfinder had an error and is not usable.
    Error,
}

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

    use glib::subclass::Signal;

    use super::*;

    #[repr(C)]
    pub struct CameraViewfinderClass {
        parent_class: glib::object::Class<gtk::Widget>,
    }

    unsafe impl ClassStruct for CameraViewfinderClass {
        type Type = CameraViewfinder;
    }

    #[derive(Debug, Default, glib::Properties)]
    #[properties(wrapper_type = super::CameraViewfinder)]
    pub struct CameraViewfinder {
        /// The state of this viewfinder.
        #[property(get, set = Self::set_state, explicit_notify, builder(CameraViewfinderState::default()))]
        state: Cell<CameraViewfinderState>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for CameraViewfinder {
        const NAME: &'static str = "CameraViewfinder";
        type Type = super::CameraViewfinder;
        type ParentType = gtk::Widget;
        type Class = CameraViewfinderClass;
    }

    #[glib::derived_properties]
    impl ObjectImpl for CameraViewfinder {
        fn signals() -> &'static [Signal] {
            static SIGNALS: LazyLock<Vec<Signal>> = LazyLock::new(|| {
                vec![Signal::builder("qrcode-detected")
                    .param_types([QrVerificationDataBoxed::static_type()])
                    .run_first()
                    .build()]
            });
            SIGNALS.as_ref()
        }

        fn constructed(&self) {
            self.parent_constructed();

            self.obj()
                .update_property(&[gtk::accessible::Property::Label(&gettext("Viewfinder"))]);
        }
    }

    impl WidgetImpl for CameraViewfinder {}

    impl CameraViewfinder {
        /// Set the state of this viewfinder.
        fn set_state(&self, state: CameraViewfinderState) {
            if self.state.get() == state {
                return;
            }

            self.state.set(state);
            self.obj().notify_state();
        }
    }
}

glib::wrapper! {
    /// Subclassable camera viewfinder widget.
    ///
    /// The widget presents the output of the camera and detects QR codes.
    ///
    /// To construct this, use `Camera::viewfinder()`.
    pub struct CameraViewfinder(ObjectSubclass<imp::CameraViewfinder>)
        @extends gtk::Widget, @implements gtk::Accessible;
}

/// Trait implemented by types that subclass [`CameraViewfinder`].
#[allow(dead_code)]
pub(super) trait CameraViewfinderExt: 'static {
    /// The state of this viewfinder.
    fn state(&self) -> CameraViewfinderState;

    /// Set the state of this viewfinder.
    fn set_state(&self, state: CameraViewfinderState);

    /// Connect to the signal emitted when a QR code is detected.
    fn connect_qrcode_detected<F: Fn(&Self, QrVerificationData) + 'static>(
        &self,
        f: F,
    ) -> glib::SignalHandlerId;

    /// Emit the signal that a QR code was detected.
    fn emit_qrcode_detected(&self, data: QrVerificationData);
}

impl<O: IsA<CameraViewfinder>> CameraViewfinderExt for O {
    fn state(&self) -> CameraViewfinderState {
        self.upcast_ref().state()
    }

    /// Set the state of this viewfinder.
    fn set_state(&self, state: CameraViewfinderState) {
        self.upcast_ref().set_state(state);
    }

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

    fn emit_qrcode_detected(&self, data: QrVerificationData) {
        self.emit_by_name::<()>("qrcode-detected", &[&QrVerificationDataBoxed(data)]);
    }
}

/// Trait that must be implemented for types that subclass `CameraViewfinder`.
///
/// Overriding a method from this Trait overrides also its behavior in
/// [`CameraViewfinderExt`].
pub(super) trait CameraViewfinderImpl: ObjectImpl {}

unsafe impl<T> IsSubclassable<T> for CameraViewfinder
where
    T: CameraViewfinderImpl + WidgetImpl,
    T::Type: IsA<CameraViewfinder>,
{
    fn class_init(class: &mut glib::Class<Self>) {
        Self::parent_class_init::<T>(class.upcast_ref_mut());
    }
}