From 45976f7b8b8381f56a0a106dc5653f01985b9579 Mon Sep 17 00:00:00 2001 From: Felis Date: Sun, 23 Aug 2026 17:33:21 -0700 Subject: [PATCH] add scrolling overview --- config/keybinds.lua | 6 ++ config/scrolling_overview.lua | 110 ++++++++++++++++++++++++++++++++++ config/settings.lua | 18 ++++++ hyprland.lua | 3 + 4 files changed, 137 insertions(+) create mode 100644 config/scrolling_overview.lua diff --git a/config/keybinds.lua b/config/keybinds.lua index d66e198..edbf4ba 100644 --- a/config/keybinds.lua +++ b/config/keybinds.lua @@ -287,6 +287,12 @@ hl.bind( ) +-- Toggle ScrollOverview with SUPER+g +hl.bind( + chord(hyper, "SPACE"), function() + hl.plugin.scrolloverview.overview("toggle all") + end) + -- Toggle a focused client into an 80%-sized centered floating mode. Returning -- it to tiled mode lets the active layout restore its previous geometry. hl.bind(chord(hyper, "F"), function() diff --git a/config/scrolling_overview.lua b/config/scrolling_overview.lua new file mode 100644 index 0000000..7d02dc6 --- /dev/null +++ b/config/scrolling_overview.lua @@ -0,0 +1,110 @@ +local lastWorkspaceScrollBind + +local function table_value(value, ...) + if value == nil then + return nil + end + + for _, key in ipairs({ ... }) do + local ok, item = pcall(function() + return value[key] + end) + + if ok and item ~= nil then + return item + end + end + + return nil +end + +local function last_workspace_state() + local monitor = hl.get_monitor_at_cursor() or hl.get_active_monitor() + if not monitor then + return false, nil + end + + local activeWorkspace = table_value(monitor, "active_workspace") + local activeWorkspaceId = table_value(activeWorkspace, "id") + if type(activeWorkspaceId) ~= "number" or activeWorkspaceId <= 0 then + return false, nil + end + + local activeWorkspaceWindows = table_value(activeWorkspace, "windows") + if type(activeWorkspaceWindows) ~= "number" or activeWorkspaceWindows == 0 then + return false, nil + end + + local lastWorkspaceId + for _, workspace in ipairs(hl.get_workspaces()) do + local id = table_value(workspace, "id") + + if type(id) == "number" + and id > 0 + and table_value(workspace, "special") ~= true + and table_value(workspace, "monitor") == monitor then + lastWorkspaceId = math.max(lastWorkspaceId or id, id) + end + end + + return activeWorkspaceId == lastWorkspaceId, monitor +end + +local function create_workspace_at_end() + local isLastWorkspace, monitor = last_workspace_state() + if not isLastWorkspace or not monitor then + return + end + + hl.dispatch(hl.dsp.focus({ monitor = table_value(monitor, "name") })) + hl.dispatch(hl.dsp.focus({ workspace = "emptynm" })) +end + +local function update_last_workspace_scroll_bind() + if not lastWorkspaceScrollBind then + return + end + + local enabled = false + if hl.get_current_submap() == "scrolloverview" then + enabled = last_workspace_state() + end + + lastWorkspaceScrollBind:set_enabled(enabled) +end + + +hl.define_submap("scrolloverview", function() + hl.bind("left", hl.plugin.scrolloverview.navigate("left")) + hl.bind("right", hl.plugin.scrolloverview.navigate("right")) + hl.bind("up", hl.plugin.scrolloverview.navigate("up")) + hl.bind("down", hl.plugin.scrolloverview.navigate("down")) + hl.bind("return", hl.plugin.scrolloverview.overview("select")) + hl.bind("escape", hl.plugin.scrolloverview.overview("off")) + hl.bind("mouse:272", function() + -- Select the clicked window, or just the workspace if no window was clicked, then close the overview. This is the default behaviour if submap is not defined. + hl.plugin.scrolloverview.overview("select") + hl.plugin.scrolloverview.window("select") + hl.plugin.scrolloverview.overview("off") + end, { mouse = true }) + hl.bind("mouse:274", hl.plugin.scrolloverview.window("close"), { mouse = true }) + lastWorkspaceScrollBind = hl.bind("mouse_down", create_workspace_at_end) + lastWorkspaceScrollBind:set_enabled(false) +end) + +hl.on("workspace.active", update_last_workspace_scroll_bind) +hl.on("workspace.created", update_last_workspace_scroll_bind) +hl.on("workspace.removed", update_last_workspace_scroll_bind) +hl.on("workspace.move_to_monitor", update_last_workspace_scroll_bind) +hl.on("window.open", update_last_workspace_scroll_bind) +hl.on("window.close", update_last_workspace_scroll_bind) +hl.on("window.move_to_workspace", update_last_workspace_scroll_bind) +hl.on("monitor.focused", update_last_workspace_scroll_bind) +hl.on("keybinds.submap", update_last_workspace_scroll_bind) + +local lastWorkspaceScrollTimer = hl.timer(update_last_workspace_scroll_bind, { + timeout = 50, + type = "repeat", +}) + +update_last_workspace_scroll_bind() diff --git a/config/settings.lua b/config/settings.lua index d23577d..5f3c9f7 100644 --- a/config/settings.lua +++ b/config/settings.lua @@ -45,6 +45,24 @@ return { name = "im", selector = "name:im", key = "F2", + layout = "scrolling" }, }, + + -- .config/hypr/hyprland.lua + plugin = { + scrolloverview = { + gesture_distance = 300, -- how far is the "max" for the gesture + scale = 0.5, -- preferred overview scale + workspace_gap = 100, + layout = "vertical", -- vertical or horizontal + wallpaper = 2, -- 0: global only, 1: per-workspace only, 2: both + blur = true, -- blur only the main overview wallpaper + + shadow = { + enabled = true, + range = 50, + }, + }, + } } diff --git a/hyprland.lua b/hyprland.lua index 9790ec5..a498fbc 100644 --- a/hyprland.lua +++ b/hyprland.lua @@ -16,6 +16,9 @@ local modules = { "config.window_rules", "config.keybinds", "config.autostart", + + -- Scrolling Overview Config + "config.scrolling_overview", } -- Requiring a module applies that domain's configuration through the global