diff --git a/komorebi-bar/src/widgets/komorebi.rs b/komorebi-bar/src/widgets/komorebi.rs index bdf6fe1d..63a0e04d 100644 --- a/komorebi-bar/src/widgets/komorebi.rs +++ b/komorebi-bar/src/widgets/komorebi.rs @@ -11,7 +11,9 @@ use crate::widgets::widget::BarWidget; use crate::ICON_CACHE; use crate::MAX_LABEL_WIDTH; use crate::MONITOR_INDEX; +use eframe::egui::text::LayoutJob; use eframe::egui::vec2; +use eframe::egui::Align; use eframe::egui::Color32; use eframe::egui::ColorImage; use eframe::egui::Context; @@ -24,6 +26,7 @@ use eframe::egui::RichText; use eframe::egui::Sense; use eframe::egui::Stroke; use eframe::egui::StrokeKind; +use eframe::egui::TextFormat; use eframe::egui::TextureHandle; use eframe::egui::TextureOptions; use eframe::egui::Ui; @@ -55,8 +58,11 @@ pub struct KomorebiConfig { pub layout: Option, /// Configure the Workspace Layer widget pub workspace_layer: Option, - /// Configure the Focused Window widget - pub focused_window: Option, + /// Configure the Focused Container widget + #[serde(alias = "focused_window")] + pub focused_container: Option, + /// Configure the Locked Container widget + pub locked_container: Option, /// Configure the Configuration Switcher widget pub configuration_switcher: Option, } @@ -96,15 +102,26 @@ pub struct KomorebiWorkspaceLayerConfig { #[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct KomorebiFocusedWindowConfig { - /// Enable the Komorebi Focused Window widget +pub struct KomorebiFocusedContainerConfig { + /// Enable the Komorebi Focused Container widget pub enable: bool, - /// DEPRECATED: use 'display' instead (Show the icon of the currently focused window) + /// DEPRECATED: use 'display' instead (Show the icon of the currently focused container) pub show_icon: Option, - /// Display format of the currently focused window + /// Display format of the currently focused container pub display: Option, } +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct KomorebiLockedContainerConfig { + /// Enable the Komorebi Locked Container widget + pub enable: bool, + /// Display format of the current locked state + pub display: Option, + /// Show the widget event if the layer is unlocked + pub show_when_unlocked: Option, +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct KomorebiConfigurationSwitcherConfig { @@ -140,15 +157,19 @@ impl From<&KomorebiConfig> for Komorebi { .unwrap_or_default(), mouse_follows_focus: true, work_area_offset: None, - focused_container_information: KomorebiNotificationStateContainerInformation::EMPTY, + focused_container_information: ( + false, + KomorebiNotificationStateContainerInformation::EMPTY, + ), stack_accent: None, monitor_index: MONITOR_INDEX.load(Ordering::SeqCst), monitor_usr_idx_map: HashMap::new(), })), workspaces: value.workspaces, layout: value.layout.clone(), - focused_window: value.focused_window, + focused_container: value.focused_container, workspace_layer: value.workspace_layer, + locked_container: value.locked_container, configuration_switcher, } } @@ -159,8 +180,9 @@ pub struct Komorebi { pub komorebi_notification_state: Rc>, pub workspaces: Option, pub layout: Option, - pub focused_window: Option, + pub focused_container: Option, pub workspace_layer: Option, + pub locked_container: Option, pub configuration_switcher: Option, } @@ -337,7 +359,7 @@ impl BarWidget for Komorebi { if matches!(layer, WorkspaceLayer::Tiling) { let (response, painter) = ui.allocate_painter(size, Sense::hover()); - let color = ui.style().visuals.text_color(); + let color = ctx.style().visuals.selection.stroke.color; let stroke = Stroke::new(1.0, color); let mut rect = response.rect; let corner = @@ -367,7 +389,7 @@ impl BarWidget for Komorebi { } else { let (response, painter) = ui.allocate_painter(size, Sense::hover()); - let color = ui.style().visuals.text_color(); + let color = ctx.style().visuals.selection.stroke.color; let stroke = Stroke::new(1.0, color); let mut rect = response.rect; let corner = @@ -504,18 +526,85 @@ impl BarWidget for Komorebi { } } - if let Some(focused_window) = self.focused_window { - if focused_window.enable { + if let Some(locked_container_config) = self.locked_container { + if locked_container_config.enable { + let is_locked = komorebi_notification_state.focused_container_information.0; + + if locked_container_config + .show_when_unlocked + .unwrap_or_default() + || is_locked + { + let titles = &komorebi_notification_state + .focused_container_information + .1 + .titles; + + if !titles.is_empty() { + let display_format = locked_container_config + .display + .unwrap_or(DisplayFormat::Text); + + let mut layout_job = LayoutJob::simple( + if display_format != DisplayFormat::Text { + if is_locked { + egui_phosphor::regular::LOCK_KEY.to_string() + } else { + egui_phosphor::regular::LOCK_SIMPLE_OPEN.to_string() + } + } else { + String::new() + }, + config.icon_font_id.clone(), + ctx.style().visuals.selection.stroke.color, + 100.0, + ); + + if display_format != DisplayFormat::Icon { + layout_job.append( + if is_locked { "Locked" } else { "Unlocked" }, + 10.0, + TextFormat { + font_id: config.text_font_id.clone(), + color: ctx.style().visuals.text_color(), + valign: Align::Center, + ..Default::default() + }, + ); + } + + config.apply_on_widget(false, ui, |ui| { + if SelectableFrame::new(false) + .show(ui, |ui| ui.add(Label::new(layout_job).selectable(false))) + .clicked() + && komorebi_client::send_batch([ + SocketMessage::FocusMonitorAtCursor, + SocketMessage::ToggleLock, + ]) + .is_err() + { + tracing::error!("could not send ToggleLock"); + } + }); + } + } + } + } + + if let Some(focused_container_config) = self.focused_container { + if focused_container_config.enable { let titles = &komorebi_notification_state .focused_container_information + .1 .titles; + if !titles.is_empty() { config.apply_on_widget(false, ui, |ui| { let icons = &komorebi_notification_state - .focused_container_information + .focused_container_information.1 .icons; let focused_window_idx = komorebi_notification_state - .focused_container_information + .focused_container_information.1 .focused_window_idx; let iter = titles.iter().zip(icons.iter()); @@ -523,13 +612,13 @@ impl BarWidget for Komorebi { for (i, (title, icon)) in iter.enumerate() { let selected = i == focused_window_idx && len != 1; - let text_color = if selected { ctx.style().visuals.selection.stroke.color} else { ui.style().visuals.text_color() }; + let text_color = if selected { ctx.style().visuals.selection.stroke.color } else { ui.style().visuals.text_color() }; if SelectableFrame::new(selected) .show(ui, |ui| { // handle legacy setting - let format = focused_window.display.unwrap_or( - if focused_window.show_icon.unwrap_or(false) { + let format = focused_container_config.display.unwrap_or( + if focused_container_config.show_icon.unwrap_or(false) { DisplayFormat::IconAndText } else { DisplayFormat::Text @@ -632,7 +721,7 @@ pub struct KomorebiNotificationState { bool, )>, pub selected_workspace: String, - pub focused_container_information: KomorebiNotificationStateContainerInformation, + pub focused_container_information: (bool, KomorebiNotificationStateContainerInformation), pub layout: KomorebiLayout, pub hide_empty_workspaces: bool, pub mouse_follows_focus: bool, @@ -800,7 +889,12 @@ impl KomorebiNotificationState { }; } - self.focused_container_information = (&monitor.workspaces()[focused_workspace_idx]).into(); + let focused_workspace = &monitor.workspaces()[focused_workspace_idx]; + let is_focused = focused_workspace + .locked_containers() + .contains(&focused_workspace.focused_container_idx()); + + self.focused_container_information = (is_focused, focused_workspace.into()); } } diff --git a/komorebi-bar/src/widgets/komorebi_layout.rs b/komorebi-bar/src/widgets/komorebi_layout.rs index 495d980c..b8361097 100644 --- a/komorebi-bar/src/widgets/komorebi_layout.rs +++ b/komorebi-bar/src/widgets/komorebi_layout.rs @@ -251,7 +251,7 @@ impl KomorebiLayout { let layout_frame = SelectableFrame::new(false) .show(ui, |ui| { if let DisplayFormat::Icon | DisplayFormat::IconAndText = format { - self.show_icon(false, font_id.clone(), ctx, ui); + self.show_icon(true, font_id.clone(), ctx, ui); } if let DisplayFormat::Text | DisplayFormat::IconAndText = format { diff --git a/komorebi-bar/src/widgets/widget.rs b/komorebi-bar/src/widgets/widget.rs index 1ad0f43d..630a5808 100644 --- a/komorebi-bar/src/widgets/widget.rs +++ b/komorebi-bar/src/widgets/widget.rs @@ -72,7 +72,7 @@ impl WidgetConfig { WidgetConfig::Komorebi(config) => { config.workspaces.as_ref().is_some_and(|w| w.enable) || config.layout.as_ref().is_some_and(|w| w.enable) - || config.focused_window.as_ref().is_some_and(|w| w.enable) + || config.focused_container.as_ref().is_some_and(|w| w.enable) || config .configuration_switcher .as_ref() diff --git a/schema.bar.json b/schema.bar.json index 7b693884..1f4ee472 100644 --- a/schema.bar.json +++ b/schema.bar.json @@ -365,15 +365,15 @@ } } }, - "focused_window": { - "description": "Configure the Focused Window widget", + "focused_container": { + "description": "Configure the Focused Container widget", "type": "object", "required": [ "enable" ], "properties": { "display": { - "description": "Display format of the currently focused window", + "description": "Display format of the currently focused container", "oneOf": [ { "description": "Show only icon", @@ -413,11 +413,11 @@ ] }, "enable": { - "description": "Enable the Komorebi Focused Window widget", + "description": "Enable the Komorebi Focused Container widget", "type": "boolean" }, "show_icon": { - "description": "DEPRECATED: use 'display' instead (Show the icon of the currently focused window)", + "description": "DEPRECATED: use 'display' instead (Show the icon of the currently focused container)", "type": "boolean" } } @@ -508,6 +508,63 @@ } } }, + "locked_container": { + "description": "Configure the Locked Container widget", + "type": "object", + "required": [ + "enable" + ], + "properties": { + "display": { + "description": "Display format of the current locked state", + "oneOf": [ + { + "description": "Show only icon", + "type": "string", + "enum": [ + "Icon" + ] + }, + { + "description": "Show only text", + "type": "string", + "enum": [ + "Text" + ] + }, + { + "description": "Show an icon and text for the selected element, and text on the rest", + "type": "string", + "enum": [ + "TextAndIconOnSelected" + ] + }, + { + "description": "Show both icon and text", + "type": "string", + "enum": [ + "IconAndText" + ] + }, + { + "description": "Show an icon and text for the selected element, and icons on the rest", + "type": "string", + "enum": [ + "IconAndTextOnSelected" + ] + } + ] + }, + "enable": { + "description": "Enable the Komorebi Locked Container widget", + "type": "boolean" + }, + "show_when_unlocked": { + "description": "Show the widget event if the layer is unlocked", + "type": "boolean" + } + } + }, "workspace_layer": { "description": "Configure the Workspace Layer widget", "type": "object", @@ -1775,15 +1832,15 @@ } } }, - "focused_window": { - "description": "Configure the Focused Window widget", + "focused_container": { + "description": "Configure the Focused Container widget", "type": "object", "required": [ "enable" ], "properties": { "display": { - "description": "Display format of the currently focused window", + "description": "Display format of the currently focused container", "oneOf": [ { "description": "Show only icon", @@ -1823,11 +1880,11 @@ ] }, "enable": { - "description": "Enable the Komorebi Focused Window widget", + "description": "Enable the Komorebi Focused Container widget", "type": "boolean" }, "show_icon": { - "description": "DEPRECATED: use 'display' instead (Show the icon of the currently focused window)", + "description": "DEPRECATED: use 'display' instead (Show the icon of the currently focused container)", "type": "boolean" } } @@ -1918,6 +1975,63 @@ } } }, + "locked_container": { + "description": "Configure the Locked Container widget", + "type": "object", + "required": [ + "enable" + ], + "properties": { + "display": { + "description": "Display format of the current locked state", + "oneOf": [ + { + "description": "Show only icon", + "type": "string", + "enum": [ + "Icon" + ] + }, + { + "description": "Show only text", + "type": "string", + "enum": [ + "Text" + ] + }, + { + "description": "Show an icon and text for the selected element, and text on the rest", + "type": "string", + "enum": [ + "TextAndIconOnSelected" + ] + }, + { + "description": "Show both icon and text", + "type": "string", + "enum": [ + "IconAndText" + ] + }, + { + "description": "Show an icon and text for the selected element, and icons on the rest", + "type": "string", + "enum": [ + "IconAndTextOnSelected" + ] + } + ] + }, + "enable": { + "description": "Enable the Komorebi Locked Container widget", + "type": "boolean" + }, + "show_when_unlocked": { + "description": "Show the widget event if the layer is unlocked", + "type": "boolean" + } + } + }, "workspace_layer": { "description": "Configure the Workspace Layer widget", "type": "object", @@ -3118,15 +3232,15 @@ } } }, - "focused_window": { - "description": "Configure the Focused Window widget", + "focused_container": { + "description": "Configure the Focused Container widget", "type": "object", "required": [ "enable" ], "properties": { "display": { - "description": "Display format of the currently focused window", + "description": "Display format of the currently focused container", "oneOf": [ { "description": "Show only icon", @@ -3166,11 +3280,11 @@ ] }, "enable": { - "description": "Enable the Komorebi Focused Window widget", + "description": "Enable the Komorebi Focused Container widget", "type": "boolean" }, "show_icon": { - "description": "DEPRECATED: use 'display' instead (Show the icon of the currently focused window)", + "description": "DEPRECATED: use 'display' instead (Show the icon of the currently focused container)", "type": "boolean" } } @@ -3261,6 +3375,63 @@ } } }, + "locked_container": { + "description": "Configure the Locked Container widget", + "type": "object", + "required": [ + "enable" + ], + "properties": { + "display": { + "description": "Display format of the current locked state", + "oneOf": [ + { + "description": "Show only icon", + "type": "string", + "enum": [ + "Icon" + ] + }, + { + "description": "Show only text", + "type": "string", + "enum": [ + "Text" + ] + }, + { + "description": "Show an icon and text for the selected element, and text on the rest", + "type": "string", + "enum": [ + "TextAndIconOnSelected" + ] + }, + { + "description": "Show both icon and text", + "type": "string", + "enum": [ + "IconAndText" + ] + }, + { + "description": "Show an icon and text for the selected element, and icons on the rest", + "type": "string", + "enum": [ + "IconAndTextOnSelected" + ] + } + ] + }, + "enable": { + "description": "Enable the Komorebi Locked Container widget", + "type": "boolean" + }, + "show_when_unlocked": { + "description": "Show the widget event if the layer is unlocked", + "type": "boolean" + } + } + }, "workspace_layer": { "description": "Configure the Workspace Layer widget", "type": "object", diff --git a/schema.json b/schema.json index 38ff1a79..2e4e15d7 100644 --- a/schema.json +++ b/schema.json @@ -354,6 +354,45 @@ "format": "color-hex" } ] + }, + "unfocused_locked": { + "description": "Border colour when the container is unfocused and locked", + "anyOf": [ + { + "description": "Colour represented as RGB", + "type": "object", + "required": [ + "b", + "g", + "r" + ], + "properties": { + "b": { + "description": "Blue", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "g": { + "description": "Green", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "r": { + "description": "Red", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + }, + { + "description": "Colour represented as Hex", + "type": "string", + "format": "color-hex" + } + ] } } },