feat(bar): add opts to show all icons on workspace widget

This commit adds 3 new display options on the komorebi workspace widget
to show all icons.
This commit is contained in:
Csaba
2025-02-23 17:45:27 +01:00
committed by LGUG2Z
parent 3a208b577c
commit d69dfeb715
4 changed files with 247 additions and 86 deletions

View File

@@ -125,6 +125,15 @@ impl KomobarConfig {
} }
} }
} }
pub fn show_all_icons_on_komorebi_workspace(widgets: &[WidgetConfig]) -> bool {
widgets
.iter()
.any(|w| matches!(w, WidgetConfig::Komorebi(config) if config.workspaces.is_some_and(|w| w.enable && w.display.is_some_and(|s| matches!(s,
WorkspacesDisplayFormat::AllIcons
| WorkspacesDisplayFormat::AllIconsAndText
| WorkspacesDisplayFormat::AllIconsAndTextOnSelected)))))
}
} }
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]

View File

@@ -1,6 +1,7 @@
use crate::bar::apply_theme; use crate::bar::apply_theme;
use crate::config::DisplayFormat; use crate::config::DisplayFormat;
use crate::config::KomobarTheme; use crate::config::KomobarTheme;
use crate::config::WorkspacesDisplayFormat;
use crate::komorebi_layout::KomorebiLayout; use crate::komorebi_layout::KomorebiLayout;
use crate::render::Grouping; use crate::render::Grouping;
use crate::render::RenderConfig; use crate::render::RenderConfig;
@@ -65,7 +66,7 @@ pub struct KomorebiWorkspacesConfig {
/// Hide workspaces without any windows /// Hide workspaces without any windows
pub hide_empty_workspaces: bool, pub hide_empty_workspaces: bool,
/// Display format of the workspace /// Display format of the workspace
pub display: Option<DisplayFormat>, pub display: Option<WorkspacesDisplayFormat>,
} }
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
@@ -156,59 +157,61 @@ impl BarWidget for Komorebi {
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) { fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
let mut komorebi_notification_state = self.komorebi_notification_state.borrow_mut(); let mut komorebi_notification_state = self.komorebi_notification_state.borrow_mut();
let icon_size = Vec2::splat(config.icon_font_id.size); let icon_size = Vec2::splat(config.icon_font_id.size);
let text_size = Vec2::splat(config.text_font_id.size);
if let Some(workspaces) = self.workspaces { if let Some(workspaces) = self.workspaces {
if workspaces.enable { if workspaces.enable {
let mut update = None; let mut update = None;
if !komorebi_notification_state.workspaces.is_empty() { if !komorebi_notification_state.workspaces.is_empty() {
let format = workspaces.display.unwrap_or(DisplayFormat::Text); let format = workspaces.display.unwrap_or(DisplayFormat::Text.into());
config.apply_on_widget(false, ui, |ui| { config.apply_on_widget(false, ui, |ui| {
for (i, (ws, container_information, _)) in for (i, (ws, containers, _)) in
komorebi_notification_state.workspaces.iter().enumerate() komorebi_notification_state.workspaces.iter().enumerate()
{ {
let is_selected = komorebi_notification_state.selected_workspace.eq(ws);
if SelectableFrame::new( if SelectableFrame::new(
komorebi_notification_state.selected_workspace.eq(ws), is_selected,
) )
.show(ui, |ui| { .show(ui, |ui| {
let mut has_icon = false; let mut has_icon = false;
if format == DisplayFormat::Icon if format == WorkspacesDisplayFormat::AllIcons
|| format == DisplayFormat::IconAndText || format == WorkspacesDisplayFormat::AllIconsAndText
|| format == DisplayFormat::IconAndTextOnSelected || format == WorkspacesDisplayFormat::AllIconsAndTextOnSelected
|| (format == DisplayFormat::TextAndIconOnSelected || format == DisplayFormat::Icon.into()
&& komorebi_notification_state.selected_workspace.eq(ws)) || format == DisplayFormat::IconAndText.into()
|| format == DisplayFormat::IconAndTextOnSelected.into()
|| (format == DisplayFormat::TextAndIconOnSelected.into() && is_selected)
{ {
let icons: Vec<_> = has_icon = containers.iter().any(|(_, container_info)| {
container_information.icons.iter().flatten().collect(); container_info.icons.iter().any(|icon| icon.is_some())
});
if !icons.is_empty() { if has_icon {
Frame::none() Frame::none()
.inner_margin(Margin::same( .inner_margin(Margin::same(
ui.style().spacing.button_padding.y, ui.style().spacing.button_padding.y,
)) ))
.show(ui, |ui| { .show(ui, |ui| {
for icon in icons { for (is_focused, container) in containers {
ui.add( for icon in container.icons.iter().flatten().collect::<Vec<_>>() {
Image::from(&img_to_texture(ctx, icon)) ui.add(
.maintain_aspect_ratio(true) Image::from(&img_to_texture(ctx, icon))
.fit_to_exact_size(icon_size), .maintain_aspect_ratio(true)
); .fit_to_exact_size(if *is_focused { icon_size } else { text_size }),
);
if !has_icon {
has_icon = true;
} }
} }
}); });
} }
} }
// draw a custom icon when there is no app icon // draw a custom icon when there is no app icon or text
if match format { if !has_icon && (matches!(format, WorkspacesDisplayFormat::AllIcons | WorkspacesDisplayFormat::Existing(DisplayFormat::Icon))
DisplayFormat::Icon => !has_icon, || (!is_selected && matches!(format, WorkspacesDisplayFormat::AllIconsAndTextOnSelected | WorkspacesDisplayFormat::Existing(DisplayFormat::IconAndTextOnSelected)))) {
_ => false,
} {
let (response, painter) = let (response, painter) =
ui.allocate_painter(icon_size, Sense::hover()); ui.allocate_painter(icon_size, Sense::hover());
let stroke = Stroke::new( let stroke = Stroke::new(
@@ -224,14 +227,15 @@ impl BarWidget for Komorebi {
painter.line_segment([c - vec2(r, r), c + vec2(r, r)], stroke); painter.line_segment([c - vec2(r, r), c + vec2(r, r)], stroke);
response.on_hover_text(ws.to_string()) response.on_hover_text(ws.to_string())
// add hover text when there are only icons
} else if match format { } else if match format {
DisplayFormat::Icon => has_icon, WorkspacesDisplayFormat::AllIcons | WorkspacesDisplayFormat::Existing(DisplayFormat::Icon) => has_icon,
_ => false, _ => false,
} { } {
ui.response().on_hover_text(ws.to_string()) ui.response().on_hover_text(ws.to_string())
} else if format != DisplayFormat::IconAndTextOnSelected // add label only
|| (format == DisplayFormat::IconAndTextOnSelected } else if (format != WorkspacesDisplayFormat::AllIconsAndTextOnSelected && format != DisplayFormat::IconAndTextOnSelected.into())
&& komorebi_notification_state.selected_workspace.eq(ws)) || (is_selected && matches!(format, WorkspacesDisplayFormat::AllIconsAndTextOnSelected | WorkspacesDisplayFormat::Existing(DisplayFormat::IconAndTextOnSelected)))
{ {
ui.add(Label::new(ws.to_string()).selectable(false)) ui.add(Label::new(ws.to_string()).selectable(false))
} else { } else {
@@ -521,11 +525,12 @@ fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
ctx.load_texture("icon", color_image, TextureOptions::default()) ctx.load_texture("icon", color_image, TextureOptions::default())
} }
#[allow(clippy::type_complexity)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct KomorebiNotificationState { pub struct KomorebiNotificationState {
pub workspaces: Vec<( pub workspaces: Vec<(
String, String,
KomorebiNotificationStateContainerInformation, Vec<(bool, KomorebiNotificationStateContainerInformation)>,
WorkspaceLayer, WorkspaceLayer,
)>, )>,
pub selected_workspace: String, pub selected_workspace: String,
@@ -557,6 +562,8 @@ impl KomorebiNotificationState {
default_theme: Option<KomobarTheme>, default_theme: Option<KomobarTheme>,
render_config: Rc<RefCell<RenderConfig>>, render_config: Rc<RefCell<RenderConfig>>,
) { ) {
let show_all_icons = render_config.borrow().show_all_icons;
match notification.event { match notification.event {
NotificationEvent::WindowManager(_) => {} NotificationEvent::WindowManager(_) => {}
NotificationEvent::Monitor(_) => {} NotificationEvent::Monitor(_) => {}
@@ -627,6 +634,7 @@ impl KomorebiNotificationState {
let focused_workspace_idx = monitor.focused_workspace_idx(); let focused_workspace_idx = monitor.focused_workspace_idx();
let mut workspaces = vec![]; let mut workspaces = vec![];
self.selected_workspace = monitor.workspaces()[focused_workspace_idx] self.selected_workspace = monitor.workspaces()[focused_workspace_idx]
.name() .name()
.to_owned() .to_owned()
@@ -642,7 +650,36 @@ impl KomorebiNotificationState {
if should_show { if should_show {
workspaces.push(( workspaces.push((
ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)), ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)),
ws.into(), if show_all_icons {
let mut containers = vec![];
let mut has_monocle = false;
// add monocle container
if let Some(container) = ws.monocle_container() {
containers.push((true, container.into()));
has_monocle = true;
}
// add all tiled windows
for (i, container) in ws.containers().iter().enumerate() {
containers.push((
!has_monocle && i == ws.focused_container_idx(),
container.into(),
));
}
// add all floating windows
for floating_window in ws.floating_windows() {
containers.push((
!has_monocle && floating_window.is_focused(),
floating_window.into(),
));
}
containers
} else {
vec![(true, ws.into())]
},
ws.layer().to_owned(), ws.layer().to_owned(),
)); ));
} }

View File

@@ -54,6 +54,8 @@ pub struct RenderConfig {
pub text_font_id: FontId, pub text_font_id: FontId,
/// FontId for icon (based on scaling the text font id) /// FontId for icon (based on scaling the text font id)
pub icon_font_id: FontId, pub icon_font_id: FontId,
/// Show all icons on the workspace section of the Komorebi widget
pub show_all_icons: bool,
} }
pub trait RenderExt { pub trait RenderExt {
@@ -87,6 +89,15 @@ impl RenderExt for &KomobarConfig {
MonitorConfigOrIndex::Index(idx) => *idx, MonitorConfigOrIndex::Index(idx) => *idx,
}; };
// check if any of the alignments have a komorebi widget with the workspace set to show all icons
let show_all_icons =
KomobarConfig::show_all_icons_on_komorebi_workspace(&self.left_widgets)
|| self
.center_widgets
.as_ref()
.is_some_and(|list| KomobarConfig::show_all_icons_on_komorebi_workspace(list))
|| KomobarConfig::show_all_icons_on_komorebi_workspace(&self.right_widgets);
RenderConfig { RenderConfig {
monitor_idx, monitor_idx,
spacing: self.widget_spacing.unwrap_or(10.0), spacing: self.widget_spacing.unwrap_or(10.0),
@@ -97,6 +108,7 @@ impl RenderExt for &KomobarConfig {
applied_on_widget: false, applied_on_widget: false,
text_font_id, text_font_id,
icon_font_id, icon_font_id,
show_all_icons,
} }
} }
} }
@@ -121,6 +133,7 @@ impl RenderConfig {
applied_on_widget: false, applied_on_widget: false,
text_font_id: FontId::default(), text_font_id: FontId::default(),
icon_font_id: FontId::default(), icon_font_id: FontId::default(),
show_all_icons: false,
} }
} }

View File

@@ -529,39 +529,73 @@
"description": "Display format of the workspace", "description": "Display format of the workspace",
"oneOf": [ "oneOf": [
{ {
"description": "Show only icon", "description": "Show all icons only",
"type": "string", "type": "string",
"enum": [ "enum": [
"Icon" "AllIcons"
] ]
}, },
{ {
"description": "Show only text", "description": "Show both all icons and text",
"type": "string", "type": "string",
"enum": [ "enum": [
"Text" "AllIconsAndText"
] ]
}, },
{ {
"description": "Show an icon and text for the selected element, and text on the rest", "description": "Show all icons and text for the selected element, and all icons on the rest",
"type": "string", "type": "string",
"enum": [ "enum": [
"TextAndIconOnSelected" "AllIconsAndTextOnSelected"
] ]
}, },
{ {
"description": "Show both icon and text", "type": "object",
"type": "string", "required": [
"enum": [ "Existing"
"IconAndText" ],
] "properties": {
}, "Existing": {
{ "oneOf": [
"description": "Show an icon and text for the selected element, and icons on the rest", {
"type": "string", "description": "Show only icon",
"enum": [ "type": "string",
"IconAndTextOnSelected" "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"
]
}
]
}
},
"additionalProperties": false
} }
] ]
}, },
@@ -1849,39 +1883,73 @@
"description": "Display format of the workspace", "description": "Display format of the workspace",
"oneOf": [ "oneOf": [
{ {
"description": "Show only icon", "description": "Show all icons only",
"type": "string", "type": "string",
"enum": [ "enum": [
"Icon" "AllIcons"
] ]
}, },
{ {
"description": "Show only text", "description": "Show both all icons and text",
"type": "string", "type": "string",
"enum": [ "enum": [
"Text" "AllIconsAndText"
] ]
}, },
{ {
"description": "Show an icon and text for the selected element, and text on the rest", "description": "Show all icons and text for the selected element, and all icons on the rest",
"type": "string", "type": "string",
"enum": [ "enum": [
"TextAndIconOnSelected" "AllIconsAndTextOnSelected"
] ]
}, },
{ {
"description": "Show both icon and text", "type": "object",
"type": "string", "required": [
"enum": [ "Existing"
"IconAndText" ],
] "properties": {
}, "Existing": {
{ "oneOf": [
"description": "Show an icon and text for the selected element, and icons on the rest", {
"type": "string", "description": "Show only icon",
"enum": [ "type": "string",
"IconAndTextOnSelected" "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"
]
}
]
}
},
"additionalProperties": false
} }
] ]
}, },
@@ -3102,39 +3170,73 @@
"description": "Display format of the workspace", "description": "Display format of the workspace",
"oneOf": [ "oneOf": [
{ {
"description": "Show only icon", "description": "Show all icons only",
"type": "string", "type": "string",
"enum": [ "enum": [
"Icon" "AllIcons"
] ]
}, },
{ {
"description": "Show only text", "description": "Show both all icons and text",
"type": "string", "type": "string",
"enum": [ "enum": [
"Text" "AllIconsAndText"
] ]
}, },
{ {
"description": "Show an icon and text for the selected element, and text on the rest", "description": "Show all icons and text for the selected element, and all icons on the rest",
"type": "string", "type": "string",
"enum": [ "enum": [
"TextAndIconOnSelected" "AllIconsAndTextOnSelected"
] ]
}, },
{ {
"description": "Show both icon and text", "type": "object",
"type": "string", "required": [
"enum": [ "Existing"
"IconAndText" ],
] "properties": {
}, "Existing": {
{ "oneOf": [
"description": "Show an icon and text for the selected element, and icons on the rest", {
"type": "string", "description": "Show only icon",
"enum": [ "type": "string",
"IconAndTextOnSelected" "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"
]
}
]
}
},
"additionalProperties": false
} }
] ]
}, },