Skip to content

Commit 9d41a29

Browse files
committed
feat(wm): add tiling and floating ws layers
This commit introduces an implementation of workspace layers to komorebi. Workspace layers change the kinds of windows that certain commands operate on. This implementation features two variants, WorkspaceLayer::Tiling and WorkspaceLayer::Floating. The default behaviour until now has been WorkspaceLayer::Tiling. When the user sets WorkspaceLayer::Floating, either through the 'toggle-workspace-layer' command or the new bar widget, the 'move', 'focus', 'cycle-focus' and 'resize-axis' commands will operate on floating windows, if the currently focused window is a floating window. As I don't have 'cycle-focus' bound to anything, 'focus up' and 'focus down' double as incrementing and decrementing cycle focus commands, iterating focus through the floating windows assigned to a workspace. Floating windows in komorebi belong to specific workspaces, therefore commands such as 'move' and 'resize-axis' will restrict movement and resizing to the bounds of their workspace's work area (or more accurately, the work area of the monitor that the workspace belongs to, as floating windows are never constrained by workspace-specific work area restrictions).
1 parent 1756983 commit 9d41a29

File tree

12 files changed

+515
-79
lines changed

12 files changed

+515
-79
lines changed

docs/cli/toggle-workspace-layer.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# toggle-workspace-layer
2+
3+
```
4+
Toggle between the Tiling and Floating layers on the focused workspace
5+
6+
Usage: komorebic.exe toggle-workspace-layer
7+
8+
Options:
9+
-h, --help
10+
Print help
11+
12+
```

komorebi-bar/src/bar.rs

+4-10
Original file line numberDiff line numberDiff line change
@@ -365,16 +365,10 @@ impl Komobar {
365365
&SocketMessage::MonitorWorkAreaOffset(monitor_index, new_rect),
366366
) {
367367
tracing::error!(
368-
"error applying work area offset to monitor '{}': {}",
369-
monitor_index,
370-
error,
368+
"error applying work area offset to monitor '{monitor_index}': {error}"
371369
);
372370
} else {
373-
tracing::info!(
374-
"work area offset applied to monitor: {}\n, {:#?}",
375-
monitor_index,
376-
new_rect
377-
);
371+
tracing::info!("work area offset applied to monitor: {monitor_index}",);
378372
}
379373
}
380374
}
@@ -631,10 +625,10 @@ impl Komobar {
631625
let window = komorebi_client::Window::from(hwnd);
632626
match window.set_position(&self.size_rect, false) {
633627
Ok(_) => {
634-
tracing::info!("updated bar position: {:#?}", &self.size_rect);
628+
tracing::info!("updated bar position");
635629
}
636630
Err(error) => {
637-
tracing::error!("{}", error.to_string())
631+
tracing::error!("{error}")
638632
}
639633
}
640634
}

komorebi-bar/src/komorebi.rs

+54-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use komorebi_client::Rect;
3333
use komorebi_client::SocketMessage;
3434
use komorebi_client::Window;
3535
use komorebi_client::Workspace;
36+
use komorebi_client::WorkspaceLayer;
3637
use schemars::JsonSchema;
3738
use serde::Deserialize;
3839
use serde::Serialize;
@@ -49,6 +50,8 @@ pub struct KomorebiConfig {
4950
pub workspaces: Option<KomorebiWorkspacesConfig>,
5051
/// Configure the Layout widget
5152
pub layout: Option<KomorebiLayoutConfig>,
53+
/// Configure the Workspace Layer widget
54+
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
5255
/// Configure the Focused Window widget
5356
pub focused_window: Option<KomorebiFocusedWindowConfig>,
5457
/// Configure the Configuration Switcher widget
@@ -75,6 +78,12 @@ pub struct KomorebiLayoutConfig {
7578
pub display: Option<DisplayFormat>,
7679
}
7780

81+
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
82+
pub struct KomorebiWorkspaceLayerConfig {
83+
/// Enable the Komorebi Workspace Layer widget
84+
pub enable: bool,
85+
}
86+
7887
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
7988
pub struct KomorebiFocusedWindowConfig {
8089
/// Enable the Komorebi Focused Window widget
@@ -127,6 +136,7 @@ impl From<&KomorebiConfig> for Komorebi {
127136
workspaces: value.workspaces,
128137
layout: value.layout.clone(),
129138
focused_window: value.focused_window,
139+
workspace_layer: value.workspace_layer,
130140
configuration_switcher,
131141
}
132142
}
@@ -138,6 +148,7 @@ pub struct Komorebi {
138148
pub workspaces: Option<KomorebiWorkspacesConfig>,
139149
pub layout: Option<KomorebiLayoutConfig>,
140150
pub focused_window: Option<KomorebiFocusedWindowConfig>,
151+
pub workspace_layer: Option<KomorebiWorkspaceLayerConfig>,
141152
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
142153
}
143154

@@ -154,7 +165,7 @@ impl BarWidget for Komorebi {
154165
let format = workspaces.display.unwrap_or(DisplayFormat::Text);
155166

156167
config.apply_on_widget(false, ui, |ui| {
157-
for (i, (ws, container_information)) in
168+
for (i, (ws, container_information, _)) in
158169
komorebi_notification_state.workspaces.iter().enumerate()
159170
{
160171
if SelectableFrame::new(
@@ -281,6 +292,42 @@ impl BarWidget for Komorebi {
281292
}
282293
}
283294

295+
if let Some(layer_config) = &self.workspace_layer {
296+
if layer_config.enable {
297+
let layer = komorebi_notification_state
298+
.workspaces
299+
.iter()
300+
.find(|o| komorebi_notification_state.selected_workspace.eq(&o.0))
301+
.map(|(_, _, layer)| layer);
302+
303+
if let Some(layer) = layer {
304+
let name = layer.to_string();
305+
config.apply_on_widget(false, ui, |ui| {
306+
if SelectableFrame::new(false)
307+
.show(ui, |ui| ui.add(Label::new(name).selectable(false)))
308+
.clicked()
309+
&& komorebi_client::send_batch([
310+
SocketMessage::MouseFollowsFocus(false),
311+
SocketMessage::ToggleWorkspaceLayer,
312+
SocketMessage::MouseFollowsFocus(
313+
komorebi_notification_state.mouse_follows_focus,
314+
),
315+
])
316+
.is_err()
317+
{
318+
tracing::error!(
319+
"could not send the following batch of messages to komorebi:\n\
320+
MouseFollowsFocus(false),
321+
ToggleWorkspaceLayer,
322+
MouseFollowsFocus({})",
323+
komorebi_notification_state.mouse_follows_focus,
324+
);
325+
}
326+
});
327+
}
328+
}
329+
}
330+
284331
if let Some(layout_config) = &self.layout {
285332
if layout_config.enable {
286333
let workspace_idx: Option<usize> = komorebi_notification_state
@@ -476,7 +523,11 @@ fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
476523

477524
#[derive(Clone, Debug)]
478525
pub struct KomorebiNotificationState {
479-
pub workspaces: Vec<(String, KomorebiNotificationStateContainerInformation)>,
526+
pub workspaces: Vec<(
527+
String,
528+
KomorebiNotificationStateContainerInformation,
529+
WorkspaceLayer,
530+
)>,
480531
pub selected_workspace: String,
481532
pub focused_container_information: KomorebiNotificationStateContainerInformation,
482533
pub layout: KomorebiLayout,
@@ -592,6 +643,7 @@ impl KomorebiNotificationState {
592643
workspaces.push((
593644
ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)),
594645
ws.into(),
646+
ws.layer().to_owned(),
595647
));
596648
}
597649
}

komorebi-client/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub use komorebi::ring::Ring;
4848
pub use komorebi::window::Window;
4949
pub use komorebi::window_manager_event::WindowManagerEvent;
5050
pub use komorebi::workspace::Workspace;
51+
pub use komorebi::workspace::WorkspaceLayer;
5152
pub use komorebi::AnimationsConfig;
5253
pub use komorebi::AspectRatio;
5354
pub use komorebi::BorderColours;

komorebi/src/core/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ pub enum SocketMessage {
149149
NamedWorkspaceLayoutCustomRule(String, usize, PathBuf),
150150
ClearWorkspaceLayoutRules(usize, usize),
151151
ClearNamedWorkspaceLayoutRules(String),
152+
ToggleWorkspaceLayer,
152153
// Configuration
153154
ReloadConfiguration,
154155
ReplaceConfiguration(PathBuf),

komorebi/src/process_command.rs

+51-3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ use crate::window_manager;
6666
use crate::window_manager::WindowManager;
6767
use crate::windows_api::WindowsApi;
6868
use crate::winevent_listener;
69+
use crate::workspace::WorkspaceLayer;
6970
use crate::workspace::WorkspaceWindowLocation;
7071
use crate::GlobalState;
7172
use crate::Notification;
@@ -291,13 +292,37 @@ impl WindowManager {
291292
}
292293
}
293294
SocketMessage::FocusWindow(direction) => {
294-
self.focus_container_in_direction(direction)?;
295+
let focused_workspace = self.focused_workspace()?;
296+
match focused_workspace.layer() {
297+
WorkspaceLayer::Tiling => {
298+
self.focus_container_in_direction(direction)?;
299+
}
300+
WorkspaceLayer::Floating => {
301+
self.focus_floating_window_in_direction(direction)?;
302+
}
303+
}
295304
}
296305
SocketMessage::MoveWindow(direction) => {
297-
self.move_container_in_direction(direction)?;
306+
let focused_workspace = self.focused_workspace()?;
307+
match focused_workspace.layer() {
308+
WorkspaceLayer::Tiling => {
309+
self.move_container_in_direction(direction)?;
310+
}
311+
WorkspaceLayer::Floating => {
312+
self.move_floating_window_in_direction(direction)?;
313+
}
314+
}
298315
}
299316
SocketMessage::CycleFocusWindow(direction) => {
300-
self.focus_container_in_cycle_direction(direction)?;
317+
let focused_workspace = self.focused_workspace()?;
318+
match focused_workspace.layer() {
319+
WorkspaceLayer::Tiling => {
320+
self.focus_container_in_cycle_direction(direction)?;
321+
}
322+
WorkspaceLayer::Floating => {
323+
self.focus_floating_window_in_cycle_direction(direction)?;
324+
}
325+
}
301326
}
302327
SocketMessage::CycleMoveWindow(direction) => {
303328
self.move_container_in_cycle_direction(direction)?;
@@ -1020,6 +1045,29 @@ impl WindowManager {
10201045
self.focus_workspace(workspace_idx)?;
10211046
}
10221047
}
1048+
SocketMessage::ToggleWorkspaceLayer => {
1049+
let mouse_follows_focus = self.mouse_follows_focus;
1050+
let workspace = self.focused_workspace_mut()?;
1051+
1052+
match workspace.layer() {
1053+
WorkspaceLayer::Tiling => {
1054+
workspace.set_layer(WorkspaceLayer::Floating);
1055+
1056+
if let Some(first) = workspace.floating_windows().first() {
1057+
first.focus(mouse_follows_focus)?;
1058+
}
1059+
}
1060+
WorkspaceLayer::Floating => {
1061+
workspace.set_layer(WorkspaceLayer::Tiling);
1062+
1063+
if let Some(container) = workspace.focused_container() {
1064+
if let Some(window) = container.focused_window() {
1065+
window.focus(mouse_follows_focus)?;
1066+
}
1067+
}
1068+
}
1069+
};
1070+
}
10231071
SocketMessage::Stop => {
10241072
self.stop(false)?;
10251073
}

komorebi/src/process_event.rs

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::window_manager::WindowManager;
2626
use crate::window_manager_event::WindowManagerEvent;
2727
use crate::windows_api::WindowsApi;
2828
use crate::winevent::WinEvent;
29+
use crate::workspace::WorkspaceLayer;
2930
use crate::workspace_reconciliator;
3031
use crate::workspace_reconciliator::ALT_TAB_HWND;
3132
use crate::workspace_reconciliator::ALT_TAB_HWND_INSTANT;
@@ -280,10 +281,13 @@ impl WindowManager {
280281
} else {
281282
workspace.focus_container_by_window(window.hwnd)?;
282283
}
284+
285+
workspace.set_layer(WorkspaceLayer::Tiling);
283286
}
284287
Some(idx) => {
285288
if let Some(window) = workspace.floating_windows().get(idx) {
286289
window.focus(false)?;
290+
workspace.set_layer(WorkspaceLayer::Floating);
287291
}
288292
}
289293
}
@@ -393,11 +397,13 @@ impl WindowManager {
393397

394398
if behaviour.float_override {
395399
workspace.floating_windows_mut().push(window);
400+
workspace.set_layer(WorkspaceLayer::Floating);
396401
self.update_focused_workspace(false, false)?;
397402
} else {
398403
match behaviour.current_behaviour {
399404
WindowContainerBehaviour::Create => {
400405
workspace.new_container_for_window(window);
406+
workspace.set_layer(WorkspaceLayer::Tiling);
401407
self.update_focused_workspace(false, false)?;
402408
}
403409
WindowContainerBehaviour::Append => {
@@ -407,6 +413,7 @@ impl WindowManager {
407413
anyhow!("there is no focused container")
408414
})?
409415
.add_window(window);
416+
workspace.set_layer(WorkspaceLayer::Tiling);
410417
self.update_focused_workspace(true, false)?;
411418
stackbar_manager::send_notification();
412419
}

0 commit comments

Comments
 (0)