Fix Hyprland workspace tab grouping

This commit is contained in:
2026-04-29 21:28:48 -07:00
parent d48edc9bb8
commit c30a67facf

View File

@@ -12,7 +12,6 @@ local scratchpad_top_margin = 60
local columns_layout = "nStack" local columns_layout = "nStack"
local monocle_layout = "monocle" local monocle_layout = "monocle"
local minimized_workspace = "special:minimized" local minimized_workspace = "special:minimized"
local tabbed_group_staging_workspace = "special:tabbed-monocle-staging"
local current_layout = columns_layout local current_layout = columns_layout
local enable_nstack = true local enable_nstack = true
local enable_hyprexpo = true local enable_hyprexpo = true
@@ -284,6 +283,51 @@ local function window_address_in_set(window, addresses)
return window and window.address and addresses[window.address] or false return window and window.address and addresses[window.address] or false
end end
local function numeric_component(value, key, index)
if type(value) ~= "table" then
return 0
end
return tonumber(value[key] or value[index]) or 0
end
local function window_center(window)
local at = window and window.at or {}
local size = window and window.size or {}
return numeric_component(at, "x", 1) + numeric_component(size, "x", 1) / 2,
numeric_component(at, "y", 2) + numeric_component(size, "y", 2) / 2
end
local function window_distance_squared(window, x, y)
local wx, wy = window_center(window)
local dx = wx - x
local dy = wy - y
return dx * dx + dy * dy
end
local function grouping_direction(window, anchor)
local wx, wy = window_center(window)
local ax, ay = window_center(anchor)
local dx = wx - ax
local dy = wy - ay
if math.abs(dx) >= math.abs(dy) then
return dx >= 0 and "left" or "right"
end
return dy >= 0 and "up" or "down"
end
local function grouping_directions(window, anchor)
local primary = grouping_direction(window, anchor)
local directions = { primary }
for _, direction in ipairs({ "left", "right", "up", "down" }) do
if direction ~= primary then
directions[#directions + 1] = direction
end
end
return directions
end
local function workspace_window_count(workspace_id) local function workspace_window_count(workspace_id)
local workspace = hl.get_workspace(tostring(workspace_id)) local workspace = hl.get_workspace(tostring(workspace_id))
if not workspace then if not workspace then
@@ -519,27 +563,31 @@ local function notify_tabbed_group(text)
}) })
end end
local function workspace_visible_normal_windows(workspace)
local windows = {}
if not workspace then
return windows
end
for _, window in ipairs(hl.get_workspace_windows(workspace)) do
if is_normal_window(window) and not window.hidden then
windows[#windows + 1] = window
end
end
return windows
end
local function active_workspace_tiled_group_candidates(workspace) local function active_workspace_tiled_group_candidates(workspace)
local candidates = tiled_windows(workspace) local candidates = tiled_windows(workspace)
sort_windows_by_focus_history(candidates) sort_windows_by_focus_history(candidates)
return candidates return candidates
end end
local function move_window_into_group(window, anchor)
local selector = window_selector(window)
if not selector then
return false
end
for _, direction in ipairs(grouping_directions(window, anchor)) do
hl.dsp.focus({ window = selector })()
hl.dsp.window.move({ into_group = direction, window = selector })()
local active = hl.get_active_window()
if active and active.group and active.group.size and active.group.size > 1 then
return true
end
end
return false
end
local function find_tabbed_group_anchor(state) local function find_tabbed_group_anchor(state)
local active = hl.get_active_window() local active = hl.get_active_window()
if active and active.group and active.group.size and active.group.size > 1 then if active and active.group and active.group.size and active.group.size > 1 then
@@ -620,31 +668,37 @@ local function gather_workspace_into_tabbed_group()
set_layout(columns_layout) set_layout(columns_layout)
local staged_windows = {}
for _, window in ipairs(workspace_visible_normal_windows(workspace)) do
if window ~= anchor then
staged_windows[#staged_windows + 1] = window
move_window_to_workspace(tabbed_group_staging_workspace, false, window)
end
end
hl.dsp.focus({ window = anchor_selector })() hl.dsp.focus({ window = anchor_selector })()
hl.dsp.group.toggle({ window = anchor_selector })() hl.dsp.group.toggle({ window = anchor_selector })()
hl.config({ group = { group_on_movetoworkspace = true } }) local group_windows = {}
for _, window in ipairs(candidates) do for _, window in ipairs(candidates) do
if window ~= anchor then if window ~= anchor and not window.group then
move_window_to_workspace(workspace.id, false, window) group_windows[#group_windows + 1] = window
end end
end end
hl.config({ group = { group_on_movetoworkspace = false } })
for _, window in ipairs(staged_windows) do local anchor_x, anchor_y = window_center(anchor)
if not window_address_in_set(window, candidate_addresses) then table.sort(group_windows, function(left, right)
move_window_to_workspace(workspace.id, false, window) return window_distance_squared(left, anchor_x, anchor_y) < window_distance_squared(right, anchor_x, anchor_y)
end)
local grouped_count = 1
for _, window in ipairs(group_windows) do
if move_window_into_group(window, anchor) then
grouped_count = grouped_count + 1
end end
end end
if grouped_count <= 1 then
hl.dsp.focus({ window = anchor_selector })()
hl.dsp.group.toggle({ window = anchor_selector })()
notify_tabbed_group("Unable to group tiled windows")
return
elseif grouped_count < #candidates then
notify_tabbed_group("Grouped " .. tostring(grouped_count) .. " of " .. tostring(#candidates) .. " tiled windows")
end
tabbed_workspace_groups[key] = { tabbed_workspace_groups[key] = {
anchor = anchor.address, anchor = anchor.address,
windows = candidate_addresses, windows = candidate_addresses,