1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-08-16 22:31:03 +02:00

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
This commit is contained in:
Jake Stanger 2025-03-22 19:19:47 +00:00
parent 0855039bce
commit c20feb77b7
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
29 changed files with 317 additions and 161 deletions

View file

@ -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. |

View file

@ -72,11 +72,12 @@ 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. |
| `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

View file

@ -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).
<details>
<summary>JSON</summary>

View file

@ -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).
<details>
<summary>JSON</summary>

View file

@ -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,

37
src/config/layout.rs Normal file
View file

@ -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`
/// <br>
/// **Default**: `horizontal`
orientation: Option<ModuleOrientation>,
/// The justification (alignment) of the widget text shown on the bar.
///
/// **Valid options**: `left`, `right`, `center`, `fill`
/// <br>
/// **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())
}
}

View file

@ -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)]

View file

@ -5,11 +5,20 @@ 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 {
#[derive(Debug, Clone)]
pub struct IconButton {
button: Button,
label: Label,
}
#[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();
let label = Label::new(Some(input));
if ImageProvider::is_definitely_image_input(input) {
let image = Image::new();
image.add_class("image");
image.add_class("icon");
@ -21,14 +30,29 @@ pub fn new_icon_button(input: &str, icon_theme: &IconTheme, size: i32) -> Button
button.set_always_show_image(true);
}
None => {
button.set_label(input);
button.set_child(Some(&label));
label.show();
}
}
} else {
button.set_label(input);
button.set_child(Some(&label));
label.show();
}
button
Self { button, label }
}
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 {

View file

@ -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<TruncateMode>,
/// 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<CommonConfig>,
@ -142,8 +148,11 @@ impl Module<Button> for ClipboardModule {
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo,
) -> color_eyre::Result<ModuleParts<Button>> {
let button = new_icon_button(&self.icon, info.icon_theme, self.icon_size);
button.style_context().add_class("btn");
let button = IconButton::new(&self.icon, info.icon_theme, self.icon_size);
button.label().set_angle(self.layout.angle(info));
button.label().set_justify(self.layout.justify.into());
button.add_class("btn");
let tx = context.tx.clone();
button.connect_clicked(move |button| {
@ -155,7 +164,7 @@ impl Module<Button> for ClipboardModule {
.into_popup(context.controller_tx.clone(), rx, context, info)
.into_popup_parts(vec![&button]);
Ok(ModuleParts::new(button, popup))
Ok(ModuleParts::new(button.deref().clone(), popup))
}
fn into_popup(

View file

@ -8,7 +8,7 @@ use serde::Deserialize;
use tokio::sync::{broadcast, mpsc};
use tokio::time::sleep;
use crate::config::{CommonConfig, ModuleJustification, ModuleOrientation};
use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::IronbarGtkExt;
use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
@ -49,22 +49,9 @@ pub struct ClockModule {
#[serde(default = "default_locale")]
locale: String,
/// The orientation to display the widget contents.
/// Setting to vertical will rotate text 90 degrees.
///
/// **Valid options**: `horizontal`, `vertical`
/// <br>
/// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
/// The justification (alignment) of the date/time shown on the bar.
///
/// **Valid options**: `left`, `right`, `center`, `fill`
/// <br>
/// **Default**: `left`
#[serde(default)]
justify: ModuleJustification,
/// See [layout options](module-level-options#layout)
#[serde(default, flatten)]
layout: LayoutConfig,
/// See [common options](module-level-options#common-options).
#[serde(flatten)]
@ -77,9 +64,8 @@ impl Default for ClockModule {
format: default_format(),
format_popup: default_popup_format(),
locale: default_locale(),
orientation: ModuleOrientation::Horizontal,
layout: LayoutConfig::default(),
common: Some(CommonConfig::default()),
justify: ModuleJustification::Left,
}
}
}
@ -136,10 +122,11 @@ impl Module<Button> for ClockModule {
) -> Result<ModuleParts<Button>> {
let button = Button::new();
let label = Label::builder()
.angle(self.orientation.to_angle())
.angle(self.layout.angle(info))
.use_markup(true)
.justify(self.justify.into())
.justify(self.layout.justify.into())
.build();
button.add(&label);
let tx = context.tx.clone();

View file

@ -1,9 +1,9 @@
use gtk::prelude::*;
use gtk::{Button, Label, Orientation};
use gtk::{Button, Label};
use serde::Deserialize;
use super::{CustomWidget, CustomWidgetContext, ExecEvent, WidgetConfig};
use crate::config::ModuleOrientation;
use crate::config::LayoutConfig;
use crate::dynamic_value::dynamic_string;
use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::PopupButton;
@ -37,13 +37,9 @@ pub struct ButtonWidget {
/// **Default**: `null`
on_click: Option<String>,
/// Orientation of the button.
///
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
/// <br />
/// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
/// See [layout options](module-level-options#layout)
#[serde(default, flatten)]
layout: LayoutConfig,
/// Modules and widgets to add to this box.
///
@ -59,7 +55,7 @@ impl CustomWidget for ButtonWidget {
context.popup_buttons.borrow_mut().push(button.clone());
if let Some(widgets) = self.widgets {
let container = gtk::Box::new(Orientation::Horizontal, 0);
let container = gtk::Box::new(self.layout.orientation(context.info), 0);
for widget in widgets {
widget.widget.add_to(&container, &context, widget.common);
@ -70,7 +66,9 @@ impl CustomWidget for ButtonWidget {
let label = Label::new(None);
label.set_use_markup(true);
label.set_angle(self.orientation.to_angle());
if !context.is_popup {
label.set_angle(self.layout.angle(context.info));
}
button.add(&label);

View file

@ -4,7 +4,7 @@ use serde::Deserialize;
use super::{CustomWidget, CustomWidgetContext};
use crate::build;
use crate::config::{ModuleJustification, ModuleOrientation, TruncateMode};
use crate::config::{LayoutConfig, TruncateMode};
use crate::dynamic_value::dynamic_string;
use crate::gtk_helpers::IronbarLabelExt;
@ -28,22 +28,9 @@ pub struct LabelWidget {
/// **Required**
label: String,
/// Orientation of the label.
/// Setting to vertical will rotate text 90 degrees.
///
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
/// <br />
/// **Default**: `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
/// The justification (alignment) of the label text.
///
/// **Valid options**: `left`, `right`, `center`, `fill`
/// <br>
/// **Default**: `left`
#[serde(default)]
justify: ModuleJustification,
/// See [layout options](module-level-options#layout)
#[serde(default, flatten)]
layout: LayoutConfig,
/// See [truncate options](module-level-options#truncate-mode).
///
@ -54,11 +41,14 @@ pub struct LabelWidget {
impl CustomWidget for LabelWidget {
type Widget = Label;
fn into_widget(self, _context: CustomWidgetContext) -> Self::Widget {
fn into_widget(self, context: CustomWidgetContext) -> Self::Widget {
let label = build!(self, Self::Widget);
label.set_angle(self.orientation.to_angle());
label.set_justify(self.justify.into());
if !context.is_popup {
label.set_angle(self.layout.angle(context.info));
}
label.set_justify(self.layout.justify.into());
label.set_use_markup(true);
if let Some(truncate) = self.truncate {

View file

@ -91,6 +91,7 @@ struct CustomWidgetContext<'a> {
info: &'a ModuleInfo<'a>,
tx: &'a mpsc::Sender<ExecEvent>,
bar_orientation: Orientation,
is_popup: bool,
icon_theme: &'a IconTheme,
popup_buttons: Rc<RefCell<Vec<Button>>>,
module_factory: AnyModuleFactory,
@ -235,6 +236,7 @@ impl Module<gtk::Box> for CustomModule {
info,
tx: &context.controller_tx,
bar_orientation: orientation,
is_popup: false,
icon_theme: info.icon_theme,
popup_buttons: popup_buttons.clone(),
module_factory: BarModuleFactory::new(context.ironbar.clone(), context.popup.clone())
@ -287,7 +289,8 @@ impl Module<gtk::Box> for CustomModule {
let custom_context = CustomWidgetContext {
info,
tx: &tx,
bar_orientation: info.bar_position.orientation(),
bar_orientation: Orientation::Horizontal,
is_popup: true,
icon_theme: info.icon_theme,
popup_buttons: Rc::new(RefCell::new(vec![])),
module_factory: PopupModuleFactory::new(

View file

@ -1,5 +1,5 @@
use crate::clients::wayland::{self, ToplevelEvent};
use crate::config::{CommonConfig, TruncateMode};
use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use crate::gtk_helpers::IronbarGtkExt;
use crate::gtk_helpers::IronbarLabelExt;
use crate::image::ImageProvider;
@ -38,6 +38,10 @@ pub struct FocusedModule {
/// **Default**: `null`
truncate: Option<TruncateMode>,
/// 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<CommonConfig>,
@ -50,6 +54,7 @@ impl Default for FocusedModule {
show_title: crate::config::default_true(),
icon_size: default_icon_size(),
truncate: None,
layout: LayoutConfig::default(),
common: Some(CommonConfig::default()),
}
}
@ -132,7 +137,7 @@ impl Module<gtk::Box> for FocusedModule {
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo,
) -> Result<ModuleParts<gtk::Box>> {
let container = gtk::Box::new(info.bar_position.orientation(), 5);
let container = gtk::Box::new(self.layout.orientation(info), 5);
let icon = gtk::Image::new();
if self.show_icon {
@ -140,7 +145,11 @@ impl Module<gtk::Box> for FocusedModule {
container.add(&icon);
}
let label = Label::new(None);
let label = Label::builder()
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
label.add_class("label");
if let Some(truncate) = self.truncate {

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
use color_eyre::Result;
use color_eyre::eyre::Report;
use gtk::{Button, prelude::*};
use gtk::prelude::*;
use serde::Deserialize;
use tokio::sync::mpsc;
use tracing::{debug, trace};
@ -10,9 +10,9 @@ use tracing::{debug, trace};
use super::{Module, ModuleInfo, ModuleParts, WidgetContext};
use crate::clients::compositor::{self, KeyboardLayoutUpdate};
use crate::clients::libinput::{Event, Key, KeyEvent};
use crate::config::CommonConfig;
use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::IronbarGtkExt;
use crate::image::IconLabel;
use crate::image::{IconButton, IconLabel};
use crate::{glib_recv, module_impl, module_update, send_async, spawn, try_send};
#[derive(Debug, Deserialize, Clone)]
@ -61,6 +61,11 @@ pub struct KeyboardModule {
#[serde(default = "default_seat")]
seat: String,
// -- common --
/// 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<CommonConfig>,
@ -249,15 +254,22 @@ impl Module<gtk::Box> for KeyboardModule {
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo,
) -> Result<ModuleParts<gtk::Box>> {
let container = gtk::Box::new(info.bar_position.orientation(), 0);
let container = gtk::Box::new(self.layout.orientation(info), 0);
let caps = IconLabel::new(&self.icons.caps_off, info.icon_theme, self.icon_size);
let num = IconLabel::new(&self.icons.num_off, info.icon_theme, self.icon_size);
let scroll = IconLabel::new(&self.icons.scroll_off, info.icon_theme, self.icon_size);
let layout_button = Button::new();
let layout = IconLabel::new("", info.icon_theme, self.icon_size);
layout_button.add(&*layout);
caps.label().set_angle(self.layout.angle(info));
caps.label().set_justify(self.layout.justify.into());
num.label().set_angle(self.layout.angle(info));
num.label().set_justify(self.layout.justify.into());
scroll.label().set_angle(self.layout.angle(info));
scroll.label().set_justify(self.layout.justify.into());
let layout_button = IconButton::new("", info.icon_theme, self.icon_size);
if self.show_caps {
caps.add_class("key");
@ -278,8 +290,8 @@ impl Module<gtk::Box> for KeyboardModule {
}
if self.show_layout {
layout.add_class("layout");
container.add(&layout_button);
layout_button.add_class("layout");
container.add(&*layout_button);
}
{
@ -318,7 +330,7 @@ impl Module<gtk::Box> for KeyboardModule {
}
KeyboardUpdate::Layout(KeyboardLayoutUpdate(language)) => {
let text = icons.layout_map.get(&language).unwrap_or(&language);
layout.set_label(Some(text));
layout_button.set_label(text);
}
};

View file

@ -1,4 +1,4 @@
use crate::config::{CommonConfig, TruncateMode};
use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use crate::dynamic_value::dynamic_string;
use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
@ -23,6 +23,10 @@ pub struct LabelModule {
/// **Default**: `null`
truncate: Option<TruncateMode>,
/// 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<CommonConfig>,
@ -33,6 +37,7 @@ impl LabelModule {
Self {
label,
truncate: None,
layout: LayoutConfig::default(),
common: Some(CommonConfig::default()),
}
}
@ -61,9 +66,13 @@ impl Module<Label> for LabelModule {
fn into_widget(
self,
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
_info: &ModuleInfo,
info: &ModuleInfo,
) -> Result<ModuleParts<Label>> {
let label = Label::builder().use_markup(true).build();
let label = Label::builder()
.use_markup(true)
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
if let Some(truncate) = self.truncate {
label.truncate(truncate);

View file

@ -9,7 +9,7 @@ use crate::{read_lock, try_send};
use glib::Propagation;
use gtk::gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY};
use gtk::prelude::*;
use gtk::{Button, IconTheme, Image, Label, Orientation};
use gtk::{Align, Button, IconTheme, Image, Justification, Label, Orientation};
use indexmap::IndexMap;
use std::ops::Deref;
use std::rc::Rc;
@ -156,6 +156,9 @@ pub struct AppearanceOptions {
pub show_icons: bool,
pub icon_size: i32,
pub truncate: TruncateMode,
pub orientation: Orientation,
pub angle: f64,
pub justify: Justification,
}
impl ItemButton {
@ -167,11 +170,13 @@ impl ItemButton {
tx: &Sender<ModuleUpdateEvent<LauncherUpdate>>,
controller_tx: &Sender<ItemEvent>,
) -> Self {
let button = ImageTextButton::new();
let button = ImageTextButton::new(appearance.orientation);
if appearance.show_names {
button.label.set_label(&item.name);
button.label.truncate(appearance.truncate);
button.label.set_angle(appearance.angle);
button.label.set_justify(appearance.justify);
}
if appearance.show_icons {
@ -329,18 +334,20 @@ pub struct ImageTextButton {
}
impl ImageTextButton {
pub(crate) fn new() -> Self {
pub(crate) fn new(orientation: Orientation) -> Self {
let button = Button::new();
let container = gtk::Box::new(Orientation::Horizontal, 0);
let container = gtk::Box::new(orientation, 0);
let label = Label::new(None);
let image = Image::new();
button.add(&container);
container.add(&image);
container.add(&label);
button.add(&container);
container.set_halign(Align::Center);
container.set_valign(Align::Center);
ImageTextButton {
button,
label,

View file

@ -6,7 +6,7 @@ use self::item::{AppearanceOptions, Item, ItemButton, Window};
use self::open_state::OpenState;
use super::{Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext};
use crate::clients::wayland::{self, ToplevelEvent};
use crate::config::{CommonConfig, EllipsizeMode, TruncateMode};
use crate::config::{CommonConfig, EllipsizeMode, LayoutConfig, TruncateMode};
use crate::desktop_file::find_desktop_file;
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::modules::launcher::item::ImageTextButton;
@ -105,6 +105,10 @@ pub struct LauncherModule {
#[serde(default = "default_truncate_popup")]
truncate_popup: TruncateMode,
/// 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<CommonConfig>,
@ -451,13 +455,13 @@ impl Module<gtk::Box> for LauncherModule {
) -> crate::Result<ModuleParts<gtk::Box>> {
let icon_theme = info.icon_theme;
let container = gtk::Box::new(info.bar_position.orientation(), 0);
let container = gtk::Box::new(self.layout.orientation(info), 0);
let page_size = self.page_size;
let pagination = Pagination::new(
&container,
self.page_size,
info.bar_position.orientation(),
self.layout.orientation(info),
IconContext {
icon_back: &self.icons.page_back,
icon_fwd: &self.icons.page_forward,
@ -477,6 +481,9 @@ impl Module<gtk::Box> for LauncherModule {
show_icons: self.show_icons,
icon_size: self.icon_size,
truncate: self.truncate,
orientation: self.layout.orientation(info),
angle: self.layout.angle(info),
justify: self.layout.justify.into(),
};
let show_names = self.show_names;
@ -636,7 +643,7 @@ impl Module<gtk::Box> for LauncherModule {
.into_iter()
.map(|(_, win)| {
// TODO: Currently has a useless image
let button = ImageTextButton::new();
let button = ImageTextButton::new(Orientation::Horizontal);
button.set_height_request(40);
button.label.set_label(&win.name);
button.label.truncate(self.truncate_popup);
@ -662,7 +669,7 @@ impl Module<gtk::Box> for LauncherModule {
if let Some(buttons) = buttons.get_mut(&app_id) {
// TODO: Currently has a useless image
let button = ImageTextButton::new();
let button = ImageTextButton::new(Orientation::Horizontal);
button.set_height_request(40);
button.label.set_label(&win.name);
button.label.truncate(self.truncate_popup);

View file

@ -1,5 +1,5 @@
use crate::gtk_helpers::IronbarGtkExt;
use crate::image::new_icon_button;
use crate::image::IconButton;
use gtk::prelude::*;
use gtk::{Button, IconTheme, Orientation};
use std::cell::RefCell;
@ -29,13 +29,13 @@ impl Pagination {
) -> Self {
let scroll_box = gtk::Box::new(orientation, 0);
let scroll_back = new_icon_button(
let scroll_back = IconButton::new(
icon_context.icon_back,
icon_context.icon_theme,
icon_context.icon_size,
);
let scroll_fwd = new_icon_button(
let scroll_fwd = IconButton::new(
icon_context.icon_fwd,
icon_context.icon_theme,
icon_context.icon_size,
@ -48,8 +48,8 @@ impl Pagination {
scroll_back.add_class("btn-back");
scroll_fwd.add_class("btn-forward");
scroll_box.add(&scroll_back);
scroll_box.add(&scroll_fwd);
scroll_box.add(&*scroll_back);
scroll_box.add(&*scroll_fwd);
container.add(&scroll_box);
let offset = Rc::new(RefCell::new(1));
@ -103,7 +103,7 @@ impl Pagination {
offset,
controls_container: scroll_box,
btn_fwd: scroll_fwd,
btn_fwd: scroll_fwd.deref().clone(),
}
}

View file

@ -1,4 +1,4 @@
use crate::config::{CommonConfig, TruncateMode};
use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use dirs::{audio_dir, home_dir};
use serde::Deserialize;
use std::path::PathBuf;
@ -147,6 +147,10 @@ pub struct MusicModule {
/// **Default**: `null`
pub(crate) truncate: Option<TruncateMode>,
/// See [layout options](module-level-options#layout)
#[serde(default, flatten)]
pub(crate) layout: LayoutConfig,
/// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option<CommonConfig>,

View file

@ -17,7 +17,7 @@ use crate::clients::music::{
self, MusicClient, PlayerState, PlayerUpdate, ProgressTick, Status, Track,
};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::image::{IconLabel, ImageProvider, new_icon_button};
use crate::image::{IconButton, IconLabel, ImageProvider};
use crate::modules::PopupButton;
use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, WidgetContext,
@ -182,7 +182,7 @@ impl Module<Button> for MusicModule {
info: &ModuleInfo,
) -> Result<ModuleParts<Button>> {
let button = Button::new();
let button_contents = gtk::Box::new(Orientation::Horizontal, 5);
let button_contents = gtk::Box::new(self.layout.orientation(info), 5);
button_contents.add_class("contents");
button.add(&button_contents);
@ -190,9 +190,16 @@ impl Module<Button> for MusicModule {
let icon_play = IconLabel::new(&self.icons.play, info.icon_theme, self.icon_size);
let icon_pause = IconLabel::new(&self.icons.pause, info.icon_theme, self.icon_size);
icon_play.label().set_angle(self.layout.angle(info));
icon_play.label().set_justify(self.layout.justify.into());
icon_pause.label().set_angle(self.layout.angle(info));
icon_pause.label().set_justify(self.layout.justify.into());
let label = Label::builder()
.use_markup(true)
.angle(info.bar_position.get_angle())
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
if let Some(truncate) = self.truncate {
@ -297,22 +304,22 @@ impl Module<Button> for MusicModule {
let controls_box = gtk::Box::new(Orientation::Horizontal, 0);
controls_box.add_class("controls");
let btn_prev = new_icon_button(&icons.prev, icon_theme, self.icon_size);
let btn_prev = IconButton::new(&icons.prev, icon_theme, self.icon_size);
btn_prev.add_class("btn-prev");
let btn_play = new_icon_button(&icons.play, icon_theme, self.icon_size);
let btn_play = IconButton::new(&icons.play, icon_theme, self.icon_size);
btn_play.add_class("btn-play");
let btn_pause = new_icon_button(&icons.pause, icon_theme, self.icon_size);
let btn_pause = IconButton::new(&icons.pause, icon_theme, self.icon_size);
btn_pause.add_class("btn-pause");
let btn_next = new_icon_button(&icons.next, icon_theme, self.icon_size);
let btn_next = IconButton::new(&icons.next, icon_theme, self.icon_size);
btn_next.add_class("btn-next");
controls_box.add(&btn_prev);
controls_box.add(&btn_play);
controls_box.add(&btn_pause);
controls_box.add(&btn_next);
controls_box.add(&*btn_prev);
controls_box.add(&*btn_play);
controls_box.add(&*btn_pause);
controls_box.add(&*btn_next);
info_box.add(&controls_box);

View file

@ -1,11 +1,10 @@
use crate::config::CommonConfig;
use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::script::{OutputStream, Script, ScriptMode};
use crate::{glib_recv, module_impl, spawn, try_send};
use color_eyre::{Help, Report, Result};
use gtk::Label;
use gtk::prelude::*;
use serde::Deserialize;
use tokio::sync::mpsc;
use tracing::error;
@ -36,6 +35,11 @@ pub struct ScriptModule {
#[serde(default = "default_interval")]
interval: u64,
// -- Common --
/// 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<CommonConfig>,
@ -99,8 +103,11 @@ impl Module<Label> for ScriptModule {
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo,
) -> Result<ModuleParts<Label>> {
let label = Label::builder().use_markup(true).build();
label.set_angle(info.bar_position.get_angle());
let label = Label::builder()
.use_markup(true)
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
{
let label = label.clone();

View file

@ -1,4 +1,4 @@
use crate::config::{CommonConfig, TruncateMode};
use crate::config::{CommonConfig, LayoutConfig, TruncateMode};
use crate::gtk_helpers::IronbarLabelExt;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
use crate::{await_sync, glib_recv, module_impl, try_send};
@ -19,6 +19,10 @@ pub struct SwayModeModule {
/// **Default**: `null`
pub truncate: Option<TruncateMode>,
/// 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<CommonConfig>,
@ -57,10 +61,13 @@ impl Module<Label> for SwayModeModule {
fn into_widget(
self,
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
_info: &ModuleInfo,
info: &ModuleInfo,
) -> Result<ModuleParts<Label>> {
let label = Label::new(None);
label.set_use_markup(true);
let label = Label::builder()
.use_markup(true)
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
{
let label = label.clone();

View file

@ -3,7 +3,7 @@ mod renderer;
mod token;
use crate::clients::sysinfo::TokenType;
use crate::config::{CommonConfig, ModuleOrientation};
use crate::config::{CommonConfig, LayoutConfig, ModuleOrientation};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::modules::sysinfo::token::Part;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
@ -34,14 +34,6 @@ pub struct SysInfoModule {
#[serde(default = "Interval::default")]
interval: Interval,
/// The orientation of text for the labels.
///
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
/// <br>
/// **Default** : `horizontal`
#[serde(default)]
orientation: ModuleOrientation,
/// The orientation by which the labels are laid out.
///
/// **Valid options**: `horizontal`, `vertical`, `h`, `v`
@ -49,6 +41,11 @@ pub struct SysInfoModule {
/// **Default** : `horizontal`
direction: Option<ModuleOrientation>,
// -- common --
/// 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<CommonConfig>,
@ -281,22 +278,25 @@ impl Module<gtk::Box> for SysInfoModule {
fn into_widget(
self,
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
_info: &ModuleInfo,
info: &ModuleInfo,
) -> Result<ModuleParts<gtk::Box>> {
let layout = match self.direction {
Some(orientation) => orientation,
None => self.orientation,
Some(orientation) => orientation.into(),
None => self.layout.orientation(info),
};
let container = gtk::Box::new(layout.into(), 10);
let container = gtk::Box::new(layout, 10);
let mut labels = Vec::new();
for _ in &self.format {
let label = Label::builder().use_markup(true).build();
let label = Label::builder()
.use_markup(true)
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
label.add_class("item");
label.set_angle(self.orientation.to_angle());
container.add(&label);
labels.push(label);

View file

@ -39,6 +39,7 @@ pub struct TrayModule {
/// **Default**: `horizontal` for horizontal bars, `vertical` for vertical bars
#[serde(default)]
direction: Option<ModuleOrientation>,
/// See [common options](module-level-options#common-options).
#[serde(flatten)]
pub common: Option<CommonConfig>,

View file

@ -8,7 +8,7 @@ use zbus;
use zbus::fdo::PropertiesProxy;
use crate::clients::upower::BatteryState;
use crate::config::CommonConfig;
use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::image::ImageProvider;
use crate::modules::PopupButton;
@ -37,6 +37,11 @@ pub struct UpowerModule {
#[serde(default = "default_icon_size")]
icon_size: i32,
// -- Common --
/// 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<CommonConfig>,
@ -172,10 +177,13 @@ impl Module<Button> for UpowerModule {
let label = Label::builder()
.label(&self.format)
.use_markup(true)
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
label.add_class("label");
let container = gtk::Box::new(info.bar_position.orientation(), 5);
let container = gtk::Box::new(self.layout.orientation(info), 5);
container.add_class("contents");
let button = Button::new();

View file

@ -1,5 +1,5 @@
use crate::clients::volume::{self, Event};
use crate::config::CommonConfig;
use crate::config::{CommonConfig, LayoutConfig};
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
use crate::modules::{
Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext,
@ -36,6 +36,11 @@ pub struct VolumeModule {
#[serde(default)]
icons: Icons,
// -- Common --
/// 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<CommonConfig>,
@ -204,7 +209,8 @@ impl Module<Button> for VolumeModule {
{
let button_label = Label::builder()
.use_markup(true)
.angle(info.bar_position.get_angle())
.angle(self.layout.angle(info))
.justify(self.layout.justify.into())
.build();
let button = Button::new();

View file

@ -1,6 +1,6 @@
use super::open_state::OpenState;
use crate::gtk_helpers::IronbarGtkExt;
use crate::image::new_icon_button;
use crate::image::IconButton;
use crate::modules::workspaces::WorkspaceItemContext;
use crate::try_send;
use gtk::Button as GtkButton;
@ -8,7 +8,7 @@ use gtk::prelude::*;
#[derive(Debug, Clone)]
pub struct Button {
button: GtkButton,
button: IconButton,
workspace_id: i64,
}
@ -16,7 +16,7 @@ impl Button {
pub fn new(id: i64, name: &str, open_state: OpenState, context: &WorkspaceItemContext) -> Self {
let label = context.name_map.get(name).map_or(name, String::as_str);
let button = new_icon_button(label, &context.icon_theme, context.icon_size);
let button = IconButton::new(label, &context.icon_theme, context.icon_size);
button.set_widget_name(name);
button.add_class("item");

View file

@ -4,7 +4,7 @@ mod open_state;
use self::button::Button;
use crate::clients::compositor::{Workspace, WorkspaceClient, WorkspaceUpdate};
use crate::config::CommonConfig;
use crate::config::{CommonConfig, LayoutConfig};
use crate::modules::workspaces::button_map::{ButtonMap, Identifier};
use crate::modules::workspaces::open_state::OpenState;
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
@ -127,6 +127,11 @@ pub struct WorkspacesModule {
#[serde(default = "default_icon_size")]
icon_size: i32,
// -- Common --
/// 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<CommonConfig>,
@ -228,7 +233,7 @@ impl Module<gtk::Box> for WorkspacesModule {
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
info: &ModuleInfo,
) -> Result<ModuleParts<gtk::Box>> {
let container = gtk::Box::new(info.bar_position.orientation(), 0);
let container = gtk::Box::new(self.layout.orientation(info), 0);
let mut button_map = ButtonMap::new();