2023-06-22 23:07:40 +01:00
|
|
|
use crate::dynamic_value::{dynamic_string, DynamicBool};
|
2023-04-21 23:51:29 +01:00
|
|
|
use crate::script::{Script, ScriptInput};
|
2023-12-17 23:51:43 +00:00
|
|
|
use glib::Propagation;
|
2023-04-21 23:51:29 +01:00
|
|
|
use gtk::gdk::ScrollDirection;
|
|
|
|
use gtk::prelude::*;
|
2023-04-22 14:49:15 +01:00
|
|
|
use gtk::{EventBox, Orientation, Revealer, RevealerTransitionType};
|
2023-04-21 23:51:29 +01:00
|
|
|
use serde::Deserialize;
|
|
|
|
use tracing::trace;
|
|
|
|
|
2024-05-19 15:16:01 +01:00
|
|
|
/// The following are module-level options which are present on **all** modules.
|
|
|
|
///
|
|
|
|
/// Each module also provides options specific to its type.
|
|
|
|
/// For details on those, check the relevant module documentation.
|
|
|
|
///
|
|
|
|
/// For information on the Script type, and embedding scripts in strings,
|
|
|
|
/// see [here](script).
|
|
|
|
/// For information on styling, please see the [styling guide](styling-guide).
|
2023-05-06 00:40:06 +01:00
|
|
|
#[derive(Debug, Default, Deserialize, Clone)]
|
2024-05-10 22:40:00 +01:00
|
|
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
2023-04-21 23:51:29 +01:00
|
|
|
pub struct CommonConfig {
|
2024-05-19 15:16:01 +01:00
|
|
|
/// Sets the unique widget name,
|
|
|
|
/// allowing you to target it in CSS using `#name`.
|
|
|
|
///
|
|
|
|
/// It is best practise (although not required) to ensure that the value is
|
|
|
|
/// globally unique throughout the Ironbar instance
|
|
|
|
/// to avoid clashes.
|
|
|
|
///
|
|
|
|
/// **Default**: `null`
|
2023-05-06 00:40:06 +01:00
|
|
|
pub name: Option<String>,
|
|
|
|
|
2024-05-19 15:16:01 +01:00
|
|
|
/// Sets one or more CSS classes,
|
|
|
|
/// allowing you to target it in CSS using `.class`.
|
|
|
|
///
|
|
|
|
/// Unlike [name](#name), the `class` property is not expected to be unique.
|
|
|
|
///
|
|
|
|
/// **Default**: `null`
|
|
|
|
pub class: Option<String>,
|
|
|
|
|
|
|
|
/// Shows this text on hover.
|
|
|
|
/// Supports embedding scripts between `{{double braces}}`.
|
|
|
|
///
|
|
|
|
/// Note that full dynamic string support is not currently supported.
|
|
|
|
///
|
|
|
|
/// **Default**: `null`
|
|
|
|
pub tooltip: Option<String>,
|
|
|
|
|
|
|
|
/// Shows the module only if the dynamic boolean evaluates to true.
|
|
|
|
///
|
|
|
|
/// This allows for modules to be dynamically shown or hidden
|
|
|
|
/// based on custom events.
|
|
|
|
///
|
|
|
|
/// **Default**: `null`
|
2023-06-22 23:07:40 +01:00
|
|
|
pub show_if: Option<DynamicBool>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// The transition animation to use when showing/hiding the widget.
|
|
|
|
///
|
|
|
|
/// Note this has no effect if `show_if` is not configured.
|
|
|
|
///
|
|
|
|
/// **Valid options**: `slide_start`, `slide_end`, `crossfade`, `none`
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `slide_start`
|
2023-04-22 14:49:15 +01:00
|
|
|
pub transition_type: Option<TransitionType>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// The length in milliseconds
|
|
|
|
/// of the transition animation to use when showing/hiding the widget.
|
|
|
|
///
|
|
|
|
/// Note this has no effect if `show_if` is not configured.
|
|
|
|
///
|
|
|
|
/// **Default**: `250`
|
2023-04-22 14:49:15 +01:00
|
|
|
pub transition_duration: Option<u32>,
|
2023-04-21 23:51:29 +01:00
|
|
|
|
2024-05-19 15:16:01 +01:00
|
|
|
/// A [script](scripts) to run when the module is left-clicked.
|
|
|
|
///
|
|
|
|
/// **Supported script types**: `oneshot`.
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `null`
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```corn
|
|
|
|
/// { on_click_left = "echo 'event' >> log.txt" }
|
|
|
|
/// ```
|
2023-04-21 23:51:29 +01:00
|
|
|
pub on_click_left: Option<ScriptInput>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// A [script](scripts) to run when the module is right-clicked.
|
|
|
|
///
|
|
|
|
/// **Supported script types**: `oneshot`.
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `null`
|
|
|
|
/// /// # Example
|
|
|
|
///
|
|
|
|
/// ```corn
|
|
|
|
/// { on_click_right = "echo 'event' >> log.txt" }
|
|
|
|
/// ```
|
2023-04-21 23:51:29 +01:00
|
|
|
pub on_click_right: Option<ScriptInput>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// A [script](scripts) to run when the module is middle-clicked.
|
|
|
|
///
|
|
|
|
/// **Supported script types**: `oneshot`.
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `null`
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```corn
|
|
|
|
/// { on_click_middle = "echo 'event' >> log.txt" }
|
|
|
|
/// ```
|
2023-04-21 23:51:29 +01:00
|
|
|
pub on_click_middle: Option<ScriptInput>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// A [script](scripts) to run when the module is scrolled up on.
|
|
|
|
///
|
|
|
|
/// **Supported script types**: `oneshot`.
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `null`
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```corn
|
|
|
|
/// { on_scroll_up = "echo 'event' >> log.txt" }
|
|
|
|
/// ```
|
2023-04-21 23:51:29 +01:00
|
|
|
pub on_scroll_up: Option<ScriptInput>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// A [script](scripts) to run when the module is scrolled down on.
|
|
|
|
///
|
|
|
|
/// **Supported script types**: `oneshot`.
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `null`
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```corn
|
|
|
|
/// { on_scroll_down = "echo 'event' >> log.txt" }
|
|
|
|
/// ```
|
2023-04-21 23:51:29 +01:00
|
|
|
pub on_scroll_down: Option<ScriptInput>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// A [script](scripts) to run when the cursor begins hovering over the module.
|
|
|
|
///
|
|
|
|
/// **Supported script types**: `oneshot`.
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `null`
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```corn
|
|
|
|
/// { on_mouse_enter = "echo 'event' >> log.txt" }
|
|
|
|
/// ```
|
2023-04-21 23:51:29 +01:00
|
|
|
pub on_mouse_enter: Option<ScriptInput>,
|
2024-05-19 15:16:01 +01:00
|
|
|
|
|
|
|
/// A [script](scripts) to run when the cursor stops hovering over the module.
|
|
|
|
///
|
|
|
|
/// **Supported script types**: `oneshot`.
|
|
|
|
/// <br>
|
|
|
|
/// **Default**: `null`
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```corn
|
|
|
|
/// { on_mouse_exit = "echo 'event' >> log.txt" }
|
|
|
|
/// ```
|
2023-04-21 23:51:29 +01:00
|
|
|
pub on_mouse_exit: Option<ScriptInput>,
|
|
|
|
|
2024-05-19 15:16:01 +01:00
|
|
|
/// Prevents the popup from opening on-click for this widget.
|
2024-04-01 15:37:11 +01:00
|
|
|
#[serde(default)]
|
2024-04-01 01:30:19 +01:00
|
|
|
pub disable_popup: bool,
|
2023-04-21 23:51:29 +01:00
|
|
|
}
|
|
|
|
|
2023-04-22 14:49:15 +01:00
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
#[serde(rename_all = "snake_case")]
|
2024-05-10 22:40:00 +01:00
|
|
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
2023-04-22 14:49:15 +01:00
|
|
|
pub enum TransitionType {
|
|
|
|
None,
|
|
|
|
Crossfade,
|
|
|
|
SlideStart,
|
|
|
|
SlideEnd,
|
|
|
|
}
|
|
|
|
|
2024-04-05 13:28:47 -04:00
|
|
|
#[derive(Debug, Default, Deserialize, Clone, Copy)]
|
2024-04-02 23:04:48 -04:00
|
|
|
#[serde(rename_all = "snake_case")]
|
2024-05-10 22:40:00 +01:00
|
|
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
2024-04-02 23:04:48 -04:00
|
|
|
pub enum ModuleOrientation {
|
2024-04-05 13:28:47 -04:00
|
|
|
#[default]
|
2024-04-04 16:12:45 -04:00
|
|
|
#[serde(alias = "h")]
|
2024-04-02 23:04:48 -04:00
|
|
|
Horizontal,
|
2024-04-04 16:12:45 -04:00
|
|
|
#[serde(alias = "v")]
|
2024-04-02 23:04:48 -04:00
|
|
|
Vertical,
|
|
|
|
}
|
|
|
|
|
2024-04-04 16:12:45 -04:00
|
|
|
impl ModuleOrientation {
|
2024-04-05 13:28:47 -04:00
|
|
|
pub const fn to_angle(self) -> f64 {
|
2024-04-04 16:12:45 -04:00
|
|
|
match self {
|
|
|
|
Self::Horizontal => 0.0,
|
2024-04-05 13:28:47 -04:00
|
|
|
Self::Vertical => 90.0,
|
2024-04-04 16:12:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ModuleOrientation> for Orientation {
|
|
|
|
fn from(o: ModuleOrientation) -> Self {
|
|
|
|
match o {
|
2024-04-05 13:28:47 -04:00
|
|
|
ModuleOrientation::Horizontal => Self::Horizontal,
|
|
|
|
ModuleOrientation::Vertical => Self::Vertical,
|
2024-04-04 16:12:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-22 14:49:15 +01:00
|
|
|
impl TransitionType {
|
2023-04-29 22:08:38 +01:00
|
|
|
pub const fn to_revealer_transition_type(
|
|
|
|
&self,
|
|
|
|
orientation: Orientation,
|
|
|
|
) -> RevealerTransitionType {
|
2023-04-22 14:49:15 +01:00
|
|
|
match (self, orientation) {
|
2023-04-29 22:08:38 +01:00
|
|
|
(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,
|
2023-04-22 14:49:15 +01:00
|
|
|
_ => RevealerTransitionType::None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-21 23:51:29 +01:00
|
|
|
impl CommonConfig {
|
|
|
|
/// Configures the module's container according to the common config options.
|
2023-05-06 00:40:06 +01:00
|
|
|
pub fn install_events(mut self, container: &EventBox, revealer: &Revealer) {
|
2023-04-22 14:49:15 +01:00
|
|
|
self.install_show_if(container, revealer);
|
2023-04-21 23:51:29 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-12-17 23:51:43 +00:00
|
|
|
Propagation::Proceed
|
2023-04-21 23:51:29 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-12-17 23:51:43 +00:00
|
|
|
Propagation::Proceed
|
2023-04-21 23:51:29 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
macro_rules! install_oneshot {
|
|
|
|
($option:expr, $method:ident) => {
|
|
|
|
$option.map(Script::new_polling).map(|script| {
|
|
|
|
container.$method(move |_, _| {
|
|
|
|
script.run_as_oneshot(None);
|
2023-12-17 23:51:43 +00:00
|
|
|
Propagation::Proceed
|
2023-04-21 23:51:29 +01:00
|
|
|
});
|
|
|
|
})
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2023-06-22 23:07:40 +01:00
|
|
|
dynamic_string(&tooltip, move |string| {
|
2023-04-21 23:51:29 +01:00
|
|
|
container.set_tooltip_text(Some(&string));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-22 14:49:15 +01:00
|
|
|
fn install_show_if(&mut self, container: &EventBox, revealer: &Revealer) {
|
2023-04-21 23:51:29 +01:00
|
|
|
self.show_if.take().map_or_else(
|
|
|
|
|| {
|
|
|
|
container.show_all();
|
|
|
|
},
|
|
|
|
|show_if| {
|
|
|
|
let container = container.clone();
|
2023-04-22 14:49:15 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
let revealer = revealer.clone();
|
|
|
|
let container = container.clone();
|
|
|
|
|
2023-06-22 23:07:40 +01:00
|
|
|
show_if.subscribe(move |success| {
|
2023-04-22 14:49:15 +01:00
|
|
|
if success {
|
|
|
|
container.show_all();
|
|
|
|
}
|
|
|
|
revealer.set_reveal_child(success);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
revealer.connect_child_revealed_notify(move |revealer| {
|
|
|
|
if !revealer.reveals_child() {
|
2023-04-29 22:08:38 +01:00
|
|
|
container.hide();
|
2023-04-22 14:49:15 +01:00
|
|
|
}
|
2023-04-21 23:51:29 +01:00
|
|
|
});
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|