diff --git a/src/dynamic_string.rs b/src/dynamic_string.rs index 1ef0e2b..077e234 100644 --- a/src/dynamic_string.rs +++ b/src/dynamic_string.rs @@ -19,6 +19,15 @@ pub struct DynamicString; impl DynamicString { /// Creates a new dynamic string, based off the input template. /// Runs `f` with the compiled string each time one of the scripts updates. + /// + /// # Example + /// + /// ```rs + /// DynamicString::new(&text, move |string| { + /// label.set_markup(&string); + /// Continue(true) + /// }); + /// ``` pub fn new(input: &str, f: F) -> Self where F: FnMut(String) -> Continue + 'static, diff --git a/src/modules/custom/box.rs b/src/modules/custom/box.rs index 6ea0748..af541e6 100644 --- a/src/modules/custom/box.rs +++ b/src/modules/custom/box.rs @@ -1,4 +1,5 @@ use super::{try_get_orientation, CustomWidget, CustomWidgetContext, Widget}; +use crate::build; use gtk::prelude::*; use gtk::Orientation; use serde::Deserialize; @@ -15,21 +16,12 @@ impl CustomWidget for BoxWidget { type Widget = gtk::Box; fn into_widget(self, context: CustomWidgetContext) -> Self::Widget { - let mut builder = gtk::Box::builder(); - - if let Some(name) = self.name { - builder = builder.name(&name); - } + let container = build!(self, Self::Widget); if let Some(orientation) = self.orientation { - builder = builder - .orientation(try_get_orientation(&orientation).unwrap_or(Orientation::Horizontal)); - } - - let container = builder.build(); - - if let Some(class) = self.class { - container.style_context().add_class(&class); + container.set_orientation( + try_get_orientation(&orientation).unwrap_or(Orientation::Horizontal), + ) } if let Some(widgets) = self.widgets { diff --git a/src/modules/custom/button.rs b/src/modules/custom/button.rs index c8d2b51..bb67a88 100644 --- a/src/modules/custom/button.rs +++ b/src/modules/custom/button.rs @@ -1,6 +1,7 @@ use super::{CustomWidget, CustomWidgetContext, ExecEvent}; +use crate::dynamic_string::DynamicString; use crate::popup::Popup; -use crate::try_send; +use crate::{build, try_send}; use gtk::prelude::*; use gtk::{Button, Label}; use serde::Deserialize; @@ -17,13 +18,7 @@ impl CustomWidget for ButtonWidget { type Widget = Button; fn into_widget(self, context: CustomWidgetContext) -> Self::Widget { - let mut builder = Button::builder(); - - if let Some(name) = self.name { - builder = builder.name(name); - } - - let button = builder.build(); + let button = build!(self, Self::Widget); if let Some(text) = self.label { let label = Label::new(None); diff --git a/src/modules/custom/image.rs b/src/modules/custom/image.rs index ba56638..715bad0 100644 --- a/src/modules/custom/image.rs +++ b/src/modules/custom/image.rs @@ -1,4 +1,5 @@ use super::{CustomWidget, CustomWidgetContext}; +use crate::build; use crate::image::ImageProvider; use gtk::prelude::*; use gtk::Image; @@ -17,13 +18,7 @@ impl CustomWidget for ImageWidget { type Widget = Image; fn into_widget(self, context: CustomWidgetContext) -> Self::Widget { - let mut builder = Image::builder(); - - if let Some(name) = self.name { - builder = builder.name(&name); - } - - let gtk_image = builder.build(); + let gtk_image = build!(self, Self::Widget); if let Some(src) = self.src { let size = self.size.unwrap_or(32); @@ -34,10 +29,6 @@ impl CustomWidget for ImageWidget { } } - if let Some(class) = self.class { - gtk_image.style_context().add_class(&class); - } - gtk_image } } diff --git a/src/modules/custom/label.rs b/src/modules/custom/label.rs index 9badc5d..4b9d682 100644 --- a/src/modules/custom/label.rs +++ b/src/modules/custom/label.rs @@ -1,4 +1,5 @@ use super::{CustomWidget, CustomWidgetContext}; +use crate::build; use crate::dynamic_string::DynamicString; use gtk::prelude::*; use gtk::Label; @@ -8,31 +9,21 @@ use serde::Deserialize; pub struct LabelWidget { name: Option, class: Option, - label: Option, + label: String, } impl CustomWidget for LabelWidget { type Widget = Label; fn into_widget(self, _context: CustomWidgetContext) -> Self::Widget { - let mut builder = Label::builder().use_markup(true); + let label = build!(self, Self::Widget); - if let Some(name) = self.name { - builder = builder.name(name); - } - - let label = builder.build(); - - if let Some(class) = self.class { - label.style_context().add_class(&class); - } - - let text = self.label.map_or_else(String::new, |text| text); + label.set_use_markup(true); { let label = label.clone(); - DynamicString::new(&text, move |string| { - label.set_label(&string); + DynamicString::new(&self.label, move |string| { + label.set_markup(&string); Continue(true) }); } diff --git a/src/modules/custom/mod.rs b/src/modules/custom/mod.rs index 8bc6fa4..1ba97a6 100644 --- a/src/modules/custom/mod.rs +++ b/src/modules/custom/mod.rs @@ -37,15 +37,6 @@ pub struct CustomModule { pub common: Option, } -/// Attempts to parse an `Orientation` from `String` -fn try_get_orientation(orientation: &str) -> Result { - match orientation.to_lowercase().as_str() { - "horizontal" | "h" => Ok(Orientation::Horizontal), - "vertical" | "v" => Ok(Orientation::Vertical), - _ => Err(Report::msg("Invalid orientation string in config")), - } -} - #[derive(Debug, Deserialize, Clone)] #[serde(tag = "type", rename_all = "snake_case")] pub enum Widget { @@ -70,6 +61,50 @@ trait CustomWidget { fn into_widget(self, context: CustomWidgetContext) -> Self::Widget; } +/// Creates a new widget of type `ty`, +/// setting its name and class based on +/// the values available on `self`. +#[macro_export] +macro_rules! build { + ($self:ident, $ty:ty) => {{ + let mut builder = <$ty>::builder(); + + if let Some(name) = &$self.name { + builder = builder.name(name); + } + + let widget = builder.build(); + + if let Some(class) = &$self.class { + widget.style_context().add_class(class); + } + + widget + }}; +} + +/// Sets the widget length, +/// using either a width or height request +/// based on the bar's orientation. +pub fn set_length(widget: &W, length: i32, bar_orientation: Orientation) { + match bar_orientation { + Orientation::Horizontal => widget.set_width_request(length), + Orientation::Vertical => widget.set_height_request(length), + _ => {} + }; +} + +/// Attempts to parse an `Orientation` from `String`. +/// Will accept `horizontal`, `vertical`, `h` or `v`. +/// Ignores case. +fn try_get_orientation(orientation: &str) -> Result { + match orientation.to_lowercase().as_str() { + "horizontal" | "h" => Ok(Orientation::Horizontal), + "vertical" | "v" => Ok(Orientation::Vertical), + _ => Err(Report::msg("Invalid orientation string in config")), + } +} + impl Widget { /// Creates this widget and adds it to the parent container fn add_to(self, parent: >k::Box, context: CustomWidgetContext) { diff --git a/src/modules/custom/progress.rs b/src/modules/custom/progress.rs index 42e07d4..d6bd285 100644 --- a/src/modules/custom/progress.rs +++ b/src/modules/custom/progress.rs @@ -1,9 +1,10 @@ use super::{try_get_orientation, CustomWidget, CustomWidgetContext}; use crate::dynamic_string::DynamicString; +use crate::modules::custom::set_length; use crate::script::{OutputStream, Script, ScriptInput}; -use crate::send; +use crate::{build, send}; use gtk::prelude::*; -use gtk::{Orientation, ProgressBar}; +use gtk::ProgressBar; use serde::Deserialize; use tokio::spawn; use tracing::error; @@ -24,34 +25,20 @@ const fn default_max() -> f64 { 100.0 } -// TODO: Reduce duplication with slider, other widgets. impl CustomWidget for ProgressWidget { type Widget = ProgressBar; fn into_widget(self, context: CustomWidgetContext) -> Self::Widget { - let mut builder = ProgressBar::builder(); - - if let Some(name) = self.name { - builder = builder.name(name); - } + let progress = build!(self, Self::Widget); if let Some(orientation) = self.orientation { - builder = builder - .orientation(try_get_orientation(&orientation).unwrap_or(context.bar_orientation)); + progress.set_orientation( + try_get_orientation(&orientation).unwrap_or(context.bar_orientation), + ); } if let Some(length) = self.length { - builder = match context.bar_orientation { - Orientation::Horizontal => builder.width_request(length), - Orientation::Vertical => builder.height_request(length), - _ => builder, - } - } - - let progress = builder.build(); - - if let Some(class) = self.class { - progress.style_context().add_class(&class); + set_length(&progress, length, context.bar_orientation); } if let Some(value) = self.value { diff --git a/src/modules/custom/slider.rs b/src/modules/custom/slider.rs index 5f1aa95..030053d 100644 --- a/src/modules/custom/slider.rs +++ b/src/modules/custom/slider.rs @@ -1,9 +1,10 @@ use super::{try_get_orientation, CustomWidget, CustomWidgetContext, ExecEvent}; +use crate::modules::custom::set_length; use crate::popup::Popup; use crate::script::{OutputStream, Script, ScriptInput}; -use crate::{send, try_send}; +use crate::{build, send, try_send}; use gtk::prelude::*; -use gtk::{Orientation, Scale}; +use gtk::Scale; use serde::Deserialize; use std::cell::Cell; use tokio::spawn; @@ -35,33 +36,20 @@ impl CustomWidget for SliderWidget { type Widget = Scale; fn into_widget(self, context: CustomWidgetContext) -> Self::Widget { - let mut builder = Scale::builder(); - - if let Some(name) = self.name { - builder = builder.name(name); - } + let scale = build!(self, Self::Widget); if let Some(orientation) = self.orientation { - builder = builder - .orientation(try_get_orientation(&orientation).unwrap_or(context.bar_orientation)); + scale.set_orientation( + try_get_orientation(&orientation).unwrap_or(context.bar_orientation), + ); } if let Some(length) = self.length { - builder = match context.bar_orientation { - Orientation::Horizontal => builder.width_request(length), - Orientation::Vertical => builder.height_request(length), - _ => builder, - } + set_length(&scale, length, context.bar_orientation); } - let scale = builder.build(); - scale.set_range(self.min, self.max); - if let Some(class) = self.class { - scale.style_context().add_class(&class); - } - if let Some(on_change) = self.on_change { let min = self.min; let max = self.max;