Skip to content

Commit

Permalink
feat(bar): optional workspaces on Komorebi widget
Browse files Browse the repository at this point in the history
This commit makes the `workspaces` on `Komorebi` widget optional. This
way it allows adding the `workspaces` on one Alignment and the
`focused_window` on another one, for example.
  • Loading branch information
alex-ds13 committed Dec 17, 2024
1 parent 3c0b12f commit 53c74a2
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 147 deletions.
61 changes: 29 additions & 32 deletions komorebi-bar/src/bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,34 +281,26 @@ impl Komobar {
self.render_config
.replace(config.new_renderconfig(ctx, theme_color));

let mut komorebi_widget = None;
let mut komorebi_widget_idx = None;
let mut komorebi_notification_state = previous_notification_state;
let mut side = None;
let mut komorebi_widgets = Vec::new();

for (idx, widget_config) in config.left_widgets.iter().enumerate() {
if let WidgetConfig::Komorebi(config) = widget_config {
komorebi_widget = Some(Komorebi::from(config));
komorebi_widget_idx = Some(idx);
side = Some(Alignment::Left);
komorebi_widgets.push((Komorebi::from(config), idx, Alignment::Left));
}
}

if let Some(center_widgets) = &config.center_widgets {
for (idx, widget_config) in center_widgets.iter().enumerate() {
if let WidgetConfig::Komorebi(config) = widget_config {
komorebi_widget = Some(Komorebi::from(config));
komorebi_widget_idx = Some(idx);
side = Some(Alignment::Center);
komorebi_widgets.push((Komorebi::from(config), idx, Alignment::Center));
}
}
}

for (idx, widget_config) in config.right_widgets.iter().enumerate() {
if let WidgetConfig::Komorebi(config) = widget_config {
komorebi_widget = Some(Komorebi::from(config));
komorebi_widget_idx = Some(idx);
side = Some(Alignment::Right);
komorebi_widgets.push((Komorebi::from(config), idx, Alignment::Right));
}
}

Expand All @@ -335,28 +327,33 @@ impl Komobar {
.map(|config| config.as_boxed_bar_widget())
.collect::<Vec<Box<dyn BarWidget>>>();

if let (Some(idx), Some(mut widget), Some(side)) =
(komorebi_widget_idx, komorebi_widget, side)
{
match komorebi_notification_state {
None => {
komorebi_notification_state = Some(widget.komorebi_notification_state.clone());
}
Some(ref previous) => {
previous
.borrow_mut()
.update_from_config(&widget.komorebi_notification_state.borrow());
if !komorebi_widgets.is_empty() {
komorebi_widgets
.into_iter()
.for_each(|(mut widget, idx, side)| {
match komorebi_notification_state {
None => {
komorebi_notification_state =
Some(widget.komorebi_notification_state.clone());
}
Some(ref previous) => {
if widget.workspaces.map_or(false, |w| w.enable) {
previous.borrow_mut().update_from_config(
&widget.komorebi_notification_state.borrow(),
);
}

widget.komorebi_notification_state = previous.clone();
}
}
widget.komorebi_notification_state = previous.clone();
}
}

let boxed: Box<dyn BarWidget> = Box::new(widget);
match side {
Alignment::Left => left_widgets[idx] = boxed,
Alignment::Center => center_widgets[idx] = boxed,
Alignment::Right => right_widgets[idx] = boxed,
}
let boxed: Box<dyn BarWidget> = Box::new(widget);
match side {
Alignment::Left => left_widgets[idx] = boxed,
Alignment::Center => center_widgets[idx] = boxed,
Alignment::Right => right_widgets[idx] = boxed,
}
});
}

right_widgets.reverse();
Expand Down
205 changes: 106 additions & 99 deletions komorebi-bar/src/komorebi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use std::sync::atomic::Ordering;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct KomorebiConfig {
/// Configure the Workspaces widget
pub workspaces: KomorebiWorkspacesConfig,
pub workspaces: Option<KomorebiWorkspacesConfig>,
/// Configure the Layout widget
pub layout: Option<KomorebiLayoutConfig>,
/// Configure the Focused Window widget
Expand Down Expand Up @@ -112,7 +112,10 @@ impl From<&KomorebiConfig> for Komorebi {
selected_workspace: String::new(),
layout: KomorebiLayout::Default(komorebi_client::DefaultLayout::BSP),
workspaces: vec![],
hide_empty_workspaces: value.workspaces.hide_empty_workspaces,
hide_empty_workspaces: value
.workspaces
.map(|w| w.hide_empty_workspaces)
.unwrap_or_default(),
mouse_follows_focus: true,
work_area_offset: None,
focused_container_information: KomorebiNotificationStateContainerInformation::EMPTY,
Expand All @@ -130,7 +133,7 @@ impl From<&KomorebiConfig> for Komorebi {
#[derive(Clone, Debug)]
pub struct Komorebi {
pub komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
pub workspaces: KomorebiWorkspacesConfig,
pub workspaces: Option<KomorebiWorkspacesConfig>,
pub layout: Option<KomorebiLayoutConfig>,
pub focused_window: Option<KomorebiFocusedWindowConfig>,
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
Expand All @@ -141,124 +144,128 @@ impl BarWidget for Komorebi {
let mut komorebi_notification_state = self.komorebi_notification_state.borrow_mut();
let icon_size = Vec2::splat(config.icon_font_id.size);

if self.workspaces.enable {
let mut update = None;

if !komorebi_notification_state.workspaces.is_empty() {
let format = self.workspaces.display.unwrap_or(DisplayFormat::Text);

config.apply_on_widget(false, ui, |ui| {
for (i, (ws, container_information)) in
komorebi_notification_state.workspaces.iter().enumerate()
{
if SelectableFrame::new(
komorebi_notification_state.selected_workspace.eq(ws),
)
.show(ui, |ui| {
let mut has_icon = false;

if let DisplayFormat::Icon | DisplayFormat::IconAndText = format {
let icons: Vec<_> =
container_information.icons.iter().flatten().collect();

if !icons.is_empty() {
Frame::none()
.inner_margin(Margin::same(
ui.style().spacing.button_padding.y,
))
.show(ui, |ui| {
for icon in icons {
ui.add(
Image::from(&img_to_texture(ctx, icon))
.maintain_aspect_ratio(true)
.fit_to_exact_size(icon_size),
);
if let Some(workspaces) = self.workspaces {
if workspaces.enable {
let mut update = None;

if !komorebi_notification_state.workspaces.is_empty() {
let format = workspaces.display.unwrap_or(DisplayFormat::Text);

if !has_icon {
has_icon = true;
config.apply_on_widget(false, ui, |ui| {
for (i, (ws, container_information)) in
komorebi_notification_state.workspaces.iter().enumerate()
{
if SelectableFrame::new(
komorebi_notification_state.selected_workspace.eq(ws),
)
.show(ui, |ui| {
let mut has_icon = false;

if let DisplayFormat::Icon | DisplayFormat::IconAndText = format {
let icons: Vec<_> =
container_information.icons.iter().flatten().collect();

if !icons.is_empty() {
Frame::none()
.inner_margin(Margin::same(
ui.style().spacing.button_padding.y,
))
.show(ui, |ui| {
for icon in icons {
ui.add(
Image::from(&img_to_texture(ctx, icon))
.maintain_aspect_ratio(true)
.fit_to_exact_size(icon_size),
);

if !has_icon {
has_icon = true;
}
}
}
});
});
}
}
}

// draw a custom icon when there is no app icon
if match format {
DisplayFormat::Icon => !has_icon,
_ => false,
} {
let (response, painter) =
ui.allocate_painter(icon_size, Sense::hover());
let stroke =
Stroke::new(1.0, ctx.style().visuals.selection.stroke.color);
let mut rect = response.rect;
let rounding = Rounding::same(rect.width() * 0.1);
rect = rect.shrink(stroke.width);
let c = rect.center();
let r = rect.width() / 2.0;
painter.rect_stroke(rect, rounding, stroke);
painter.line_segment([c - vec2(r, r), c + vec2(r, r)], stroke);

response.on_hover_text(ws.to_string())
} else if match format {
DisplayFormat::Icon => has_icon,
_ => false,
} {
ui.response().on_hover_text(ws.to_string())
} else {
ui.add(Label::new(ws.to_string()).selectable(false))
}
})
.clicked()
{
update = Some(ws.to_string());
// draw a custom icon when there is no app icon
if match format {
DisplayFormat::Icon => !has_icon,
_ => false,
} {
let (response, painter) =
ui.allocate_painter(icon_size, Sense::hover());
let stroke = Stroke::new(
1.0,
ctx.style().visuals.selection.stroke.color,
);
let mut rect = response.rect;
let rounding = Rounding::same(rect.width() * 0.1);
rect = rect.shrink(stroke.width);
let c = rect.center();
let r = rect.width() / 2.0;
painter.rect_stroke(rect, rounding, stroke);
painter.line_segment([c - vec2(r, r), c + vec2(r, r)], stroke);

response.on_hover_text(ws.to_string())
} else if match format {
DisplayFormat::Icon => has_icon,
_ => false,
} {
ui.response().on_hover_text(ws.to_string())
} else {
ui.add(Label::new(ws.to_string()).selectable(false))
}
})
.clicked()
{
update = Some(ws.to_string());

if komorebi_notification_state.mouse_follows_focus {
if komorebi_client::send_batch([
SocketMessage::MouseFollowsFocus(false),
if komorebi_notification_state.mouse_follows_focus {
if komorebi_client::send_batch([
SocketMessage::MouseFollowsFocus(false),
SocketMessage::FocusMonitorWorkspaceNumber(
komorebi_notification_state.monitor_index,
i,
),
SocketMessage::RetileWithResizeDimensions,
SocketMessage::MouseFollowsFocus(true),
])
.is_err()
{
tracing::error!(
"could not send the following batch of messages to komorebi:\n
MouseFollowsFocus(false)\n
FocusMonitorWorkspaceNumber({}, {})\n
RetileWithResizeDimensions
MouseFollowsFocus(true)\n",
komorebi_notification_state.monitor_index,
i,
);
}
} else if komorebi_client::send_batch([
SocketMessage::FocusMonitorWorkspaceNumber(
komorebi_notification_state.monitor_index,
i,
),
SocketMessage::RetileWithResizeDimensions,
SocketMessage::MouseFollowsFocus(true),
])
.is_err()
.is_err()
{
tracing::error!(
"could not send the following batch of messages to komorebi:\n
MouseFollowsFocus(false)\n
FocusMonitorWorkspaceNumber({}, {})\n
RetileWithResizeDimensions
MouseFollowsFocus(true)\n",
RetileWithResizeDimensions",
komorebi_notification_state.monitor_index,
i,
);
}
} else if komorebi_client::send_batch([
SocketMessage::FocusMonitorWorkspaceNumber(
komorebi_notification_state.monitor_index,
i,
),
SocketMessage::RetileWithResizeDimensions,
])
.is_err()
{
tracing::error!(
"could not send the following batch of messages to komorebi:\n
FocusMonitorWorkspaceNumber({}, {})\n
RetileWithResizeDimensions",
komorebi_notification_state.monitor_index,
i,
);
}
}
}
});
}
});
}

if let Some(update) = update {
komorebi_notification_state.selected_workspace = update;
if let Some(update) = update {
komorebi_notification_state.selected_workspace = update;
}
}
}

Expand Down
23 changes: 7 additions & 16 deletions komorebi-bar/src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,13 @@ impl WidgetConfig {
WidgetConfig::Cpu(config) => config.enable,
WidgetConfig::Date(config) => config.enable,
WidgetConfig::Komorebi(config) => {
config.workspaces.enable
|| (if let Some(layout) = &config.layout {
layout.enable
} else {
false
})
|| (if let Some(focused_window) = &config.focused_window {
focused_window.enable
} else {
false
})
|| (if let Some(configuration_switcher) = &config.configuration_switcher {
configuration_switcher.enable
} else {
false
})
config.workspaces.as_ref().map_or(false, |w| w.enable)
|| config.layout.as_ref().map_or(false, |w| w.enable)
|| config.focused_window.as_ref().map_or(false, |w| w.enable)
|| config
.configuration_switcher
.as_ref()
.map_or(false, |w| w.enable)
}
WidgetConfig::Media(config) => config.enable,
WidgetConfig::Memory(config) => config.enable,
Expand Down

0 comments on commit 53c74a2

Please sign in to comment.