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