mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-25 01:58:51 +02:00
feat(bar): optional workspaces on Komorebi widget
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.
This commit is contained in:
@@ -281,34 +281,26 @@ impl Komobar {
|
|||||||
self.render_config
|
self.render_config
|
||||||
.replace(config.new_renderconfig(ctx, theme_color));
|
.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 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() {
|
for (idx, widget_config) in config.left_widgets.iter().enumerate() {
|
||||||
if let WidgetConfig::Komorebi(config) = widget_config {
|
if let WidgetConfig::Komorebi(config) = widget_config {
|
||||||
komorebi_widget = Some(Komorebi::from(config));
|
komorebi_widgets.push((Komorebi::from(config), idx, Alignment::Left));
|
||||||
komorebi_widget_idx = Some(idx);
|
|
||||||
side = Some(Alignment::Left);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(center_widgets) = &config.center_widgets {
|
if let Some(center_widgets) = &config.center_widgets {
|
||||||
for (idx, widget_config) in center_widgets.iter().enumerate() {
|
for (idx, widget_config) in center_widgets.iter().enumerate() {
|
||||||
if let WidgetConfig::Komorebi(config) = widget_config {
|
if let WidgetConfig::Komorebi(config) = widget_config {
|
||||||
komorebi_widget = Some(Komorebi::from(config));
|
komorebi_widgets.push((Komorebi::from(config), idx, Alignment::Center));
|
||||||
komorebi_widget_idx = Some(idx);
|
|
||||||
side = Some(Alignment::Center);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (idx, widget_config) in config.right_widgets.iter().enumerate() {
|
for (idx, widget_config) in config.right_widgets.iter().enumerate() {
|
||||||
if let WidgetConfig::Komorebi(config) = widget_config {
|
if let WidgetConfig::Komorebi(config) = widget_config {
|
||||||
komorebi_widget = Some(Komorebi::from(config));
|
komorebi_widgets.push((Komorebi::from(config), idx, Alignment::Right));
|
||||||
komorebi_widget_idx = Some(idx);
|
|
||||||
side = Some(Alignment::Right);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,28 +327,33 @@ impl Komobar {
|
|||||||
.map(|config| config.as_boxed_bar_widget())
|
.map(|config| config.as_boxed_bar_widget())
|
||||||
.collect::<Vec<Box<dyn BarWidget>>>();
|
.collect::<Vec<Box<dyn BarWidget>>>();
|
||||||
|
|
||||||
if let (Some(idx), Some(mut widget), Some(side)) =
|
if !komorebi_widgets.is_empty() {
|
||||||
(komorebi_widget_idx, komorebi_widget, side)
|
komorebi_widgets
|
||||||
{
|
.into_iter()
|
||||||
match komorebi_notification_state {
|
.for_each(|(mut widget, idx, side)| {
|
||||||
None => {
|
match komorebi_notification_state {
|
||||||
komorebi_notification_state = Some(widget.komorebi_notification_state.clone());
|
None => {
|
||||||
}
|
komorebi_notification_state =
|
||||||
Some(ref previous) => {
|
Some(widget.komorebi_notification_state.clone());
|
||||||
previous
|
}
|
||||||
.borrow_mut()
|
Some(ref previous) => {
|
||||||
.update_from_config(&widget.komorebi_notification_state.borrow());
|
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);
|
let boxed: Box<dyn BarWidget> = Box::new(widget);
|
||||||
match side {
|
match side {
|
||||||
Alignment::Left => left_widgets[idx] = boxed,
|
Alignment::Left => left_widgets[idx] = boxed,
|
||||||
Alignment::Center => center_widgets[idx] = boxed,
|
Alignment::Center => center_widgets[idx] = boxed,
|
||||||
Alignment::Right => right_widgets[idx] = boxed,
|
Alignment::Right => right_widgets[idx] = boxed,
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
right_widgets.reverse();
|
right_widgets.reverse();
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ use std::sync::atomic::Ordering;
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct KomorebiConfig {
|
pub struct KomorebiConfig {
|
||||||
/// Configure the Workspaces widget
|
/// Configure the Workspaces widget
|
||||||
pub workspaces: KomorebiWorkspacesConfig,
|
pub workspaces: Option<KomorebiWorkspacesConfig>,
|
||||||
/// Configure the Layout widget
|
/// Configure the Layout widget
|
||||||
pub layout: Option<KomorebiLayoutConfig>,
|
pub layout: Option<KomorebiLayoutConfig>,
|
||||||
/// Configure the Focused Window widget
|
/// Configure the Focused Window widget
|
||||||
@@ -112,7 +112,10 @@ impl From<&KomorebiConfig> for Komorebi {
|
|||||||
selected_workspace: String::new(),
|
selected_workspace: String::new(),
|
||||||
layout: KomorebiLayout::Default(komorebi_client::DefaultLayout::BSP),
|
layout: KomorebiLayout::Default(komorebi_client::DefaultLayout::BSP),
|
||||||
workspaces: vec![],
|
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,
|
mouse_follows_focus: true,
|
||||||
work_area_offset: None,
|
work_area_offset: None,
|
||||||
focused_container_information: KomorebiNotificationStateContainerInformation::EMPTY,
|
focused_container_information: KomorebiNotificationStateContainerInformation::EMPTY,
|
||||||
@@ -130,7 +133,7 @@ impl From<&KomorebiConfig> for Komorebi {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Komorebi {
|
pub struct Komorebi {
|
||||||
pub komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
pub komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
||||||
pub workspaces: KomorebiWorkspacesConfig,
|
pub workspaces: Option<KomorebiWorkspacesConfig>,
|
||||||
pub layout: Option<KomorebiLayoutConfig>,
|
pub layout: Option<KomorebiLayoutConfig>,
|
||||||
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
||||||
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
|
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
|
||||||
@@ -141,124 +144,128 @@ impl BarWidget for Komorebi {
|
|||||||
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);
|
||||||
|
|
||||||
if self.workspaces.enable {
|
if let Some(workspaces) = self.workspaces {
|
||||||
let mut update = None;
|
if workspaces.enable {
|
||||||
|
let mut update = None;
|
||||||
|
|
||||||
if !komorebi_notification_state.workspaces.is_empty() {
|
if !komorebi_notification_state.workspaces.is_empty() {
|
||||||
let format = self.workspaces.display.unwrap_or(DisplayFormat::Text);
|
let format = workspaces.display.unwrap_or(DisplayFormat::Text);
|
||||||
|
|
||||||
config.apply_on_widget(false, ui, |ui| {
|
config.apply_on_widget(false, ui, |ui| {
|
||||||
for (i, (ws, container_information)) in
|
for (i, (ws, container_information)) in
|
||||||
komorebi_notification_state.workspaces.iter().enumerate()
|
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());
|
if SelectableFrame::new(
|
||||||
|
komorebi_notification_state.selected_workspace.eq(ws),
|
||||||
|
)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
let mut has_icon = false;
|
||||||
|
|
||||||
if komorebi_notification_state.mouse_follows_focus {
|
if let DisplayFormat::Icon | DisplayFormat::IconAndText = format {
|
||||||
if komorebi_client::send_batch([
|
let icons: Vec<_> =
|
||||||
SocketMessage::MouseFollowsFocus(false),
|
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());
|
||||||
|
|
||||||
|
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(
|
SocketMessage::FocusMonitorWorkspaceNumber(
|
||||||
komorebi_notification_state.monitor_index,
|
komorebi_notification_state.monitor_index,
|
||||||
i,
|
i,
|
||||||
),
|
),
|
||||||
SocketMessage::RetileWithResizeDimensions,
|
SocketMessage::RetileWithResizeDimensions,
|
||||||
SocketMessage::MouseFollowsFocus(true),
|
|
||||||
])
|
])
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
"could not send the following batch of messages to komorebi:\n
|
"could not send the following batch of messages to komorebi:\n
|
||||||
MouseFollowsFocus(false)\n
|
|
||||||
FocusMonitorWorkspaceNumber({}, {})\n
|
FocusMonitorWorkspaceNumber({}, {})\n
|
||||||
RetileWithResizeDimensions
|
RetileWithResizeDimensions",
|
||||||
MouseFollowsFocus(true)\n",
|
|
||||||
komorebi_notification_state.monitor_index,
|
komorebi_notification_state.monitor_index,
|
||||||
i,
|
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 {
|
if let Some(update) = update {
|
||||||
komorebi_notification_state.selected_workspace = update;
|
komorebi_notification_state.selected_workspace = update;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,22 +61,13 @@ impl WidgetConfig {
|
|||||||
WidgetConfig::Cpu(config) => config.enable,
|
WidgetConfig::Cpu(config) => config.enable,
|
||||||
WidgetConfig::Date(config) => config.enable,
|
WidgetConfig::Date(config) => config.enable,
|
||||||
WidgetConfig::Komorebi(config) => {
|
WidgetConfig::Komorebi(config) => {
|
||||||
config.workspaces.enable
|
config.workspaces.as_ref().map_or(false, |w| w.enable)
|
||||||
|| (if let Some(layout) = &config.layout {
|
|| config.layout.as_ref().map_or(false, |w| w.enable)
|
||||||
layout.enable
|
|| config.focused_window.as_ref().map_or(false, |w| w.enable)
|
||||||
} else {
|
|| config
|
||||||
false
|
.configuration_switcher
|
||||||
})
|
.as_ref()
|
||||||
|| (if let Some(focused_window) = &config.focused_window {
|
.map_or(false, |w| w.enable)
|
||||||
focused_window.enable
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
})
|
|
||||||
|| (if let Some(configuration_switcher) = &config.configuration_switcher {
|
|
||||||
configuration_switcher.enable
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
WidgetConfig::Media(config) => config.enable,
|
WidgetConfig::Media(config) => config.enable,
|
||||||
WidgetConfig::Memory(config) => config.enable,
|
WidgetConfig::Memory(config) => config.enable,
|
||||||
|
|||||||
Reference in New Issue
Block a user