Add per-monitor workspace history cycling
This commit is contained in:
@@ -43,13 +43,13 @@ Required behavior:
|
|||||||
- Moving the focused window to the next empty workspace and following it is a
|
- Moving the focused window to the next empty workspace and following it is a
|
||||||
first-class operation.
|
first-class operation.
|
||||||
- Normal workspaces are bounded to `1..9`.
|
- Normal workspaces are bounded to `1..9`.
|
||||||
- Workspace history is tracked per monitor.
|
|
||||||
- Last-workspace toggle uses the current monitor's workspace history.
|
|
||||||
- Workspace cycling works on the current monitor within the bounded workspace
|
|
||||||
set.
|
|
||||||
|
|
||||||
Important behavior:
|
Important behavior:
|
||||||
|
|
||||||
|
- Workspace history is tracked per monitor.
|
||||||
|
- Last-workspace toggle uses the current monitor's workspace history.
|
||||||
|
- Workspace history cycling works on the current monitor within the bounded
|
||||||
|
workspace set.
|
||||||
- Swapping the current workspace contents with another workspace is available.
|
- Swapping the current workspace contents with another workspace is available.
|
||||||
- Moving a window to an empty workspace on another monitor is available.
|
- Moving a window to an empty workspace on another monitor is available.
|
||||||
- Moving the focused window to another monitor without following keeps keyboard
|
- Moving the focused window to another monitor without following keeps keyboard
|
||||||
@@ -62,6 +62,30 @@ Important behavior:
|
|||||||
- Hidden/special workspaces are excluded from the status bar's normal workspace
|
- Hidden/special workspaces are excluded from the status bar's normal workspace
|
||||||
list.
|
list.
|
||||||
|
|
||||||
|
### Workspace History Cycling
|
||||||
|
|
||||||
|
Important behavior:
|
||||||
|
|
||||||
|
- The model is most-recently-used workspace switching, scoped to the monitor
|
||||||
|
where the action starts.
|
||||||
|
- Each monitor has its own ordered workspace history. The focused monitor's
|
||||||
|
history is not shared with other monitors.
|
||||||
|
- Only ordinary bounded workspaces are candidates. Special, scratchpad,
|
||||||
|
minimized, hidden, and out-of-range workspaces are excluded.
|
||||||
|
- Starting a cycle freezes the candidate list for that cycle. Previewing
|
||||||
|
workspaces while the cycle is active must not rewrite the history order.
|
||||||
|
- Starting a cycle previews the previous workspace for the current monitor.
|
||||||
|
- Repeating the forward cycle action continues farther back through that
|
||||||
|
monitor's frozen history.
|
||||||
|
- A reverse cycle action moves through the same frozen history in the opposite
|
||||||
|
direction.
|
||||||
|
- Releasing the initiating modifier key commits the currently previewed
|
||||||
|
workspace and updates history exactly once.
|
||||||
|
- A cancel path may return to the workspace where the cycle started.
|
||||||
|
|
||||||
|
This behavior is important for workflow continuity, but it is not a hard
|
||||||
|
requirement for a minimal daily-driver window manager.
|
||||||
|
|
||||||
## Directional Navigation
|
## Directional Navigation
|
||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
@@ -282,7 +306,6 @@ Required behavior:
|
|||||||
- `Super+g` opens the go-to-window picker.
|
- `Super+g` opens the go-to-window picker.
|
||||||
- `Super+b` opens the bring-window picker.
|
- `Super+b` opens the bring-window picker.
|
||||||
- `Super+Shift+b` opens the replace-window picker.
|
- `Super+Shift+b` opens the replace-window picker.
|
||||||
- `Super+\` toggles to the previous workspace on the current monitor.
|
|
||||||
- `Super+Shift+e` moves the focused window to the next empty workspace and
|
- `Super+Shift+e` moves the focused window to the next empty workspace and
|
||||||
follows it. This is the target replacement for the older `Super+Shift+h`
|
follows it. This is the target replacement for the older `Super+Shift+h`
|
||||||
binding.
|
binding.
|
||||||
@@ -290,6 +313,13 @@ Required behavior:
|
|||||||
- `Hyper+5` swaps the current workspace with a selected workspace.
|
- `Hyper+5` swaps the current workspace with a selected workspace.
|
||||||
- `Hyper+g` gathers windows of the focused class onto the current workspace.
|
- `Hyper+g` gathers windows of the focused class onto the current workspace.
|
||||||
|
|
||||||
|
Important behavior:
|
||||||
|
|
||||||
|
- `Super+\` starts or advances current-monitor workspace history cycling.
|
||||||
|
- `Super+/` reverses current-monitor workspace history cycling while the
|
||||||
|
initiating `Super` key is held.
|
||||||
|
- Releasing the initiating `Super` key commits the workspace history cycle.
|
||||||
|
|
||||||
### Directional Navigation Bindings
|
### Directional Navigation Bindings
|
||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ local stack_update_timer = nil
|
|||||||
local monocle_notice = nil
|
local monocle_notice = nil
|
||||||
local scratchpad_pending = {}
|
local scratchpad_pending = {}
|
||||||
local monitor_workspace_history = {}
|
local monitor_workspace_history = {}
|
||||||
|
local workspace_history_cycle = nil
|
||||||
|
|
||||||
local scratchpads = {
|
local scratchpads = {
|
||||||
htop = {
|
htop = {
|
||||||
@@ -570,29 +571,120 @@ local function monitor_key(monitor)
|
|||||||
return tostring(monitor.name or monitor.id or "unknown")
|
return tostring(monitor.name or monitor.id or "unknown")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function workspace_history_workspace_id(workspace)
|
||||||
|
if not workspace or not workspace.id or workspace.id < 1 or workspace.id > max_workspace then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return workspace.id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function workspace_history_monitor_key(workspace)
|
||||||
|
return monitor_key(workspace and workspace.monitor or hl.get_active_monitor())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove_workspace_history_id(history, workspace_id)
|
||||||
|
for index = #history, 1, -1 do
|
||||||
|
if history[index] == workspace_id then
|
||||||
|
table.remove(history, index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function remember_workspace_for_monitor(workspace)
|
local function remember_workspace_for_monitor(workspace)
|
||||||
workspace = workspace or active_workspace()
|
workspace = workspace or active_workspace()
|
||||||
if not workspace or not workspace.id or workspace.id < 1 then
|
if workspace_history_cycle then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local key = monitor_key(workspace.monitor or hl.get_active_monitor())
|
local workspace_id = workspace_history_workspace_id(workspace)
|
||||||
|
if not workspace_id then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local key = workspace_history_monitor_key(workspace)
|
||||||
local history = monitor_workspace_history[key] or {}
|
local history = monitor_workspace_history[key] or {}
|
||||||
if history.current ~= workspace.id then
|
if history[1] ~= workspace_id then
|
||||||
history.previous = history.current
|
remove_workspace_history_id(history, workspace_id)
|
||||||
history.current = workspace.id
|
table.insert(history, 1, workspace_id)
|
||||||
end
|
end
|
||||||
monitor_workspace_history[key] = history
|
monitor_workspace_history[key] = history
|
||||||
end
|
end
|
||||||
|
|
||||||
local function focus_previous_workspace_for_monitor()
|
local function clone_workspace_history(history)
|
||||||
local key = monitor_key(hl.get_active_monitor())
|
local clone = {}
|
||||||
local history = monitor_workspace_history[key]
|
for index, workspace_id in ipairs(history or {}) do
|
||||||
if history and history.previous then
|
clone[index] = workspace_id
|
||||||
focus_workspace(history.previous)
|
|
||||||
else
|
|
||||||
hl.dsp.focus({ workspace = "previous_per_monitor" })()
|
|
||||||
end
|
end
|
||||||
|
return clone
|
||||||
|
end
|
||||||
|
|
||||||
|
local function workspace_history_cycle_start()
|
||||||
|
if workspace_history_cycle then
|
||||||
|
return workspace_history_cycle
|
||||||
|
end
|
||||||
|
|
||||||
|
remember_workspace_for_monitor()
|
||||||
|
|
||||||
|
local workspace = active_workspace()
|
||||||
|
local workspace_id = workspace_history_workspace_id(workspace)
|
||||||
|
if not workspace_id then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local key = workspace_history_monitor_key(workspace)
|
||||||
|
local history = clone_workspace_history(monitor_workspace_history[key])
|
||||||
|
if history[1] ~= workspace_id then
|
||||||
|
remove_workspace_history_id(history, workspace_id)
|
||||||
|
table.insert(history, 1, workspace_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if #history < 2 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
workspace_history_cycle = {
|
||||||
|
monitor_key = key,
|
||||||
|
original_workspace = workspace_id,
|
||||||
|
history = history,
|
||||||
|
index = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
return workspace_history_cycle
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cycle_workspace_history(direction)
|
||||||
|
local cycle = workspace_history_cycle_start()
|
||||||
|
if not cycle then
|
||||||
|
hl.dsp.focus({ workspace = "previous_per_monitor" })()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace_id = cycle.history[cycle.index]
|
||||||
|
if not workspace_id then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
focus_workspace(workspace_id)
|
||||||
|
cycle.index = ((cycle.index - 1 + direction) % #cycle.history) + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function commit_workspace_history_cycle()
|
||||||
|
if not workspace_history_cycle then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
workspace_history_cycle = nil
|
||||||
|
remember_workspace_for_monitor()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cancel_workspace_history_cycle()
|
||||||
|
local cycle = workspace_history_cycle
|
||||||
|
if not cycle then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
workspace_history_cycle = nil
|
||||||
|
focus_workspace(cycle.original_workspace)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function move_window_to_workspace(workspace_id, follow, window)
|
local function move_window_to_workspace(workspace_id, follow, window)
|
||||||
@@ -1765,7 +1857,14 @@ for i = 1, 9 do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
bind(main_mod .. " + backslash", focus_previous_workspace_for_monitor)
|
bind(main_mod .. " + backslash", function()
|
||||||
|
cycle_workspace_history(1)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + slash", function()
|
||||||
|
cycle_workspace_history(-1)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + Super_L", commit_workspace_history_cycle, { release = true })
|
||||||
|
bind(main_mod .. " + Escape", cancel_workspace_history_cycle)
|
||||||
bind(main_mod .. " + Z", hl.dsp.focus({ monitor = "+1" }))
|
bind(main_mod .. " + Z", hl.dsp.focus({ monitor = "+1" }))
|
||||||
bind(main_mod .. " + SHIFT + Z", hl.dsp.window.move({ monitor = "+1" }))
|
bind(main_mod .. " + SHIFT + Z", hl.dsp.window.move({ monitor = "+1" }))
|
||||||
bind(main_mod .. " + mouse_down", function()
|
bind(main_mod .. " + mouse_down", function()
|
||||||
|
|||||||
Reference in New Issue
Block a user