feat(bar): add locked container widget, use accent colour for icons

This commit adds a new komorebi widget to indicate whether or not the
focused container is locked.

This commit also includes an icon colour change on the layer and layout
widgets to the accent colour.

The commit also renames the locked_window widget to locked_container as
it is more suitable.

PR: #1394
This commit is contained in:
Csaba
2025-04-02 20:15:43 +02:00
committed by LGUG2Z
parent 052eb1c763
commit d38d3c956d
5 changed files with 341 additions and 37 deletions

View File

@@ -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<KomorebiLayoutConfig>,
/// Configure the Workspace Layer widget
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
/// Configure the Focused Window widget
pub focused_window: Option<KomorebiFocusedWindowConfig>,
/// Configure the Focused Container widget
#[serde(alias = "focused_window")]
pub focused_container: Option<KomorebiFocusedContainerConfig>,
/// Configure the Locked Container widget
pub locked_container: Option<KomorebiLockedContainerConfig>,
/// Configure the Configuration Switcher widget
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
}
@@ -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<bool>,
/// Display format of the currently focused window
/// Display format of the currently focused container
pub display: Option<DisplayFormat>,
}
#[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<DisplayFormat>,
/// Show the widget event if the layer is unlocked
pub show_when_unlocked: Option<bool>,
}
#[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<RefCell<KomorebiNotificationState>>,
pub workspaces: Option<KomorebiWorkspacesConfig>,
pub layout: Option<KomorebiLayoutConfig>,
pub focused_window: Option<KomorebiFocusedWindowConfig>,
pub focused_container: Option<KomorebiFocusedContainerConfig>,
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
pub locked_container: Option<KomorebiLockedContainerConfig>,
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
}
@@ -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());
}
}

View File

@@ -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 {

View File

@@ -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()