oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

Added vanity gaps. with toggle =)

Commit
605d25b8f04e8c3be82c0f02509557e060284e06
Parent
6f26d6c
Author
tonybtw <tonybtw@tonybtw.com>
Date
2025-10-06 05:16:37

Diff

diff --git a/src/config.rs b/src/config.rs
index be28ee9..ccb40fd 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -6,11 +6,20 @@ use x11rb::protocol::xproto::KeyButMask;
 // ========================================
 // APPEARANCE
 // ========================================
-pub const BORDER_WIDTH: u32 = 1;
+pub const BORDER_WIDTH: u32 = 0;
 pub const BORDER_FOCUSED: u32 = 0x6dade3;
 pub const BORDER_UNFOCUSED: u32 = 0xbbbbbb;
 pub const FONT: &str = "JetBrainsMono Nerd Font:style=Bold:size=14";
 
+// ========================================
+// GAPS (Vanity Gaps)
+// ========================================
+pub const GAPS_ENABLED: bool = true;
+pub const GAP_INNER_HORIZONTAL: u32 = 3;
+pub const GAP_INNER_VERTICAL: u32 = 3;
+pub const GAP_OUTER_HORIZONTAL: u32 = 3;
+pub const GAP_OUTER_VERTICAL: u32 = 3;
+//
 // ========================================
 // DEFAULTS
 // ========================================
@@ -89,6 +98,7 @@ pub const KEYBINDINGS: &[Key] = &[
     Key::new(&[MODKEY],        keycodes::S,      KeyAction::Spawn,      Arg::Array(SCREENSHOT_CMD)),
     Key::new(&[MODKEY],        keycodes::D,      KeyAction::Spawn,      Arg::Array(DMENU_CMD)),
     Key::new(&[MODKEY],        keycodes::Q,      KeyAction::KillClient, Arg::None),
+    Key::new(&[MODKEY],        keycodes::A,      KeyAction::ToggleGaps, Arg::None),
     Key::new(&[MODKEY, SHIFT], keycodes::Q,      KeyAction::Quit,       Arg::None),
     Key::new(&[MODKEY, SHIFT], keycodes::R,      KeyAction::Restart,    Arg::None),
     Key::new(&[MODKEY],        keycodes::J,      KeyAction::FocusStack, Arg::Int(-1)),
diff --git a/src/keyboard/handlers.rs b/src/keyboard/handlers.rs
index e13a690..6288bde 100644
--- a/src/keyboard/handlers.rs
+++ b/src/keyboard/handlers.rs
@@ -11,6 +11,7 @@ pub enum KeyAction {
     Quit,
     Restart,
     ViewTag,
+    ToggleGaps,
     MoveToTag,
     None,
 }
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index df0e970..f8a9918 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -2,12 +2,20 @@ pub mod tiling;
 
 use x11rb::protocol::xproto::Window;
 
+pub struct GapConfig {
+    pub inner_horizontal: u32,
+    pub inner_vertical: u32,
+    pub outer_horizontal: u32,
+    pub outer_vertical: u32,
+}
+
 pub trait Layout {
     fn arrange(
         &self,
         windows: &[Window],
         screen_width: u32,
         screen_height: u32,
+        gaps: &GapConfig,
     ) -> Vec<WindowGeometry>;
 }
 
diff --git a/src/layout/tiling.rs b/src/layout/tiling.rs
index 14d7ac9..6bfef1f 100644
--- a/src/layout/tiling.rs
+++ b/src/layout/tiling.rs
@@ -1,4 +1,4 @@
-use super::{Layout, WindowGeometry};
+use super::{GapConfig, Layout, WindowGeometry};
 use x11rb::protocol::xproto::Window;
 
 pub struct TilingLayout;
@@ -9,6 +9,7 @@ impl Layout for TilingLayout {
         windows: &[Window],
         screen_width: u32,
         screen_height: u32,
+        gaps: &GapConfig,
     ) -> Vec<WindowGeometry> {
         let window_count = windows.len();
         if window_count == 0 {
@@ -16,31 +17,60 @@ impl Layout for TilingLayout {
         }
 
         if window_count == 1 {
+            let x = gaps.outer_horizontal as i32;
+            let y = gaps.outer_vertical as i32;
+            let width = screen_width.saturating_sub(2 * gaps.outer_horizontal);
+            let height = screen_height.saturating_sub(2 * gaps.outer_vertical);
+
             vec![WindowGeometry {
-                x_coordinate: 0,
-                y_coordinate: 0,
-                width: screen_width,
-                height: screen_height,
+                x_coordinate: x,
+                y_coordinate: y,
+                width,
+                height,
             }]
         } else {
-            let master_width = screen_width / 2;
-            let mut geometries = vec![WindowGeometry {
-                x_coordinate: 0,
-                y_coordinate: 0,
+            let mut geometries = Vec::new();
+
+            let master_width = (screen_width / 2)
+                .saturating_sub(gaps.outer_horizontal)
+                .saturating_sub(gaps.inner_horizontal / 2);
+
+            let master_x = gaps.outer_horizontal as i32;
+            let master_y = gaps.outer_vertical as i32;
+            let master_height = screen_height.saturating_sub(2 * gaps.outer_vertical);
+
+            geometries.push(WindowGeometry {
+                x_coordinate: master_x,
+                y_coordinate: master_y,
                 width: master_width,
-                height: screen_height,
-            }];
+                height: master_height,
+            });
+
+            let stack_count = window_count - 1;
+            let stack_x = (screen_width / 2 + gaps.inner_horizontal / 2) as i32;
+            let stack_width = (screen_width / 2)
+                .saturating_sub(gaps.outer_horizontal)
+                .saturating_sub(gaps.inner_horizontal / 2);
+
+            let total_stack_height = screen_height.saturating_sub(2 * gaps.outer_vertical);
+
+            let total_inner_gaps = gaps.inner_vertical * (stack_count as u32 - 1);
+            let stack_height =
+                total_stack_height.saturating_sub(total_inner_gaps) / stack_count as u32;
 
-            let stack_height = screen_height / (window_count - 1) as u32;
             for i in 1..window_count {
-                let y_offset = ((i - 1) as u32) * stack_height;
+                let stack_index = i - 1;
+                let y_offset = gaps.outer_vertical
+                    + (stack_index as u32) * (stack_height + gaps.inner_vertical);
+
                 geometries.push(WindowGeometry {
-                    x_coordinate: master_width as i32,
+                    x_coordinate: stack_x,
                     y_coordinate: y_offset as i32,
-                    width: master_width,
+                    width: stack_width,
                     height: stack_height,
                 });
             }
+
             return geometries;
         }
     }
diff --git a/src/window_manager.rs b/src/window_manager.rs
index 8148e70..bf7f0a4 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -1,6 +1,10 @@
 use crate::bar::Bar;
-use crate::config::{BORDER_FOCUSED, BORDER_UNFOCUSED, BORDER_WIDTH, TAG_COUNT};
+use crate::config::{
+    BORDER_FOCUSED, BORDER_UNFOCUSED, BORDER_WIDTH, GAP_INNER_HORIZONTAL, GAP_INNER_VERTICAL,
+    GAP_OUTER_HORIZONTAL, GAP_OUTER_VERTICAL, GAPS_ENABLED, TAG_COUNT,
+};
 use crate::keyboard::{self, Arg, KeyAction};
+use crate::layout::GapConfig;
 use crate::layout::Layout;
 use crate::layout::tiling::TilingLayout;
 use anyhow::Result;
@@ -25,6 +29,7 @@ pub struct WindowManager {
     layout: Box<dyn Layout>,
     window_tags: std::collections::HashMap<Window, TagMask>,
     selected_tags: TagMask,
+    gaps_enabled: bool,
     bar: Bar,
 }
 
@@ -49,6 +54,7 @@ impl WindowManager {
         let bar = Bar::new(&connection, &screen, screen_number)?;
 
         let selected_tags = Self::get_saved_selected_tags(&connection, root)?;
+        let gaps_enabled = GAPS_ENABLED;
 
         let mut window_manger = Self {
             connection,
@@ -60,6 +66,7 @@ impl WindowManager {
             layout: Box::new(TilingLayout),
             window_tags: std::collections::HashMap::new(),
             selected_tags,
+            gaps_enabled,
             bar,
         };
 
@@ -287,6 +294,10 @@ impl WindowManager {
                     self.move_to_tag(*tag_index as usize)?;
                 }
             }
+            KeyAction::ToggleGaps => {
+                self.gaps_enabled = !self.gaps_enabled;
+                self.apply_layout()?;
+            }
             KeyAction::None => {
                 //no-op
             }
@@ -520,8 +531,26 @@ impl WindowManager {
         let bar_height = self.bar.height() as u32;
         let usable_height = screen_height.saturating_sub(bar_height);
 
+        let gaps = if self.gaps_enabled {
+            GapConfig {
+                inner_horizontal: GAP_INNER_HORIZONTAL,
+                inner_vertical: GAP_INNER_VERTICAL,
+                outer_horizontal: GAP_OUTER_HORIZONTAL,
+                outer_vertical: GAP_OUTER_VERTICAL,
+            }
+        } else {
+            GapConfig {
+                inner_horizontal: 0,
+                inner_vertical: 0,
+                outer_horizontal: 0,
+                outer_vertical: 0,
+            }
+        };
+
         let visible = self.visible_windows();
-        let geometries = self.layout.arrange(&visible, screen_width, usable_height);
+        let geometries = self
+            .layout
+            .arrange(&visible, screen_width, usable_height, &gaps);
 
         for (window, geometry) in visible.iter().zip(geometries.iter()) {
             let adjusted_width = geometry.width.saturating_sub(2 * border_width);