fractal/contrib/qr_code_scanner/
mod.rsuse 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);