use crate::dynamic_value::{dynamic_string, DynamicBool}; use crate::script::{Script, ScriptInput}; use gtk::gdk::ScrollDirection; use gtk::prelude::*; use gtk::{EventBox, Orientation, Revealer, RevealerTransitionType}; use serde::Deserialize; use tracing::trace; /// Common configuration options /// which can be set on every module. #[derive(Debug, Default, Deserialize, Clone)] pub struct CommonConfig { pub class: Option, pub name: Option, pub show_if: Option, pub transition_type: Option, pub transition_duration: Option, pub on_click_left: Option, pub on_click_right: Option, pub on_click_middle: Option, pub on_scroll_up: Option, pub on_scroll_down: Option, pub on_mouse_enter: Option, pub on_mouse_exit: Option, pub tooltip: Option, } #[derive(Debug, Deserialize, Clone)] #[serde(rename_all = "snake_case")] pub enum TransitionType { None, Crossfade, SlideStart, SlideEnd, } impl TransitionType { pub const fn to_revealer_transition_type( &self, orientation: Orientation, ) -> RevealerTransitionType { match (self, orientation) { (Self::SlideStart, Orientation::Horizontal) => RevealerTransitionType::SlideLeft, (Self::SlideStart, Orientation::Vertical) => RevealerTransitionType::SlideUp, (Self::SlideEnd, Orientation::Horizontal) => RevealerTransitionType::SlideRight, (Self::SlideEnd, Orientation::Vertical) => RevealerTransitionType::SlideDown, (Self::Crossfade, _) => RevealerTransitionType::Crossfade, _ => RevealerTransitionType::None, } } } impl CommonConfig { /// Configures the module's container according to the common config options. pub fn install_events(mut self, container: &EventBox, revealer: &Revealer) { self.install_show_if(container, revealer); let left_click_script = self.on_click_left.map(Script::new_polling); let middle_click_script = self.on_click_middle.map(Script::new_polling); let right_click_script = self.on_click_right.map(Script::new_polling); container.connect_button_press_event(move |_, event| { let script = match event.button() { 1 => left_click_script.as_ref(), 2 => middle_click_script.as_ref(), 3 => right_click_script.as_ref(), _ => None, }; if let Some(script) = script { trace!("Running on-click script: {}", event.button()); script.run_as_oneshot(None); } Inhibit(false) }); let scroll_up_script = self.on_scroll_up.map(Script::new_polling); let scroll_down_script = self.on_scroll_down.map(Script::new_polling); container.connect_scroll_event(move |_, event| { let script = match event.direction() { ScrollDirection::Up => scroll_up_script.as_ref(), ScrollDirection::Down => scroll_down_script.as_ref(), _ => None, }; if let Some(script) = script { trace!("Running on-scroll script: {}", event.direction()); script.run_as_oneshot(None); } Inhibit(false) }); macro_rules! install_oneshot { ($option:expr, $method:ident) => { $option.map(Script::new_polling).map(|script| { container.$method(move |_, _| { script.run_as_oneshot(None); Inhibit(false) }); }) }; } install_oneshot!(self.on_mouse_enter, connect_enter_notify_event); install_oneshot!(self.on_mouse_exit, connect_leave_notify_event); if let Some(tooltip) = self.tooltip { let container = container.clone(); dynamic_string(&tooltip, move |string| { container.set_tooltip_text(Some(&string)); Continue(true) }); } } fn install_show_if(&mut self, container: &EventBox, revealer: &Revealer) { self.show_if.take().map_or_else( || { container.show_all(); }, |show_if| { let container = container.clone(); { let revealer = revealer.clone(); let container = container.clone(); show_if.subscribe(move |success| { if success { container.show_all(); } revealer.set_reveal_child(success); Continue(true) }); } revealer.connect_child_revealed_notify(move |revealer| { if !revealer.reveals_child() { container.hide(); } }); }, ); } }