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

@ -310,9 +310,12 @@ For information on the `Script` type, and embedding scripts in strings, see [her
| `transition_type` | `slide_start` or `slide_end` or `crossfade` or `none` | `slide_start` | The transition animation to use when showing/hiding the widget. |
| `transition_duration` | `Integer` | `250` | The length of the transition animation to use when showing/hiding the widget. |
#### Other
#### Appearance
| 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`. |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -4,17 +4,37 @@ To style the bar, create a file at `~/.config/ironbar/style.css`.
Style changes are hot-loaded so there is no need to reload the bar.
A reminder: since the bar is GTK-based, it uses GTK's implementation of CSS,
Since the bar is GTK-based, it uses [GTK's implementation of CSS](https://docs.gtk.org/gtk3/css-overview.html),
which only includes a subset of the full web spec (plus a few non-standard properties).
The below table describes the selectors provided by the bar itself.
Information on styling individual modules can be found on their pages in the sidebar.
| Selector | Description |
|----------------|-------------------------------------------|
| `.background` | Top-level window |
| `#bar` | Bar root box |
| `#bar #start` | Bar left or top modules container box |
| `#bar #center` | Bar center modules container box |
| `#bar #end` | Bar right or bottom modules container box |
| `.container` | All of the above |
|----------------|--------------------------------------------|
| `.background` | Top-level window. |
| `#bar` | Bar root box. |
| `#bar #start` | Bar left or top modules container box. |
| `#bar #center` | Bar center modules container box. |
| `#bar #end` | Bar right or bottom modules container box. |
| `.container` | All of the above. |
| `.popup` | Any popup box. |
Every widget can be selected using a `kebab-case` class name matching its name.
You can also target popups by prefixing `popup-` to the name. For example, you can use `.clock` and `.popup-clock` respectively.
Setting the `name` option on a widget allows you to target that specific instance using `#name`.
You can also add additional classes to re-use styles. In both cases, `popup-` is automatically prefixed to the popup (`#popup-name` or `.popup-my-class`).
You can also target all GTK widgets of a certain type directly using their name. For example, `button:hover` will select the hover state on *all* buttons.
These names are all lower case with no separator, so `MenuBar` -> `menubar`.
GTK CSS does not support custom properties, but it does have its own custom `@define-color` syntax which you can use for re-using colours:
```css
@define-color color_bg #2d2d2d;
box, menubar {
background-color: @color_bg;
}
```

View File

@ -84,11 +84,13 @@ end:
| Selector | Description |
|--------------------------------------|------------------------------------------------------|
| `#clipboard` | Clipboard widget. |
| `#clipboard .btn` | Clipboard widget button. |
| `#popup-clipboard` | Clipboard popup box. |
| `#popup-clipboard .item` | Clipboard row item inside the popup. |
| `#popup-clipboard .item .btn` | Clipboard row item radio button. |
| `#popup-clipboard .item .btn.text` | Clipboard row item radio button (text values only). |
| `#popup-clipboard .item .btn.image` | Clipboard row item radio button (image values only). |
| `#popup-clipboard .item .btn-remove` | Clipboard row item remove button. |
| `.clipboard` | Clipboard widget. |
| `.clipboard .btn` | Clipboard widget button. |
| `.popup-clipboard` | Clipboard popup box. |
| `.popup-clipboard .item` | Clipboard row item inside the popup. |
| `.popup-clipboard .item .btn` | Clipboard row item radio button. |
| `.popup-clipboard .item .btn.text` | Clipboard row item radio button (text values only). |
| `.popup-clipboard .item .btn.image` | Clipboard row item radio button (image values only). |
| `.popup-clipboard .item .btn-remove` | Clipboard row item remove button. |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -71,7 +71,9 @@ end:
| Selector | Description |
|--------------------------------|------------------------------------------------------------------------------------|
| `#clock` | Clock widget button |
| `#popup-clock` | Clock popup box |
| `#popup-clock #calendar-clock` | Clock inside the popup |
| `#popup-clock #calendar` | Calendar widget inside the popup. GTK provides some OOTB styling options for this. |
| `.clock` | Clock widget button |
| `.popup-clock` | Clock popup box |
| `.popup-clock .calendar-clock` | Clock inside the popup |
| `.popup-clock .calendar` | Calendar widget inside the popup. GTK provides some OOTB styling options for this. |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -392,10 +392,13 @@ let {
## Styling
Since the widgets are all custom, you can use the `name` and `class` attributes, then target them using `#name` and `.class`.
Since the widgets are all custom, you can use their `name` and `class` attributes, then target them using `#name` and `.class`.
The following top-level selector is always available:
The following top-level selectors are always available:
| Selector | Description |
|-----------|-------------------------|
| `#custom` | Custom widget container |
|-----------------|--------------------------------|
| `.custom` | Custom widget container. |
| `.popup-custom` | Custom widget popup container. |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -87,7 +87,9 @@ end:
## Styling
| Selector | Description |
|--------------------------|--------------------|
| `#focused` | Focused widget box |
| `#focused #icon` | App icon |
| `#focused #label` | App name |
|-------------------|--------------------|
| `.focused` | Focused widget box |
| `.focused .icon` | App icon |
| `.focused .label` | App name |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -66,5 +66,7 @@ end:
## Styling
| Selector | Description |
|--------------------------------|------------------------------------------------------------------------------------|
| `#label` | Label widget |
|----------|------------------------------------------------------------------------------------|
| `.label` | Label widget |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -90,10 +90,12 @@ start:
| Selector | Description |
|-------------------------------|--------------------------|
| `#launcher` | Launcher widget box |
| `#launcher .item` | App button |
| `#launcher .item.open` | App button (open app) |
| `#launcher .item.focused` | App button (focused app) |
| `#launcher .item.urgent` | App button (urgent app) |
| `#launcher-popup` | Popup container |
| `#launcher-popup .popup-item` | Window button in popup |
| `.launcher` | Launcher widget box |
| `.launcher .item` | App button |
| `.launcher .item.open` | App button (open app) |
| `.launcher .item.focused` | App button (focused app) |
| `.launcher .item.urgent` | App button (urgent app) |
| `.popup-launcher` | Popup container |
| `.popup-launcher .popup-item` | Window button in popup |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -135,24 +135,26 @@ and will be replaced with values from the currently playing track:
| Selector | Description |
|-------------------------------------|------------------------------------------|
| `#music` | Tray widget button |
| `#music #contents` | Tray widget button contents box |
| `#popup-music` | Popup box |
| `#popup-music #album-art` | Album art image inside popup box |
| `#popup-music #title` | Track title container inside popup box |
| `#popup-music #title .icon` | Track title icon label inside popup box |
| `#popup-music #title .label` | Track title label inside popup box |
| `#popup-music #album` | Track album container inside popup box |
| `#popup-music #album .icon` | Track album icon label inside popup box |
| `#popup-music #album .label` | Track album label inside popup box |
| `#popup-music #artist` | Track artist container inside popup box |
| `#popup-music #artist .icon` | Track artist icon label inside popup box |
| `#popup-music #artist .label` | Track artist label inside popup box |
| `#popup-music #controls` | Controls container inside popup box |
| `#popup-music #controls #btn-prev` | Previous button inside popup box |
| `#popup-music #controls #btn-play` | Play button inside popup box |
| `#popup-music #controls #btn-pause` | Pause button inside popup box |
| `#popup-music #controls #btn-next` | Next button inside popup box |
| `#popup-music #volume` | Volume container inside popup box |
| `#popup-music #volume #slider` | Volume slider popup box |
| `#popup-music #volume .icon` | Volume icon label inside popup box |
| `.music` | Tray widget button |
| `.music .contents` | Tray widget button contents box |
| `.popup-music` | Popup box |
| `.popup-music .album-art` | Album art image inside popup box |
| `.popup-music .title` | Track title container inside popup box |
| `.popup-music .title .icon` | Track title icon label inside popup box |
| `.popup-music .title .label` | Track title label inside popup box |
| `.popup-music .album` | Track album container inside popup box |
| `.popup-music .album .icon` | Track album icon label inside popup box |
| `.popup-music .album .label` | Track album label inside popup box |
| `.popup-music .artist` | Track artist container inside popup box |
| `.popup-music .artist .icon` | Track artist icon label inside popup box |
| `.popup-music .artist .label` | Track artist label inside popup box |
| `.popup-music .controls` | Controls container inside popup box |
| `.popup-music .controls .btn-prev` | Previous button inside popup box |
| `.popup-music .controls .btn-play` | Play button inside popup box |
| `.popup-music .controls .btn-pause` | Pause button inside popup box |
| `.popup-music .controls .btn-next` | Next button inside popup box |
| `.popup-music .volume` | Volume container inside popup box |
| `.popup-music .volume .slider` | Volume slider popup box |
| `.popup-music .volume .icon` | Volume icon label inside popup box |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -83,5 +83,7 @@ end:
## Styling
| Selector | Description |
|---------------|---------------------|
| `#script` | Script widget label |
|-----------|---------------------|
| `.script` | Script widget label |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -172,5 +172,7 @@ The following tokens can be used in the `format` configuration option:
| Selector | Description |
|------------------|------------------------------|
| `#sysinfo` | Sysinfo widget box |
| `#sysinfo #item` | Individual information label |
| `.sysinfo` | Sysinfo widget box |
| `.sysinfo .item` | Individual information label |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -60,5 +60,7 @@ end:
| Selector | Description |
|---------------|------------------|
| `#tray` | Tray widget box |
| `#tray .item` | Tray icon button |
| `.tray` | Tray widget box |
| `.tray .item` | Tray icon button |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -72,9 +72,11 @@ end:
| Selector | Description |
|---------------------------------|-----------------------------|
| `#upower` | Upower widget container. |
| `#upower #icon` | Upower widget battery icon. |
| `#upower #button` | Upower widget button. |
| `#upower #button #label` | Upower widget button label. |
| `#popup-upower` | Upower popup box. |
| `#popup-upower #upower-details` | Label inside the popup. |
| `.upower` | Upower widget container. |
| `.upower .icon` | Upower widget battery icon. |
| `.upower .button` | Upower widget button. |
| `.upower .button .label` | Upower widget button label. |
| `.popup-upower` | Upower popup box. |
| `.popup-upower .upower-details` | Label inside the popup. |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -91,6 +91,8 @@ end:
| Selector | Description |
|-----------------------------|--------------------------------------|
| `#workspaces` | Workspaces widget box |
| `#workspaces .item` | Workspace button |
| `#workspaces .item.focused` | Workspace button (workspace focused) |
| `.workspaces` | Workspaces widget box |
| `.workspaces .item` | Workspace button |
| `.workspaces .item.focused` | Workspace button (workspace focused) |
For more information on styling, please see the [styling guide](styling-guide).

View File

@ -14,16 +14,11 @@
border-radius: 0;
}
box, menubar {
box, menubar, button {
background-color: @color_bg;
}
button {
color: @color_text;
background-color: @color_bg;
}
label {
button, label {
color: @color_text;
}
@ -43,12 +38,12 @@ button:hover {
/* -- clipboard -- */
#clipboard {
.clipboard {
margin-left: 5px;
font-size: 1.1em;
}
#popup-clipboard .item {
.popup-clipboard .item {
padding-bottom: 0.3em;
border-bottom: 1px solid @color_border;
}
@ -56,125 +51,125 @@ button:hover {
/* -- clock -- */
#clock {
.clock {
font-weight: bold;
margin-left: 5px;
}
#calendar-clock {
.popup-clock .calendar-clock {
color: @color_text;
font-size: 2.5em;
padding-bottom: 0.1em;
}
#popup-clock #calendar {
.popup-clock .calendar {
background-color: @color_bg;
color: @color_text;
}
#popup-clock #calendar .header {
.popup-clock .calendar .header {
padding-top: 1em;
border-top: 1px solid @color_border;
font-size: 1.5em;
}
#popup-clock #calendar:selected {
.popup-clock .calendar:selected {
background-color: @color_border_active;
}
/* -- launcher -- */
#launcher .item {
.launcher .item {
margin-right: 4px;
}
#launcher .item:not(.focused):hover {
.launcher .item:not(.focused):hover {
background-color: @color_bg_dark;
}
#launcher .open {
border-bottom: 2px solid @color_text;
.launcher .open {
border-bottom: 1px solid @color_text;
}
#launcher .focused {
border-bottom-color: @color_border_active;
.launcher .focused {
border-bottom: 2px solid @color_border_active;
}
#launcher .urgent {
.launcher .urgent {
border-bottom-color: @color_urgent;
}
#popup-launcher {
.popup-launcher {
padding: 0;
}
#popup-launcher .popup-item:not(:first-child) {
.popup-launcher .popup-item:not(:first-child) {
border-top: 1px solid @color_border;
}
/* -- music -- */
#music:hover * {
.music:hover * {
background-color: @color_bg_dark;
}
#popup-music #album-art {
.popup-music .album-art {
margin-right: 1em;
}
#popup-music #title .icon *, #popup-music #title .label {
.popup-music .title .icon *, .popup-music .title .label {
font-size: 1.7em;
}
#popup-music #controls *:disabled {
.popup-music .controls *:disabled {
color: @color_border;
}
#popup-music #volume scale slider {
.popup-music .volume scale slider {
border-radius: 100%;
}
/* volume icon */
#popup-music #volume > box:last-child label {
.popup-music .volume > box:last-child label {
margin-left: 6px;
}
/* -- script -- */
#script {
.script {
padding-left: 10px;
}
/* -- sys_info -- */
#sysinfo {
.sysinfo {
margin-left: 10px;
}
#sysinfo #item {
.sysinfo .item {
margin-left: 5px;
}
/* -- tray -- */
#tray {
.tray {
margin-left: 10px;
}
/* -- workspaces -- */
#workspaces .item.focused {
.workspaces .item.focused {
box-shadow: inset 0 -3px;
background-color: @color_bg_dark;
}
#workspaces .item:hover {
.workspaces .item:hover {
box-shadow: inset 0 -3px;
}
@ -185,7 +180,7 @@ button:hover {
font-size: 1.4em;
padding-bottom: 0.4em;
margin-bottom: 0.6em;
border-bottom: 1px solid @color_border
border-bottom: 1px solid @color_border;
}
.popup-power-menu .power-btn {
@ -196,3 +191,4 @@ button:hover {
.popup-power-menu #buttons > *:nth-child(1) .power-btn {
margin-right: 1em;
}

72
scripts/migrate-styles.sh Executable file
View File

@ -0,0 +1,72 @@
#!/usr/bin/env bash
# Migrates CSS selectors from widget names to CSS classes.
# These changed as part of the 0.12 release.
# ⚠ This script will **NOT** check for custom styles and may mangle them!
# ⚠ It is *highly recommended* that you back up your existing styles before running this!
style_path="$HOME/.config/ironbar/style.css"
# general
sed -i 's/#icon/.icon/g' "$style_path"
sed -i 's/#label/.label/g' "$style_path"
sed -i 's/#image/.image/g' "$style_path"
# clipboard
sed -i 's/#clipboard/.clipboard/g' "$style_path"
sed -i 's/#popup-clipboard/.popup-clipboard/g' "$style_path"
# clock
sed -i 's/#clock/.clock/g' "$style_path"
sed -i 's/#popup-clock/.popup-clock/g' "$style_path"
sed -i 's/#calendar-clock/.calendar-clock/g' "$style_path"
sed -i 's/#calendar/.calendar/g' "$style_path"
# custom
sed -i 's/#custom/.custom/g' "$style_path"
sed -i 's/#popup-custom/.popup-custom/g' "$style_path"
# focused
sed -i 's/#focused/.focused/g' "$style_path"
# launcher
sed -i 's/#launcher/.launcher/g' "$style_path"
sed -i 's/#popup-launcher/.popup-launcher/g' "$style_path"
sed -i 's/#launcher-popup/.popup-launcher/g' "$style_path" # was incorrect in docs
# music
sed -i 's/#music/.music/g' "$style_path"
sed -i 's/#contents/.contents/g' "$style_path"
sed -i 's/#popup-music/.popup-music/g' "$style_path"
sed -i 's/#album-art/.album-art/g' "$style_path"
sed -i 's/#title/.title/g' "$style_path"
sed -i 's/#album/.album/g' "$style_path"
sed -i 's/#artist/.artist/g' "$style_path"
sed -i 's/#controls/.controls/g' "$style_path"
sed -i 's/#btn-prev/.btn-prev/g' "$style_path"
sed -i 's/#btn-play/.btn-play/g' "$style_path"
sed -i 's/#btn-pause/.btn-pause/g' "$style_path"
sed -i 's/#btn-next/.btn-next/g' "$style_path"
sed -i 's/#volume/.volume/g' "$style_path"
sed -i 's/#slider/.slider/g' "$style_path"
# script
sed -i 's/#script/.script/g' "$style_path"
# sys_info
sed -i 's/#sysinfo/.sysinfo/g' "$style_path"
sed -i 's/#item/.item/g' "$style_path"
# tray
sed -i 's/#tray/.tray/g' "$style_path"
# upower
sed -i 's/#upower/.upower/g' "$style_path"
sed -i 's/#button/.button/g' "$style_path"
sed -i 's/#popup-upower/.popup-upower/g' "$style_path"
sed -i 's/#upower-details/.upower-details/g' "$style_path"
# workspaces
sed -i 's/#workspaces/.workspaces/g' "$style_path"
sed -i 's/#item/.item/g' "$style_path"

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 revealer = Revealer::builder()
.transition_type(
common
let transition_type = common
.transition_type
.as_ref()
.unwrap_or(&TransitionType::SlideStart)
.to_revealer_transition_type(orientation),
)
.to_revealer_transition_type(orientation);
let revealer = Revealer::builder()
.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();