1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-01 10:41:03 +02:00

feat: module-level name and class options

BREAKING CHANGE: To allow for the `name` property, any widgets that were previously targeted by name should be targeted by class instead. This affects **all modules and all popups**, as well as several widgets inside modules. **This will break a lot of rules in your stylesheet**. To attempt to mitigate the damage, a migration script can be found [here](https://raw.githubusercontent.com/JakeStanger/ironbar/master/scripts/migrate-styles.sh) that should get you most of the way.

Resolves #75.
This commit is contained in:
Jake Stanger 2023-05-06 00:40:06 +01:00
parent 528a8d6dd6
commit dea66415c2
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
30 changed files with 352 additions and 215 deletions

View file

@ -1,5 +1,7 @@
use crate::config::{BarPosition, MarginConfig, ModuleConfig};
use crate::modules::{create_module, wrap_widget, ModuleInfo, ModuleLocation};
use crate::modules::{
create_module, set_widget_identifiers, wrap_widget, ModuleInfo, ModuleLocation,
};
use crate::popup::Popup;
use crate::Config;
use color_eyre::Result;
@ -195,8 +197,10 @@ fn add_modules(
macro_rules! add_module {
($module:expr, $id:expr) => {{
let common = $module.common.take().expect("Common config did not exist");
let widget = create_module(*$module, $id, &info, &Arc::clone(&popup))?;
let container = wrap_widget(&widget, common, orientation);
let widget_parts = create_module(*$module, $id, &info, &Arc::clone(&popup))?;
set_widget_identifiers(&widget_parts, &common);
let container = wrap_widget(&widget_parts.widget, common, orientation);
content.add(&container);
}};
}

View file

@ -10,8 +10,11 @@ use tracing::trace;
/// Common configuration options
/// which can be set on every module.
#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Default, Deserialize, Clone)]
pub struct CommonConfig {
pub class: Option<String>,
pub name: Option<String>,
pub show_if: Option<ScriptInput>,
pub transition_type: Option<TransitionType>,
pub transition_duration: Option<u32>,
@ -54,7 +57,7 @@ impl TransitionType {
impl CommonConfig {
/// Configures the module's container according to the common config options.
pub fn install(mut self, container: &EventBox, revealer: &Revealer) {
pub fn install_events(mut self, container: &EventBox, revealer: &Revealer) {
self.install_show_if(container, revealer);
let left_click_script = self.on_click_left.map(Script::new_polling);

8
src/gtk_helpers.rs Normal file
View file

@ -0,0 +1,8 @@
use glib::IsA;
use gtk::prelude::*;
use gtk::Widget;
/// Adds a new CSS class to a widget.
pub fn add_class<W: IsA<Widget>>(widget: &W, class: &str) {
widget.style_context().add_class(class);
}

View file

@ -1,4 +1,5 @@
use super::ImageProvider;
use crate::gtk_helpers::add_class;
use gtk::prelude::*;
use gtk::{Button, IconTheme, Image, Label, Orientation};
use tracing::error;
@ -9,7 +10,7 @@ pub fn new_icon_button(input: &str, icon_theme: &IconTheme, size: i32) -> Button
if ImageProvider::is_definitely_image_input(input) {
let image = Image::new();
image.set_widget_name("image");
add_class(&image, "image");
match ImageProvider::parse(input, icon_theme, size)
.and_then(|provider| provider.load_into_image(image.clone()))
@ -36,7 +37,7 @@ pub fn new_icon_label(input: &str, icon_theme: &IconTheme, size: i32) -> gtk::Bo
if ImageProvider::is_definitely_image_input(input) {
let image = Image::new();
image.set_widget_name("image");
add_class(&image, "image");
container.add(&image);
@ -47,7 +48,7 @@ pub fn new_icon_label(input: &str, icon_theme: &IconTheme, size: i32) -> gtk::Bo
}
} else {
let label = Label::new(Some(input));
label.set_widget_name("label");
add_class(&label, "label");
container.add(&label);
}

View file

@ -7,6 +7,7 @@ mod config;
mod desktop_file;
mod dynamic_string;
mod error;
mod gtk_helpers;
mod image;
mod logging;
mod macros;

View file

@ -154,11 +154,7 @@ impl Module<Button> for ClipboardModule {
where
Self: Sized,
{
let container = gtk::Box::builder()
.orientation(Orientation::Vertical)
.spacing(10)
.name("popup-clipboard")
.build();
let container = gtk::Box::new(Orientation::Vertical, 10);
let entries = gtk::Box::new(Orientation::Vertical, 5);
container.add(&entries);

View file

@ -1,4 +1,5 @@
use crate::config::CommonConfig;
use crate::gtk_helpers::add_class;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::popup::Popup;
use crate::{send_async, try_send};
@ -96,20 +97,16 @@ impl Module<Button> for ClockModule {
rx: glib::Receiver<Self::SendMessage>,
_info: &ModuleInfo,
) -> Option<gtk::Box> {
let container = gtk::Box::builder()
.orientation(Orientation::Vertical)
.name("popup-clock")
.build();
let container = gtk::Box::new(Orientation::Vertical, 0);
let clock = Label::builder()
.name("calendar-clock")
.halign(Align::Center)
.build();
let clock = Label::builder().halign(Align::Center).build();
add_class(&clock, "calendar-clock");
let format = "%H:%M:%S";
container.add(&clock);
let calendar = Calendar::builder().name("calendar").build();
let calendar = Calendar::new();
add_class(&calendar, "calendar");
container.add(&calendar);
{

View file

@ -28,8 +28,6 @@ use tracing::{debug, error};
#[derive(Debug, Deserialize, Clone)]
pub struct CustomModule {
/// Container class name
class: Option<String>,
/// Widgets to add to the bar container
bar: Vec<WidgetConfig>,
/// Widgets to add to the popup container
@ -197,10 +195,6 @@ impl Module<gtk::Box> for CustomModule {
let orientation = info.bar_position.get_orientation();
let container = gtk::Box::builder().orientation(orientation).build();
if let Some(ref class) = self.class {
container.style_context().add_class(class);
}
let custom_context = CustomWidgetContext {
tx: &context.controller_tx,
bar_orientation: orientation,
@ -230,13 +224,7 @@ impl Module<gtk::Box> for CustomModule {
where
Self: Sized,
{
let container = gtk::Box::builder().name("popup-custom").build();
if let Some(class) = self.class {
container
.style_context()
.add_class(format!("popup-{class}").as_str());
}
let container = gtk::Box::new(Orientation::Horizontal, 0);
if let Some(popup) = self.popup {
let custom_context = CustomWidgetContext {

View file

@ -1,5 +1,6 @@
use crate::clients::wayland::{self, ToplevelEvent};
use crate::config::{CommonConfig, TruncateMode};
use crate::gtk_helpers::add_class;
use crate::image::ImageProvider;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::{send_async, try_send};
@ -95,8 +96,11 @@ impl Module<gtk::Box> for FocusedModule {
let container = gtk::Box::new(info.bar_position.get_orientation(), 5);
let icon = gtk::Image::builder().name("icon").build();
let label = Label::builder().name("label").build();
let icon = gtk::Image::new();
add_class(&icon, "icon");
let label = Label::new(None);
add_class(&label, "label");
if let Some(truncate) = self.truncate {
truncate.truncate_label(&label);

View file

@ -413,10 +413,7 @@ impl Module<gtk::Box> for LauncherModule {
) -> Option<gtk::Box> {
const MAX_WIDTH: i32 = 250;
let container = gtk::Box::builder()
.orientation(Orientation::Vertical)
.name("popup-launcher")
.build();
let container = gtk::Box::new(Orientation::Vertical, 0);
// we need some content to force the container to have a size
let placeholder = Button::with_label("PLACEHOLDER");

View file

@ -120,7 +120,7 @@ pub fn create_module<TModule, TWidget, TSend, TRec>(
id: usize,
info: &ModuleInfo,
popup: &Arc<RwLock<Popup>>,
) -> Result<TWidget>
) -> Result<ModuleWidget<TWidget>>
where
TModule: Module<TWidget, SendMessage = TSend, ReceiveMessage = TRec>,
TWidget: IsA<Widget>,
@ -145,17 +145,21 @@ where
let name = TModule::name();
let module_parts = module.into_widget(context, info)?;
module_parts.widget.set_widget_name(name);
module_parts.widget.style_context().add_class(name);
let mut has_popup = false;
if let Some(popup_content) = module_parts.popup {
if let Some(popup_content) = module_parts.popup.clone() {
popup_content
.style_context()
.add_class(&format!("popup-{name}"));
register_popup_content(popup, id, popup_content);
has_popup = true;
}
setup_receiver(channel, w_tx, p_tx, popup.clone(), name, id, has_popup);
Ok(module_parts.widget)
Ok(module_parts)
}
/// Registers the popup content with the popup.
@ -234,6 +238,32 @@ fn setup_receiver<TSend>(
});
}
pub fn set_widget_identifiers<TWidget: IsA<Widget>>(
widget_parts: &ModuleWidget<TWidget>,
common: &CommonConfig,
) {
if let Some(ref name) = common.name {
widget_parts.widget.set_widget_name(name);
if let Some(ref popup) = widget_parts.popup {
popup.set_widget_name(&format!("popup-{name}"));
}
}
if let Some(ref class) = common.class {
// gtk counts classes with spaces as the same class
for part in class.split(' ') {
widget_parts.widget.style_context().add_class(part);
}
if let Some(ref popup) = widget_parts.popup {
for part in class.split(' ') {
popup.style_context().add_class(&format!("popup-{part}"));
}
}
}
}
/// Takes a widget and adds it into a new `gtk::EventBox`.
/// The event box container is returned.
pub fn wrap_widget<W: IsA<Widget>>(
@ -241,14 +271,14 @@ pub fn wrap_widget<W: IsA<Widget>>(
common: CommonConfig,
orientation: Orientation,
) -> EventBox {
let transition_type = common
.transition_type
.as_ref()
.unwrap_or(&TransitionType::SlideStart)
.to_revealer_transition_type(orientation);
let revealer = Revealer::builder()
.transition_type(
common
.transition_type
.as_ref()
.unwrap_or(&TransitionType::SlideStart)
.to_revealer_transition_type(orientation),
)
.transition_type(transition_type)
.transition_duration(common.transition_duration.unwrap_or(250))
.build();
@ -259,7 +289,7 @@ pub fn wrap_widget<W: IsA<Widget>>(
container.add_events(EventMask::SCROLL_MASK);
container.add(&revealer);
common.install(&container, &revealer);
common.install_events(&container, &revealer);
container
}

View file

@ -1,6 +1,7 @@
mod config;
use crate::clients::music::{self, MusicClient, PlayerState, PlayerUpdate, Status, Track};
use crate::gtk_helpers::add_class;
use crate::image::{new_icon_button, new_icon_label, ImageProvider};
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::popup::Popup;
@ -135,7 +136,7 @@ impl Module<Button> for MusicModule {
PlayerCommand::Play => client.play(),
PlayerCommand::Pause => client.pause(),
PlayerCommand::Next => client.next(),
PlayerCommand::Volume(vol) => client.set_volume_percent(vol), // .unwrap_or_else(|_| error!("Failed to update player volume")),
PlayerCommand::Volume(vol) => client.set_volume_percent(vol),
};
if let Err(err) = res {
@ -154,11 +155,8 @@ impl Module<Button> for MusicModule {
info: &ModuleInfo,
) -> Result<ModuleWidget<Button>> {
let button = Button::new();
let button_contents = gtk::Box::builder()
.orientation(Orientation::Horizontal)
.spacing(5)
.name("contents")
.build();
let button_contents = gtk::Box::new(Orientation::Horizontal, 5);
add_class(&button_contents, "contents");
button.add(&button_contents);
@ -243,17 +241,13 @@ impl Module<Button> for MusicModule {
) -> Option<gtk::Box> {
let icon_theme = info.icon_theme;
let container = gtk::Box::builder()
.orientation(Orientation::Horizontal)
.spacing(10)
.name("popup-music")
.build();
let container = gtk::Box::new(Orientation::Horizontal, 10);
let album_image = gtk::Image::builder()
.width_request(128)
.height_request(128)
.name("album-art")
.build();
add_class(&album_image, "album-art");
let icons = self.icons;
@ -262,27 +256,28 @@ impl Module<Button> for MusicModule {
let album_label = IconLabel::new(&icons.album, None, icon_theme);
let artist_label = IconLabel::new(&icons.artist, None, icon_theme);
title_label.container.set_widget_name("title");
album_label.container.set_widget_name("album");
artist_label.container.set_widget_name("artist");
add_class(&title_label.container, "title");
add_class(&album_label.container, "album");
add_class(&artist_label.container, "artist");
info_box.add(&title_label.container);
info_box.add(&album_label.container);
info_box.add(&artist_label.container);
let controls_box = gtk::Box::builder().name("controls").build();
let controls_box = gtk::Box::new(Orientation::Horizontal, 0);
add_class(&controls_box, "controls");
let btn_prev = new_icon_button(&icons.prev, icon_theme, self.icon_size);
btn_prev.set_widget_name("btn-prev");
add_class(&btn_prev, "btn-prev");
let btn_play = new_icon_button(&icons.play, icon_theme, self.icon_size);
btn_play.set_widget_name("btn-play");
add_class(&btn_play, "btn-play");
let btn_pause = new_icon_button(&icons.pause, icon_theme, self.icon_size);
btn_pause.set_widget_name("btn-pause");
add_class(&btn_pause, "btn-pause");
let btn_next = new_icon_button(&icons.next, icon_theme, self.icon_size);
btn_next.set_widget_name("btn-next");
add_class(&btn_next, "btn-next");
controls_box.add(&btn_prev);
controls_box.add(&btn_play);
@ -291,18 +286,15 @@ impl Module<Button> for MusicModule {
info_box.add(&controls_box);
let volume_box = gtk::Box::builder()
.orientation(Orientation::Vertical)
.spacing(5)
.name("volume")
.build();
let volume_box = gtk::Box::new(Orientation::Vertical, 5);
add_class(&volume_box, "volume");
let volume_slider = Scale::with_range(Orientation::Vertical, 0.0, 100.0, 5.0);
volume_slider.set_inverted(true);
volume_slider.set_widget_name("slider");
add_class(&volume_slider, "slider");
let volume_icon = new_icon_label(&icons.volume, icon_theme, self.icon_size);
volume_icon.style_context().add_class("icon");
add_class(&volume_icon, "icon");
volume_box.pack_start(&volume_slider, true, true, 0);
volume_box.pack_end(&volume_icon, false, false, 0);
@ -466,8 +458,8 @@ impl IconLabel {
let icon = new_icon_label(icon_input, icon_theme, 24);
let label = Label::new(label);
icon.style_context().add_class("icon");
label.style_context().add_class("label");
add_class(&icon, "icon");
add_class(&label, "label");
container.add(&icon);
container.add(&label);

View file

@ -1,4 +1,5 @@
use crate::config::CommonConfig;
use crate::gtk_helpers::add_class;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::send_async;
use color_eyre::Result;
@ -193,12 +194,11 @@ impl Module<gtk::Box> for SysInfoModule {
let mut labels = Vec::new();
for format in &self.format {
let label = Label::builder()
.label(format)
.use_markup(true)
.name("item")
.build();
let label = Label::builder().label(format).use_markup(true).build();
add_class(&label, "item");
label.set_angle(info.bar_position.get_angle());
container.add(&label);
labels.push(label);
}

View file

@ -1,5 +1,6 @@
use crate::clients::upower::get_display_proxy;
use crate::config::CommonConfig;
use crate::gtk_helpers::add_class;
use crate::image::ImageProvider;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::popup::Popup;
@ -144,20 +145,20 @@ impl Module<gtk::Box> for UpowerModule {
info: &ModuleInfo,
) -> Result<ModuleWidget<gtk::Box>> {
let icon_theme = info.icon_theme.clone();
let icon = gtk::Image::builder().name("icon").build();
let icon = gtk::Image::new();
add_class(&icon, "icon");
let label = Label::builder()
.label(&self.format)
.use_markup(true)
.name("label")
.build();
add_class(&label, "label");
let container = gtk::Box::builder()
.orientation(Orientation::Horizontal)
.name("upower")
.build();
let container = gtk::Box::new(Orientation::Horizontal, 0);
add_class(&container, "upower");
let button = Button::builder().name("button").build();
let button = Button::new();
add_class(&button, "button");
button.add(&label);
container.add(&button);
@ -207,11 +208,10 @@ impl Module<gtk::Box> for UpowerModule {
{
let container = gtk::Box::builder()
.orientation(Orientation::Horizontal)
.name("popup-upower")
.build();
let label = Label::builder().name("upower-details").build();
container.add(&label);
let label = Label::new(None);
add_class(&label, "upower-details");
rx.attach(None, move |properties| {
let mut format = String::new();