oxwm

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

Add view next/previous non-empty tag actions

Commit
183a8e110116fa7cdcfcbb86c9ccece2c410190b
Parent
ddba5ce
Author
emzywastaken <amiamemetoo@gmail.com>
Date
2025-12-14 12:27:48
exposed as `oxwm.tag.view_next_nonempty` and
`oxwm.tag.view_previous_nonempty`

Diff

diff --git a/src/config/lua_api.rs b/src/config/lua_api.rs
index cb71369..093af47 100644
--- a/src/config/lua_api.rs
+++ b/src/config/lua_api.rs
@@ -309,6 +309,13 @@ fn register_tag_module(lua: &Lua, parent: &Table) -> Result<(), ConfigError> {
     let view_previous =
         lua.create_function(|lua, ()| create_action_table(lua, "ViewPreviousTag", Value::Nil))?;
 
+    let view_next_nonempty =
+        lua.create_function(|lua, ()| create_action_table(lua, "ViewNextNonEmptyTag", Value::Nil))?;
+
+    let view_previous_nonempty = lua.create_function(|lua, ()| {
+        create_action_table(lua, "ViewPreviousNonEmptyTag", Value::Nil)
+    })?;
+
     let toggleview = lua.create_function(|lua, idx: i32| {
         create_action_table(lua, "ToggleView", Value::Integer(idx as i64))
     })?;
@@ -324,6 +331,8 @@ fn register_tag_module(lua: &Lua, parent: &Table) -> Result<(), ConfigError> {
     tag_table.set("view", view)?;
     tag_table.set("view_next", view_next)?;
     tag_table.set("view_previous", view_previous)?;
+    tag_table.set("view_next_nonempty", view_next_nonempty)?;
+    tag_table.set("view_previous_nonempty", view_previous_nonempty)?;
     tag_table.set("toggleview", toggleview)?;
     tag_table.set("move_to", move_to)?;
     tag_table.set("toggletag", toggletag)?;
@@ -855,6 +864,8 @@ fn string_to_action(s: &str) -> mlua::Result<KeyAction> {
         "ViewTag" => Ok(KeyAction::ViewTag),
         "ViewNextTag" => Ok(KeyAction::ViewNextTag),
         "ViewPreviousTag" => Ok(KeyAction::ViewPreviousTag),
+        "ViewNextNonEmptyTag" => Ok(KeyAction::ViewNextNonEmptyTag),
+        "ViewPreviousNonEmptyTag" => Ok(KeyAction::ViewPreviousNonEmptyTag),
         "ToggleView" => Ok(KeyAction::ToggleView),
         "MoveToTag" => Ok(KeyAction::MoveToTag),
         "ToggleTag" => Ok(KeyAction::ToggleTag),
diff --git a/src/keyboard/handlers.rs b/src/keyboard/handlers.rs
index 87e7bf3..9f6b841 100644
--- a/src/keyboard/handlers.rs
+++ b/src/keyboard/handlers.rs
@@ -19,6 +19,8 @@ pub enum KeyAction {
     ViewTag,
     ViewNextTag,
     ViewPreviousTag,
+    ViewNextNonEmptyTag,
+    ViewPreviousNonEmptyTag,
     ToggleView,
     MoveToTag,
     ToggleTag,
diff --git a/src/overlay/keybind.rs b/src/overlay/keybind.rs
index 2a02df0..a151bb0 100644
--- a/src/overlay/keybind.rs
+++ b/src/overlay/keybind.rs
@@ -222,6 +222,8 @@ impl KeybindOverlay {
             },
             KeyAction::ViewNextTag => "View Next Workspace".to_string(),
             KeyAction::ViewPreviousTag => "View Previous Workspace".to_string(),
+            KeyAction::ViewNextNonEmptyTag => "View Next Non-Empty Workspace".to_string(),
+            KeyAction::ViewPreviousNonEmptyTag => "View Previous Non-Empty Workspace".to_string(),
             KeyAction::ToggleView => match &binding.arg {
                 Arg::Int(n) => format!("Toggle View Workspace {}", n),
                 _ => "Toggle View Workspace".to_string(),
diff --git a/src/window_manager.rs b/src/window_manager.rs
index 4a7ca3a..c5f569a 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -825,6 +825,30 @@ impl WindowManager {
                 let current_tag_index = unmask_tag(current_tag_mask);
                 self.view_tag(current_tag_index.saturating_sub(1))?;
             }
+            KeyAction::ViewNextNonEmptyTag => {
+                let monitor = self.get_selected_monitor();
+                let current_tag_mask = monitor.get_selected_tag();
+                let current_tag_index = unmask_tag(current_tag_mask);
+
+                for next_tag_index in (current_tag_index + 1)..self.config.tags.len() {
+                    if self.has_windows_on_tag(monitor.monitor_number, next_tag_index) {
+                        self.view_tag(next_tag_index)?;
+                        break;
+                    }
+                }
+            }
+            KeyAction::ViewPreviousNonEmptyTag => {
+                let monitor = self.get_selected_monitor();
+                let current_tag_mask = monitor.get_selected_tag();
+                let current_tag_index = unmask_tag(current_tag_mask);
+
+                for prev_tag_index in (0..current_tag_index).rev() {
+                    if self.has_windows_on_tag(monitor.monitor_number, prev_tag_index) {
+                        self.view_tag(prev_tag_index)?;
+                        break;
+                    }
+                }
+            }
             KeyAction::ToggleView => {
                 if let Arg::Int(tag_index) = arg {
                     self.toggleview(*tag_index as usize)?;
@@ -4198,6 +4222,27 @@ impl WindowManager {
         &self.monitors[self.selected_monitor]
     }
 
+    fn has_windows_on_tag(&self, monitor_number: usize, tag_index: usize) -> bool {
+        let Some(monitor) = self.monitors.get(monitor_number) else {
+            return false;
+        };
+
+        let mut current = monitor.clients_head;
+        while let Some(window) = current {
+            // A window should always have a client attatched to it.
+            let Some(client) = self.clients.get(&window) else {
+                break;
+            };
+
+            if unmask_tag(client.tags) == tag_index {
+                return true;
+            }
+            current = client.next;
+        }
+
+        false
+    }
+
     fn run_autostart_commands(&self) {
         for command in &self.config.autostart {
             crate::signal::spawn_detached(command);
diff --git a/templates/oxwm.lua b/templates/oxwm.lua
index 26fd328..4fa26aa 100644
--- a/templates/oxwm.lua
+++ b/templates/oxwm.lua
@@ -213,6 +213,14 @@ function oxwm.tag.view_next() end
 ---@return table Action table for keybinding
 function oxwm.tag.view_previous() end
 
+---View/switch to next non-empty tag
+---@return table Action table for keybinding
+function oxwm.tag.view_next_nonempty() end
+
+---View/switch to previous non-empty tag
+---@return table Action table for keybinding
+function oxwm.tag.view_previous_nonempty() end
+
 ---Move focused window to tag
 ---@param index integer Tag index (0-based)
 ---@return table Action table for keybinding