diff --git a/docs/komorebi.bar.example.json b/docs/komorebi.bar.example.json index 292f7df4..9ea41479 100644 --- a/docs/komorebi.bar.example.json +++ b/docs/komorebi.bar.example.json @@ -1,14 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.33/schema.bar.json", - "monitor": { - "index": 0, - "work_area_offset": { - "left": 0, - "top": 40, - "right": 0, - "bottom": 40 - } - }, + "monitor": 0, "font_family": "JetBrains Mono", "theme": { "palette": "Base16", diff --git a/komorebi-bar/src/bar.rs b/komorebi-bar/src/bar.rs index 08b9fc21..a9cf0699 100644 --- a/komorebi-bar/src/bar.rs +++ b/komorebi-bar/src/bar.rs @@ -1,5 +1,7 @@ +use crate::config::get_individual_spacing; use crate::config::KomobarConfig; use crate::config::KomobarTheme; +use crate::config::MonitorConfigOrIndex; use crate::config::Position; use crate::config::PositionConfig; use crate::komorebi::Komorebi; @@ -12,6 +14,7 @@ use crate::render::RenderExt; use crate::widget::BarWidget; use crate::widget::WidgetConfig; use crate::BAR_HEIGHT; +use crate::DEFAULT_PADDING; use crate::MAX_LABEL_WIDTH; use crate::MONITOR_LEFT; use crate::MONITOR_RIGHT; @@ -34,6 +37,7 @@ use eframe::egui::Margin; use eframe::egui::Rgba; use eframe::egui::Style; use eframe::egui::TextStyle; +use eframe::egui::Vec2; use eframe::egui::Visuals; use font_loader::system_fonts; use font_loader::system_fonts::FontPropertyBuilder; @@ -52,6 +56,7 @@ use std::sync::Arc; pub struct Komobar { pub hwnd: Option, + pub monitor_index: usize, pub config: Arc, pub render_config: Rc>, pub komorebi_notification_state: Option>>, @@ -64,6 +69,7 @@ pub struct Komobar { pub bg_color_with_alpha: Rc>, pub scale_factor: f32, pub size_rect: komorebi_client::Rect, + pub work_area_offset: komorebi_client::Rect, applied_theme_on_first_frame: bool, } @@ -199,7 +205,7 @@ impl Komobar { // Update the `size_rect` so that the bar position can be changed on the EGUI update // function - self.update_size_rect(config.position.clone()); + self.update_size_rect(config); self.try_apply_theme(config, ctx); @@ -295,23 +301,64 @@ impl Komobar { self.center_widgets = center_widgets; self.right_widgets = right_widgets; - if let (Some(prev_rect), Some(new_rect)) = ( - &self.config.monitor.work_area_offset, - &config.monitor.work_area_offset, - ) { + let (monitor_index, config_work_area_offset) = match &config.monitor { + MonitorConfigOrIndex::MonitorConfig(monitor_config) => { + (monitor_config.index, monitor_config.work_area_offset) + } + MonitorConfigOrIndex::Index(idx) => (*idx, None), + }; + self.monitor_index = monitor_index; + + if let (prev_rect, Some(new_rect)) = (&self.work_area_offset, &config_work_area_offset) { if new_rect != prev_rect { + self.work_area_offset = *new_rect; if let Err(error) = komorebi_client::send_message( - &SocketMessage::MonitorWorkAreaOffset(config.monitor.index, *new_rect), + &SocketMessage::MonitorWorkAreaOffset(self.monitor_index, *new_rect), ) { tracing::error!( "error applying work area offset to monitor '{}': {}", - config.monitor.index, + self.monitor_index, error, ); } else { tracing::info!( "work area offset applied to monitor: {}", - config.monitor.index + self.monitor_index + ); + } + } + } else if let Some(height) = config.height.or(Some(BAR_HEIGHT)) { + // We only add the `bottom_margin` to the work_area_offset since the top margin is + // already considered on the `size_rect.top` + let bottom_margin = config + .margin + .as_ref() + .map_or(0, |v| v.to_individual(0.0).bottom as i32); + let new_rect = komorebi_client::Rect { + left: 0, + top: (height as i32) + + (self.size_rect.top - MONITOR_TOP.load(Ordering::SeqCst)) + + bottom_margin, + right: 0, + bottom: (height as i32) + + (self.size_rect.top - MONITOR_TOP.load(Ordering::SeqCst)) + + bottom_margin, + }; + + if new_rect != self.work_area_offset { + self.work_area_offset = new_rect; + if let Err(error) = komorebi_client::send_message( + &SocketMessage::MonitorWorkAreaOffset(self.monitor_index, new_rect), + ) { + tracing::error!( + "error applying work area offset to monitor '{}': {}", + self.monitor_index, + error, + ); + } else { + tracing::info!( + "work area offset applied to monitor: {}", + self.monitor_index ); } } @@ -325,8 +372,8 @@ impl Komobar { } /// Updates the `size_rect` field. Returns a bool indicating if the field was changed or not - fn update_size_rect(&mut self, position: Option) { - let position = position.unwrap_or(PositionConfig { + fn update_size_rect(&mut self, config: &KomobarConfig) { + let position = config.position.clone().unwrap_or(PositionConfig { start: Some(Position { x: MONITOR_LEFT.load(Ordering::SeqCst) as f32, y: MONITOR_TOP.load(Ordering::SeqCst) as f32, @@ -337,16 +384,26 @@ impl Komobar { }), }); - let start = position.start.unwrap_or(Position { + let mut start = position.start.unwrap_or(Position { x: MONITOR_LEFT.load(Ordering::SeqCst) as f32, y: MONITOR_TOP.load(Ordering::SeqCst) as f32, }); - let end = position.end.unwrap_or(Position { + let mut end = position.end.unwrap_or(Position { x: MONITOR_RIGHT.load(Ordering::SeqCst) as f32, y: BAR_HEIGHT, }); + if let Some(height) = config.height { + end.y = height; + } + + let margin = get_individual_spacing(0.0, &config.margin); + + start.y += margin.top; + start.x += margin.left; + end.x -= margin.left + margin.right; + if end.y == 0.0 { tracing::warn!("position.end.y is set to 0.0 which will make your bar invisible on a config reload - this is usually set to 50.0 by default") } @@ -453,6 +510,7 @@ impl Komobar { ) -> Self { let mut komobar = Self { hwnd: process_hwnd(), + monitor_index: 0, config: config.clone(), render_config: Rc::new(RefCell::new(RenderConfig::new())), komorebi_notification_state: None, @@ -465,6 +523,7 @@ impl Komobar { bg_color_with_alpha: Rc::new(RefCell::new(Style::default().visuals.panel_fill)), scale_factor: cc.egui_ctx.native_pixels_per_point().unwrap_or(1.0), size_rect: komorebi_client::Rect::default(), + work_area_offset: komorebi_client::Rect::default(), applied_theme_on_first_frame: false, }; @@ -578,7 +637,7 @@ impl eframe::App for Komobar { .borrow_mut() .handle_notification( ctx, - self.config.monitor.index, + self.monitor_index, self.rx_gui.clone(), self.bg_color.clone(), self.bg_color_with_alpha.clone(), @@ -619,40 +678,78 @@ impl eframe::App for Komobar { } } - let frame = if let Some(frame) = &self.config.frame { - Frame::none() - .inner_margin(Margin::symmetric( - frame.inner_margin.x, - frame.inner_margin.y, - )) - .fill(*self.bg_color_with_alpha.borrow()) - } else { - Frame::none().fill(*self.bg_color_with_alpha.borrow()) + let frame = match &self.config.padding { + None => { + if let Some(frame) = &self.config.frame { + Frame::none() + .inner_margin(Margin::symmetric( + frame.inner_margin.x, + frame.inner_margin.y, + )) + .fill(*self.bg_color_with_alpha.borrow()) + } else { + Frame::none() + .inner_margin(Margin::same(0.0)) + .fill(*self.bg_color_with_alpha.borrow()) + } + } + Some(padding) => { + let padding = padding.to_individual(DEFAULT_PADDING); + Frame::none() + .inner_margin(Margin { + top: padding.top, + bottom: padding.bottom, + left: padding.left, + right: padding.right, + }) + .fill(*self.bg_color_with_alpha.borrow()) + } }; let mut render_config = self.render_config.borrow_mut(); let frame = render_config.change_frame_on_bar(frame, &ctx.style()); - CentralPanel::default().frame(frame).show(ctx, |_| { + CentralPanel::default().frame(frame).show(ctx, |ui| { // Apply grouping logic for the bar as a whole let area_frame = if let Some(frame) = &self.config.frame { - Frame::none().inner_margin(Margin::symmetric(0.0, frame.inner_margin.y)) + Frame::none() + .inner_margin(Margin::symmetric(0.0, frame.inner_margin.y)) + .outer_margin(Margin::same(0.0)) } else { Frame::none() + .inner_margin(Margin::same(0.0)) + .outer_margin(Margin::same(0.0)) }; + let available_height = ui.max_rect().max.y; + ctx.style_mut(|style| { + style.spacing.interact_size.y = available_height; + }); + if !self.left_widgets.is_empty() { // Left-aligned widgets layout Area::new(Id::new("left_panel")) .anchor(Align2::LEFT_CENTER, [0.0, 0.0]) // Align in the left center of the window .show(ctx, |ui| { let mut left_area_frame = area_frame; - if let Some(frame) = &self.config.frame { + if let Some(padding) = self + .config + .padding + .as_ref() + .map(|s| s.to_individual(DEFAULT_PADDING)) + { + left_area_frame.inner_margin.left = padding.left; + left_area_frame.inner_margin.top = padding.top; + left_area_frame.inner_margin.bottom = padding.bottom; + } else if let Some(frame) = &self.config.frame { left_area_frame.inner_margin.left = frame.inner_margin.x; + left_area_frame.inner_margin.top = frame.inner_margin.y; + left_area_frame.inner_margin.bottom = frame.inner_margin.y; } + left_area_frame.show(ui, |ui| { - ui.with_layout(Layout::left_to_right(Align::Center), |ui| { + ui.horizontal(|ui| { let mut render_conf = render_config.clone(); render_conf.alignment = Some(Alignment::Left); @@ -672,20 +769,40 @@ impl eframe::App for Komobar { .anchor(Align2::RIGHT_CENTER, [0.0, 0.0]) // Align in the right center of the window .show(ctx, |ui| { let mut right_area_frame = area_frame; - if let Some(frame) = &self.config.frame { + if let Some(padding) = self + .config + .padding + .as_ref() + .map(|s| s.to_individual(DEFAULT_PADDING)) + { + right_area_frame.inner_margin.right = padding.right; + right_area_frame.inner_margin.top = padding.top; + right_area_frame.inner_margin.bottom = padding.bottom; + } else if let Some(frame) = &self.config.frame { right_area_frame.inner_margin.right = frame.inner_margin.x; + right_area_frame.inner_margin.top = frame.inner_margin.y; + right_area_frame.inner_margin.bottom = frame.inner_margin.y; } - right_area_frame.show(ui, |ui| { - ui.with_layout(Layout::right_to_left(Align::Center), |ui| { - let mut render_conf = render_config.clone(); - render_conf.alignment = Some(Alignment::Right); - render_config.apply_on_alignment(ui, |ui| { - for w in &mut self.right_widgets { - w.render(ctx, ui, &mut render_conf); - } - }); - }); + right_area_frame.show(ui, |ui| { + let initial_size = Vec2 { + x: ui.available_size_before_wrap().x, + y: ui.spacing().interact_size.y, + }; + ui.allocate_ui_with_layout( + initial_size, + Layout::right_to_left(Align::Center), + |ui| { + let mut render_conf = render_config.clone(); + render_conf.alignment = Some(Alignment::Right); + + render_config.apply_on_alignment(ui, |ui| { + for w in &mut self.right_widgets { + w.render(ctx, ui, &mut render_conf); + } + }); + }, + ); }); }); } @@ -695,9 +812,22 @@ impl eframe::App for Komobar { Area::new(Id::new("center_panel")) .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) // Align in the center of the window .show(ctx, |ui| { - let center_area_frame = area_frame; + let mut center_area_frame = area_frame; + if let Some(padding) = self + .config + .padding + .as_ref() + .map(|s| s.to_individual(DEFAULT_PADDING)) + { + center_area_frame.inner_margin.top = padding.top; + center_area_frame.inner_margin.bottom = padding.bottom; + } else if let Some(frame) = &self.config.frame { + center_area_frame.inner_margin.top = frame.inner_margin.y; + center_area_frame.inner_margin.bottom = frame.inner_margin.y; + } + center_area_frame.show(ui, |ui| { - ui.with_layout(Layout::left_to_right(Align::Center), |ui| { + ui.horizontal(|ui| { let mut render_conf = render_config.clone(); render_conf.alignment = Some(Alignment::Center); diff --git a/komorebi-bar/src/config.rs b/komorebi-bar/src/config.rs index 1f3ee36a..0e153dae 100644 --- a/komorebi-bar/src/config.rs +++ b/komorebi-bar/src/config.rs @@ -1,5 +1,6 @@ use crate::render::Grouping; use crate::widget::WidgetConfig; +use crate::DEFAULT_PADDING; use eframe::egui::Pos2; use eframe::egui::TextBuffer; use eframe::egui::Vec2; @@ -14,13 +15,65 @@ use std::path::PathBuf; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] /// The `komorebi.bar.json` configuration file reference for `v0.1.34` pub struct KomobarConfig { + /// Bar height (default: 50) + pub height: Option, + /// Bar padding. Use one value for all sides or use a grouped padding for horizontal and/or + /// vertical definition which can each take a single value for a symmetric padding or two + /// values for each side, i.e.: + /// ```json + /// "padding": { + /// "horizontal": 10 + /// } + /// ``` + /// or: + /// ```json + /// "padding": { + /// "horizontal": [left, right] + /// } + /// ``` + /// You can also set individual padding on each side like this: + /// ```json + /// "padding": { + /// "top": 10, + /// "bottom": 10, + /// "left": 10, + /// "right": 10, + /// } + /// ``` + /// By default, padding is set to 10 on all sides. + pub padding: Option, + /// Bar margin. Use one value for all sides or use a grouped margin for horizontal and/or + /// vertical definition which can each take a single value for a symmetric margin or two + /// values for each side, i.e.: + /// ```json + /// "margin": { + /// "horizontal": 10 + /// } + /// ``` + /// or: + /// ```json + /// "margin": { + /// "vertical": [top, bottom] + /// } + /// ``` + /// You can also set individual margin on each side like this: + /// ```json + /// "margin": { + /// "top": 10, + /// "bottom": 10, + /// "left": 10, + /// "right": 10, + /// } + /// ``` + /// By default, margin is set to 0 on all sides. + pub margin: Option, /// Bar positioning options #[serde(alias = "viewport")] pub position: Option, /// Frame options (see: https://docs.rs/egui/latest/egui/containers/frame/struct.Frame.html) pub frame: Option, - /// Monitor options - pub monitor: MonitorConfig, + /// The monitor index or the full monitor options + pub monitor: MonitorConfigOrIndex, /// Font family pub font_family: Option, /// Font size (default: 12.5) @@ -90,6 +143,15 @@ pub struct FrameConfig { pub inner_margin: Position, } +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +pub enum MonitorConfigOrIndex { + /// The monitor index where you want the bar to show + Index(usize), + /// The full monitor options with the index and an optional work_area_offset + MonitorConfig(MonitorConfig), +} + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] pub struct MonitorConfig { /// Komorebi monitor index of the monitor on which to render the bar @@ -98,6 +160,154 @@ pub struct MonitorConfig { pub work_area_offset: Option, } +pub type Padding = SpacingKind; +pub type Margin = SpacingKind; + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +// WARNING: To any developer messing with this code in the future: The order here matters! +// `Grouped` needs to come last, otherwise serde might mistaken an `IndividualSpacingConfig` for a +// `GroupedSpacingConfig` with both `vertical` and `horizontal` set to `None` ignoring the +// individual values. +pub enum SpacingKind { + All(f32), + Individual(IndividualSpacingConfig), + Grouped(GroupedSpacingConfig), +} + +impl SpacingKind { + pub fn to_individual(&self, default: f32) -> IndividualSpacingConfig { + match self { + SpacingKind::All(m) => IndividualSpacingConfig::all(*m), + SpacingKind::Grouped(grouped_spacing_config) => { + let vm = grouped_spacing_config.vertical.as_ref().map_or( + IndividualSpacingConfig::vertical(default), + |vm| match vm { + GroupedSpacingOptions::Symmetrical(m) => { + IndividualSpacingConfig::vertical(*m) + } + GroupedSpacingOptions::Split(tm, bm) => { + IndividualSpacingConfig::vertical(*tm).bottom(*bm) + } + }, + ); + let hm = grouped_spacing_config.horizontal.as_ref().map_or( + IndividualSpacingConfig::horizontal(default), + |hm| match hm { + GroupedSpacingOptions::Symmetrical(m) => { + IndividualSpacingConfig::horizontal(*m) + } + GroupedSpacingOptions::Split(lm, rm) => { + IndividualSpacingConfig::horizontal(*lm).right(*rm) + } + }, + ); + IndividualSpacingConfig { + top: vm.top, + bottom: vm.bottom, + left: hm.left, + right: hm.right, + } + } + SpacingKind::Individual(m) => *m, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct GroupedSpacingConfig { + pub vertical: Option, + pub horizontal: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +pub enum GroupedSpacingOptions { + Symmetrical(f32), + Split(f32, f32), +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct IndividualSpacingConfig { + pub top: f32, + pub bottom: f32, + pub left: f32, + pub right: f32, +} + +#[allow(dead_code)] +impl IndividualSpacingConfig { + pub const ZERO: Self = IndividualSpacingConfig { + top: 0.0, + bottom: 0.0, + left: 0.0, + right: 0.0, + }; + + pub fn all(value: f32) -> Self { + IndividualSpacingConfig { + top: value, + bottom: value, + left: value, + right: value, + } + } + + pub fn horizontal(value: f32) -> Self { + IndividualSpacingConfig { + top: 0.0, + bottom: 0.0, + left: value, + right: value, + } + } + + pub fn vertical(value: f32) -> Self { + IndividualSpacingConfig { + top: value, + bottom: value, + left: 0.0, + right: 0.0, + } + } + + pub fn top(self, value: f32) -> Self { + IndividualSpacingConfig { top: value, ..self } + } + + pub fn bottom(self, value: f32) -> Self { + IndividualSpacingConfig { + bottom: value, + ..self + } + } + + pub fn left(self, value: f32) -> Self { + IndividualSpacingConfig { + left: value, + ..self + } + } + + pub fn right(self, value: f32) -> Self { + IndividualSpacingConfig { + right: value, + ..self + } + } +} + +pub fn get_individual_spacing( + default: f32, + spacing: &Option, +) -> IndividualSpacingConfig { + spacing + .as_ref() + .map_or(IndividualSpacingConfig::all(default), |s| { + s.to_individual(default) + }) +} + impl KomobarConfig { pub fn read(path: &PathBuf) -> color_eyre::Result { let content = std::fs::read_to_string(path)?; @@ -108,7 +318,10 @@ impl KomobarConfig { if value.frame.is_none() { value.frame = Some(FrameConfig { - inner_margin: Position { x: 10.0, y: 10.0 }, + inner_margin: Position { + x: DEFAULT_PADDING, + y: DEFAULT_PADDING, + }, }); } diff --git a/komorebi-bar/src/main.rs b/komorebi-bar/src/main.rs index f4701862..2ffa9e3f 100644 --- a/komorebi-bar/src/main.rs +++ b/komorebi-bar/src/main.rs @@ -21,6 +21,7 @@ use crate::config::KomobarConfig; use crate::config::Position; use crate::config::PositionConfig; use clap::Parser; +use config::MonitorConfigOrIndex; use eframe::egui::ViewportBuilder; use font_loader::system_fonts; use hotwatch::EventKind; @@ -57,6 +58,7 @@ pub static MONITOR_TOP: AtomicI32 = AtomicI32::new(0); pub static MONITOR_RIGHT: AtomicI32 = AtomicI32::new(0); pub static MONITOR_INDEX: AtomicUsize = AtomicUsize::new(0); pub static BAR_HEIGHT: f32 = 50.0; +pub static DEFAULT_PADDING: f32 = 10.0; pub static ICON_CACHE: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -230,32 +232,39 @@ fn main() -> color_eyre::Result<()> { &SocketMessage::State, )?)?; + let (monitor_index, work_area_offset) = match &config.monitor { + MonitorConfigOrIndex::MonitorConfig(monitor_config) => { + (monitor_config.index, monitor_config.work_area_offset) + } + MonitorConfigOrIndex::Index(idx) => (*idx, None), + }; + MONITOR_RIGHT.store( - state.monitors.elements()[config.monitor.index].size().right, + state.monitors.elements()[monitor_index].size().right, Ordering::SeqCst, ); MONITOR_TOP.store( - state.monitors.elements()[config.monitor.index].size().top, + state.monitors.elements()[monitor_index].size().top, Ordering::SeqCst, ); - MONITOR_TOP.store( - state.monitors.elements()[config.monitor.index].size().left, + MONITOR_LEFT.store( + state.monitors.elements()[monitor_index].size().left, Ordering::SeqCst, ); - MONITOR_INDEX.store(config.monitor.index, Ordering::SeqCst); + MONITOR_INDEX.store(monitor_index, Ordering::SeqCst); match config.position { None => { config.position = Some(PositionConfig { start: Some(Position { - x: state.monitors.elements()[config.monitor.index].size().left as f32, - y: state.monitors.elements()[config.monitor.index].size().top as f32, + x: state.monitors.elements()[monitor_index].size().left as f32, + y: state.monitors.elements()[monitor_index].size().top as f32, }), end: Some(Position { - x: state.monitors.elements()[config.monitor.index].size().right as f32, + x: state.monitors.elements()[monitor_index].size().right as f32, y: 50.0, }), }) @@ -263,14 +272,14 @@ fn main() -> color_eyre::Result<()> { Some(ref mut position) => { if position.start.is_none() { position.start = Some(Position { - x: state.monitors.elements()[config.monitor.index].size().left as f32, - y: state.monitors.elements()[config.monitor.index].size().top as f32, + x: state.monitors.elements()[monitor_index].size().left as f32, + y: state.monitors.elements()[monitor_index].size().top as f32, }); } if position.end.is_none() { position.end = Some(Position { - x: state.monitors.elements()[config.monitor.index].size().right as f32, + x: state.monitors.elements()[monitor_index].size().right as f32, y: 50.0, }) } @@ -287,15 +296,9 @@ fn main() -> color_eyre::Result<()> { ..Default::default() }; - if let Some(rect) = &config.monitor.work_area_offset { - komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset( - config.monitor.index, - *rect, - ))?; - tracing::info!( - "work area offset applied to monitor: {}", - config.monitor.index - ); + if let Some(rect) = &work_area_offset { + komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset(monitor_index, *rect))?; + tracing::info!("work area offset applied to monitor: {}", monitor_index); } let (tx_gui, rx_gui) = crossbeam_channel::unbounded(); @@ -374,10 +377,17 @@ fn main() -> color_eyre::Result<()> { tracing::info!("reconnected to komorebi"); - if let Some(rect) = &config_cl.monitor.work_area_offset { + let (monitor_index, work_area_offset) = match &config_cl.monitor { + MonitorConfigOrIndex::MonitorConfig(monitor_config) => { + (monitor_config.index, monitor_config.work_area_offset) + } + MonitorConfigOrIndex::Index(idx) => (*idx, None), + }; + + if let Some(rect) = &work_area_offset { while komorebi_client::send_message( &SocketMessage::MonitorWorkAreaOffset( - config_cl.monitor.index, + monitor_index, *rect, ), ) diff --git a/komorebi-bar/src/render.rs b/komorebi-bar/src/render.rs index d6edc81c..87e447ed 100644 --- a/komorebi-bar/src/render.rs +++ b/komorebi-bar/src/render.rs @@ -1,5 +1,6 @@ use crate::bar::Alignment; use crate::config::KomobarConfig; +use crate::config::MonitorConfigOrIndex; use eframe::egui::Color32; use eframe::egui::Context; use eframe::egui::FontId; @@ -81,8 +82,13 @@ impl RenderExt for &KomobarConfig { let mut icon_font_id = text_font_id.clone(); icon_font_id.size *= icon_scale.unwrap_or(1.4).clamp(1.0, 2.0); + let monitor_idx = match &self.monitor { + MonitorConfigOrIndex::MonitorConfig(monitor_config) => monitor_config.index, + MonitorConfigOrIndex::Index(idx) => *idx, + }; + RenderConfig { - monitor_idx: self.monitor.index, + monitor_idx, spacing: self.widget_spacing.unwrap_or(10.0), grouping: self.grouping.unwrap_or(Grouping::None), background_color, diff --git a/schema.bar.json b/schema.bar.json index 903ec221..718029a9 100644 --- a/schema.bar.json +++ b/schema.bar.json @@ -1214,6 +1214,63 @@ } ] }, + "height": { + "description": "Bar height (default: 50)", + "type": "number", + "format": "float" + }, + "horizontal_margin": { + "description": "Bar horizontal margin. Use one value for symmetric margin or use `[left, right]` to specify a different margin on each side (default: 0)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, + "horizontal_padding": { + "description": "Bar horizontal padding. Use one value for symmetric padding or use `[left, right]` to specify a different padding on each side (default: 10)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, "icon_scale": { "description": "Scale of the icons relative to the font_size [[1.0-2.0]]. (default: 1.4)", "type": "number", @@ -2090,51 +2147,62 @@ "format": "float" }, "monitor": { - "description": "Monitor options", - "type": "object", - "required": [ - "index" - ], - "properties": { - "index": { - "description": "Komorebi monitor index of the monitor on which to render the bar", + "description": "The monitor index or the full monitor options", + "anyOf": [ + { + "description": "The monitor index where you want the bar to show", "type": "integer", "format": "uint", "minimum": 0.0 }, - "work_area_offset": { - "description": "Automatically apply a work area offset for this monitor to accommodate the bar", + { + "description": "The full monitor options with the index and an optional work_area_offset", "type": "object", "required": [ - "bottom", - "left", - "right", - "top" + "index" ], "properties": { - "bottom": { - "description": "The bottom point in a Win32 Rect", + "index": { + "description": "Komorebi monitor index of the monitor on which to render the bar", "type": "integer", - "format": "int32" + "format": "uint", + "minimum": 0.0 }, - "left": { - "description": "The left point in a Win32 Rect", - "type": "integer", - "format": "int32" - }, - "right": { - "description": "The right point in a Win32 Rect", - "type": "integer", - "format": "int32" - }, - "top": { - "description": "The top point in a Win32 Rect", - "type": "integer", - "format": "int32" + "work_area_offset": { + "description": "Automatically apply a work area offset for this monitor to accommodate the bar", + "type": "object", + "required": [ + "bottom", + "left", + "right", + "top" + ], + "properties": { + "bottom": { + "description": "The bottom point in a Win32 Rect", + "type": "integer", + "format": "int32" + }, + "left": { + "description": "The left point in a Win32 Rect", + "type": "integer", + "format": "int32" + }, + "right": { + "description": "The right point in a Win32 Rect", + "type": "integer", + "format": "int32" + }, + "top": { + "description": "The top point in a Win32 Rect", + "type": "integer", + "format": "int32" + } + } } } } - } + ] }, "position": { "description": "Bar positioning options", @@ -3427,6 +3495,58 @@ "format": "uint8", "minimum": 0.0 }, + "vertical_margin": { + "description": "Bar vertical margin. Use one value for symmetric margin or use `[top, bottom]` to specify a different margin on each side (default: 0)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, + "vertical_padding": { + "description": "Bar vertical padding. Use one value for symmetric padding or use `[top, bottom]` to specify a different padding on each side (default: 10)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, "widget_spacing": { "description": "Spacing between widgets (default: 10.0)", "type": "number",