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:
parent
528a8d6dd6
commit
dea66415c2
30 changed files with 352 additions and 215 deletions
10
src/bar.rs
10
src/bar.rs
|
@ -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);
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -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
8
src/gtk_helpers.rs
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ mod config;
|
|||
mod desktop_file;
|
||||
mod dynamic_string;
|
||||
mod error;
|
||||
mod gtk_helpers;
|
||||
mod image;
|
||||
mod logging;
|
||||
mod macros;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue