Skip to main content

fractal/components/camera/
viewfinder.rs

1//! Camera viewfinder API.
2
3use gettextrs::gettext;
4use gtk::{glib, glib::closure_local, prelude::*, subclass::prelude::*};
5use matrix_sdk::encryption::verification::QrVerificationData;
6
7use super::QrVerificationDataBoxed;
8
9/// The possible states of a [`CameraViewfinder`].
10#[derive(Default, Debug, Copy, Clone, glib::Enum, PartialEq)]
11#[enum_type(name = "CameraViewfinderState")]
12pub enum CameraViewfinderState {
13    /// The viewfinder is still loading.
14    #[default]
15    Loading,
16    /// The viewfinder is ready for use.
17    Ready,
18    /// The viewfinder could not find any cameras to use.
19    NoCameras,
20    /// The viewfinder had an error and is not usable.
21    Error,
22}
23
24mod imp {
25    use std::{cell::Cell, sync::LazyLock};
26
27    use glib::subclass::Signal;
28
29    use super::*;
30
31    #[repr(C)]
32    pub struct CameraViewfinderClass {
33        parent_class: glib::object::Class<gtk::Widget>,
34    }
35
36    unsafe impl ClassStruct for CameraViewfinderClass {
37        type Type = CameraViewfinder;
38    }
39
40    #[derive(Debug, Default, glib::Properties)]
41    #[properties(wrapper_type = super::CameraViewfinder)]
42    pub struct CameraViewfinder {
43        /// The state of this viewfinder.
44        #[property(get, set = Self::set_state, explicit_notify, builder(CameraViewfinderState::default()))]
45        state: Cell<CameraViewfinderState>,
46    }
47
48    #[glib::object_subclass]
49    impl ObjectSubclass for CameraViewfinder {
50        const NAME: &'static str = "CameraViewfinder";
51        type Type = super::CameraViewfinder;
52        type ParentType = gtk::Widget;
53        type Class = CameraViewfinderClass;
54    }
55
56    #[glib::derived_properties]
57    impl ObjectImpl for CameraViewfinder {
58        fn signals() -> &'static [Signal] {
59            static SIGNALS: LazyLock<Vec<Signal>> = LazyLock::new(|| {
60                vec![
61                    Signal::builder("qrcode-detected")
62                        .param_types([QrVerificationDataBoxed::static_type()])
63                        .run_first()
64                        .build(),
65                ]
66            });
67            SIGNALS.as_ref()
68        }
69
70        fn constructed(&self) {
71            self.parent_constructed();
72
73            self.obj()
74                .update_property(&[gtk::accessible::Property::Label(&gettext("Viewfinder"))]);
75        }
76    }
77
78    impl WidgetImpl for CameraViewfinder {}
79
80    impl CameraViewfinder {
81        /// Set the state of this viewfinder.
82        fn set_state(&self, state: CameraViewfinderState) {
83            if self.state.get() == state {
84                return;
85            }
86
87            self.state.set(state);
88            self.obj().notify_state();
89        }
90    }
91}
92
93glib::wrapper! {
94    /// Subclassable camera viewfinder widget.
95    ///
96    /// The widget presents the output of the camera and detects QR codes.
97    ///
98    /// To construct this, use `Camera::viewfinder()`.
99    pub struct CameraViewfinder(ObjectSubclass<imp::CameraViewfinder>)
100        @extends gtk::Widget,
101        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
102}
103
104/// Trait implemented by types that subclass [`CameraViewfinder`].
105#[allow(dead_code)]
106pub(super) trait CameraViewfinderExt: 'static {
107    /// The state of this viewfinder.
108    fn state(&self) -> CameraViewfinderState;
109
110    /// Set the state of this viewfinder.
111    fn set_state(&self, state: CameraViewfinderState);
112
113    /// Connect to the signal emitted when a QR code is detected.
114    fn connect_qrcode_detected<F: Fn(&Self, QrVerificationData) + 'static>(
115        &self,
116        f: F,
117    ) -> glib::SignalHandlerId;
118
119    /// Emit the signal that a QR code was detected.
120    fn emit_qrcode_detected(&self, data: QrVerificationData);
121}
122
123impl<O: IsA<CameraViewfinder>> CameraViewfinderExt for O {
124    fn state(&self) -> CameraViewfinderState {
125        self.upcast_ref().state()
126    }
127
128    /// Set the state of this viewfinder.
129    fn set_state(&self, state: CameraViewfinderState) {
130        self.upcast_ref().set_state(state);
131    }
132
133    fn connect_qrcode_detected<F: Fn(&Self, QrVerificationData) + 'static>(
134        &self,
135        f: F,
136    ) -> glib::SignalHandlerId {
137        self.connect_closure(
138            "qrcode-detected",
139            true,
140            closure_local!(|obj: Self, data: QrVerificationDataBoxed| {
141                f(&obj, data.0);
142            }),
143        )
144    }
145
146    fn emit_qrcode_detected(&self, data: QrVerificationData) {
147        self.emit_by_name::<()>("qrcode-detected", &[&QrVerificationDataBoxed(data)]);
148    }
149}
150
151/// Trait that must be implemented for types that subclass `CameraViewfinder`.
152///
153/// Overriding a method from this Trait overrides also its behavior in
154/// [`CameraViewfinderExt`].
155pub(super) trait CameraViewfinderImpl: ObjectImpl {}
156
157unsafe impl<T> IsSubclassable<T> for CameraViewfinder
158where
159    T: CameraViewfinderImpl + WidgetImpl,
160    T::Type: IsA<CameraViewfinder>,
161{
162    fn class_init(class: &mut glib::Class<Self>) {
163        Self::parent_class_init::<T>(class.upcast_ref_mut());
164    }
165}