diff --git a/src/area_selector/mod.rs b/src/area_selector/mod.rs index 8aa135d1..791a628a 100644 --- a/src/area_selector/mod.rs +++ b/src/area_selector/mod.rs @@ -183,7 +183,7 @@ impl AreaSelector { // Setup paintable let paintable = sink.property::("paintable"); - imp.view_port.set_paintable(Some(&paintable)); + imp.view_port.set_paintable(Some(paintable)); pipeline.set_state(gst::State::Playing)?; diff --git a/src/area_selector/view_port.rs b/src/area_selector/view_port.rs index 57e7ef64..ac79328a 100644 --- a/src/area_selector/view_port.rs +++ b/src/area_selector/view_port.rs @@ -105,15 +105,16 @@ impl Selection { mod imp { use super::*; - use once_cell::sync::Lazy; - #[derive(Debug, Default)] + #[derive(Debug, Default, glib::Properties)] + #[properties(wrapper_type = super::ViewPort)] pub struct ViewPort { + #[property(get, set = Self::set_paintable, explicit_notify, nullable)] pub(super) paintable: RefCell>, + #[property(get)] + pub(super) selection: Cell>, pub(super) paintable_rect: Cell>, - - pub(super) selection: Cell>, pub(super) selection_handles: Cell>, // [top-left, top-right, bottom-right, bottom-left] pub(super) drag_start: Cell>, @@ -132,43 +133,6 @@ mod imp { } impl ObjectImpl for ViewPort { - fn properties() -> &'static [glib::ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { - vec![ - glib::ParamSpecObject::builder::("paintable") - .explicit_notify() - .build(), - glib::ParamSpecBoxed::builder::("selection") - .read_only() - .build(), - ] - }); - - PROPERTIES.as_ref() - } - - fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { - let obj = self.obj(); - - match pspec.name() { - "paintable" => { - let paintable: Option = value.get().unwrap(); - obj.set_paintable(paintable.as_ref()); - } - _ => unimplemented!(), - } - } - - fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { - let obj = self.obj(); - - match pspec.name() { - "paintable" => obj.paintable().to_value(), - "selection" => obj.selection().to_value(), - _ => unimplemented!(), - } - } - fn constructed(&self) { self.parent_constructed(); @@ -198,6 +162,8 @@ mod imp { })); obj.add_controller(gesture_drag); } + + crate::derived_properties!(); } impl WidgetImpl for ViewPort { @@ -344,67 +310,52 @@ mod imp { } } } -} - -glib::wrapper! { - pub struct ViewPort(ObjectSubclass) - @extends gtk::Widget; -} -impl ViewPort { - pub fn new() -> Self { - glib::Object::builder().build() - } - - pub fn set_paintable(&self, paintable: Option<&impl IsA>) { - let paintable = paintable.map(|p| p.as_ref()); + impl ViewPort { + fn set_paintable(&self, paintable: Option) { + let obj = self.obj(); - if paintable == self.paintable().as_ref() { - return; - } + if paintable == obj.paintable() { + return; + } - let _freeze_guard = self.freeze_notify(); + let _freeze_guard = obj.freeze_notify(); - let imp = self.imp(); + let mut handler_ids = self.handler_ids.borrow_mut(); - let mut handler_ids = imp.handler_ids.borrow_mut(); + if let Some(previous_paintable) = self.paintable.replace(paintable.clone()) { + for handler_id in handler_ids.drain(..) { + previous_paintable.disconnect(handler_id); + } + } - if let Some(previous_paintable) = imp.paintable.replace(paintable.cloned()) { - for handler_id in handler_ids.drain(..) { - previous_paintable.disconnect(handler_id); + if let Some(paintable) = paintable { + handler_ids.push(paintable.connect_invalidate_contents( + clone!(@weak obj => move |_| { + obj.queue_draw(); + }), + )); + handler_ids.push( + paintable.connect_invalidate_size(clone!(@weak obj => move |_| { + obj.queue_resize(); + })), + ); } - } - if let Some(paintable) = paintable { - handler_ids.push(paintable.connect_invalidate_contents( - clone!(@weak self as obj => move |_| { - obj.queue_draw(); - }), - )); - handler_ids.push(paintable.connect_invalidate_size( - clone!(@weak self as obj => move |_| { - obj.queue_resize(); - }), - )); + obj.queue_resize(); + obj.notify_paintable(); } - - self.queue_resize(); - self.notify("paintable"); - } - - pub fn paintable(&self) -> Option { - self.imp().paintable.borrow().clone() } +} - pub fn selection(&self) -> Option { - self.imp().selection.get() - } +glib::wrapper! { + pub struct ViewPort(ObjectSubclass) + @extends gtk::Widget; +} - pub fn connect_selection_notify(&self, f: F) -> glib::SignalHandlerId - where - F: Fn(&Self) + 'static, - { - self.connect_notify_local(Some("selection"), move |obj, _| f(obj)) +impl ViewPort { + pub fn new() -> Self { + glib::Object::builder().build() } pub fn paintable_rect(&self) -> Option { @@ -419,7 +370,7 @@ impl ViewPort { fn set_selection(&self, selection: Option) { self.imp().selection.set(selection); - self.notify("selection"); + self.notify_selection(); } fn on_enter(&self, _controller: >k::EventControllerMotion, x: f64, y: f64) { diff --git a/src/preferences_window.rs b/src/preferences_window.rs index 2aca9ea7..59b0190d 100644 --- a/src/preferences_window.rs +++ b/src/preferences_window.rs @@ -56,8 +56,6 @@ mod imp { } impl ObjectImpl for PreferencesWindow { - crate::derived_properties!(); - fn constructed(&self) { self.parent_constructed(); @@ -133,6 +131,8 @@ mod imp { } })); } + + crate::derived_properties!(); } impl WidgetImpl for PreferencesWindow {} diff --git a/src/recording.rs b/src/recording.rs index d27adb0a..a3894625 100644 --- a/src/recording.rs +++ b/src/recording.rs @@ -3,7 +3,7 @@ use gettextrs::gettext; use gst::prelude::*; use gtk::{ gio::{self, prelude::*}, - glib::{self, clone, closure_local, subclass::prelude::*, translate::IntoGlib}, + glib::{self, clone, closure_local, subclass::prelude::*}, }; use once_cell::{sync::Lazy, unsync::OnceCell}; @@ -63,17 +63,20 @@ mod imp { use super::*; use glib::subclass::Signal; - #[derive(Debug, Default)] + #[derive(Debug, Default, glib::Properties)] + #[properties(wrapper_type = super::Recording)] pub struct Recording { + #[property(get)] + pub(super) state: Cell, + #[property(get)] + pub(super) duration: Cell, + pub(super) file: OnceCell, pub(super) timer: RefCell>, pub(super) session: RefCell>, pub(super) duration_source_id: RefCell>, pub(super) pipeline: OnceCell, - - pub(super) state: Cell, - pub(super) duration: Cell, } #[glib::object_subclass] @@ -83,42 +86,6 @@ mod imp { } impl ObjectImpl for Recording { - fn properties() -> &'static [glib::ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { - vec![ - glib::ParamSpecBoxed::builder::("state") - .read_only() - .build(), - glib::ParamSpecUInt64::builder("duration") - .maximum(gst::ClockTime::MAX.into_glib()) - .read_only() - .build(), - ] - }); - - PROPERTIES.as_ref() - } - - fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { - let obj = self.obj(); - - match pspec.name() { - "state" => obj.state().to_value(), - "duration" => obj.duration().to_value(), - _ => unimplemented!(), - } - } - - fn signals() -> &'static [glib::subclass::Signal] { - static SIGNALS: Lazy> = Lazy::new(|| { - vec![Signal::builder("finished") - .param_types([BoxedResult::static_type()]) - .build()] - }); - - SIGNALS.as_ref() - } - fn dispose(&self) { if let Some(timer) = self.timer.take() { timer.cancel(); @@ -138,6 +105,18 @@ mod imp { source_id.remove(); } } + + crate::derived_properties!(); + + fn signals() -> &'static [glib::subclass::Signal] { + static SIGNALS: Lazy> = Lazy::new(|| { + vec![Signal::builder("finished") + .param_types([BoxedResult::static_type()]) + .build()] + }); + + SIGNALS.as_ref() + } } } @@ -358,28 +337,6 @@ impl Recording { self.delete_file(); } - pub fn state(&self) -> State { - self.imp().state.get() - } - - pub fn connect_state_notify(&self, f: F) -> glib::SignalHandlerId - where - F: Fn(&Self) + 'static, - { - self.connect_notify_local(Some("state"), move |obj, _| f(obj)) - } - - pub fn duration(&self) -> gst::ClockTime { - self.imp().duration.get() - } - - pub fn connect_duration_notify(&self, f: F) -> glib::SignalHandlerId - where - F: Fn(&Self) + 'static, - { - self.connect_notify_local(Some("duration"), move |obj, _| f(obj)) - } - pub fn connect_finished(&self, f: F) -> glib::SignalHandlerId where F: Fn(&Self, &Result) + 'static, @@ -413,7 +370,7 @@ impl Recording { } self.imp().state.replace(state); - self.notify("state"); + self.notify_state(); } fn pipeline(&self) -> &gst::Pipeline { @@ -467,7 +424,7 @@ impl Recording { } self.imp().duration.set(clock_time); - self.notify("duration"); + self.notify_duration(); } fn handle_bus_message(&self, message: &gst::Message) -> glib::Continue { @@ -640,36 +597,3 @@ async fn new_screencast_session( Ok((screencast_session, streams, restore_token, fd)) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn duration() { - let recording = Recording::new(); - assert_eq!(recording.duration(), gst::ClockTime::ZERO); - assert_eq!( - recording.property::("duration"), - gst::ClockTime::ZERO - ); - assert_eq!( - recording.property::("duration"), - gst::ClockTime::ZERO.into_glib() - ); - - recording - .imp() - .duration - .set(gst::ClockTime::from_seconds(3)); - assert_eq!(recording.duration(), gst::ClockTime::from_seconds(3)); - assert_eq!( - recording.property::("duration"), - gst::ClockTime::from_seconds(3) - ); - assert_eq!( - recording.property::("duration"), - gst::ClockTime::from_seconds(3).into_glib() - ); - } -} diff --git a/src/toggle_button.rs b/src/toggle_button.rs index 899f983d..ec919f14 100644 --- a/src/toggle_button.rs +++ b/src/toggle_button.rs @@ -4,13 +4,21 @@ use std::cell::RefCell; mod imp { use super::*; - use once_cell::sync::Lazy; - #[derive(Debug, Default)] + #[derive(Debug, Default, glib::Properties)] + #[properties(wrapper_type = super::ToggleButton)] pub struct ToggleButton { + /// Icon name to show on un-toggled state + #[property(get, set = Self::set_default_icon_name, explicit_notify)] pub(super) default_icon_name: RefCell, + /// Icon name to show on toggled state + #[property(get, set = Self::set_toggled_icon_name, explicit_notify)] pub(super) toggled_icon_name: RefCell, + /// Tooltip text to show on un-toggled state + #[property(get, set = Self::set_default_tooltip_text, explicit_notify)] pub(super) default_tooltip_text: RefCell, + /// Tooltip text to show on toggled state + #[property(get, set = Self::set_toggled_tooltip_text, explicit_notify)] pub(super) toggled_tooltip_text: RefCell, } @@ -22,74 +30,7 @@ mod imp { } impl ObjectImpl for ToggleButton { - fn properties() -> &'static [glib::ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { - vec![ - // Icon name to show on un-toggled state - glib::ParamSpecString::builder("default-icon-name") - .explicit_notify() - .build(), - // Icon name to show on toggled state - glib::ParamSpecString::builder("toggled-icon-name") - .explicit_notify() - .build(), - // Tooltip text to show on un-toggled state - glib::ParamSpecString::builder("default-tooltip-text") - .explicit_notify() - .build(), - // Tooltip text to show on toggled state - glib::ParamSpecString::builder("toggled-tooltip-text") - .explicit_notify() - .build(), - glib::ParamSpecOverride::for_class::("icon-name"), - glib::ParamSpecOverride::for_class::("tooltip-text"), - ] - }); - - PROPERTIES.as_ref() - } - - fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { - let obj = self.obj(); - - match pspec.name() { - "default-icon-name" => { - let default_icon_name = value.get().unwrap(); - obj.set_default_icon_name(default_icon_name); - } - "toggled-icon-name" => { - let toggled_icon_name = value.get().unwrap(); - obj.set_toggled_icon_name(toggled_icon_name); - } - "default-tooltip-text" => { - let default_tooltip_text = value.get().unwrap(); - obj.set_default_tooltip_text(default_tooltip_text); - } - "toggled-tooltip-text" => { - let toggled_tooltip_text = value.get().unwrap(); - obj.set_toggled_tooltip_text(toggled_tooltip_text); - } - "icon-name" | "tooltip-text" => { - panic!( - "KoohaToggleButton does not support `{}` property", - pspec.name() - ); - } - _ => unimplemented!(), - } - } - - fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { - let obj = self.obj(); - - match pspec.name() { - "default-icon-name" => obj.default_icon_name().to_value(), - "toggled-icon-name" => obj.toggled_icon_name().to_value(), - "default-tooltip-text" => obj.default_tooltip_text().to_value(), - "toggled-tooltip-text" => obj.toggled_tooltip_text().to_value(), - _ => unimplemented!(), - } - } + crate::derived_properties!(); } impl WidgetImpl for ToggleButton {} @@ -105,83 +46,73 @@ mod imp { self.parent_toggled(); } } -} -glib::wrapper! { - /// A toggle button that shows different icons and tooltips depending on the state. - /// - /// Note: `icon-name` and `tooltip-text` must not be set directly. - pub struct ToggleButton(ObjectSubclass) - @extends gtk::Widget, gtk::Button, gtk::ToggleButton; -} + impl ToggleButton { + fn set_default_icon_name(&self, default_icon_name: &str) { + let obj = self.obj(); -impl ToggleButton { - pub fn new() -> Self { - glib::Object::builder().build() - } + if default_icon_name == obj.default_icon_name().as_str() { + return; + } - pub fn set_default_icon_name(&self, default_icon_name: &str) { - if default_icon_name == self.default_icon_name().as_str() { - return; + self.default_icon_name + .replace(default_icon_name.to_string()); + obj.update_icon_name(); + obj.notify_default_icon_name(); } - self.imp() - .default_icon_name - .replace(default_icon_name.to_string()); - self.update_icon_name(); - self.notify("default-icon-name"); - } + fn set_toggled_icon_name(&self, toggled_icon_name: &str) { + let obj = self.obj(); - pub fn default_icon_name(&self) -> String { - self.imp().default_icon_name.borrow().clone() - } + if toggled_icon_name == obj.toggled_icon_name().as_str() { + return; + } - pub fn set_toggled_icon_name(&self, toggled_icon_name: &str) { - if toggled_icon_name == self.toggled_icon_name().as_str() { - return; + self.toggled_icon_name + .replace(toggled_icon_name.to_string()); + obj.update_icon_name(); + obj.notify_toggled_icon_name(); } - self.imp() - .toggled_icon_name - .replace(toggled_icon_name.to_string()); - self.update_icon_name(); - self.notify("toggled-icon-name"); - } + fn set_default_tooltip_text(&self, default_tooltip_text: &str) { + let obj = self.obj(); - pub fn toggled_icon_name(&self) -> String { - self.imp().toggled_icon_name.borrow().clone() - } + if default_tooltip_text == obj.default_tooltip_text().as_str() { + return; + } - pub fn set_default_tooltip_text(&self, default_tooltip_text: &str) { - if default_tooltip_text == self.default_tooltip_text().as_str() { - return; + self.default_tooltip_text + .replace(default_tooltip_text.to_string()); + obj.update_tooltip_text(); + obj.notify_default_tooltip_text(); } - self.imp() - .default_tooltip_text - .replace(default_tooltip_text.to_string()); - self.update_tooltip_text(); - self.notify("default-tooltip-text"); - } + fn set_toggled_tooltip_text(&self, toggled_tooltip_text: &str) { + let obj = self.obj(); - pub fn default_tooltip_text(&self) -> String { - self.imp().default_tooltip_text.borrow().clone() - } + if toggled_tooltip_text == obj.toggled_tooltip_text().as_str() { + return; + } - pub fn set_toggled_tooltip_text(&self, toggled_tooltip_text: &str) { - if toggled_tooltip_text == self.toggled_tooltip_text().as_str() { - return; + self.toggled_tooltip_text + .replace(toggled_tooltip_text.to_string()); + obj.update_tooltip_text(); + obj.notify_toggled_tooltip_text(); } - - self.imp() - .toggled_tooltip_text - .replace(toggled_tooltip_text.to_string()); - self.update_tooltip_text(); - self.notify("toggled-tooltip-text"); } +} - pub fn toggled_tooltip_text(&self) -> String { - self.imp().toggled_tooltip_text.borrow().clone() +glib::wrapper! { + /// A toggle button that shows different icons and tooltips depending on the state. + /// + /// Note: `icon-name` and `tooltip-text` must not be set directly. + pub struct ToggleButton(ObjectSubclass) + @extends gtk::Widget, gtk::Button, gtk::ToggleButton; +} + +impl ToggleButton { + pub fn new() -> Self { + glib::Object::builder().build() } fn update_icon_name(&self) {