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

Merge pull request #907 from JakeStanger/feat/widget-rotation

feat: fully implement orientation/justify options
This commit is contained in:
Jake Stanger 2025-03-22 21:47:43 +00:00 committed by GitHub
commit 5a7ad5675d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
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

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

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

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();