hyprland: keep file chooser dialogs focused

This commit is contained in:
2026-05-16 00:24:51 -07:00
parent 83b4889982
commit 890bdb0925
3 changed files with 83 additions and 2 deletions

View File

@@ -87,6 +87,10 @@ function M.setup(ctx)
hl.on("window.open", adopt_matching_scratchpad_window) hl.on("window.open", adopt_matching_scratchpad_window)
hl.on("window.class", adopt_matching_scratchpad_window) hl.on("window.class", adopt_matching_scratchpad_window)
hl.on("window.title", adopt_matching_scratchpad_window) hl.on("window.title", adopt_matching_scratchpad_window)
hl.on("window.open", raise_file_chooser_window_later)
hl.on("window.class", raise_file_chooser_window_later)
hl.on("window.title", raise_file_chooser_window_later)
end end
return M return M

View File

@@ -77,6 +77,7 @@ function M.setup(ctx)
local function scratchpad_window_matches(window, def) local function scratchpad_window_matches(window, def)
return window return window
and not (type(is_file_chooser_window) == "function" and is_file_chooser_window(window))
and lower_contains_any(window.class, def.classes or def.class) and lower_contains_any(window.class, def.classes or def.class)
and lower_contains(window.title, def.title) and lower_contains(window.title, def.title)
end end

View File

@@ -2,10 +2,77 @@ local M = {}
function M.setup(ctx) function M.setup(ctx)
local _ENV = ctx local _ENV = ctx
local file_chooser_title_rule = "^(Open File|Open Files|Save File|Save Files|Save As|Select File|Select Files|Choose File|Choose Files|File Upload|Upload File|Upload Files|Select Folder|Choose Folder|Open Folder|Save Folder)$"
local function plugin_path(env_name, default) local function plugin_path(env_name, default)
return os.getenv(env_name) or default return os.getenv(env_name) or default
end end
local function lower_string(value)
return string.lower(tostring(value or ""))
end
local function title_indicates_file_chooser(title)
title = lower_string(title)
if title == "" then
return false
end
for _, exact in ipairs({
"open file",
"open files",
"save file",
"save files",
"save as",
"select file",
"select files",
"choose file",
"choose files",
"file upload",
"upload file",
"upload files",
"select folder",
"choose folder",
"open folder",
"save folder",
}) do
if title == exact then
return true
end
end
return title:find("file chooser", 1, true) ~= nil
or title:find("file picker", 1, true) ~= nil
end
local function is_file_chooser_window(window)
return window
and (title_indicates_file_chooser(window.title) or title_indicates_file_chooser(window.initial_title))
end
local function raise_file_chooser_window(window)
if verify_config or not is_file_chooser_window(window) then
return
end
local selector = window_selector(window)
if not selector then
return
end
dispatch(hl.dsp.window.float({ action = "enable", window = selector }))
dispatch(hl.dsp.window.center({ window = selector }))
dispatch(hl.dsp.focus({ window = selector }))
dispatch(hl.dsp.window.bring_to_top({ window = selector }))
end
local function raise_file_chooser_window_later(window, timeout)
hl.timer(function()
local refreshed = window and window.address and hl.get_window(window_selector(window)) or window
raise_file_chooser_window(refreshed)
end, { timeout = timeout or 50, type = "oneshot" })
end
if enable_nstack and not verify_config then if enable_nstack and not verify_config then
hl.plugin.load("/run/current-system/sw/lib/libhyprNStack.so") hl.plugin.load("/run/current-system/sw/lib/libhyprNStack.so")
end end
@@ -255,8 +322,14 @@ function M.setup(ctx)
hl.window_rule({ match = { class = "^()$", title = "^()$" }, float = true }) hl.window_rule({ match = { class = "^()$", title = "^()$" }, float = true })
hl.window_rule({ match = { title = "^(Picture-in-Picture)$" }, float = true }) hl.window_rule({ match = { title = "^(Picture-in-Picture)$" }, float = true })
hl.window_rule({ match = { title = "^(Open File)$" }, float = true }) hl.window_rule({
hl.window_rule({ match = { title = "^(Save File)$" }, float = true }) name = "file-chooser-dialogs",
match = { title = file_chooser_title_rule },
float = true,
center = true,
focus_on_activate = true,
stay_focused = true,
})
hl.window_rule({ match = { title = "^(Confirm)$" }, float = true }) hl.window_rule({ match = { title = "^(Confirm)$" }, float = true })
for index, match in ipairs({ for index, match in ipairs({
@@ -317,6 +390,9 @@ function M.setup(ctx)
ctx.apply_hyprglass_config = apply_hyprglass_config ctx.apply_hyprglass_config = apply_hyprglass_config
ctx.apply_hyprwobbly_config = apply_hyprwobbly_config ctx.apply_hyprwobbly_config = apply_hyprwobbly_config
ctx.apply_visual_performance_mode = apply_visual_performance_mode ctx.apply_visual_performance_mode = apply_visual_performance_mode
ctx.is_file_chooser_window = is_file_chooser_window
ctx.raise_file_chooser_window = raise_file_chooser_window
ctx.raise_file_chooser_window_later = raise_file_chooser_window_later
ctx.toggle_visual_performance_mode = toggle_visual_performance_mode ctx.toggle_visual_performance_mode = toggle_visual_performance_mode
end end