Split Hyprland Lua config into modules
This commit is contained in:
@@ -137,8 +137,9 @@ notifications, SNI/tray support, fonts, and app defaults.
|
|||||||
|
|
||||||
The currently important pieces are:
|
The currently important pieces are:
|
||||||
|
|
||||||
- Hyprland configuration in [[file:dotfiles/config/hypr/hyprland.lua][dotfiles/config/hypr/hyprland.lua]], backed by custom
|
- Hyprland configuration in [[file:dotfiles/config/hypr/hyprland.lua][dotfiles/config/hypr/hyprland.lua]], with imported Lua
|
||||||
plugin inputs in the NixOS flake.
|
modules under [[file:dotfiles/config/hypr/hyprland/][dotfiles/config/hypr/hyprland/]], backed by custom plugin inputs in
|
||||||
|
the NixOS flake.
|
||||||
- XMonad configuration in [[file:dotfiles/config/xmonad/xmonad.hs][dotfiles/config/xmonad/xmonad.hs]], with upstream
|
- XMonad configuration in [[file:dotfiles/config/xmonad/xmonad.hs][dotfiles/config/xmonad/xmonad.hs]], with upstream
|
||||||
=xmonad= and =xmonad-contrib= available as submodules/checkouts.
|
=xmonad= and =xmonad-contrib= available as submodules/checkouts.
|
||||||
- Taffybar configuration in [[file:dotfiles/config/taffybar/taffybar.hs][dotfiles/config/taffybar/taffybar.hs]], plus a local
|
- Taffybar configuration in [[file:dotfiles/config/taffybar/taffybar.hs][dotfiles/config/taffybar/taffybar.hs]], plus a local
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
269
dotfiles/config/hypr/hyprland/binds.lua
Normal file
269
dotfiles/config/hypr/hyprland/binds.lua
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.setup(ctx)
|
||||||
|
local _ENV = ctx
|
||||||
|
bind(main_mod .. " + P", exec(launcher_command))
|
||||||
|
bind(main_mod .. " + SHIFT + P", exec(run_menu))
|
||||||
|
bind(hyper .. " + SHIFT + N", exec(shell_ui_command .. " control-center"))
|
||||||
|
bind(hyper .. " + CTRL + N", exec(shell_ui_command .. " settings"))
|
||||||
|
bind(main_mod .. " + SHIFT + Return", exec(terminal))
|
||||||
|
bind(main_mod .. " + Q", exec("hyprctl reload"))
|
||||||
|
bind(main_mod .. " + SHIFT + C", hl.dsp.window.close())
|
||||||
|
bind(main_mod .. " + SHIFT + Q", hl.dsp.exit())
|
||||||
|
bind(main_mod .. " + E", exec("emacsclient --eval '(emacs-everywhere)'"))
|
||||||
|
bind(main_mod .. " + V", exec("wl-paste | xdotool type --file -"))
|
||||||
|
bind(main_mod .. " + Tab", hyprwinview({
|
||||||
|
action = "show",
|
||||||
|
start_in_filter_mode = true,
|
||||||
|
default_action = "bring",
|
||||||
|
}), overview_bind_opts)
|
||||||
|
bind(main_mod .. " + SHIFT + Tab", hyprwinview({
|
||||||
|
action = "show",
|
||||||
|
include_current_workspace = false,
|
||||||
|
start_in_filter_mode = true,
|
||||||
|
default_action = "bring",
|
||||||
|
}), overview_bind_opts)
|
||||||
|
bind(main_mod .. " + SHIFT + slash", hyprwinview({ action = "toggle-filter" }), overview_bind_opts)
|
||||||
|
bind("ALT + Tab", hyprexpo("open"), overview_bind_opts)
|
||||||
|
bind("ALT + SHIFT + Tab", hyprexpo("bring"), overview_bind_opts)
|
||||||
|
bind(main_mod .. " + G", hyprwinview({
|
||||||
|
action = "show",
|
||||||
|
start_in_filter_mode = true,
|
||||||
|
default_action = "select",
|
||||||
|
}), overview_bind_opts)
|
||||||
|
bind(main_mod .. " + B", hyprwinview({
|
||||||
|
action = "show",
|
||||||
|
start_in_filter_mode = true,
|
||||||
|
default_action = "bring",
|
||||||
|
}), overview_bind_opts)
|
||||||
|
bind(main_mod .. " + SHIFT + B", hyprwinview({
|
||||||
|
action = "show",
|
||||||
|
start_in_filter_mode = true,
|
||||||
|
default_action = "bring-replace",
|
||||||
|
}), overview_bind_opts)
|
||||||
|
|
||||||
|
bind(main_mod .. " + W", function()
|
||||||
|
focus_direction("up")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + S", function()
|
||||||
|
focus_direction("down")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + A", function()
|
||||||
|
focus_direction("left")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + D", function()
|
||||||
|
focus_direction("right")
|
||||||
|
end)
|
||||||
|
|
||||||
|
bind(main_mod .. " + SHIFT + W", function()
|
||||||
|
swap_direction("up")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + SHIFT + S", function()
|
||||||
|
swap_direction("down")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + SHIFT + A", function()
|
||||||
|
swap_direction("left")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + SHIFT + D", function()
|
||||||
|
swap_direction("right")
|
||||||
|
end)
|
||||||
|
|
||||||
|
bind(main_mod .. " + CTRL + W", function()
|
||||||
|
move_window_to_monitor("u", false)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + S", function()
|
||||||
|
move_window_to_monitor("d", false)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + A", function()
|
||||||
|
move_window_to_monitor("l", false)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + D", function()
|
||||||
|
move_window_to_monitor("r", false)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + SHIFT + W", function()
|
||||||
|
move_window_to_empty_workspace_on_monitor("u")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + SHIFT + S", function()
|
||||||
|
move_window_to_empty_workspace_on_monitor("d")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + SHIFT + A", function()
|
||||||
|
move_window_to_empty_workspace_on_monitor("l")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + SHIFT + D", function()
|
||||||
|
move_window_to_empty_workspace_on_monitor("r")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hl.define_submap("swap-workspace", function()
|
||||||
|
for i = 1, 9 do
|
||||||
|
local workspace_id = i
|
||||||
|
bind(tostring(i), function()
|
||||||
|
swap_current_workspace_with(workspace_id)
|
||||||
|
dispatch(hl.dsp.submap("reset"))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
bind("Escape", hl.dsp.submap("reset"))
|
||||||
|
bind("catchall", hl.dsp.submap("reset"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
hl.define_submap("window-picker", function()
|
||||||
|
for i = 1, 9 do
|
||||||
|
local index = i
|
||||||
|
bind(tostring(i), function()
|
||||||
|
activate_window_picker_candidate(index)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
bind("Escape", hl.dsp.submap("reset"))
|
||||||
|
bind("catchall", hl.dsp.submap("reset"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
bind(mod_alt .. " + SHIFT + W", hl.dsp.window.resize({ x = 0, y = -50, relative = true }), { repeating = true })
|
||||||
|
bind(mod_alt .. " + SHIFT + S", hl.dsp.window.resize({ x = 0, y = 50, relative = true }), { repeating = true })
|
||||||
|
bind(mod_alt .. " + SHIFT + A", hl.dsp.window.resize({ x = -50, y = 0, relative = true }), { repeating = true })
|
||||||
|
bind(mod_alt .. " + SHIFT + D", hl.dsp.window.resize({ x = 50, y = 0, relative = true }), { repeating = true })
|
||||||
|
|
||||||
|
bind(hyper .. " + W", hl.dsp.focus({ monitor = "u" }))
|
||||||
|
bind(hyper .. " + S", hl.dsp.focus({ monitor = "d" }))
|
||||||
|
bind(hyper .. " + A", hl.dsp.focus({ monitor = "l" }))
|
||||||
|
bind(hyper .. " + D", hl.dsp.focus({ monitor = "r" }))
|
||||||
|
bind(hyper .. " + SHIFT + W", function()
|
||||||
|
move_window_to_monitor("u", true)
|
||||||
|
end)
|
||||||
|
bind(hyper .. " + SHIFT + S", function()
|
||||||
|
move_window_to_monitor("d", true)
|
||||||
|
end)
|
||||||
|
bind(hyper .. " + SHIFT + A", function()
|
||||||
|
move_window_to_monitor("l", true)
|
||||||
|
end)
|
||||||
|
bind(hyper .. " + SHIFT + D", function()
|
||||||
|
move_window_to_monitor("r", true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
bind(main_mod .. " + Space", cycle_layout_or_restore_tabbed_group)
|
||||||
|
bind(main_mod .. " + SHIFT + Space", force_columns_layout)
|
||||||
|
bind(main_mod .. " + CTRL + Space", gather_workspace_into_tabbed_group)
|
||||||
|
bind(main_mod .. " + bracketright", monocle_next)
|
||||||
|
bind(main_mod .. " + bracketleft", monocle_prev)
|
||||||
|
bind(main_mod .. " + T", hl.dsp.window.float({ action = "disable" }))
|
||||||
|
bind(main_mod .. " + O", toggle_pinned_active_window)
|
||||||
|
bind(main_mod .. " + M", minimize_active_window)
|
||||||
|
bind(main_mod .. " + SHIFT + M", restore_last_minimized)
|
||||||
|
bind(main_mod .. " + CTRL + SHIFT + M", function()
|
||||||
|
enter_window_picker("minimized")
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + SHIFT + equal", schedule_nstack_count_update)
|
||||||
|
bind(main_mod .. " + CTRL + M", hl.dsp.window.toggle_swallow())
|
||||||
|
bind(main_mod .. " + SHIFT + E", function()
|
||||||
|
move_to_next_empty_workspace(true)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + CTRL + E", function()
|
||||||
|
move_to_next_empty_workspace(false)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + apostrophe", focus_next_class)
|
||||||
|
bind(mod_alt .. " + W", show_active_window_info)
|
||||||
|
|
||||||
|
bind(main_mod .. " + X", exec("rofi_command.sh"))
|
||||||
|
bind(main_mod .. " + SHIFT + X", hl.dsp.workspace.toggle_special("NSP"))
|
||||||
|
bind(mod_alt .. " + C", function()
|
||||||
|
toggle_scratchpad("codex")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + E", function()
|
||||||
|
toggle_scratchpad("element")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + H", function()
|
||||||
|
toggle_scratchpad("htop")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + K", function()
|
||||||
|
toggle_scratchpad("slack")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + M", function()
|
||||||
|
toggle_scratchpad("messages")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + S", function()
|
||||||
|
toggle_scratchpad("spotify")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + T", function()
|
||||||
|
toggle_scratchpad("transmission")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + V", function()
|
||||||
|
toggle_scratchpad("volume")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + grave", function()
|
||||||
|
toggle_scratchpad("dropdown")
|
||||||
|
end)
|
||||||
|
bind(mod_alt .. " + Space", minimize_other_classes)
|
||||||
|
bind(mod_alt .. " + SHIFT + Space", restore_focused_class)
|
||||||
|
bind(mod_alt .. " + Return", restore_all_minimized)
|
||||||
|
|
||||||
|
for i = 1, 9 do
|
||||||
|
local workspace = tostring(i)
|
||||||
|
bind(main_mod .. " + " .. workspace, hl.dsp.focus({ workspace = workspace, on_current_monitor = true }))
|
||||||
|
bind(main_mod .. " + SHIFT + " .. workspace, hl.dsp.window.move({ workspace = workspace, follow = false }))
|
||||||
|
bind(main_mod .. " + CTRL + " .. workspace, function()
|
||||||
|
dispatch(hl.dsp.window.move({ workspace = workspace, follow = false }))
|
||||||
|
dispatch(hl.dsp.focus({ workspace = workspace, on_current_monitor = true }))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
bind(main_mod .. " + backslash", workspacehistory("cycle", 1))
|
||||||
|
bind(main_mod .. " + slash", workspacehistory("cycle", -1))
|
||||||
|
bind(main_mod .. " + Escape", workspacehistory("cancel"))
|
||||||
|
bind(main_mod .. " + Z", hl.dsp.focus({ monitor = "+1" }))
|
||||||
|
bind(main_mod .. " + SHIFT + Z", hl.dsp.window.move({ monitor = "+1" }))
|
||||||
|
bind(main_mod .. " + mouse_down", function()
|
||||||
|
cycle_workspace(1)
|
||||||
|
end)
|
||||||
|
bind(main_mod .. " + mouse_up", function()
|
||||||
|
cycle_workspace(-1)
|
||||||
|
end)
|
||||||
|
bind(hyper .. " + E", focus_next_empty_workspace)
|
||||||
|
bind(hyper .. " + 5", enter_workspace_swap_mode)
|
||||||
|
bind(hyper .. " + G", gather_focused_class)
|
||||||
|
|
||||||
|
bind(main_mod .. " + I", exec("set_volume --unmute --change-volume +5"), { repeating = true })
|
||||||
|
bind(main_mod .. " + K", exec("set_volume --unmute --change-volume -5"), { repeating = true })
|
||||||
|
bind(main_mod .. " + U", exec("set_volume --toggle-mute"))
|
||||||
|
bind(main_mod .. " + semicolon", exec("playerctl play-pause"))
|
||||||
|
bind(main_mod .. " + L", exec("playerctl next"))
|
||||||
|
bind(main_mod .. " + J", exec("playerctl previous"))
|
||||||
|
|
||||||
|
bind("XF86AudioPlay", exec("playerctl play-pause"))
|
||||||
|
bind("XF86AudioPause", exec("playerctl play-pause"))
|
||||||
|
bind("XF86AudioNext", exec("playerctl next"))
|
||||||
|
bind("XF86AudioPrev", exec("playerctl previous"))
|
||||||
|
bind("XF86AudioRaiseVolume", exec("set_volume --unmute --change-volume +5"), { repeating = true })
|
||||||
|
bind("XF86AudioLowerVolume", exec("set_volume --unmute --change-volume -5"), { repeating = true })
|
||||||
|
bind("XF86AudioMute", exec("set_volume --toggle-mute"))
|
||||||
|
bind("XF86MonBrightnessUp", exec("brightness.sh up"), { repeating = true })
|
||||||
|
bind("XF86MonBrightnessDown", exec("brightness.sh down"), { repeating = true })
|
||||||
|
|
||||||
|
bind(hyper .. " + V", exec([[cliphist list | rofi -dmenu -p "Clipboard" | cliphist decode | wl-copy]]))
|
||||||
|
bind(hyper .. " + P", exec("rofi-pass"))
|
||||||
|
bind(hyper .. " + H", exec([[grim -g "$(slurp)" - | swappy -f -]]))
|
||||||
|
bind(hyper .. " + C", exec("rofi_tmcodex.sh"))
|
||||||
|
bind(hyper .. " + SHIFT + C", exec("rofi_tmcodex.sh resume"))
|
||||||
|
bind(hyper .. " + SHIFT + L", exec("hyprlock"))
|
||||||
|
bind(hyper .. " + L", exec("hypr_rofi_layout"))
|
||||||
|
bind(hyper .. " + K", exec("rofi_kill_process.sh"))
|
||||||
|
bind(hyper .. " + SHIFT + K", exec("rofi_kill_all.sh"))
|
||||||
|
bind(hyper .. " + R", exec("rofi-systemd"))
|
||||||
|
bind(hyper .. " + slash", function()
|
||||||
|
hl.exec_cmd("toggle_taffybar")
|
||||||
|
refresh_monitor_reserved_cache(0.25)
|
||||||
|
refresh_active_scratchpad_geometries_later(600)
|
||||||
|
end)
|
||||||
|
bind(hyper .. " + I", exec("rofi_select_input.hs"))
|
||||||
|
bind(hyper .. " + backslash", exec("/home/imalison/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle"))
|
||||||
|
bind(hyper .. " + SHIFT + backslash", workspacehistory("debug"))
|
||||||
|
bind(hyper .. " + O", exec("rofi_paswitch"))
|
||||||
|
bind(hyper .. " + comma", exec("rofi_wallpaper.sh"))
|
||||||
|
bind(hyper .. " + SHIFT + comma", exec("/home/imalison/dotfiles/dotfiles/lib/bin/neowall-wallpaper toggle"))
|
||||||
|
bind(hyper .. " + Y", exec("rofi_agentic_skill"))
|
||||||
|
bind(main_mod .. " + R", exec("hyprctl reload"))
|
||||||
|
|
||||||
|
bind(main_mod .. " + mouse:272", float_and_drag_active_window)
|
||||||
|
bind(main_mod .. " + mouse:273", float_and_resize_active_window)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
584
dotfiles/config/hypr/hyprland/core.lua
Normal file
584
dotfiles/config/hypr/hyprland/core.lua
Normal file
@@ -0,0 +1,584 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.setup(ctx)
|
||||||
|
local _ENV = ctx
|
||||||
|
local function command_line_contains(needle)
|
||||||
|
local command_line = io.open("/proc/self/cmdline", "rb")
|
||||||
|
if not command_line then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local contents = command_line:read("*a") or ""
|
||||||
|
command_line:close()
|
||||||
|
return contents:find(needle, 1, true) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
verify_config = command_line_contains("--verify-config")
|
||||||
|
|
||||||
|
local function bind(keys, dispatcher, opts)
|
||||||
|
hl.bind(keys, dispatcher, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function exec(command)
|
||||||
|
return hl.dsp.exec_cmd(command)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dispatch(dispatcher)
|
||||||
|
return hl.dispatch(dispatcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function shell_quote(value)
|
||||||
|
return "'" .. tostring(value):gsub("'", "'\\''") .. "'"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function overview_trace(label)
|
||||||
|
local enabled = io.open(overview_trace_enabled_path, "r")
|
||||||
|
if not enabled then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
enabled:close()
|
||||||
|
|
||||||
|
local trace = io.open(overview_trace_path, "a")
|
||||||
|
if trace then
|
||||||
|
trace:write(os.date("%Y-%m-%d %H:%M:%S "), label, "\n")
|
||||||
|
trace:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function window_selector(window)
|
||||||
|
if not window or not window.address then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return "address:" .. tostring(window.address)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hyprexpo(action)
|
||||||
|
action = action or "toggle"
|
||||||
|
return function()
|
||||||
|
overview_trace("hyprexpo " .. tostring(action))
|
||||||
|
if hl.plugin and hl.plugin.hyprexpo and hl.plugin.hyprexpo.expo then
|
||||||
|
hl.plugin.hyprexpo.expo(action)
|
||||||
|
else
|
||||||
|
hl.notification.create({
|
||||||
|
text = "hyprexpo is not loaded",
|
||||||
|
duration = 1800,
|
||||||
|
icon = notification_icons.warning,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hyprwinview(action)
|
||||||
|
return function()
|
||||||
|
local label = "hyprwinview"
|
||||||
|
if type(action) == "table" and action.action then
|
||||||
|
label = label .. " " .. tostring(action.action)
|
||||||
|
elseif type(action) ~= "table" and action ~= nil then
|
||||||
|
label = label .. " " .. tostring(action)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function invoke()
|
||||||
|
overview_trace(label)
|
||||||
|
if hl.plugin and hl.plugin.hyprwinview and hl.plugin.hyprwinview.overview then
|
||||||
|
hl.plugin.hyprwinview.overview(action)
|
||||||
|
else
|
||||||
|
hl.notification.create({
|
||||||
|
text = "hyprwinview is not loaded",
|
||||||
|
duration = 1800,
|
||||||
|
icon = notification_icons.warning,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
invoke()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function workspacehistory(action, arg)
|
||||||
|
return function()
|
||||||
|
if hl.plugin and hl.plugin.workspacehistory and hl.plugin.workspacehistory[action] then
|
||||||
|
hl.plugin.workspacehistory[action](arg)
|
||||||
|
else
|
||||||
|
hl.notification.create({
|
||||||
|
text = "workspacehistory is not loaded",
|
||||||
|
duration = 1800,
|
||||||
|
icon = notification_icons.warning,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_nstack_config()
|
||||||
|
if verify_config or not enable_nstack or not configure_nstack_plugin_from_lua then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
plugin = {
|
||||||
|
nstack = {
|
||||||
|
layout = {
|
||||||
|
orientation = "left",
|
||||||
|
new_on_top = false,
|
||||||
|
new_near_focused = true,
|
||||||
|
new_is_master = false,
|
||||||
|
no_gaps_when_only = true,
|
||||||
|
special_scale_factor = 0.8,
|
||||||
|
inherit_fullscreen = true,
|
||||||
|
stacks = 1,
|
||||||
|
center_single_master = false,
|
||||||
|
mfact = 0.0,
|
||||||
|
single_mfact = 1.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_hyprexpo_config()
|
||||||
|
if verify_config or not enable_hyprexpo then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
plugin = {
|
||||||
|
hyprexpo = {
|
||||||
|
columns = 3,
|
||||||
|
gap_size = 5,
|
||||||
|
bg_col = "rgba(111111ff)",
|
||||||
|
workspace_method = "center current",
|
||||||
|
skip_empty = false,
|
||||||
|
max_workspace = max_workspace,
|
||||||
|
show_workspace_numbers = true,
|
||||||
|
workspace_number_color = "rgba(edb443ff)",
|
||||||
|
gesture_distance = 200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_hyprwinview_config()
|
||||||
|
if verify_config or not enable_hyprwinview then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
plugin = {
|
||||||
|
hyprwinview = {
|
||||||
|
gap_size = 24,
|
||||||
|
margin = 48,
|
||||||
|
background = "rgba(10101400)",
|
||||||
|
background_blur = 1,
|
||||||
|
border_col = "rgba(ffffff33)",
|
||||||
|
hover_border_col = "rgba(66ccffee)",
|
||||||
|
border_size = 3,
|
||||||
|
window_order = "application",
|
||||||
|
keys_default_action = "return,enter,space,g,f",
|
||||||
|
keys_filter_toggle = "/",
|
||||||
|
show_app_icon = 1,
|
||||||
|
app_icon_size = 48,
|
||||||
|
app_icon_theme_source = "auto",
|
||||||
|
app_icon_position = "bottom right",
|
||||||
|
app_icon_margin_x = 12,
|
||||||
|
app_icon_margin_y = 12,
|
||||||
|
app_icon_margin_relative_x = 0.0,
|
||||||
|
app_icon_margin_relative_y = 0.0,
|
||||||
|
app_icon_offset_x = 0,
|
||||||
|
app_icon_offset_y = 0,
|
||||||
|
app_icon_backplate_col = "rgba(00000066)",
|
||||||
|
app_icon_backplate_padding = 6,
|
||||||
|
show_window_text = 1,
|
||||||
|
window_text_font = "Sans",
|
||||||
|
window_text_size = 14,
|
||||||
|
window_text_color = "rgba(ffffffff)",
|
||||||
|
window_text_backplate_col = "rgba(00000099)",
|
||||||
|
window_text_padding = 6,
|
||||||
|
filter_animation_ms = 140,
|
||||||
|
animation = "workspace_zoom",
|
||||||
|
animation_in_ms = 280,
|
||||||
|
animation_out_ms = 220,
|
||||||
|
animation_speed = 1.0,
|
||||||
|
animation_scale = 0.94,
|
||||||
|
animation_stagger_ms = 16,
|
||||||
|
animation_stagger_max_ms = 120,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if hl.plugin and hl.plugin.hyprwinview and hl.plugin.hyprwinview.configure then
|
||||||
|
hl.plugin.hyprwinview.configure({
|
||||||
|
keys = {
|
||||||
|
left = { "a", "h", "left" },
|
||||||
|
right = { "d", "l", "right" },
|
||||||
|
up = { "w", "k", "up" },
|
||||||
|
down = { "s", "j", "down" },
|
||||||
|
default_action = { "return", "enter", "space", "g", "f" },
|
||||||
|
bring = { "b", "shift+return", "shift+space" },
|
||||||
|
bring_replace = { "shift + b" },
|
||||||
|
close = { "escape", "q" },
|
||||||
|
filter_toggle = { "/" },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function active_workspace()
|
||||||
|
return hl.get_active_workspace()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function active_workspace_id()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if workspace and type(workspace.id) == "number" and workspace.id >= 1 then
|
||||||
|
return math.min(max_workspace, math.max(1, workspace.id))
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function workspace_key(workspace)
|
||||||
|
workspace = workspace or active_workspace()
|
||||||
|
if workspace and workspace.id then
|
||||||
|
return tostring(workspace.id)
|
||||||
|
end
|
||||||
|
return tostring(active_workspace_id())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function current_workspace_layout()
|
||||||
|
return workspace_layouts[workspace_key()] or columns_layout
|
||||||
|
end
|
||||||
|
|
||||||
|
local function write_layout_state()
|
||||||
|
local runtime_dir = os.getenv("XDG_RUNTIME_DIR")
|
||||||
|
if not runtime_dir then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local file = io.open(runtime_dir .. "/hyprland-layout-state", "w")
|
||||||
|
if not file then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace = active_workspace()
|
||||||
|
file:write("workspace=", workspace_key(workspace), "\n")
|
||||||
|
file:write("layout=", current_layout, "\n")
|
||||||
|
for key, layout in pairs(workspace_layouts) do
|
||||||
|
file:write("workspace.", tostring(key), "=", tostring(layout), "\n")
|
||||||
|
end
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_normal_workspace(workspace)
|
||||||
|
return workspace and not workspace.special and workspace.id and workspace.id >= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lower_contains(value, needle)
|
||||||
|
if not needle or needle == "" then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
value = string.lower(tostring(value or ""))
|
||||||
|
needle = string.lower(tostring(needle))
|
||||||
|
return value:find(needle, 1, true) ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lower_contains_any(value, needles)
|
||||||
|
if type(needles) ~= "table" then
|
||||||
|
return lower_contains(value, needles)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, needle in ipairs(needles) do
|
||||||
|
if lower_contains(value, needle) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scratchpad_window_matches(window, def)
|
||||||
|
return window
|
||||||
|
and lower_contains_any(window.class, def.classes or def.class)
|
||||||
|
and lower_contains(window.title, def.title)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_scratchpad_window(window)
|
||||||
|
for _, def in pairs(scratchpads) do
|
||||||
|
if scratchpad_window_matches(window, def) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function matching_scratchpad_name(window)
|
||||||
|
for name, def in pairs(scratchpads) do
|
||||||
|
if scratchpad_window_matches(window, def) then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function same_workspace(left, right)
|
||||||
|
if not left or not right then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if left.name and right.name and tostring(left.name) == tostring(right.name) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return left.id and right.id and left.id == right.id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_minimized_workspace(workspace)
|
||||||
|
if not workspace then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = tostring(workspace.name or "")
|
||||||
|
return name == minimized_workspace or name == "minimized" or (workspace.special and name:find("minimized", 1, true) ~= nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_minimized_window(window)
|
||||||
|
return window and is_minimized_workspace(window.workspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_normal_window(window)
|
||||||
|
return window
|
||||||
|
and window.mapped ~= false
|
||||||
|
and not window.hidden
|
||||||
|
and window.workspace
|
||||||
|
and is_normal_workspace(window.workspace)
|
||||||
|
and not is_scratchpad_window(window)
|
||||||
|
and not is_minimized_window(window)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tiled_windows(workspace)
|
||||||
|
local windows = {}
|
||||||
|
if not workspace then
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(hl.get_workspace_windows(workspace)) do
|
||||||
|
if not window.floating and not window.hidden then
|
||||||
|
windows[#windows + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tiled_window_count(workspace)
|
||||||
|
return #tiled_windows(workspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sort_windows_by_focus_history(windows)
|
||||||
|
table.sort(windows, function(left, right)
|
||||||
|
return (left.focus_history_id or 0) < (right.focus_history_id or 0)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function window_address_set(windows)
|
||||||
|
local addresses = {}
|
||||||
|
for _, window in ipairs(windows) do
|
||||||
|
if window and window.address then
|
||||||
|
addresses[window.address] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return addresses
|
||||||
|
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)
|
||||||
|
return window and window.address and addresses[window.address] or false
|
||||||
|
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)
|
||||||
|
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 tiled_window_geometry(window)
|
||||||
|
if not window or window.floating then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local selector = window_selector(window)
|
||||||
|
if not selector then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local at = window.at or {}
|
||||||
|
local size = window.size or {}
|
||||||
|
local width = math.floor(numeric_component(size, "x", 1))
|
||||||
|
local height = math.floor(numeric_component(size, "y", 2))
|
||||||
|
if width <= 0 or height <= 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
selector = selector,
|
||||||
|
x = math.floor(numeric_component(at, "x", 1)),
|
||||||
|
y = math.floor(numeric_component(at, "y", 2)),
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
}
|
||||||
|
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 sort_windows_by_visual_position(windows)
|
||||||
|
table.sort(windows, function(left, right)
|
||||||
|
local left_x, left_y = window_center(left)
|
||||||
|
local right_x, right_y = window_center(right)
|
||||||
|
|
||||||
|
if math.abs(left_x - right_x) > 10 then
|
||||||
|
return left_x < right_x
|
||||||
|
end
|
||||||
|
if math.abs(left_y - right_y) > 10 then
|
||||||
|
return left_y < right_y
|
||||||
|
end
|
||||||
|
return tostring(left.address or "") < tostring(right.address or "")
|
||||||
|
end)
|
||||||
|
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 workspace = hl.get_workspace(tostring(workspace_id))
|
||||||
|
if not workspace then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
return workspace.windows or tiled_window_count(workspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_empty_workspace(target_monitor, exclude_id)
|
||||||
|
local unused_candidate = nil
|
||||||
|
local elsewhere_empty_candidate = nil
|
||||||
|
local target_monitor_name = target_monitor and target_monitor.name or nil
|
||||||
|
|
||||||
|
for i = 1, max_workspace do
|
||||||
|
if i ~= exclude_id then
|
||||||
|
local workspace = hl.get_workspace(tostring(i))
|
||||||
|
|
||||||
|
if not workspace then
|
||||||
|
unused_candidate = unused_candidate or i
|
||||||
|
elseif is_normal_workspace(workspace) and workspace_window_count(i) == 0 then
|
||||||
|
local monitor = workspace.monitor
|
||||||
|
if target_monitor_name and monitor and monitor.name == target_monitor_name then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
elsewhere_empty_candidate = elsewhere_empty_candidate or i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return unused_candidate or elsewhere_empty_candidate
|
||||||
|
end
|
||||||
|
|
||||||
|
ctx.command_line_contains = command_line_contains
|
||||||
|
ctx.bind = bind
|
||||||
|
ctx.exec = exec
|
||||||
|
ctx.dispatch = dispatch
|
||||||
|
ctx.shell_quote = shell_quote
|
||||||
|
ctx.overview_trace = overview_trace
|
||||||
|
ctx.window_selector = window_selector
|
||||||
|
ctx.hyprexpo = hyprexpo
|
||||||
|
ctx.hyprwinview = hyprwinview
|
||||||
|
ctx.workspacehistory = workspacehistory
|
||||||
|
ctx.apply_nstack_config = apply_nstack_config
|
||||||
|
ctx.apply_hyprexpo_config = apply_hyprexpo_config
|
||||||
|
ctx.apply_hyprwinview_config = apply_hyprwinview_config
|
||||||
|
ctx.active_workspace = active_workspace
|
||||||
|
ctx.active_workspace_id = active_workspace_id
|
||||||
|
ctx.workspace_key = workspace_key
|
||||||
|
ctx.current_workspace_layout = current_workspace_layout
|
||||||
|
ctx.write_layout_state = write_layout_state
|
||||||
|
ctx.is_normal_workspace = is_normal_workspace
|
||||||
|
ctx.lower_contains = lower_contains
|
||||||
|
ctx.lower_contains_any = lower_contains_any
|
||||||
|
ctx.scratchpad_window_matches = scratchpad_window_matches
|
||||||
|
ctx.is_scratchpad_window = is_scratchpad_window
|
||||||
|
ctx.matching_scratchpad_name = matching_scratchpad_name
|
||||||
|
ctx.same_workspace = same_workspace
|
||||||
|
ctx.is_minimized_workspace = is_minimized_workspace
|
||||||
|
ctx.is_minimized_window = is_minimized_window
|
||||||
|
ctx.is_normal_window = is_normal_window
|
||||||
|
ctx.tiled_windows = tiled_windows
|
||||||
|
ctx.tiled_window_count = tiled_window_count
|
||||||
|
ctx.sort_windows_by_focus_history = sort_windows_by_focus_history
|
||||||
|
ctx.window_address_set = window_address_set
|
||||||
|
ctx.window_address_list = window_address_list
|
||||||
|
ctx.window_address_in_set = window_address_in_set
|
||||||
|
ctx.windows_by_address = windows_by_address
|
||||||
|
ctx.numeric_component = numeric_component
|
||||||
|
ctx.window_center = window_center
|
||||||
|
ctx.tiled_window_geometry = tiled_window_geometry
|
||||||
|
ctx.window_distance_squared = window_distance_squared
|
||||||
|
ctx.sort_windows_by_visual_position = sort_windows_by_visual_position
|
||||||
|
ctx.grouping_direction = grouping_direction
|
||||||
|
ctx.grouping_directions = grouping_directions
|
||||||
|
ctx.workspace_window_count = workspace_window_count
|
||||||
|
ctx.find_empty_workspace = find_empty_workspace
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
48
dotfiles/config/hypr/hyprland/events.lua
Normal file
48
dotfiles/config/hypr/hyprland/events.lua
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.setup(ctx)
|
||||||
|
local _ENV = ctx
|
||||||
|
hl.on("hyprland.start", function()
|
||||||
|
apply_nstack_config()
|
||||||
|
apply_hyprexpo_config()
|
||||||
|
apply_hyprwinview_config()
|
||||||
|
apply_rules()
|
||||||
|
hl.exec_cmd("sh -lc '/run/current-system/sw/bin/uwsm finalize HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE XAUTHORITY IMALISON_SESSION_TYPE=wayland IMALISON_WINDOW_MANAGER=hyprland || dbus-update-activation-environment --systemd XDG_RUNTIME_DIR WAYLAND_DISPLAY DISPLAY XAUTHORITY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE IMALISON_SESSION_TYPE IMALISON_WINDOW_MANAGER; systemctl --user start hyprland-session.target'")
|
||||||
|
hl.exec_cmd("hypridle")
|
||||||
|
hl.exec_cmd("wl-paste --type text --watch cliphist store")
|
||||||
|
hl.exec_cmd("wl-paste --type image --watch cliphist store")
|
||||||
|
write_layout_state()
|
||||||
|
schedule_nstack_count_update()
|
||||||
|
refresh_monitor_reserved_cache(0.25)
|
||||||
|
refresh_monitor_reserved_cache(1.25)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hl.on("config.reloaded", apply_nstack_config)
|
||||||
|
hl.on("config.reloaded", apply_hyprexpo_config)
|
||||||
|
hl.on("config.reloaded", apply_hyprwinview_config)
|
||||||
|
hl.on("config.reloaded", apply_rules)
|
||||||
|
hl.on("config.reloaded", refresh_shell_workarea_and_scratchpads)
|
||||||
|
hl.on("layer.opened", refresh_shell_workarea_and_scratchpads)
|
||||||
|
hl.on("layer.closed", refresh_shell_workarea_and_scratchpads)
|
||||||
|
hl.on("monitor.added", refresh_shell_workarea_and_scratchpads)
|
||||||
|
hl.on("monitor.removed", refresh_shell_workarea_and_scratchpads)
|
||||||
|
hl.on("monitor.layout_changed", refresh_shell_workarea_and_scratchpads)
|
||||||
|
|
||||||
|
hl.on("window.open", schedule_nstack_count_update)
|
||||||
|
hl.on("window.destroy", schedule_nstack_count_update)
|
||||||
|
hl.on("window.kill", schedule_nstack_count_update)
|
||||||
|
hl.on("window.move_to_workspace", schedule_nstack_count_update)
|
||||||
|
hl.on("workspace.active", sync_layout_for_active_workspace)
|
||||||
|
hl.on("monitor.focused", sync_layout_for_active_workspace)
|
||||||
|
|
||||||
|
hl.on("window.open", update_monocle_notice)
|
||||||
|
hl.on("window.destroy", update_monocle_notice)
|
||||||
|
hl.on("window.kill", update_monocle_notice)
|
||||||
|
hl.on("window.move_to_workspace", update_monocle_notice)
|
||||||
|
|
||||||
|
hl.on("window.open", adopt_matching_scratchpad_window)
|
||||||
|
hl.on("window.class", adopt_matching_scratchpad_window)
|
||||||
|
hl.on("window.title", adopt_matching_scratchpad_window)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
596
dotfiles/config/hypr/hyprland/layouts.lua
Normal file
596
dotfiles/config/hypr/hyprland/layouts.lua
Normal file
@@ -0,0 +1,596 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.setup(ctx)
|
||||||
|
local _ENV = ctx
|
||||||
|
local function is_nstack_layout(layout)
|
||||||
|
return layout == columns_layout or layout == grid_layout
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hyprland_layout(layout)
|
||||||
|
if layout == grid_layout then
|
||||||
|
return columns_layout
|
||||||
|
end
|
||||||
|
return layout
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update_nstack_count()
|
||||||
|
if not enable_nstack or not is_nstack_layout(current_layout) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace = hl.get_active_workspace()
|
||||||
|
local count = tiled_window_count(workspace)
|
||||||
|
if count == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local stack_count = count
|
||||||
|
if current_layout == grid_layout then
|
||||||
|
stack_count = math.ceil(math.sqrt(count))
|
||||||
|
end
|
||||||
|
|
||||||
|
stack_count = math.max(stack_count, 2)
|
||||||
|
dispatch(hl.dsp.layout("setstackcount " .. tostring(stack_count)))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function schedule_nstack_count_update()
|
||||||
|
if stack_update_timer then
|
||||||
|
stack_update_timer:set_enabled(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
stack_update_timer = hl.timer(update_nstack_count, { timeout = 25, type = "oneshot" })
|
||||||
|
end
|
||||||
|
|
||||||
|
local function dismiss_monocle_notice()
|
||||||
|
if monocle_notice and monocle_notice:is_alive() then
|
||||||
|
monocle_notice:dismiss()
|
||||||
|
end
|
||||||
|
monocle_notice = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update_monocle_notice()
|
||||||
|
if current_layout ~= monocle_layout then
|
||||||
|
dismiss_monocle_notice()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace = hl.get_active_workspace()
|
||||||
|
local count = tiled_window_count(workspace)
|
||||||
|
if count <= 1 then
|
||||||
|
dismiss_monocle_notice()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local text = "Monocle: " .. tostring(count) .. " windows"
|
||||||
|
if monocle_notice and monocle_notice:is_alive() then
|
||||||
|
monocle_notice:set_text(text)
|
||||||
|
monocle_notice:set_timeout(60000)
|
||||||
|
monocle_notice:pause()
|
||||||
|
else
|
||||||
|
monocle_notice = hl.notification.create({
|
||||||
|
text = text,
|
||||||
|
duration = 60000,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
monocle_notice:pause()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function layout_name(layout)
|
||||||
|
return layout_names[layout] or tostring(layout)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function notify_layout(layout)
|
||||||
|
hl.notification.create({
|
||||||
|
text = "Layout: " .. layout_name(layout),
|
||||||
|
duration = 1200,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_layout(layout)
|
||||||
|
workspace_layouts[workspace_key()] = layout
|
||||||
|
current_layout = layout
|
||||||
|
hl.config({ general = { layout = hyprland_layout(layout) } })
|
||||||
|
write_layout_state()
|
||||||
|
|
||||||
|
if is_nstack_layout(layout) then
|
||||||
|
dismiss_monocle_notice()
|
||||||
|
schedule_nstack_count_update()
|
||||||
|
else
|
||||||
|
update_monocle_notice()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
_G.im_hyprland_set_layout = function(layout)
|
||||||
|
if not layout_names[layout] then
|
||||||
|
hl.notification.create({
|
||||||
|
text = "Unknown layout: " .. tostring(layout),
|
||||||
|
duration = 1800,
|
||||||
|
icon = notification_icons.warning,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
set_layout(layout)
|
||||||
|
notify_layout(layout)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sync_layout_for_active_workspace()
|
||||||
|
current_layout = current_workspace_layout()
|
||||||
|
hl.config({ general = { layout = hyprland_layout(current_layout) } })
|
||||||
|
write_layout_state()
|
||||||
|
|
||||||
|
if is_nstack_layout(current_layout) then
|
||||||
|
dismiss_monocle_notice()
|
||||||
|
schedule_nstack_count_update()
|
||||||
|
else
|
||||||
|
update_monocle_notice()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cycle_layout(delta)
|
||||||
|
local current_index = 1
|
||||||
|
for index, layout in ipairs(layout_cycle) do
|
||||||
|
if layout == current_layout then
|
||||||
|
current_index = index
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local next_index = ((current_index - 1 + delta) % #layout_cycle) + 1
|
||||||
|
local next_layout = layout_cycle[next_index]
|
||||||
|
set_layout(next_layout)
|
||||||
|
notify_layout(next_layout)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggle_columns_monocle()
|
||||||
|
if current_layout == columns_layout then
|
||||||
|
set_layout(monocle_layout)
|
||||||
|
else
|
||||||
|
set_layout(columns_layout)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function active_group_size()
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
return window and window.group and window.group.size or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function monocle_next()
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
if window and window.group and window.group.size and window.group.size > 1 then
|
||||||
|
dispatch(hl.dsp.group.next({ window = window_selector(window) }))
|
||||||
|
elseif current_layout == monocle_layout then
|
||||||
|
dispatch(hl.dsp.layout("cyclenext"))
|
||||||
|
update_monocle_notice()
|
||||||
|
else
|
||||||
|
dispatch(hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function monocle_prev()
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
if window and window.group and window.group.size and window.group.size > 1 then
|
||||||
|
dispatch(hl.dsp.group.prev({ window = window_selector(window) }))
|
||||||
|
elseif current_layout == monocle_layout then
|
||||||
|
dispatch(hl.dsp.layout("cycleprev"))
|
||||||
|
update_monocle_notice()
|
||||||
|
else
|
||||||
|
dispatch(hl.dsp.window.cycle_next({ next = false, tiled = true, floating = false }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function focus_direction(direction)
|
||||||
|
overview_trace("focus_direction " .. direction)
|
||||||
|
if active_group_size() > 1 or current_layout == monocle_layout then
|
||||||
|
if direction == "up" or direction == "left" then
|
||||||
|
monocle_prev()
|
||||||
|
else
|
||||||
|
monocle_next()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
dispatch(hl.dsp.focus({ direction = direction }))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function swap_direction(direction)
|
||||||
|
if enable_nstack and is_nstack_layout(current_layout) and active_group_size() <= 1 then
|
||||||
|
dispatch(hl.dsp.layout("swapdirection " .. direction))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
dispatch(hl.dsp.window.swap({ direction = direction }))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function focus_workspace(workspace_id)
|
||||||
|
dispatch(hl.dsp.focus({ workspace = tostring(workspace_id), on_current_monitor = true }))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function move_window_to_workspace(workspace_id, follow, window)
|
||||||
|
local target_window = window or hl.get_active_window()
|
||||||
|
local target_selector = window_selector(target_window)
|
||||||
|
dispatch(hl.dsp.window.move({ workspace = tostring(workspace_id), follow = false, window = target_selector }))
|
||||||
|
if follow then
|
||||||
|
focus_workspace(workspace_id)
|
||||||
|
if target_selector then
|
||||||
|
dispatch(hl.dsp.focus({ window = target_selector }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function notify_tabbed_group(text)
|
||||||
|
hl.notification.create({
|
||||||
|
text = text,
|
||||||
|
duration = 1800,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function active_workspace_tiled_group_candidates(workspace)
|
||||||
|
local candidates = tiled_windows(workspace)
|
||||||
|
sort_windows_by_focus_history(candidates)
|
||||||
|
return candidates
|
||||||
|
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
|
||||||
|
dispatch(hl.dsp.focus({ window = selector }))
|
||||||
|
dispatch(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 active = hl.get_active_window()
|
||||||
|
if active and active.group and active.group.size and active.group.size > 1 then
|
||||||
|
return active
|
||||||
|
end
|
||||||
|
|
||||||
|
if not state then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
if window and window.address == state.anchor and window.group and window.group.size and window.group.size > 1 then
|
||||||
|
return window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
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 key = workspace_key()
|
||||||
|
local state = tabbed_workspace_groups[key]
|
||||||
|
local anchor = find_tabbed_group_anchor(state)
|
||||||
|
local anchor_selector = window_selector(anchor)
|
||||||
|
local target_workspace_id = anchor and anchor.workspace and anchor.workspace.id
|
||||||
|
|
||||||
|
if not anchor_selector then
|
||||||
|
tabbed_workspace_groups[key] = nil
|
||||||
|
set_layout(columns_layout)
|
||||||
|
notify_tabbed_group("No tabbed group to restore")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
||||||
|
dispatch(hl.dsp.group.toggle({ window = anchor_selector }))
|
||||||
|
tabbed_workspace_groups[key] = nil
|
||||||
|
set_layout(columns_layout)
|
||||||
|
restore_tabbed_group_window_order(state, target_workspace_id)
|
||||||
|
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
||||||
|
schedule_nstack_count_update()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function gather_workspace_into_tabbed_group()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if not is_normal_workspace(workspace) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local key = workspace_key(workspace)
|
||||||
|
if tabbed_workspace_groups[key] or active_group_size() > 1 then
|
||||||
|
restore_workspace_tabbed_group()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local original_windows = tiled_windows(workspace)
|
||||||
|
sort_windows_by_visual_position(original_windows)
|
||||||
|
local original_order = window_address_list(original_windows)
|
||||||
|
local candidates = active_workspace_tiled_group_candidates(workspace)
|
||||||
|
if #candidates <= 1 then
|
||||||
|
set_layout(columns_layout)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local candidate_addresses = window_address_set(candidates)
|
||||||
|
local focused = hl.get_active_window()
|
||||||
|
local anchor = nil
|
||||||
|
if focused and not focused.floating and not focused.group and window_address_in_set(focused, candidate_addresses) then
|
||||||
|
anchor = focused
|
||||||
|
end
|
||||||
|
|
||||||
|
if not anchor then
|
||||||
|
for _, window in ipairs(candidates) do
|
||||||
|
if not window.group then
|
||||||
|
anchor = window
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local anchor_selector = window_selector(anchor)
|
||||||
|
if not anchor_selector then
|
||||||
|
notify_tabbed_group("Current tiled windows are already grouped")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
set_layout(columns_layout)
|
||||||
|
|
||||||
|
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
||||||
|
dispatch(hl.dsp.group.toggle({ window = anchor_selector }))
|
||||||
|
|
||||||
|
local group_windows = {}
|
||||||
|
for _, window in ipairs(candidates) do
|
||||||
|
if window ~= anchor and not window.group then
|
||||||
|
group_windows[#group_windows + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local anchor_x, anchor_y = window_center(anchor)
|
||||||
|
table.sort(group_windows, function(left, right)
|
||||||
|
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
|
||||||
|
|
||||||
|
if grouped_count <= 1 then
|
||||||
|
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
||||||
|
dispatch(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] = {
|
||||||
|
anchor = anchor.address,
|
||||||
|
order = original_order,
|
||||||
|
windows = candidate_addresses,
|
||||||
|
}
|
||||||
|
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function force_columns_layout()
|
||||||
|
if active_group_size() > 1 or tabbed_workspace_groups[workspace_key()] then
|
||||||
|
restore_workspace_tabbed_group()
|
||||||
|
else
|
||||||
|
set_layout(columns_layout)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cycle_layout_or_restore_tabbed_group()
|
||||||
|
if active_group_size() > 1 or tabbed_workspace_groups[workspace_key()] then
|
||||||
|
restore_workspace_tabbed_group()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
cycle_layout(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function copy_windows(workspace)
|
||||||
|
local windows = {}
|
||||||
|
if not workspace then
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(hl.get_workspace_windows(workspace)) do
|
||||||
|
if window and not window.hidden then
|
||||||
|
windows[#windows + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function swap_current_workspace_with(target_id)
|
||||||
|
local current = active_workspace()
|
||||||
|
if not current or not current.id or current.id == target_id then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local target = hl.get_workspace(tostring(target_id))
|
||||||
|
local current_windows = copy_windows(current)
|
||||||
|
local target_windows = copy_windows(target)
|
||||||
|
|
||||||
|
for _, window in ipairs(current_windows) do
|
||||||
|
move_window_to_workspace(target_id, false, window)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(target_windows) do
|
||||||
|
move_window_to_workspace(current.id, false, window)
|
||||||
|
end
|
||||||
|
|
||||||
|
focus_workspace(current.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function enter_workspace_swap_mode()
|
||||||
|
hl.notification.create({
|
||||||
|
text = "Swap with workspace 1-9",
|
||||||
|
duration = 2200,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
dispatch(hl.dsp.submap("swap-workspace"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function focus_next_empty_workspace()
|
||||||
|
local workspace_id = find_empty_workspace(hl.get_active_monitor(), active_workspace_id())
|
||||||
|
if workspace_id then
|
||||||
|
focus_workspace(workspace_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function move_to_next_empty_workspace(follow)
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
if not window then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace_id = find_empty_workspace(hl.get_active_monitor(), active_workspace_id())
|
||||||
|
if workspace_id then
|
||||||
|
move_window_to_workspace(workspace_id, follow, window)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cycle_workspace(delta)
|
||||||
|
local current = active_workspace_id()
|
||||||
|
local next_workspace = ((current - 1 + delta) % max_workspace) + 1
|
||||||
|
focus_workspace(next_workspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function move_window_to_monitor(direction, follow)
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
if not window then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local original_monitor = hl.get_active_monitor()
|
||||||
|
dispatch(hl.dsp.window.move({ monitor = direction, follow = follow, window = window_selector(window) }))
|
||||||
|
|
||||||
|
if not follow and original_monitor then
|
||||||
|
dispatch(hl.dsp.focus({ monitor = original_monitor }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function move_window_to_empty_workspace_on_monitor(direction)
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
local original_monitor = hl.get_active_monitor()
|
||||||
|
local target_monitor = hl.get_monitor(direction)
|
||||||
|
|
||||||
|
if not window or not original_monitor or not target_monitor or target_monitor == original_monitor then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace_id = find_empty_workspace(target_monitor, active_workspace_id())
|
||||||
|
if not workspace_id then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
dispatch(hl.dsp.focus({ monitor = target_monitor }))
|
||||||
|
focus_workspace(workspace_id)
|
||||||
|
dispatch(hl.dsp.focus({ monitor = original_monitor }))
|
||||||
|
move_window_to_workspace(workspace_id, false, window)
|
||||||
|
end
|
||||||
|
|
||||||
|
ctx.is_nstack_layout = is_nstack_layout
|
||||||
|
ctx.hyprland_layout = hyprland_layout
|
||||||
|
ctx.update_nstack_count = update_nstack_count
|
||||||
|
ctx.schedule_nstack_count_update = schedule_nstack_count_update
|
||||||
|
ctx.dismiss_monocle_notice = dismiss_monocle_notice
|
||||||
|
ctx.update_monocle_notice = update_monocle_notice
|
||||||
|
ctx.layout_name = layout_name
|
||||||
|
ctx.notify_layout = notify_layout
|
||||||
|
ctx.set_layout = set_layout
|
||||||
|
ctx.sync_layout_for_active_workspace = sync_layout_for_active_workspace
|
||||||
|
ctx.cycle_layout = cycle_layout
|
||||||
|
ctx.toggle_columns_monocle = toggle_columns_monocle
|
||||||
|
ctx.active_group_size = active_group_size
|
||||||
|
ctx.monocle_next = monocle_next
|
||||||
|
ctx.monocle_prev = monocle_prev
|
||||||
|
ctx.focus_direction = focus_direction
|
||||||
|
ctx.swap_direction = swap_direction
|
||||||
|
ctx.focus_workspace = focus_workspace
|
||||||
|
ctx.move_window_to_workspace = move_window_to_workspace
|
||||||
|
ctx.notify_tabbed_group = notify_tabbed_group
|
||||||
|
ctx.active_workspace_tiled_group_candidates = active_workspace_tiled_group_candidates
|
||||||
|
ctx.move_window_into_group = move_window_into_group
|
||||||
|
ctx.find_tabbed_group_anchor = find_tabbed_group_anchor
|
||||||
|
ctx.ordered_windows_for_tabbed_group_restore = ordered_windows_for_tabbed_group_restore
|
||||||
|
ctx.restore_tabbed_group_window_order = restore_tabbed_group_window_order
|
||||||
|
ctx.restore_workspace_tabbed_group = restore_workspace_tabbed_group
|
||||||
|
ctx.gather_workspace_into_tabbed_group = gather_workspace_into_tabbed_group
|
||||||
|
ctx.force_columns_layout = force_columns_layout
|
||||||
|
ctx.cycle_layout_or_restore_tabbed_group = cycle_layout_or_restore_tabbed_group
|
||||||
|
ctx.copy_windows = copy_windows
|
||||||
|
ctx.swap_current_workspace_with = swap_current_workspace_with
|
||||||
|
ctx.enter_workspace_swap_mode = enter_workspace_swap_mode
|
||||||
|
ctx.focus_next_empty_workspace = focus_next_empty_workspace
|
||||||
|
ctx.move_to_next_empty_workspace = move_to_next_empty_workspace
|
||||||
|
ctx.cycle_workspace = cycle_workspace
|
||||||
|
ctx.move_window_to_monitor = move_window_to_monitor
|
||||||
|
ctx.move_window_to_empty_workspace_on_monitor = move_window_to_empty_workspace_on_monitor
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
196
dotfiles/config/hypr/hyprland/settings.lua
Normal file
196
dotfiles/config/hypr/hyprland/settings.lua
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.setup(ctx)
|
||||||
|
local _ENV = ctx
|
||||||
|
if enable_nstack then
|
||||||
|
hl.plugin.load("/run/current-system/sw/lib/libhyprNStack.so")
|
||||||
|
end
|
||||||
|
if enable_hyprexpo and not verify_config then
|
||||||
|
hl.plugin.load("/run/current-system/sw/lib/libhyprexpo.so")
|
||||||
|
end
|
||||||
|
if enable_hyprwinview and not verify_config then
|
||||||
|
hl.plugin.load("/run/current-system/sw/lib/libhyprwinview.so")
|
||||||
|
end
|
||||||
|
if enable_workspace_history and not verify_config then
|
||||||
|
hl.plugin.load("/run/current-system/sw/lib/libhypr-workspace-history.so")
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.env("XCURSOR_SIZE", "24")
|
||||||
|
hl.env("HYPRCURSOR_SIZE", "24")
|
||||||
|
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct")
|
||||||
|
hl.env("HYPR_MAX_WORKSPACE", "9")
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
input = {
|
||||||
|
kb_layout = "us",
|
||||||
|
kb_variant = "",
|
||||||
|
kb_model = "",
|
||||||
|
kb_options = "",
|
||||||
|
kb_rules = "",
|
||||||
|
follow_mouse = 1,
|
||||||
|
sensitivity = 0,
|
||||||
|
touchpad = {
|
||||||
|
natural_scroll = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cursor = {
|
||||||
|
persistent_warps = true,
|
||||||
|
},
|
||||||
|
general = {
|
||||||
|
gaps_in = 5,
|
||||||
|
gaps_out = 10,
|
||||||
|
border_size = 2,
|
||||||
|
col = {
|
||||||
|
active_border = { colors = { "rgba(3b82f6ee)", "rgba(33ccffee)" }, angle = 45 },
|
||||||
|
inactive_border = "rgba(00000000)",
|
||||||
|
},
|
||||||
|
layout = columns_layout,
|
||||||
|
allow_tearing = false,
|
||||||
|
},
|
||||||
|
decoration = {
|
||||||
|
rounding = 5,
|
||||||
|
blur = {
|
||||||
|
enabled = true,
|
||||||
|
size = 3,
|
||||||
|
passes = 1,
|
||||||
|
},
|
||||||
|
active_opacity = 1.0,
|
||||||
|
inactive_opacity = 0.9,
|
||||||
|
},
|
||||||
|
animations = {
|
||||||
|
enabled = true,
|
||||||
|
},
|
||||||
|
binds = {
|
||||||
|
allow_workspace_cycles = true,
|
||||||
|
workspace_back_and_forth = true,
|
||||||
|
},
|
||||||
|
group = {
|
||||||
|
group_on_movetoworkspace = false,
|
||||||
|
col = {
|
||||||
|
border_active = "rgba(edb443ff)",
|
||||||
|
border_inactive = "rgba(091f2eff)",
|
||||||
|
},
|
||||||
|
groupbar = {
|
||||||
|
enabled = true,
|
||||||
|
blur = true,
|
||||||
|
font_size = 13,
|
||||||
|
gradients = true,
|
||||||
|
height = 26,
|
||||||
|
indicator_gap = 0,
|
||||||
|
indicator_height = 1,
|
||||||
|
rounding = 5,
|
||||||
|
gradient_rounding = 5,
|
||||||
|
text_padding = 8,
|
||||||
|
col = {
|
||||||
|
active = "rgba(edb443ff)",
|
||||||
|
inactive = "rgba(101820f2)",
|
||||||
|
},
|
||||||
|
text_color = "rgba(091018ff)",
|
||||||
|
text_color_inactive = "rgba(f2f5f7ff)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
misc = {
|
||||||
|
force_default_wallpaper = 0,
|
||||||
|
disable_hyprland_logo = true,
|
||||||
|
exit_window_retains_fullscreen = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.curve("overshoot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.1 } } })
|
||||||
|
hl.curve("smoothOut", { type = "bezier", points = { { 0.36, 1 }, { 0.3, 1 } } })
|
||||||
|
hl.curve("smoothInOut", { type = "bezier", points = { { 0.42, 0 }, { 0.58, 1 } } })
|
||||||
|
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
|
||||||
|
|
||||||
|
local animations = {
|
||||||
|
{ leaf = "global", enabled = true, speed = 8, bezier = "default" },
|
||||||
|
|
||||||
|
{ leaf = "windows", enabled = true, speed = 6, bezier = "overshoot", style = "gnomed" },
|
||||||
|
{ leaf = "windowsIn", enabled = true, speed = 6, bezier = "overshoot", style = "gnomed" },
|
||||||
|
{ leaf = "windowsOut", enabled = true, speed = 5, bezier = "smoothInOut", style = "gnomed" },
|
||||||
|
{ leaf = "windowsMove", enabled = true, speed = 6, bezier = "smoothOut" },
|
||||||
|
|
||||||
|
{ leaf = "border", enabled = false },
|
||||||
|
{ leaf = "borderangle", enabled = false },
|
||||||
|
|
||||||
|
{ leaf = "fade", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeIn", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeOut", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeSwitch", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeShadow", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeGlow", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeDim", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeLayers", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeLayersIn", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeLayersOut", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadePopups", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadePopupsIn", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadePopupsOut", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "fadeDpms", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
|
||||||
|
{ leaf = "layers", enabled = true, speed = 5, bezier = "smoothOut", style = "fade" },
|
||||||
|
{ leaf = "layersIn", enabled = true, speed = 5, bezier = "smoothOut", style = "fade" },
|
||||||
|
{ leaf = "layersOut", enabled = true, speed = 5, bezier = "smoothOut", style = "fade" },
|
||||||
|
|
||||||
|
{ leaf = "workspaces", enabled = true, speed = 6, bezier = "smoothOut", style = "slidefade 15%" },
|
||||||
|
{ leaf = "workspacesIn", enabled = true, speed = 6, bezier = "smoothOut", style = "slidefade 15%" },
|
||||||
|
{ leaf = "workspacesOut", enabled = true, speed = 6, bezier = "smoothOut", style = "slidefade 15%" },
|
||||||
|
{ leaf = "specialWorkspace", enabled = true, speed = 6, bezier = "smoothOut", style = "slidevert" },
|
||||||
|
{ leaf = "specialWorkspaceIn", enabled = true, speed = 6, bezier = "smoothOut", style = "slidevert" },
|
||||||
|
{ leaf = "specialWorkspaceOut", enabled = true, speed = 6, bezier = "smoothOut", style = "slidevert" },
|
||||||
|
|
||||||
|
{ leaf = "zoomFactor", enabled = true, speed = 7, bezier = "smoothOut" },
|
||||||
|
-- Disabled for now: Hyprland 0.54.0 can crash while damaging a monitor
|
||||||
|
-- from this startup animation's update callback during output discovery.
|
||||||
|
-- { leaf = "monitorAdded", enabled = true, speed = 5, bezier = "smoothOut" },
|
||||||
|
{ leaf = "monitorAdded", enabled = false, speed = 5, bezier = "smoothOut" },
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, animation in ipairs(animations) do
|
||||||
|
hl.animation(animation)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_rules()
|
||||||
|
if verify_config then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.workspace_rule({ workspace = "w[tv1]s[false]", gaps_out = 0, gaps_in = 0 })
|
||||||
|
hl.workspace_rule({ workspace = "f[1]s[false]", gaps_out = 0, gaps_in = 0 })
|
||||||
|
|
||||||
|
hl.window_rule({ match = { class = "^()$", title = "^()$" }, float = true })
|
||||||
|
hl.window_rule({ match = { title = "^(Picture-in-Picture)$" }, float = true })
|
||||||
|
hl.window_rule({ match = { title = "^(Open File)$" }, float = true })
|
||||||
|
hl.window_rule({ match = { title = "^(Save File)$" }, float = true })
|
||||||
|
hl.window_rule({ match = { title = "^(Confirm)$" }, float = true })
|
||||||
|
hl.window_rule({
|
||||||
|
match = { class = "^(com\\.mitchellh\\.ghostty\\.dropdown)$" },
|
||||||
|
animation = "slide top",
|
||||||
|
})
|
||||||
|
hl.window_rule({
|
||||||
|
match = { class = "^(.*[Rr]umno.*)$" },
|
||||||
|
float = true,
|
||||||
|
pin = true,
|
||||||
|
center = true,
|
||||||
|
decorate = false,
|
||||||
|
no_shadow = true,
|
||||||
|
})
|
||||||
|
hl.window_rule({
|
||||||
|
match = { title = "^(.*[Rr]umno.*)$" },
|
||||||
|
float = true,
|
||||||
|
pin = true,
|
||||||
|
center = true,
|
||||||
|
decorate = false,
|
||||||
|
no_shadow = true,
|
||||||
|
})
|
||||||
|
hl.window_rule({
|
||||||
|
name = "subtle-pinned-window-border",
|
||||||
|
match = { pin = true },
|
||||||
|
border_size = 2,
|
||||||
|
border_color = "rgba(edb443ff) rgba(ff4d5dcc)",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
ctx.apply_rules = apply_rules
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
105
dotfiles/config/hypr/hyprland/state.lua
Normal file
105
dotfiles/config/hypr/hyprland/state.lua
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
local shell_ui_command = "hypr_shell_ui"
|
||||||
|
local columns_layout = "nStack"
|
||||||
|
local large_main_layout = "master"
|
||||||
|
local grid_layout = "grid"
|
||||||
|
local monocle_layout = "monocle"
|
||||||
|
|
||||||
|
return {
|
||||||
|
main_mod = "SUPER",
|
||||||
|
mod_alt = "SUPER + ALT",
|
||||||
|
hyper = "SUPER + CTRL + ALT",
|
||||||
|
|
||||||
|
terminal = "ghostty --gtk-single-instance=false",
|
||||||
|
shell_ui_command = shell_ui_command,
|
||||||
|
launcher_command = shell_ui_command .. " launcher",
|
||||||
|
run_menu = shell_ui_command .. " run",
|
||||||
|
|
||||||
|
-- Hyprland shadows ordinary keybinds after one fires; without transparent,
|
||||||
|
-- the first overview chord after a focus-moving bind can be skipped.
|
||||||
|
overview_bind_opts = { dont_inhibit = true, transparent = true },
|
||||||
|
overview_trace_enabled_path = "/tmp/hypr-overview-bind.enable",
|
||||||
|
overview_trace_path = "/tmp/hypr-overview-bind.log",
|
||||||
|
notification_icons = {
|
||||||
|
warning = 0,
|
||||||
|
info = 1,
|
||||||
|
hint = 2,
|
||||||
|
error = 3,
|
||||||
|
confused = 4,
|
||||||
|
ok = 5,
|
||||||
|
none = 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
max_workspace = 9,
|
||||||
|
scratchpad_size_ratio = 0.95,
|
||||||
|
dropdown_height_ratio = 0.5,
|
||||||
|
columns_layout = columns_layout,
|
||||||
|
large_main_layout = large_main_layout,
|
||||||
|
grid_layout = grid_layout,
|
||||||
|
monocle_layout = monocle_layout,
|
||||||
|
layout_cycle = { columns_layout, large_main_layout, grid_layout },
|
||||||
|
layout_names = {
|
||||||
|
[columns_layout] = "Columns",
|
||||||
|
[large_main_layout] = "Large main",
|
||||||
|
[grid_layout] = "Grid",
|
||||||
|
[monocle_layout] = "Monocle",
|
||||||
|
},
|
||||||
|
minimized_workspace = "special:minimized",
|
||||||
|
tabbed_group_restore_workspace_prefix = "special:tabbed-monocle-restore-",
|
||||||
|
current_layout = columns_layout,
|
||||||
|
enable_nstack = true,
|
||||||
|
enable_hyprexpo = true,
|
||||||
|
enable_hyprwinview = true,
|
||||||
|
enable_workspace_history = true,
|
||||||
|
configure_nstack_plugin_from_lua = false,
|
||||||
|
workspace_layouts = {},
|
||||||
|
minimized_windows = {},
|
||||||
|
tabbed_workspace_groups = {},
|
||||||
|
window_picker_mode = nil,
|
||||||
|
window_picker_candidates = {},
|
||||||
|
stack_update_timer = nil,
|
||||||
|
monocle_notice = nil,
|
||||||
|
scratchpad_pending = {},
|
||||||
|
monitor_reserved_cache_path = (os.getenv("XDG_RUNTIME_DIR") or "/tmp") .. "/hyprland-monitor-reserved.tsv",
|
||||||
|
scratchpad_fallback_reserved_top = 60,
|
||||||
|
|
||||||
|
scratchpads = {
|
||||||
|
codex = {
|
||||||
|
command = "codex-desktop",
|
||||||
|
class = "codex-desktop",
|
||||||
|
},
|
||||||
|
htop = {
|
||||||
|
command = "alacritty --class htop-scratch --title htop -e htop",
|
||||||
|
class = "htop-scratch",
|
||||||
|
},
|
||||||
|
volume = {
|
||||||
|
command = "pavucontrol",
|
||||||
|
class = "org.pulseaudio.pavucontrol",
|
||||||
|
},
|
||||||
|
spotify = {
|
||||||
|
command = "spotify",
|
||||||
|
class = "spotify",
|
||||||
|
},
|
||||||
|
element = {
|
||||||
|
command = "element-desktop",
|
||||||
|
classes = { "Element", "electron" },
|
||||||
|
title = "Element",
|
||||||
|
},
|
||||||
|
slack = {
|
||||||
|
command = "slack",
|
||||||
|
class = "Slack",
|
||||||
|
},
|
||||||
|
messages = {
|
||||||
|
command = "google-chrome-stable --profile-directory=Default --app=https://messages.google.com/web/conversations",
|
||||||
|
class = "chrome-messages.google.com",
|
||||||
|
},
|
||||||
|
transmission = {
|
||||||
|
command = "transmission-gtk",
|
||||||
|
class = "transmission-gtk",
|
||||||
|
},
|
||||||
|
dropdown = {
|
||||||
|
command = "ghostty --config-file=/home/imalison/.config/ghostty/dropdown",
|
||||||
|
class = "com.mitchellh.ghostty.dropdown",
|
||||||
|
dropdown = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
801
dotfiles/config/hypr/hyprland/windows.lua
Normal file
801
dotfiles/config/hypr/hyprland/windows.lua
Normal file
@@ -0,0 +1,801 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.setup(ctx)
|
||||||
|
local _ENV = ctx
|
||||||
|
local function same_class_windows(class_name)
|
||||||
|
local windows = {}
|
||||||
|
if not class_name or class_name == "" then
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
if is_normal_window(window) and window.class == class_name then
|
||||||
|
windows[#windows + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function short_text(value, limit)
|
||||||
|
value = tostring(value or "")
|
||||||
|
value = value:gsub("[%c\t\r\n]", " ")
|
||||||
|
if #value <= limit then
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
return value:sub(1, limit - 3) .. "..."
|
||||||
|
end
|
||||||
|
|
||||||
|
local function normal_windows()
|
||||||
|
local windows = {}
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
if is_normal_window(window) then
|
||||||
|
windows[#windows + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(windows, function(left, right)
|
||||||
|
local left_workspace = left.workspace and left.workspace.id or max_workspace + 1
|
||||||
|
local right_workspace = right.workspace and right.workspace.id or max_workspace + 1
|
||||||
|
if left_workspace ~= right_workspace then
|
||||||
|
return left_workspace < right_workspace
|
||||||
|
end
|
||||||
|
return (left.focus_history_id or 0) < (right.focus_history_id or 0)
|
||||||
|
end)
|
||||||
|
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function window_picker_entry(index, window)
|
||||||
|
local workspace = window.workspace and window.workspace.id or "?"
|
||||||
|
local class = short_text(window.class, 18)
|
||||||
|
local title = short_text(window.title, 48)
|
||||||
|
return tostring(index) .. " [" .. tostring(workspace) .. "] " .. class .. " " .. title
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove_minimized_window(target)
|
||||||
|
local remaining = {}
|
||||||
|
local target_address = target and target.address
|
||||||
|
for _, window in ipairs(minimized_windows) do
|
||||||
|
if window and window.address ~= target_address then
|
||||||
|
remaining[#remaining + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
minimized_windows = remaining
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_minimized_window(window)
|
||||||
|
if not window or not window.address then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_minimized_window(window)
|
||||||
|
minimized_windows[#minimized_windows + 1] = window
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hydrate_minimized_windows()
|
||||||
|
local by_address = {}
|
||||||
|
local current_by_address = {}
|
||||||
|
local hydrated = {}
|
||||||
|
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
if window and window.address then
|
||||||
|
current_by_address[window.address] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(minimized_windows) do
|
||||||
|
local current = window and window.address and current_by_address[window.address]
|
||||||
|
if current and is_minimized_window(current) and not by_address[current.address] then
|
||||||
|
by_address[current.address] = true
|
||||||
|
hydrated[#hydrated + 1] = current
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in pairs(current_by_address) do
|
||||||
|
if window and window.address and is_minimized_window(window) and not by_address[window.address] then
|
||||||
|
by_address[window.address] = true
|
||||||
|
hydrated[#hydrated + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minimized_windows = hydrated
|
||||||
|
end
|
||||||
|
|
||||||
|
local function window_workspace_name(window)
|
||||||
|
return window and window.workspace and window.workspace.name or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scratchpad_workspace(name)
|
||||||
|
return "special:scratch-" .. name
|
||||||
|
end
|
||||||
|
|
||||||
|
local function as_number(value, default)
|
||||||
|
local number = tonumber(value)
|
||||||
|
if number == nil then
|
||||||
|
return default
|
||||||
|
end
|
||||||
|
return number
|
||||||
|
end
|
||||||
|
|
||||||
|
local function logical_monitor_dimension(value, scale)
|
||||||
|
value = as_number(value, 0)
|
||||||
|
scale = as_number(scale, 1)
|
||||||
|
if scale <= 0 then
|
||||||
|
scale = 1
|
||||||
|
end
|
||||||
|
return math.floor((value / scale) + 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split_tsv(line)
|
||||||
|
local fields = {}
|
||||||
|
for field in (line .. "\t"):gmatch("([^\t]*)\t") do
|
||||||
|
fields[#fields + 1] = field
|
||||||
|
end
|
||||||
|
return fields
|
||||||
|
end
|
||||||
|
|
||||||
|
local function monitor_from_reserved_fields(monitor, fields)
|
||||||
|
if not monitor or not monitor.name or fields[1] ~= monitor.name or #fields < 10 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
name = monitor.name,
|
||||||
|
x = tonumber(fields[2]),
|
||||||
|
y = tonumber(fields[3]),
|
||||||
|
width = tonumber(fields[4]),
|
||||||
|
height = tonumber(fields[5]),
|
||||||
|
scale = tonumber(fields[6]),
|
||||||
|
reserved = {
|
||||||
|
tonumber(fields[7]),
|
||||||
|
tonumber(fields[8]),
|
||||||
|
tonumber(fields[9]),
|
||||||
|
tonumber(fields[10]),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function monitor_from_reserved_lines(monitor, lines)
|
||||||
|
if not monitor or not monitor.name then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in lines do
|
||||||
|
local cached = monitor_from_reserved_fields(monitor, split_tsv(line))
|
||||||
|
if cached then
|
||||||
|
return cached
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function monitor_from_reserved_cache(monitor)
|
||||||
|
if verify_config or not monitor or not monitor.name then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local file = io.open(monitor_reserved_cache_path, "r")
|
||||||
|
if not file then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local cached = monitor_from_reserved_lines(monitor, file:lines())
|
||||||
|
file:close()
|
||||||
|
return cached
|
||||||
|
end
|
||||||
|
|
||||||
|
local function refresh_monitor_reserved_cache(delay)
|
||||||
|
if verify_config then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local command = string.format(
|
||||||
|
[=[sleep %.2f; cache="${XDG_RUNTIME_DIR:-/tmp}/hyprland-monitor-reserved.tsv"; tmp="$cache.tmp"; /run/current-system/sw/bin/hyprctl -j monitors 2>/dev/null | /run/current-system/sw/bin/jq -r '.[] | [.name, .x, .y, .width, .height, .scale, .reserved[0], .reserved[1], .reserved[2], .reserved[3]] | @tsv' > "$tmp" && mv "$tmp" "$cache"]=],
|
||||||
|
as_number(delay, 0)
|
||||||
|
)
|
||||||
|
hl.exec_cmd("sh -lc " .. shell_quote(command))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function monitor_workarea(monitor)
|
||||||
|
monitor = monitor_from_reserved_cache(monitor) or monitor
|
||||||
|
local width = logical_monitor_dimension(monitor.width, monitor.scale)
|
||||||
|
local height = logical_monitor_dimension(monitor.height, monitor.scale)
|
||||||
|
local reserved = monitor.reserved or { 0, scratchpad_fallback_reserved_top, 0, 0 }
|
||||||
|
local left = math.floor(as_number(reserved[1], 0))
|
||||||
|
local top = math.floor(as_number(reserved[2], 0))
|
||||||
|
local right = math.floor(as_number(reserved[3], 0))
|
||||||
|
local bottom = math.floor(as_number(reserved[4], 0))
|
||||||
|
local work_width = width - left - right
|
||||||
|
local work_height = height - top - bottom
|
||||||
|
|
||||||
|
if work_width <= 0 then
|
||||||
|
left = 0
|
||||||
|
right = 0
|
||||||
|
work_width = width
|
||||||
|
end
|
||||||
|
if work_height <= 0 then
|
||||||
|
top = 0
|
||||||
|
bottom = 0
|
||||||
|
work_height = height
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
x = math.floor(as_number(monitor.x, 0)) + left,
|
||||||
|
y = math.floor(as_number(monitor.y, 0)) + top,
|
||||||
|
width = work_width,
|
||||||
|
height = work_height,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function matching_scratchpad_windows(name)
|
||||||
|
local def = scratchpads[name]
|
||||||
|
local windows = {}
|
||||||
|
if not def then
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
if scratchpad_window_matches(window, def) then
|
||||||
|
windows[#windows + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_scratchpad_geometry(name, window, target_monitor)
|
||||||
|
local def = scratchpads[name]
|
||||||
|
local monitor = target_monitor or hl.get_active_monitor()
|
||||||
|
if not def or not window or not monitor then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workarea = monitor_workarea(monitor)
|
||||||
|
local width
|
||||||
|
local height
|
||||||
|
local x
|
||||||
|
local y
|
||||||
|
if def.dropdown then
|
||||||
|
width = workarea.width
|
||||||
|
height = math.floor(workarea.height * dropdown_height_ratio)
|
||||||
|
x = workarea.x
|
||||||
|
y = workarea.y
|
||||||
|
else
|
||||||
|
width = math.floor(workarea.width * scratchpad_size_ratio)
|
||||||
|
height = math.floor(workarea.height * scratchpad_size_ratio)
|
||||||
|
x = workarea.x + math.floor((workarea.width - width) / 2)
|
||||||
|
y = workarea.y + math.floor((workarea.height - height) / 2)
|
||||||
|
end
|
||||||
|
local selector = window_selector(window)
|
||||||
|
|
||||||
|
dispatch(hl.dsp.window.float({ action = "enable", window = selector }))
|
||||||
|
dispatch(hl.dsp.window.tag({ tag = "+scratchpad", window = selector }))
|
||||||
|
dispatch(hl.dsp.window.tag({ tag = "+scratchpad-" .. name, window = selector }))
|
||||||
|
dispatch(hl.dsp.window.resize({ x = width, y = height, relative = false, window = selector }))
|
||||||
|
dispatch(hl.dsp.window.move({ x = x, y = y, relative = false, window = selector }))
|
||||||
|
if def.dropdown then
|
||||||
|
dispatch(hl.dsp.window.set_prop({ prop = "border_size", value = "0", window = selector }))
|
||||||
|
dispatch(hl.dsp.window.set_prop({ prop = "no_shadow", value = "1", window = selector }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function float_active_window_preserving_tiled_geometry()
|
||||||
|
local geometry = tiled_window_geometry(hl.get_active_window())
|
||||||
|
dispatch(hl.dsp.window.float({ action = "enable", window = geometry and geometry.selector or nil }))
|
||||||
|
if geometry then
|
||||||
|
dispatch(hl.dsp.window.resize({ x = geometry.width, y = geometry.height, relative = false, window = geometry.selector }))
|
||||||
|
dispatch(hl.dsp.window.move({ x = geometry.x, y = geometry.y, relative = false, window = geometry.selector }))
|
||||||
|
end
|
||||||
|
return geometry
|
||||||
|
end
|
||||||
|
|
||||||
|
local function float_and_drag_active_window()
|
||||||
|
float_active_window_preserving_tiled_geometry()
|
||||||
|
dispatch(hl.dsp.window.drag())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function float_and_resize_active_window()
|
||||||
|
float_active_window_preserving_tiled_geometry()
|
||||||
|
dispatch(hl.dsp.window.resize())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggle_pinned_active_window()
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
local selector = window_selector(window)
|
||||||
|
if not window or not selector then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if window.pinned then
|
||||||
|
dispatch(hl.dsp.window.pin({ action = "disable", window = selector }))
|
||||||
|
dispatch(hl.dsp.window.float({ action = "disable", window = selector }))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not window.floating then
|
||||||
|
float_active_window_preserving_tiled_geometry()
|
||||||
|
end
|
||||||
|
dispatch(hl.dsp.window.pin({ action = "enable", window = selector }))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function schedule_scratchpad_geometry(name, window, target_monitor)
|
||||||
|
hl.timer(function()
|
||||||
|
apply_scratchpad_geometry(name, window, target_monitor)
|
||||||
|
end, { timeout = 50, type = "oneshot" })
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hide_scratchpad_window(name, window)
|
||||||
|
remove_minimized_window(window)
|
||||||
|
move_window_to_workspace(scratchpad_workspace(name), false, window)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_scratchpad_window(name, window, workspace, target_monitor)
|
||||||
|
workspace = workspace or active_workspace()
|
||||||
|
if not workspace then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_minimized_window(window)
|
||||||
|
move_window_to_workspace(workspace.id, false, window)
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
||||||
|
schedule_scratchpad_geometry(name, window, target_monitor or hl.get_active_monitor())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scratchpad_is_visible(window)
|
||||||
|
local workspace = active_workspace()
|
||||||
|
return workspace and window and same_workspace(window.workspace, workspace)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Active scratchpads are scratchpad windows visible on the active workspace.
|
||||||
|
-- Invoking a different scratchpad replaces that active set.
|
||||||
|
local function active_scratchpad_windows(except_name)
|
||||||
|
local windows = {}
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
local name = matching_scratchpad_name(window)
|
||||||
|
if name and name ~= except_name and scratchpad_is_visible(window) then
|
||||||
|
windows[#windows + 1] = {
|
||||||
|
name = name,
|
||||||
|
window = window,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hide_active_scratchpads(except_name)
|
||||||
|
for _, active in ipairs(active_scratchpad_windows(except_name)) do
|
||||||
|
hide_scratchpad_window(active.name, active.window)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function refresh_active_scratchpad_geometries()
|
||||||
|
local monitor = hl.get_active_monitor()
|
||||||
|
for _, active in ipairs(active_scratchpad_windows()) do
|
||||||
|
schedule_scratchpad_geometry(active.name, active.window, monitor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function refresh_active_scratchpad_geometries_later(timeout)
|
||||||
|
hl.timer(refresh_active_scratchpad_geometries, { timeout = timeout or 300, type = "oneshot" })
|
||||||
|
end
|
||||||
|
|
||||||
|
local function refresh_shell_workarea_and_scratchpads()
|
||||||
|
refresh_monitor_reserved_cache(0.15)
|
||||||
|
refresh_active_scratchpad_geometries_later(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function adopt_matching_scratchpad_window(window)
|
||||||
|
if not window then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, def in pairs(scratchpads) do
|
||||||
|
if scratchpad_window_matches(window, def) then
|
||||||
|
if scratchpad_pending[name] then
|
||||||
|
local pending = scratchpad_pending[name]
|
||||||
|
scratchpad_pending[name] = nil
|
||||||
|
show_scratchpad_window(name, window, pending.workspace or active_workspace(), pending.monitor or hl.get_active_monitor())
|
||||||
|
elseif scratchpad_is_visible(window) then
|
||||||
|
schedule_scratchpad_geometry(name, window, hl.get_active_monitor())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function current_minimized_windows()
|
||||||
|
hydrate_minimized_windows()
|
||||||
|
|
||||||
|
local windows = {}
|
||||||
|
for _, window in ipairs(minimized_windows) do
|
||||||
|
if window and window.address and is_minimized_window(window) then
|
||||||
|
windows[#windows + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
minimized_windows = windows
|
||||||
|
return windows
|
||||||
|
end
|
||||||
|
|
||||||
|
local function restore_minimized_window(window, workspace)
|
||||||
|
if not window or not workspace then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
move_window_to_workspace(workspace.id, false, window)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function window_picker_candidates_for(mode)
|
||||||
|
if mode == "minimized" then
|
||||||
|
return current_minimized_windows()
|
||||||
|
end
|
||||||
|
|
||||||
|
local focused = hl.get_active_window()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
local candidates = {}
|
||||||
|
|
||||||
|
for _, window in ipairs(normal_windows()) do
|
||||||
|
local include = true
|
||||||
|
if mode == "bring" and workspace and window.workspace == workspace then
|
||||||
|
include = false
|
||||||
|
elseif mode == "replace" and focused and window == focused then
|
||||||
|
include = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if include then
|
||||||
|
candidates[#candidates + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return candidates
|
||||||
|
end
|
||||||
|
|
||||||
|
local function activate_window_picker_candidate(index)
|
||||||
|
local window = window_picker_candidates[index]
|
||||||
|
local mode = window_picker_mode
|
||||||
|
window_picker_mode = nil
|
||||||
|
window_picker_candidates = {}
|
||||||
|
dispatch(hl.dsp.submap("reset"))
|
||||||
|
|
||||||
|
if not window then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if mode == "go" then
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if mode == "bring" and workspace then
|
||||||
|
move_window_to_workspace(workspace.id, false, window)
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if mode == "minimized" and workspace then
|
||||||
|
remove_minimized_window(window)
|
||||||
|
restore_minimized_window(window, workspace)
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if mode == "replace" then
|
||||||
|
local focused = hl.get_active_window()
|
||||||
|
if focused and focused ~= window then
|
||||||
|
dispatch(hl.dsp.window.swap({ target = window_selector(window), window = window_selector(focused) }))
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function enter_window_picker(mode)
|
||||||
|
window_picker_mode = mode
|
||||||
|
window_picker_candidates = window_picker_candidates_for(mode)
|
||||||
|
|
||||||
|
if #window_picker_candidates == 0 then
|
||||||
|
local empty_text = "No windows available"
|
||||||
|
if mode == "minimized" then
|
||||||
|
empty_text = "No minimized windows"
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.notification.create({
|
||||||
|
text = empty_text,
|
||||||
|
duration = 1800,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local lines = {}
|
||||||
|
local count = math.min(#window_picker_candidates, 9)
|
||||||
|
for i = 1, count do
|
||||||
|
lines[#lines + 1] = window_picker_entry(i, window_picker_candidates[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.notification.create({
|
||||||
|
text = table.concat(lines, "\n"),
|
||||||
|
duration = 5000,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 11,
|
||||||
|
})
|
||||||
|
dispatch(hl.dsp.submap("window-picker"))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function gather_focused_class()
|
||||||
|
local focused = hl.get_active_window()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if not focused or not workspace or not focused.class or focused.class == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
for _, window in ipairs(same_class_windows(focused.class)) do
|
||||||
|
if window ~= focused and window.workspace ~= workspace then
|
||||||
|
move_window_to_workspace(workspace.id, false, window)
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.notification.create({
|
||||||
|
text = "Gathered " .. tostring(count) .. " " .. focused.class .. " windows",
|
||||||
|
duration = 1600,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function focus_next_class()
|
||||||
|
local focused = hl.get_active_window()
|
||||||
|
if not focused or not focused.class or focused.class == "" then
|
||||||
|
dispatch(hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false }))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local classes = {}
|
||||||
|
local first_by_class = {}
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
if is_normal_window(window) and window.class and window.class ~= "" and not first_by_class[window.class] then
|
||||||
|
first_by_class[window.class] = window
|
||||||
|
classes[#classes + 1] = window.class
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(classes)
|
||||||
|
if #classes <= 1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local current_index = 1
|
||||||
|
for index, class_name in ipairs(classes) do
|
||||||
|
if class_name == focused.class then
|
||||||
|
current_index = index
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local next_class = classes[(current_index % #classes) + 1]
|
||||||
|
local target = first_by_class[next_class]
|
||||||
|
if target then
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(target) }))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_active_window_info()
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
if not window then
|
||||||
|
hl.notification.create({
|
||||||
|
text = "No active window",
|
||||||
|
duration = 1800,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 13,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local workspace = window.workspace and (window.workspace.name or window.workspace.id) or "?"
|
||||||
|
local lines = {
|
||||||
|
"Class: " .. tostring(window.class or ""),
|
||||||
|
"Title: " .. tostring(window.title or ""),
|
||||||
|
"Workspace: " .. tostring(workspace),
|
||||||
|
"Pinned: " .. tostring(window.pinned or false),
|
||||||
|
"Address: " .. tostring(window.address or ""),
|
||||||
|
"PID: " .. tostring(window.pid or ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
hl.notification.create({
|
||||||
|
text = table.concat(lines, "\n"),
|
||||||
|
duration = 5000,
|
||||||
|
icon = notification_icons.info,
|
||||||
|
color = "rgba(edb443ff)",
|
||||||
|
font_size = 11,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function raise_or_spawn(class_fragment, command)
|
||||||
|
local fragment = string.lower(class_fragment)
|
||||||
|
for _, window in ipairs(hl.get_windows()) do
|
||||||
|
if is_normal_window(window) and window.class and string.find(string.lower(window.class), fragment, 1, true) then
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.exec_cmd(command)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function minimize_active_window()
|
||||||
|
local window = hl.get_active_window()
|
||||||
|
if not window then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
add_minimized_window(window)
|
||||||
|
move_window_to_workspace(minimized_workspace, false, window)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function restore_last_minimized()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if not workspace then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
hydrate_minimized_windows()
|
||||||
|
|
||||||
|
while #minimized_windows > 0 do
|
||||||
|
local window = table.remove(minimized_windows)
|
||||||
|
if window and window.address and is_minimized_window(window) then
|
||||||
|
restore_minimized_window(window, workspace)
|
||||||
|
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function restore_all_minimized()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if not workspace then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
hydrate_minimized_windows()
|
||||||
|
|
||||||
|
while #minimized_windows > 0 do
|
||||||
|
restore_minimized_window(table.remove(minimized_windows), workspace)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function minimize_other_classes()
|
||||||
|
local focused = hl.get_active_window()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if not focused or not workspace then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, window in ipairs(tiled_windows(workspace)) do
|
||||||
|
if window ~= focused and window.class ~= focused.class then
|
||||||
|
add_minimized_window(window)
|
||||||
|
move_window_to_workspace(minimized_workspace, false, window)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function restore_focused_class()
|
||||||
|
local focused = hl.get_active_window()
|
||||||
|
local workspace = active_workspace()
|
||||||
|
if not focused or not workspace or not focused.class then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
hydrate_minimized_windows()
|
||||||
|
|
||||||
|
local remaining = {}
|
||||||
|
for _, window in ipairs(minimized_windows) do
|
||||||
|
if window and window.class == focused.class and is_minimized_window(window) then
|
||||||
|
restore_minimized_window(window, workspace)
|
||||||
|
else
|
||||||
|
remaining[#remaining + 1] = window
|
||||||
|
end
|
||||||
|
end
|
||||||
|
minimized_windows = remaining
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggle_scratchpad(name)
|
||||||
|
local def = scratchpads[name]
|
||||||
|
if not def then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if current_layout == monocle_layout then
|
||||||
|
set_layout(columns_layout)
|
||||||
|
end
|
||||||
|
|
||||||
|
local windows = matching_scratchpad_windows(name)
|
||||||
|
if #windows == 0 then
|
||||||
|
hide_active_scratchpads(name)
|
||||||
|
scratchpad_pending[name] = {
|
||||||
|
monitor = hl.get_active_monitor(),
|
||||||
|
workspace = active_workspace(),
|
||||||
|
}
|
||||||
|
hl.exec_cmd(def.command)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local any_visible = false
|
||||||
|
for _, window in ipairs(windows) do
|
||||||
|
if scratchpad_is_visible(window) then
|
||||||
|
any_visible = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if any_visible then
|
||||||
|
for _, window in ipairs(windows) do
|
||||||
|
hide_scratchpad_window(name, window)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
hide_active_scratchpads(name)
|
||||||
|
local workspace = active_workspace()
|
||||||
|
local target_monitor = hl.get_active_monitor()
|
||||||
|
for _, window in ipairs(windows) do
|
||||||
|
show_scratchpad_window(name, window, workspace, target_monitor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ctx.same_class_windows = same_class_windows
|
||||||
|
ctx.short_text = short_text
|
||||||
|
ctx.normal_windows = normal_windows
|
||||||
|
ctx.window_picker_entry = window_picker_entry
|
||||||
|
ctx.remove_minimized_window = remove_minimized_window
|
||||||
|
ctx.add_minimized_window = add_minimized_window
|
||||||
|
ctx.hydrate_minimized_windows = hydrate_minimized_windows
|
||||||
|
ctx.window_workspace_name = window_workspace_name
|
||||||
|
ctx.scratchpad_workspace = scratchpad_workspace
|
||||||
|
ctx.as_number = as_number
|
||||||
|
ctx.logical_monitor_dimension = logical_monitor_dimension
|
||||||
|
ctx.split_tsv = split_tsv
|
||||||
|
ctx.monitor_from_reserved_fields = monitor_from_reserved_fields
|
||||||
|
ctx.monitor_from_reserved_lines = monitor_from_reserved_lines
|
||||||
|
ctx.monitor_from_reserved_cache = monitor_from_reserved_cache
|
||||||
|
ctx.refresh_monitor_reserved_cache = refresh_monitor_reserved_cache
|
||||||
|
ctx.monitor_workarea = monitor_workarea
|
||||||
|
ctx.matching_scratchpad_windows = matching_scratchpad_windows
|
||||||
|
ctx.apply_scratchpad_geometry = apply_scratchpad_geometry
|
||||||
|
ctx.float_active_window_preserving_tiled_geometry = float_active_window_preserving_tiled_geometry
|
||||||
|
ctx.float_and_drag_active_window = float_and_drag_active_window
|
||||||
|
ctx.float_and_resize_active_window = float_and_resize_active_window
|
||||||
|
ctx.toggle_pinned_active_window = toggle_pinned_active_window
|
||||||
|
ctx.schedule_scratchpad_geometry = schedule_scratchpad_geometry
|
||||||
|
ctx.hide_scratchpad_window = hide_scratchpad_window
|
||||||
|
ctx.show_scratchpad_window = show_scratchpad_window
|
||||||
|
ctx.scratchpad_is_visible = scratchpad_is_visible
|
||||||
|
ctx.active_scratchpad_windows = active_scratchpad_windows
|
||||||
|
ctx.hide_active_scratchpads = hide_active_scratchpads
|
||||||
|
ctx.refresh_active_scratchpad_geometries = refresh_active_scratchpad_geometries
|
||||||
|
ctx.refresh_active_scratchpad_geometries_later = refresh_active_scratchpad_geometries_later
|
||||||
|
ctx.refresh_shell_workarea_and_scratchpads = refresh_shell_workarea_and_scratchpads
|
||||||
|
ctx.adopt_matching_scratchpad_window = adopt_matching_scratchpad_window
|
||||||
|
ctx.current_minimized_windows = current_minimized_windows
|
||||||
|
ctx.restore_minimized_window = restore_minimized_window
|
||||||
|
ctx.window_picker_candidates_for = window_picker_candidates_for
|
||||||
|
ctx.activate_window_picker_candidate = activate_window_picker_candidate
|
||||||
|
ctx.enter_window_picker = enter_window_picker
|
||||||
|
ctx.gather_focused_class = gather_focused_class
|
||||||
|
ctx.focus_next_class = focus_next_class
|
||||||
|
ctx.show_active_window_info = show_active_window_info
|
||||||
|
ctx.raise_or_spawn = raise_or_spawn
|
||||||
|
ctx.minimize_active_window = minimize_active_window
|
||||||
|
ctx.restore_last_minimized = restore_last_minimized
|
||||||
|
ctx.restore_all_minimized = restore_all_minimized
|
||||||
|
ctx.minimize_other_classes = minimize_other_classes
|
||||||
|
ctx.restore_focused_class = restore_focused_class
|
||||||
|
ctx.toggle_scratchpad = toggle_scratchpad
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -20,5 +20,5 @@ Avoid dropping scripts in `~/bin` or `~/.local/bin` unless the user explicitly a
|
|||||||
|
|
||||||
- Existing rofi scripts live in `../dotfiles/lib/bin/` (e.g. `rofi_command.sh`).
|
- Existing rofi scripts live in `../dotfiles/lib/bin/` (e.g. `rofi_command.sh`).
|
||||||
- Keybind locations:
|
- Keybind locations:
|
||||||
- Hyprland: `../dotfiles/config/hypr/hyprland.lua`
|
- Hyprland: `../dotfiles/config/hypr/hyprland/binds.lua`
|
||||||
- XMonad: `../dotfiles/config/xmonad/xmonad.hs`
|
- XMonad: `../dotfiles/config/xmonad/xmonad.hs`
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
hyprlandConfig,
|
hyprlandConfigDir,
|
||||||
}:
|
}:
|
||||||
pkgs.runCommand "hyprland-config-syntax" {
|
pkgs.runCommand "hyprland-config-syntax" {
|
||||||
nativeBuildInputs = [pkgs.lua5_4];
|
nativeBuildInputs = [pkgs.lua5_4];
|
||||||
} ''
|
} ''
|
||||||
cp ${hyprlandConfig} hyprland.lua
|
cp -r ${hyprlandConfigDir}/. .
|
||||||
luac -p hyprland.lua
|
chmod -R +w .
|
||||||
|
luac -p hyprland.lua hyprland/*.lua
|
||||||
|
|
||||||
if grep -n 'hyprctl' hyprland.lua | grep -v 'hyprctl reload' | grep -v 'hyprctl eval' | grep -v 'hyprctl_eval' | grep -v 'hyprctl -j monitors'; then
|
if grep -Rn 'hyprctl' hyprland.lua hyprland/*.lua | grep -v 'hyprctl reload' | grep -v 'hyprctl eval' | grep -v 'hyprctl_eval' | grep -v 'hyprctl -j monitors'; then
|
||||||
echo "hyprland.lua should not shell out to hyprctl for window/workspace manipulation" >&2
|
echo "Hyprland Lua config should not shell out to hyprctl for window/workspace manipulation" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if grep -nE 'hl[.]dsp.*[)][(][)]' hyprland.lua; then
|
if grep -RnE 'hl[.]dsp.*[)][(][)]' hyprland.lua hyprland/*.lua; then
|
||||||
echo "hyprland.lua should use hl.dispatch(...) instead of calling dispatcher objects directly" >&2
|
echo "Hyprland Lua config should use hl.dispatch(...) instead of calling dispatcher objects directly" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -55,15 +55,16 @@ in {
|
|||||||
hypr-workspace-history = inputs.hypr-workspace-history.packages.${system}.hypr-workspace-history;
|
hypr-workspace-history = inputs.hypr-workspace-history.packages.${system}.hypr-workspace-history;
|
||||||
hyprland-config-syntax = import ../checks/hyprland-config-syntax {
|
hyprland-config-syntax = import ../checks/hyprland-config-syntax {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
hyprlandConfig = ../../dotfiles/config/hypr/hyprland.lua;
|
hyprlandConfigDir = ../../dotfiles/config/hypr;
|
||||||
};
|
};
|
||||||
hyprland-verify-config = let
|
hyprland-verify-config = let
|
||||||
hyprlandPackage = inputs.hyprland.packages.${system}.hyprland;
|
hyprlandPackage = inputs.hyprland.packages.${system}.hyprland;
|
||||||
hyprNStackPackage = inputs.hyprNStack.packages.${system}.hyprNStack;
|
hyprNStackPackage = inputs.hyprNStack.packages.${system}.hyprNStack;
|
||||||
in
|
in
|
||||||
pkgs.runCommand "hyprland-lua-verify-config" {} ''
|
pkgs.runCommand "hyprland-lua-verify-config" {} ''
|
||||||
cp ${../../dotfiles/config/hypr/hyprland.lua} hyprland.lua
|
cp -r ${../../dotfiles/config/hypr}/. .
|
||||||
substituteInPlace hyprland.lua \
|
chmod -R +w .
|
||||||
|
substituteInPlace hyprland/settings.lua \
|
||||||
--replace-fail /run/current-system/sw/lib/libhyprNStack.so \
|
--replace-fail /run/current-system/sw/lib/libhyprNStack.so \
|
||||||
${hyprNStackPackage}/lib/libhyprNStack.so
|
${hyprNStackPackage}/lib/libhyprNStack.so
|
||||||
export XDG_RUNTIME_DIR="$TMPDIR/runtime"
|
export XDG_RUNTIME_DIR="$TMPDIR/runtime"
|
||||||
|
|||||||
Reference in New Issue
Block a user