hyprland: preserve tab group restore order

This commit is contained in:
2026-05-02 12:02:01 -07:00
parent 5664aa7aae
commit 600be3e2b7
4 changed files with 93 additions and 12 deletions

View File

@@ -22,6 +22,7 @@ local layout_names = {
[monocle_layout] = "Monocle", [monocle_layout] = "Monocle",
} }
local minimized_workspace = "special:minimized" local minimized_workspace = "special:minimized"
local tabbed_group_restore_workspace_prefix = "special:tabbed-monocle-restore-"
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
@@ -413,10 +414,30 @@ local function window_address_set(windows)
return addresses return addresses
end end
local function window_address_list(windows)
local addresses = {}
for _, window in ipairs(windows) do
if window and window.address then
addresses[#addresses + 1] = window.address
end
end
return addresses
end
local function window_address_in_set(window, addresses) 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 windows_by_address()
local windows = {}
for _, window in ipairs(hl.get_windows()) do
if window and window.address then
windows[window.address] = window
end
end
return windows
end
local function numeric_component(value, key, index) local function numeric_component(value, key, index)
if type(value) ~= "table" then if type(value) ~= "table" then
return 0 return 0
@@ -755,10 +776,56 @@ local function find_tabbed_group_anchor(state)
return nil return nil
end end
local function ordered_windows_for_tabbed_group_restore(state, workspace_id)
local ordered = {}
local seen = {}
local live_windows = windows_by_address()
local workspace = workspace_id and hl.get_workspace(tostring(workspace_id)) or active_workspace()
if state and state.order then
for _, address in ipairs(state.order) do
local window = live_windows[address]
if window and not window.floating and not window.hidden and (not workspace or same_workspace(window.workspace, workspace)) then
ordered[#ordered + 1] = window
seen[address] = true
end
end
end
if workspace then
for _, window in ipairs(tiled_windows(workspace)) do
if window and window.address and not seen[window.address] then
ordered[#ordered + 1] = window
seen[window.address] = true
end
end
end
return ordered
end
local function restore_tabbed_group_window_order(state, workspace_id)
local ordered = ordered_windows_for_tabbed_group_restore(state, workspace_id)
if #ordered <= 1 or not workspace_id then
return
end
local restore_workspace = tabbed_group_restore_workspace_prefix .. tostring(workspace_id)
for _, window in ipairs(ordered) do
move_window_to_workspace(restore_workspace, false, window)
end
for _, window in ipairs(ordered) do
move_window_to_workspace(workspace_id, false, window)
end
end
local function restore_workspace_tabbed_group() local function restore_workspace_tabbed_group()
local key = workspace_key() local key = workspace_key()
local anchor = find_tabbed_group_anchor(tabbed_workspace_groups[key]) local state = tabbed_workspace_groups[key]
local anchor = find_tabbed_group_anchor(state)
local anchor_selector = window_selector(anchor) local anchor_selector = window_selector(anchor)
local target_workspace_id = anchor and anchor.workspace and anchor.workspace.id
if not anchor_selector then if not anchor_selector then
tabbed_workspace_groups[key] = nil tabbed_workspace_groups[key] = nil
@@ -771,6 +838,8 @@ local function restore_workspace_tabbed_group()
hl.dsp.group.toggle({ window = anchor_selector })() hl.dsp.group.toggle({ window = anchor_selector })()
tabbed_workspace_groups[key] = nil tabbed_workspace_groups[key] = nil
set_layout(columns_layout) set_layout(columns_layout)
restore_tabbed_group_window_order(state, target_workspace_id)
hl.dsp.focus({ window = anchor_selector })()
schedule_nstack_count_update() schedule_nstack_count_update()
end end
@@ -786,6 +855,7 @@ local function gather_workspace_into_tabbed_group()
return return
end end
local original_order = window_address_list(tiled_windows(workspace))
local candidates = active_workspace_tiled_group_candidates(workspace) local candidates = active_workspace_tiled_group_candidates(workspace)
if #candidates <= 1 then if #candidates <= 1 then
set_layout(columns_layout) set_layout(columns_layout)
@@ -849,6 +919,7 @@ local function gather_workspace_into_tabbed_group()
tabbed_workspace_groups[key] = { tabbed_workspace_groups[key] = {
anchor = anchor.address, anchor = anchor.address,
order = original_order,
windows = candidate_addresses, windows = candidate_addresses,
} }
hl.dsp.focus({ window = anchor_selector })() hl.dsp.focus({ window = anchor_selector })()

View File

@@ -17,6 +17,7 @@
# Use the flake source for enumeration (pure), but point links at the worktree. # Use the flake source for enumeration (pure), but point links at the worktree.
srcDotfiles = ../dotfiles; srcDotfiles = ../dotfiles;
srcConfig = srcDotfiles + "/config";
excludedTop = [ excludedTop = [
# Managed by nix-shared/home-manager/codex-generated-skills.nix so # Managed by nix-shared/home-manager/codex-generated-skills.nix so
@@ -57,6 +58,22 @@
lib.nameValuePair ".${rel}" { lib.nameValuePair ".${rel}" {
source = oos "${worktreeDotfiles}/${rel}"; source = oos "${worktreeDotfiles}/${rel}";
}; };
configEntries = builtins.readDir srcConfig;
configDirExclusions = [
# Home Manager writes generated fontconfig fragments under this tree.
"fontconfig"
# Home Manager's gtk module writes settings.ini and gtk.css here.
"gtk-3.0"
];
configDirNames =
lib.filter
(name: configEntries.${name} == "directory" && !(lib.elem name configDirExclusions))
(builtins.attrNames configEntries);
mkConfigDir = name:
lib.nameValuePair name {
source = oos "${worktreeDotfiles}/config/${name}";
};
in { in {
imports = [ imports = [
../nix-shared/home-manager/codex-generated-skills.nix ../nix-shared/home-manager/codex-generated-skills.nix
@@ -65,6 +82,9 @@ in {
home.file = home.file =
builtins.listToAttrs (map mkManaged managedRelFiles); builtins.listToAttrs (map mkManaged managedRelFiles);
xdg.configFile =
builtins.listToAttrs (map mkConfigDir configDirNames);
myModules.codexGeneratedSkills.enable = true; myModules.codexGeneratedSkills.enable = true;
# Home Manager directory links for .emacs.d resolve through the store on this # Home Manager directory links for .emacs.d resolve through the store on this

View File

@@ -204,10 +204,6 @@ in {
xdg.configFile."qt5ct/qt5ct.conf".text = qtctConfig; xdg.configFile."qt5ct/qt5ct.conf".text = qtctConfig;
xdg.configFile."qt6ct/qt6ct.conf".text = qtctConfig; xdg.configFile."qt6ct/qt6ct.conf".text = qtctConfig;
xdg.configFile."zellij/config.kdl".source =
config.lib.file.mkOutOfStoreSymlink
"${config.home.homeDirectory}/dotfiles/dotfiles/config/zellij/config.kdl";
xdg.configFile."menus/applications.menu" = lib.mkIf nixos.config.myModules.desktop.enable { xdg.configFile."menus/applications.menu" = lib.mkIf nixos.config.myModules.desktop.enable {
source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu"; source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
}; };

View File

@@ -189,11 +189,7 @@
config, config,
lib, lib,
... ...
}: let }: {
hyprConfigDir =
config.lib.file.mkOutOfStoreSymlink
"${config.home.homeDirectory}/dotfiles/dotfiles/config/hypr";
in {
services.kanshi = { services.kanshi = {
enable = true; enable = true;
systemdTarget = "graphical-session.target"; systemdTarget = "graphical-session.target";
@@ -247,8 +243,6 @@
}; };
}; };
xdg.configFile."hypr".source = hyprConfigDir;
xdg.configFile."systemd/user/wayland-wm@hyprland.desktop.service.d/10-cleanup-stale-session.conf".text = '' xdg.configFile."systemd/user/wayland-wm@hyprland.desktop.service.d/10-cleanup-stale-session.conf".text = ''
[Service] [Service]
ExecStopPost=${cleanupStaleGraphicalSession} ExecStopPost=${cleanupStaleGraphicalSession}