From c20feb77b7391a5c3f74de6ae98da03c30b51081 Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Sat, 22 Mar 2025 19:19:47 +0000 Subject: [PATCH] feat: fully implement orientation/justify options Adds `orientation` and `justify` options to all modules and custom widgets where it makes sense to do so. Any modules without support document this. Widgets fully document the options inline where present for now. Resolves #296 --- docs/Configuration guide.md | 11 +++++- docs/modules/Custom.md | 13 ++++--- docs/modules/Network-Manager.md | 3 ++ docs/modules/Notifications.md | 2 + src/config/impl.rs | 2 +- src/config/layout.rs | 37 ++++++++++++++++++ src/config/mod.rs | 2 + src/image/gtk.rs | 62 ++++++++++++++++++++++-------- src/modules/clipboard.rs | 19 ++++++--- src/modules/clock.rs | 29 ++++---------- src/modules/custom/button.rs | 20 +++++----- src/modules/custom/label.rs | 30 +++++---------- src/modules/custom/mod.rs | 5 ++- src/modules/focused.rs | 15 ++++++-- src/modules/keyboard.rs | 32 ++++++++++----- src/modules/label.rs | 15 ++++++-- src/modules/launcher/item.rs | 19 ++++++--- src/modules/launcher/mod.rs | 17 +++++--- src/modules/launcher/pagination.rs | 12 +++--- src/modules/music/config.rs | 6 ++- src/modules/music/mod.rs | 29 ++++++++------ src/modules/script.rs | 15 ++++++-- src/modules/sway/mode.rs | 15 ++++++-- src/modules/sysinfo/mod.rs | 30 +++++++-------- src/modules/tray/mod.rs | 1 + src/modules/upower.rs | 12 +++++- src/modules/volume.rs | 10 ++++- src/modules/workspaces/button.rs | 6 +-- src/modules/workspaces/mod.rs | 9 ++++- 29 files changed, 317 insertions(+), 161 deletions(-) create mode 100644 src/config/layout.rs diff --git a/docs/Configuration guide.md b/docs/Configuration guide.md index 50682cf..a93b7c6 100644 --- a/docs/Configuration guide.md +++ b/docs/Configuration guide.md @@ -351,7 +351,14 @@ For information on the `Script` type, and embedding scripts in strings, see [her | Name | Type | Default | Description | |-----------|----------|---------|-----------------------------------------------------------------------------------| | `tooltip` | `string` | `null` | Shows this text on hover. Supports embedding scripts between `{{double braces}}`. | -| `name` | `string` | `null` | Sets the unique widget name, allowing you to style it using `#name`. | -| `class` | `string` | `null` | Sets one or more CSS classes, allowing you to style it using `.class`. | +| `name` | `string` | `null` | The unique widget name, allowing you to style it using `#name`. | +| `class` | `string` | `null` | One or more CSS classes, allowing you to style it using `.class`. | For more information on styling, please see the [styling guide](styling-guide). + +#### Formatting + +| Name | Type | Default | Description | +|---------------|--------------------------------------------------------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| +| `orientation` | `horizontal` or `vertical` (shorthand: `'h'` or `'v'`) | `horizontal` or `vertical` | The direction in which the widget and its text are laid out. Some modules additionally provide a `direction` option to provide further control. | +| `justify` | `left`, `right`, `center`, `fill` | `left` | The justification (alignment) of the widget text shown on the bar. | diff --git a/docs/modules/Custom.md b/docs/modules/Custom.md index d93d7c7..ca9c430 100644 --- a/docs/modules/Custom.md +++ b/docs/modules/Custom.md @@ -71,12 +71,13 @@ A clickable button, which can run a command when clicked. > Type `button` -| Name | Type | Default | Description | -|------------|-------------------------------------------------|---------|--------------------------------------------------------------------------------------------------| -| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. Ignored if `widgets` is set. | -| `widgets` | `(Module or Widget)[]` | `[]` | List of modules/widgets to add to this button. | -| `on_click` | `string [command]` | `null` | Command to execute. More on this [below](#commands). | -| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the button. | +| Name | Type | Default | Description | +|---------------|------------------------------------------------------------|----------------|--------------------------------------------------------------------------------------------------| +| `label` | [Dynamic String](dynamic-values#dynamic-string) | `null` | Widget text label. Pango markup and embedded scripts are supported. Ignored if `widgets` is set. | +| `widgets` | `(Module or Widget)[]` | `[]` | List of modules/widgets to add to this button. | +| `on_click` | `string [command]` | `null` | Command to execute. More on this [below](#commands). | +| `orientation` | `'horizontal'` or `'vertical'` (shorthand: `'h'` or `'v'`) | `'horizontal'` | Orientation of the label text. | +| `justify` | `'left'`, `'right'`, `'center'`, or `'fill'` | `'left'` | Justification (alignment) of the label text. | #### Image diff --git a/docs/modules/Network-Manager.md b/docs/modules/Network-Manager.md index 6dfadae..689de6e 100644 --- a/docs/modules/Network-Manager.md +++ b/docs/modules/Network-Manager.md @@ -17,6 +17,9 @@ Supports wired ethernet, wifi, cellular data and VPN connections among others. |-------------|-----------|---------|-------------------------| | `icon_size` | `integer` | `24` | Size to render icon at. | +> [!NOTE] +> This module does not support module-level [layout options](module-level-options#layout). +
JSON diff --git a/docs/modules/Notifications.md b/docs/modules/Notifications.md index 33617e3..2648432 100644 --- a/docs/modules/Notifications.md +++ b/docs/modules/Notifications.md @@ -21,6 +21,8 @@ Clicking the widget opens the SwayNC panel. | `icons.open_some` | `string` | `󱥁` | Icon to show when the panel is open, with notifications. | | `icons.open_dnd` | `string` | `󱅮` | Icon to show when the panel is open, with DnD enabled. Takes higher priority than count-based icons. | +> [!NOTE] +> This module does not support module-level [layout options](module-level-options#layout).
JSON diff --git a/src/config/impl.rs b/src/config/impl.rs index 696db62..dd64599 100644 --- a/src/config/impl.rs +++ b/src/config/impl.rs @@ -77,7 +77,7 @@ impl BarPosition { /// Gets the angle that label text should be displayed at /// based on this position. - pub const fn get_angle(self) -> f64 { + pub const fn angle(self) -> f64 { match self { Self::Top | Self::Bottom => 0.0, Self::Left => 90.0, diff --git a/src/config/layout.rs b/src/config/layout.rs new file mode 100644 index 0000000..bc2601c --- /dev/null +++ b/src/config/layout.rs @@ -0,0 +1,37 @@ +use crate::config::{ModuleJustification, ModuleOrientation}; +use crate::modules::ModuleInfo; +use serde::Deserialize; + +#[derive(Clone, Debug, Deserialize, Default)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct LayoutConfig { + /// The orientation to display the widget contents. + /// Setting to vertical will rotate text 90 degrees. + /// + /// **Valid options**: `horizontal`, `vertical` + ///
+ /// **Default**: `horizontal` + orientation: Option, + + /// The justification (alignment) of the widget text shown on the bar. + /// + /// **Valid options**: `left`, `right`, `center`, `fill` + ///
+ /// **Default**: `left` + #[serde(default)] + pub justify: ModuleJustification, +} + +impl LayoutConfig { + pub fn orientation(&self, info: &ModuleInfo) -> gtk::Orientation { + self.orientation + .map(ModuleOrientation::into) + .unwrap_or(info.bar_position.orientation()) + } + + pub fn angle(&self, info: &ModuleInfo) -> f64 { + self.orientation + .map(ModuleOrientation::to_angle) + .unwrap_or(info.bar_position.angle()) + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index f4666d7..ae658da 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,5 +1,6 @@ mod common; mod r#impl; +mod layout; mod truncate; #[cfg(feature = "cairo")] @@ -46,6 +47,7 @@ use std::collections::HashMap; use schemars::JsonSchema; pub use self::common::{CommonConfig, ModuleJustification, ModuleOrientation, TransitionType}; +pub use self::layout::LayoutConfig; pub use self::truncate::{EllipsizeMode, TruncateMode}; #[derive(Debug, Deserialize, Clone)] diff --git a/src/image/gtk.rs b/src/image/gtk.rs index 6a59dcd..f2d2f83 100644 --- a/src/image/gtk.rs +++ b/src/image/gtk.rs @@ -5,30 +5,54 @@ use gtk::{Button, IconTheme, Image, Label, Orientation}; use std::ops::Deref; #[cfg(any(feature = "music", feature = "workspaces", feature = "clipboard"))] -pub fn new_icon_button(input: &str, icon_theme: &IconTheme, size: i32) -> Button { - let button = Button::new(); +#[derive(Debug, Clone)] +pub struct IconButton { + button: Button, + label: Label, +} - if ImageProvider::is_definitely_image_input(input) { +#[cfg(any(feature = "music", feature = "workspaces", feature = "clipboard"))] +impl IconButton { + pub fn new(input: &str, icon_theme: &IconTheme, size: i32) -> Self { + let button = Button::new(); let image = Image::new(); - image.add_class("image"); - image.add_class("icon"); + let label = Label::new(Some(input)); - match ImageProvider::parse(input, icon_theme, false, size) - .map(|provider| provider.load_into_image(&image)) - { - Some(_) => { - button.set_image(Some(&image)); - button.set_always_show_image(true); - } - None => { - button.set_label(input); + if ImageProvider::is_definitely_image_input(input) { + image.add_class("image"); + image.add_class("icon"); + + match ImageProvider::parse(input, icon_theme, false, size) + .map(|provider| provider.load_into_image(&image)) + { + Some(_) => { + button.set_image(Some(&image)); + button.set_always_show_image(true); + } + None => { + button.set_child(Some(&label)); + label.show(); + } } + } else { + button.set_child(Some(&label)); + label.show(); } - } else { - button.set_label(input); + + Self { button, label } } - button + pub fn label(&self) -> &Label { + &self.label + } +} + +impl Deref for IconButton { + type Target = Button; + + fn deref(&self) -> &Self::Target { + &self.button + } } #[cfg(any(feature = "music", feature = "keyboard"))] @@ -98,6 +122,10 @@ impl IconLabel { image.hide(); } } + + pub fn label(&self) -> &Label { + &self.label + } } impl Deref for IconLabel { diff --git a/src/modules/clipboard.rs b/src/modules/clipboard.rs index 9dbd68c..22648ed 100644 --- a/src/modules/clipboard.rs +++ b/src/modules/clipboard.rs @@ -1,8 +1,9 @@ use crate::clients::clipboard::{self, ClipboardEvent}; use crate::clients::wayland::{ClipboardItem, ClipboardValue}; -use crate::config::{CommonConfig, TruncateMode}; +use crate::config::{CommonConfig, LayoutConfig, TruncateMode}; +use crate::gtk_helpers::IronbarGtkExt; use crate::gtk_helpers::IronbarLabelExt; -use crate::image::new_icon_button; +use crate::image::IconButton; use crate::modules::{ Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext, }; @@ -14,6 +15,7 @@ use gtk::prelude::*; use gtk::{Button, EventBox, Image, Label, Orientation, RadioButton, Widget}; use serde::Deserialize; use std::collections::HashMap; +use std::ops::Deref; use tokio::sync::{broadcast, mpsc}; use tracing::{debug, error}; @@ -47,6 +49,10 @@ pub struct ClipboardModule { /// **Default**: `null` truncate: Option, + /// See [layout options](module-level-options#layout) + #[serde(default, flatten)] + layout: LayoutConfig, + /// See [common options](module-level-options#common-options). #[serde(flatten)] pub common: Option, @@ -142,8 +148,11 @@ impl Module