From dbeeed60d9bb890f79e6249934bef3964f389af2 Mon Sep 17 00:00:00 2001 From: BinaryHarbinger Date: Sun, 1 Mar 2026 19:18:24 +0300 Subject: [PATCH] feat: Add multiple bar support --- src/config/toml.rs | 50 +++++-- src/main.rs | 294 +++++++++++++++++++++++--------------- src/modules/box_module.rs | 2 +- src/modules/revealer.rs | 2 +- 4 files changed, 215 insertions(+), 133 deletions(-) diff --git a/src/config/toml.rs b/src/config/toml.rs index 7ec2783..ec5e244 100644 --- a/src/config/toml.rs +++ b/src/config/toml.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, fs, path::PathBuf}; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct Config { #[serde(default)] - pub bar: BarConfig, + pub bars: std::collections::HashMap, #[serde(default)] pub modules_left: Vec, @@ -50,17 +50,35 @@ pub struct Config { pub revealers: std::collections::HashMap, } +/* #[derive(Debug, Deserialize, Serialize, Clone)] +pub struct BarsConfig { + #[serde(default)] + pub bars: std::collections::HashMap, +} */ + #[derive(Debug, Deserialize, Serialize, Clone)] pub struct BarConfig { + #[serde(default)] + pub modules_left: Option>, + + #[serde(default)] + pub modules_center: Option>, + + #[serde(default)] + pub modules_right: Option>, + #[serde(default = "default_height")] pub height: u32, - #[serde(default = "default_position")] + #[serde(default = "default_layer_and_position")] pub position: String, - #[serde(default = "default_layer")] + #[serde(default = "default_layer_and_position")] pub layer: String, + #[serde(default = "default_namespace")] + pub namespace: String, + #[serde(default = "default_spacing")] pub spacing: i32, } @@ -352,11 +370,11 @@ pub struct RevealerConfig { fn default_height() -> u32 { 30 } -fn default_position() -> String { - "top".to_string() +fn default_layer_and_position() -> String { + String::from("top") } -fn default_layer() -> String { - "top".to_string() +fn default_namespace() -> String { + String::from("bar-container") } fn default_spacing() -> i32 { 10 @@ -372,9 +390,13 @@ fn default_command() -> String { impl Default for BarConfig { fn default() -> Self { Self { + modules_left: default_modules(), + modules_center: default_modules(), + modules_right: default_modules(), height: default_height(), - position: default_position(), - layer: default_layer(), + position: default_layer_and_position(), + layer: default_layer_and_position(), + namespace: default_namespace(), spacing: default_spacing(), } } @@ -712,13 +734,11 @@ impl Config { let example = r#" # Riftbar Configuration -# Module positions (MUST be at root level, BEFORE any [sections]) +[bar.main] modules_left = ["box/left"] modules_center = ["hyprland/workspaces"] modules_right = ["box/right"] - -[bar] -position = "top" # top, bottom +position = "top" # top, bottom, left, right layer = "top" # background, bottom, top, overlay # Clock module configuration @@ -865,3 +885,7 @@ fn optional_format() -> Option { fn default_icons() -> Option> { None } + +fn default_modules() -> Option> { + Some(Vec::from([String::new()])) +} diff --git a/src/main.rs b/src/main.rs index a6c0bb6..fbf405d 100755 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use gtk4 as gtk; use gtk4::prelude::*; use gtk4_layer_shell::LayerShell; -use std::{env, path::PathBuf, sync::Arc}; +use std::{cell::RefCell, env, path::PathBuf, sync::Arc}; mod config; mod modules; @@ -49,136 +49,191 @@ fn main() { let app = gtk::Application::new(Some("com.binaryharb.RiftBar"), Default::default()); + let gtk_windows: RefCell> = RefCell::new(Vec::new()); + app.connect_activate(move |app| { - let window = gtk::Window::new(); - - // Initialize layer shell - window.init_layer_shell(); - - // Set layer from config - let layer = match config.bar.layer.as_str() { - "background" => gtk4_layer_shell::Layer::Background, - "bottom" => gtk4_layer_shell::Layer::Bottom, - "overlay" => gtk4_layer_shell::Layer::Overlay, - _ => gtk4_layer_shell::Layer::Top, - }; - window.set_layer(layer); - - // Set anchors based on position - match config.bar.position.as_str() { - "bottom" => { - window.set_anchor(gtk4_layer_shell::Edge::Bottom, true); - window.set_anchor(gtk4_layer_shell::Edge::Left, true); - window.set_anchor(gtk4_layer_shell::Edge::Right, true); - } + for (name, bar_config) in &config.bars { + let window = gtk::Window::new(); + + // Initialize layer shell + window.init_layer_shell(); + + window.set_namespace(Some("bar-container")); + window.auto_exclusive_zone_enable(); + window.set_application(Some(app)); + window.add_css_class("bar-container"); + + // Set layer from config + let layer = match bar_config.layer.as_str() { + "background" => gtk4_layer_shell::Layer::Background, + "bottom" => gtk4_layer_shell::Layer::Bottom, + "overlay" => gtk4_layer_shell::Layer::Overlay, + _ => gtk4_layer_shell::Layer::Top, + }; + window.set_layer(layer); + + // Set anchors based on position + match bar_config.position.as_str() { + "bottom" => { + window.set_anchor(gtk4_layer_shell::Edge::Bottom, true); + window.set_anchor(gtk4_layer_shell::Edge::Left, true); + window.set_anchor(gtk4_layer_shell::Edge::Right, true); + } - "left" => { - window.set_anchor(gtk4_layer_shell::Edge::Top, true); - window.set_anchor(gtk4_layer_shell::Edge::Bottom, true); - window.set_anchor(gtk4_layer_shell::Edge::Left, true); - } + "left" => { + window.set_anchor(gtk4_layer_shell::Edge::Top, true); + window.set_anchor(gtk4_layer_shell::Edge::Bottom, true); + window.set_anchor(gtk4_layer_shell::Edge::Left, true); + } - "right" => { - window.set_anchor(gtk4_layer_shell::Edge::Top, true); - window.set_anchor(gtk4_layer_shell::Edge::Bottom, true); - window.set_anchor(gtk4_layer_shell::Edge::Right, true); - } + "right" => { + window.set_anchor(gtk4_layer_shell::Edge::Top, true); + window.set_anchor(gtk4_layer_shell::Edge::Bottom, true); + window.set_anchor(gtk4_layer_shell::Edge::Right, true); + } - _ => { - // top - window.set_anchor(gtk4_layer_shell::Edge::Top, true); - window.set_anchor(gtk4_layer_shell::Edge::Left, true); - window.set_anchor(gtk4_layer_shell::Edge::Right, true); + _ => { + // top + window.set_anchor(gtk4_layer_shell::Edge::Top, true); + window.set_anchor(gtk4_layer_shell::Edge::Left, true); + window.set_anchor(gtk4_layer_shell::Edge::Right, true); + } } - } - - window.set_namespace(Some("bar-container")); - window.auto_exclusive_zone_enable(); - window.set_application(Some(app)); - window.add_css_class("bar-container"); - - let orientation = match config.bar.position.as_str() { - "right" | "left" => gtk::Orientation::Vertical, - _ => gtk::Orientation::Horizontal, - }; - - if config.bar.position.as_str() != "right" && config.bar.position.as_str() != "left" { - // Use a center box for proper three-column layout - let layout_container = gtk::CenterBox::new(); - layout_container.add_css_class("riftbar"); - - // Left section - let left_box = gtk::Box::new(orientation, config.bar.spacing); - left_box.set_halign(gtk::Align::Start); - left_box.set_hexpand(true); - left_box.set_vexpand(false); - left_box.add_css_class("left-section"); - build_modules(&left_box, &config.modules_left, &config, 0); - - // Center section - let center_box = gtk::Box::new(orientation, config.bar.spacing); - center_box.set_halign(gtk::Align::Center); - center_box.set_hexpand(true); - center_box.set_vexpand(false); - center_box.add_css_class("center-section"); - build_modules(¢er_box, &config.modules_center, &config, 0); - - // Right section - let right_box = gtk::Box::new(orientation, config.bar.spacing); - right_box.set_halign(gtk::Align::End); - right_box.set_hexpand(true); - right_box.set_vexpand(false); - right_box.add_css_class("right-section"); - build_modules(&right_box, &config.modules_right, &config, 0); - - // Attach to center box - each section gets equal width - layout_container.set_start_widget(Some(&left_box)); - layout_container.set_center_widget(Some(¢er_box)); - layout_container.set_end_widget(Some(&right_box)); - - window.set_child(Some(&layout_container)); - } else { - let layout_container = gtk::Overlay::new(); - layout_container.add_css_class("riftbar"); - - let main_vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); - - let start_box = gtk::Box::new(orientation, config.bar.spacing); - start_box.set_halign(gtk::Align::Fill); - start_box.set_hexpand(true); - start_box.add_css_class("left-section"); - build_modules(&start_box, &config.modules_left, &config, 0); - - main_vbox.append(&start_box); - - let spacer = gtk::Box::new(gtk::Orientation::Vertical, 0); - spacer.set_vexpand(true); - main_vbox.append(&spacer); - - let end_box = gtk::Box::new(orientation, config.bar.spacing); - end_box.set_halign(gtk::Align::Fill); - end_box.set_hexpand(true); - end_box.add_css_class("right-section"); - build_modules(&end_box, &config.modules_right, &config, 0); + let orientation = match bar_config.position.as_str() { + "right" | "left" => gtk::Orientation::Vertical, + _ => gtk::Orientation::Horizontal, + }; + + if bar_config.position.as_str() != "right" && bar_config.position.as_str() != "left" { + // Use a center box for proper three-column layout + let layout_container = gtk::CenterBox::new(); + layout_container.add_css_class("riftbar"); + + // Left section + let left_box = gtk::Box::new(orientation, bar_config.spacing); + left_box.set_halign(gtk::Align::Start); + left_box.set_hexpand(true); + left_box.set_vexpand(false); + left_box.add_css_class("left-section"); + if bar_config.modules_left.is_some() { + build_modules( + &left_box, + &bar_config.modules_left.clone().unwrap_or_default(), + &config, + Some(bar_config), + 0, + ); + } - main_vbox.append(&end_box); + // Center section + let center_box = gtk::Box::new(orientation, bar_config.spacing); + center_box.set_halign(gtk::Align::Center); + center_box.set_hexpand(true); + center_box.set_vexpand(false); + center_box.add_css_class("center-section"); + if bar_config.modules_center.is_some() { + build_modules( + ¢er_box, + &bar_config.modules_center.clone().unwrap_or_default(), + &config, + Some(bar_config), + 0, + ); + } - layout_container.set_child(Some(&main_vbox)); + // Right section + let right_box = gtk::Box::new(orientation, bar_config.spacing); + right_box.set_halign(gtk::Align::End); + right_box.set_hexpand(true); + right_box.set_vexpand(false); + right_box.add_css_class("right-section"); + if bar_config.modules_right.is_some() { + build_modules( + &right_box, + &bar_config.modules_right.clone().unwrap_or_default(), + &config, + Some(bar_config), + 0, + ); + } - let center_box = gtk::Box::new(orientation, config.bar.spacing); - center_box.add_css_class("center-section"); - build_modules(¢er_box, &config.modules_center, &config, 0); + // Attach to center box - each section gets equal width + layout_container.set_start_widget(Some(&left_box)); + layout_container.set_center_widget(Some(¢er_box)); + layout_container.set_end_widget(Some(&right_box)); - layout_container.add_overlay(¢er_box); - center_box.set_halign(gtk::Align::Center); - center_box.set_valign(gtk::Align::Center); + // Set css class + layout_container.add_css_class(name); - window.set_child(Some(&layout_container)); + window.set_child(Some(&layout_container)); + } else { + let layout_container = gtk::Overlay::new(); + layout_container.add_css_class("riftbar"); + + let main_vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + + let start_box = gtk::Box::new(orientation, bar_config.spacing); + start_box.set_halign(gtk::Align::Fill); + start_box.set_hexpand(true); + start_box.add_css_class("left-section"); + build_modules( + &start_box, + &bar_config.modules_left.clone().unwrap_or_default(), + &config, + Some(bar_config), + 0, + ); + + main_vbox.append(&start_box); + + let spacer = gtk::Box::new(gtk::Orientation::Vertical, 0); + spacer.set_vexpand(true); + main_vbox.append(&spacer); + + let end_box = gtk::Box::new(orientation, bar_config.spacing); + end_box.set_halign(gtk::Align::Fill); + end_box.set_hexpand(true); + end_box.add_css_class("right-section"); + build_modules( + &end_box, + &bar_config.modules_right.clone().unwrap_or_default(), + &config, + Some(bar_config), + 0, + ); + + main_vbox.append(&end_box); + + layout_container.set_child(Some(&main_vbox)); + + let center_box = gtk::Box::new(orientation, bar_config.spacing); + center_box.add_css_class("center-section"); + build_modules( + ¢er_box, + &bar_config.modules_center.clone().unwrap_or_default(), + &config, + Some(bar_config), + 0, + ); + + layout_container.add_overlay(¢er_box); + center_box.set_halign(gtk::Align::Center); + center_box.set_valign(gtk::Align::Center); + + // Set css class + layout_container.add_css_class(name); + + window.set_child(Some(&layout_container)); + } + gtk_windows.borrow_mut().push(window); } + // Load CSS after window is set up apply_css_to_gtk(); - window.present(); + for window in gtk_windows.borrow().iter() { + window.present(); + } }); app.run_with_args::(&[]); @@ -188,6 +243,7 @@ fn build_modules( container: >k::Box, module_names: &[String], config: &config::Config, + some_widget_config: Option<&config::BarConfig>, container_type: i32, ) { let container_name = match container_type { @@ -197,9 +253,11 @@ fn build_modules( _ => "", }; + let widget_config = some_widget_config.cloned().unwrap_or_default(); + println!("Building modules{}: {:?}", container_name, module_names); - let orientation = !matches!(config.bar.position.as_str(), "right" | "left"); + let orientation = !matches!(widget_config.position.as_str(), "right" | "left"); for name in module_names { match name.as_str() { diff --git a/src/modules/box_module.rs b/src/modules/box_module.rs index 2a9bd7e..a78add1 100644 --- a/src/modules/box_module.rs +++ b/src/modules/box_module.rs @@ -32,7 +32,7 @@ impl BoxWidget { create_gesture_handler(&container, config.gestures); // Build the modules inside this box - crate::build_modules(&container, &config.modules, app_config, 1); + crate::build_modules(&container, &config.modules, app_config, None, 1); Self { container } } diff --git a/src/modules/revealer.rs b/src/modules/revealer.rs index b69d3fe..4c205e0 100644 --- a/src/modules/revealer.rs +++ b/src/modules/revealer.rs @@ -66,7 +66,7 @@ impl RevealerWidget { content_box.add_css_class("revealer-content"); // Build modules in the content box - crate::build_modules(&content_box, &config.modules, app_config, 2); + crate::build_modules(&content_box, &config.modules, app_config, None, 2); revealer.set_child(Some(&content_box)); // State tracking