feat(wm): add per-workspace tiling config + toggle

Added two commands, 'komorebic toggle-tiling' and 'komorebic
workspace-tiling MONITOR_IDX WORKSPACE_IDX on|off' which allow for
tiling on the currently focused workspace to be toggled on and off, and
for the tiling for a specific workspace to be set to on or off (useful
if you want a specific workspace to always have tiling set to off at
startup).

resolve #5
This commit is contained in:
LGUG2Z
2021-08-14 10:17:54 -07:00
parent b8929cbead
commit 820432f9d4
5 changed files with 94 additions and 23 deletions

View File

@@ -40,6 +40,7 @@ pub enum SocketMessage {
// Monitor and Workspace Commands // Monitor and Workspace Commands
EnsureWorkspaces(usize, usize), EnsureWorkspaces(usize, usize),
NewWorkspace, NewWorkspace,
ToggleTiling,
Stop, Stop,
TogglePause, TogglePause,
Retile, Retile,
@@ -47,6 +48,7 @@ pub enum SocketMessage {
FocusWorkspaceNumber(usize), FocusWorkspaceNumber(usize),
ContainerPadding(usize, usize, i32), ContainerPadding(usize, usize, i32),
WorkspacePadding(usize, usize, i32), WorkspacePadding(usize, usize, i32),
WorkspaceTiling(usize, usize, bool),
WorkspaceName(usize, usize, String), WorkspaceName(usize, usize, String),
WorkspaceLayout(usize, usize, Layout), WorkspaceLayout(usize, usize, Layout),
// Configuration // Configuration

View File

@@ -102,6 +102,9 @@ impl WindowManager {
tracing::info!("pausing"); tracing::info!("pausing");
self.is_paused = !self.is_paused; self.is_paused = !self.is_paused;
} }
SocketMessage::ToggleTiling => {
self.toggle_tiling()?;
}
SocketMessage::FocusMonitorNumber(monitor_idx) => { SocketMessage::FocusMonitorNumber(monitor_idx) => {
self.focus_monitor(monitor_idx)?; self.focus_monitor(monitor_idx)?;
self.update_focused_workspace(true)?; self.update_focused_workspace(true)?;
@@ -123,6 +126,9 @@ impl WindowManager {
} }
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?, SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout(layout)?, SocketMessage::ChangeLayout(layout) => self.change_workspace_layout(layout)?,
SocketMessage::WorkspaceTiling(monitor_idx, workspace_idx, tile) => {
self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?;
}
SocketMessage::WorkspaceLayout(monitor_idx, workspace_idx, layout) => { SocketMessage::WorkspaceLayout(monitor_idx, workspace_idx, layout) => {
self.set_workspace_layout(monitor_idx, workspace_idx, layout)?; self.set_workspace_layout(monitor_idx, workspace_idx, layout)?;
} }

View File

@@ -326,6 +326,13 @@ impl WindowManager {
self.update_focused_workspace(true) self.update_focused_workspace(true)
} }
#[tracing::instrument(skip(self))]
pub fn toggle_tiling(&mut self) -> Result<()> {
let workspace = self.focused_workspace_mut()?;
workspace.set_tile(!workspace.tile());
self.update_focused_workspace(false)
}
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn toggle_float(&mut self) -> Result<()> { pub fn toggle_float(&mut self) -> Result<()> {
let hwnd = WindowsApi::top_visible_window()?; let hwnd = WindowsApi::top_visible_window()?;
@@ -496,6 +503,28 @@ impl WindowManager {
self.update_focused_workspace(false) self.update_focused_workspace(false)
} }
#[tracing::instrument(skip(self))]
pub fn set_workspace_tiling(
&mut self,
monitor_idx: usize,
workspace_idx: usize,
tile: bool,
) -> Result<()> {
let monitor = self
.monitors_mut()
.get_mut(monitor_idx)
.context("there is no monitor")?;
let workspace = monitor
.workspaces_mut()
.get_mut(workspace_idx)
.context("there is no monitor")?;
workspace.set_tile(tile);
self.update_focused_workspace(false)
}
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn set_workspace_layout( pub fn set_workspace_layout(
&mut self, &mut self,

View File

@@ -44,6 +44,8 @@ pub struct Workspace {
#[serde(skip_serializing)] #[serde(skip_serializing)]
#[getset(get = "pub", get_mut = "pub")] #[getset(get = "pub", get_mut = "pub")]
resize_dimensions: Vec<Option<Rect>>, resize_dimensions: Vec<Option<Rect>>,
#[getset(get = "pub", set = "pub")]
tile: bool,
} }
impl_ring_elements!(Workspace, Container); impl_ring_elements!(Workspace, Container);
@@ -62,6 +64,7 @@ impl Default for Workspace {
container_padding: Option::from(10), container_padding: Option::from(10),
latest_layout: vec![], latest_layout: vec![],
resize_dimensions: vec![], resize_dimensions: vec![],
tile: true,
} }
} }
} }
@@ -102,33 +105,37 @@ impl Workspace {
self.enforce_resize_constraints(); self.enforce_resize_constraints();
if let Some(container) = self.monocle_container_mut() { if *self.tile() {
if let Some(window) = container.focused_window_mut() { if let Some(container) = self.monocle_container_mut() {
window.set_position(&adjusted_work_area, true)?; if let Some(window) = container.focused_window_mut() {
} window.set_position(&adjusted_work_area, true)?;
} else {
let layouts = self.layout().calculate(
&adjusted_work_area,
self.containers().len(),
self.container_padding(),
self.layout_flip(),
self.resize_dimensions(),
);
let windows = self.visible_windows_mut();
for (i, window) in windows.into_iter().enumerate() {
if let (Some(window), Some(layout)) = (window, layouts.get(i)) {
window.set_position(layout, false)?;
} }
} } else {
let layouts = self.layout().calculate(
&adjusted_work_area,
self.containers().len(),
self.container_padding(),
self.layout_flip(),
self.resize_dimensions(),
);
// Always make sure that the length of the resize dimensions vec is the same as the let windows = self.visible_windows_mut();
// number of layouts / containers. This should never actually truncate as the remove_window for (i, window) in windows.into_iter().enumerate() {
// function takes care of cleaning up resize dimensions when destroying empty containers if let (Some(window), Some(layout)) = (window, layouts.get(i)) {
self.resize_dimensions_mut().resize(layouts.len(), None); window.set_position(layout, false)?;
self.set_latest_layout(layouts); }
}
self.set_latest_layout(layouts);
}
} }
// Always make sure that the length of the resize dimensions vec is the same as the
// number of layouts / containers. This should never actually truncate as the remove_window
// function takes care of cleaning up resize dimensions when destroying empty containers
let container_count = self.containers().len();
self.resize_dimensions_mut().resize(container_count, None);
Ok(()) Ok(())
} }

View File

@@ -47,7 +47,9 @@ enum SubCommand {
ContainerPadding(SizeForMonitorWorkspace), ContainerPadding(SizeForMonitorWorkspace),
WorkspacePadding(SizeForMonitorWorkspace), WorkspacePadding(SizeForMonitorWorkspace),
WorkspaceLayout(LayoutForMonitorWorkspace), WorkspaceLayout(LayoutForMonitorWorkspace),
WorkspaceTiling(TilingForMonitorWorkspace),
WorkspaceName(NameForMonitorWorkspace), WorkspaceName(NameForMonitorWorkspace),
ToggleTiling,
ToggleFloat, ToggleFloat,
TogglePause, TogglePause,
ToggleMonocle, ToggleMonocle,
@@ -91,6 +93,22 @@ struct LayoutForMonitorWorkspace {
layout: Layout, layout: Layout,
} }
fn on_or_off(s: &str) -> Result<bool, &'static str> {
match s {
"on" => Ok(true),
"off" => Ok(false),
_ => Err("expected `on` or `off`"),
}
}
#[derive(Clap)]
struct TilingForMonitorWorkspace {
monitor: usize,
workspace: usize,
#[clap(parse(try_from_str = on_or_off))]
tile: bool,
}
#[derive(Clap)] #[derive(Clap)]
struct Target { struct Target {
number: usize, number: usize,
@@ -187,6 +205,9 @@ fn main() -> Result<()> {
.as_bytes()?, .as_bytes()?,
)?; )?;
} }
SubCommand::ToggleTiling => {
send_message(&*SocketMessage::ToggleTiling.as_bytes()?)?;
}
SubCommand::ToggleFloat => { SubCommand::ToggleFloat => {
send_message(&*SocketMessage::ToggleFloat.as_bytes()?)?; send_message(&*SocketMessage::ToggleFloat.as_bytes()?)?;
} }
@@ -199,6 +220,12 @@ fn main() -> Result<()> {
.as_bytes()?, .as_bytes()?,
)?; )?;
} }
SubCommand::WorkspaceTiling(layout) => {
send_message(
&*SocketMessage::WorkspaceTiling(layout.monitor, layout.workspace, layout.tile)
.as_bytes()?,
)?;
}
SubCommand::Start => { SubCommand::Start => {
let script = r#"Start-Process komorebi -WindowStyle hidden"#; let script = r#"Start-Process komorebi -WindowStyle hidden"#;
match powershell_script::run(script, true) { match powershell_script::run(script, true) {