太长不看版本 #
local args, state = ...
local scroll = require("scroll")
local debug_notify = function(msg)
scroll.command(nil, 'exec notify-send "' .. msg .. '"')
end
local column_limit = tonumber(args[2]) or 3
local fit_size = tonumber(args[3]) or 0
local grid_rows = tonumber(args[4]) or column_limit
local grid_columns = tonumber(args[5]) or grid_rows
local grid = scroll.state_get_value(state, "grid_state")
if grid == nil then
scroll.state_set_value(state, "grid_state", {
map_id = nil,
active_workspaces = {},
last_view = nil,
})
grid = scroll.state_get_value(state, "grid_state")
end
local function on_destroy(view, _)
local workspace = scroll.focused_workspace()
if not workspace then return end
local ws_name = scroll.workspace_get_name(workspace)
local tiling = scroll.workspace_get_tiling(workspace)
if not grid["active_workspaces"][ws_name] then return end
local children = scroll.container_get_children(tiling[1])
if #tiling==1 and #children == 1 then
grid["active_workspaces"][ws_name] = nil
scroll.workspace_set_mode(workspace, { insert = "after", focus = true })
end
end
local function on_create_view(view, _)
local focused_view = scroll.focused_view()
local workspace = scroll.focused_workspace()
if not workspace then
return
end
local tiling = scroll.workspace_get_tiling(workspace)
local current_ws_name = scroll.workspace_get_name(workspace)
grid["last_view"] = focused_view
-- Check if Grid Mode is active for this workspace
local ws_config = grid["active_workspaces"][current_ws_name]
if not ws_config then
return
end
local container = scroll.view_get_container(view)
if #tiling > 1 then
local target_tiling = nil
-- Iterate through existing columns to find a gap based on column_limit
for i = 1, #tiling - 1 do
local prev_container = tiling[i]
local children = scroll.container_get_children(prev_container)
if children and #children < ws_config[1] then
target_tiling = i
break
end
end
if target_tiling then
local steps = (#tiling - target_tiling - 1) * 2 + 1
for i = 1, steps do
scroll.command(container, "move left nomode")
end
end
end
if ws_config[4] == 1 then
scroll.command(container, "set_size h " .. (1 / ws_config[3]))
scroll.command(container, "set_size v " .. (1 / ws_config[2]))
end
if focused_view then
local focused_con = scroll.view_get_container(focused_view)
scroll.container_set_focus(focused_con)
end
if ws_config[4] == 1 and #tiling > 1 then
scroll.command(nil, "exec scrollmsg lua ~/.config/scroll/scripts/focus_back.lua toggle")
end
end
local workspace = scroll.focused_workspace()
if not workspace then
return
end
local ws_name = scroll.workspace_get_name(workspace)
local function ensure_callback()
if not grid["map_id"] then
grid["map_id"] = scroll.add_callback("view_map", on_create_view, nil)
end
if not grid["unmap_id"] then
grid["unmap_id"] = scroll.add_callback("view_unmap", on_destroy, nil)
end
end
if args[1] == "toggle" then
if grid["active_workspaces"][ws_name] then
grid["active_workspaces"][ws_name] = nil
scroll.workspace_set_mode(workspace, { insert = "after", focus = true })
debug_notify("Grid Mode: DISABLED for [" .. ws_name .. "]")
else
grid["active_workspaces"][ws_name] = { column_limit, grid_rows, grid_columns, fit_size }
scroll.workspace_set_mode(workspace, { insert = "end", focus = true })
debug_notify(
string.format(
"Grid Mode: ENABLED for %s (Lim:%d, H:%d, V:%d)",
ws_name,
column_limit,
grid_rows,
grid_columns
)
)
end
elseif args[1] == "disable" then
grid["active_workspaces"][ws_name] = nil
scroll.workspace_set_mode(workspace, { insert = "after", focus = true })
debug_notify("Grid Mode: DISABLED for [" .. ws_name .. "]")
elseif args[1] == "enable" then
grid["active_workspaces"][ws_name] = { column_limit, grid_rows, grid_columns, fit_size }
scroll.workspace_set_mode(workspace, { insert = "end", focus = true })
debug_notify(
string.format("Grid Mode: ENABLED for %s (Lim:%d, H:%d, V:%d)", ws_name, column_limit, grid_rows, grid_columns)
)
end
ensure_callback()
scroll介绍 #
由于 Hyprland 最近一次破坏性更新(Break Update)带来的不便,在习惯了 hyprscroller 的操作逻辑后,我选择了由同一作者维护的新项目:scroll。scroll 提供了更加灵活的“卷轴”体验,支持上下左右全方位的平铺与滚动。体验很舒服,但是grid模式没有了,没法愉快的九宫格刷b站了,幸好作者提供了丰富的Lua接口,实在不行咱们可以自己实现一个。
初步实现 #
说干就干,询问作者得到了个 初步的例子,核心逻辑:将新窗口的插入位置固定在工作区的最后,脚本会检查它的前一列容器中的子窗口数量。如果数量小于 3,则执行一次 move left,将新窗口塞进前一列。
遇到问题及优化 #
很容易发现我们的初步脚本存在以下问题
- 只能实现按顺序的移动, 如果中间有窗口关闭则会继续从最右边重新开始,体验上很怪。
寻坑-加入的逻辑