Paneru Lua脚本进阶:事件钩子与纯窗口集实战(附命名Scratchpad案例) Paneru Lua脚本进阶事件钩子与纯窗口集实战附命名Scratchpad案例【免费下载链接】paneruA sliding, tiling window manager for MacOS.项目地址: https://gitcode.com/gh_mirrors/pan/paneruPaneru 是一款面向 macOS 的滑动平铺窗口管理器它内置了 Lua 脚本运行时你可以用paneru.on事件钩子监听窗口创建、聚焦、移动等窗口管理事件用纯窗口集Window Set对整块布局做函数式变换还可以用paneru.state持久化脚本数据。本文带你走一遍 Lua 脚本进阶的两条主线并附一个可直接上手的命名 Scratchpad临时便签窗口完整案例。脚本位置启用 Lua 脚本的第一步Paneru 按以下顺序查找 Lua 脚本找到即生效环境变量$PANERU_LUA$HOME/.paneru.lua$XDG_CONFIG_HOME/paneru/init.lua两个必须知道的关键机制 ⚙️脚本与 TOML 互斥一旦上述任一文件存在paneru.toml将完全不被读取——不存在双配置互相覆盖的隐患热重载保存脚本文件即自动重载整个配置含键绑定、菜单栏无需重启窗口管理器。事件钩子速查用 paneru.on 注册回调paneru.on的注册语法支持可选过滤器paneru.on(event_name, [filter,] handler)handler 接收两个参数事件对象event和当前纯窗口集ws。常用事件一览事件名含义window_spawned窗口完成创建含window_id、app_name、bundle_id、title、framewindow_focused/window_destroyed窗口获得焦点 / 被关闭window_resized/window_moved窗口尺寸变化 / 位置变化window_title_changed窗口标题改变space_changed/space_created虚拟工作区切换 / 新建mouse_down/mouse_dragged/mouse_moved鼠标动作过滤器可以是普通表也可以用paneru.match构造app、bundle、title均为正则-- 只对 Ghostty 生效小窗自动改为浮动 paneru.on(window_spawned, paneru.match{ app Ghostty }, function(event, ws) if event.frame.width 400 or event.frame.height 400 then return ws:float(event.window_id) end end) 核心要点只有命中过滤器的线程上才会真正执行 Lua 回调所以挂再多钩子开销也很低。纯窗口集实战返回才提交paneru.bind的函数回调会收到一个ws——整个布局显示器、工作区、列、窗口的纯值设计上参考了 xmonad 的StackSet每个方法都返回一个新的窗口集不修改传入的值在你return之前也不会触碰任何真实窗口。paneru.bind(alt - 3, function(ws) return ws:view(3) end) paneru.bind(alt - h, function(ws) return ws:focus(ws:west(ws:focused())) end) paneru.bind(alt - shift-3, function(ws) return ws:shift(ws:focused(), 3) end)两组常用方法读取布局ws:focused()、ws:windows()、ws:find(pred)、ws:columns()、ws:workspace_of(id)、ws:display_of(id)变换布局ws:focus、ws:swap、ws:shift、ws:view、ws:float、ws:sink、ws:manage、ws:width、ws:stack、ws:tab、ws:unstack纯的最大好处是可以先算后决定分支中途放弃也不留副作用paneru.bind(alt - b, function(ws) local tidied ws:width(ws:focused(), 0.6) -- 先算出候选布局 if #ws:columns() 3 then return end -- 什么都不返回 什么都不变 return tidied -- 返回最终布局才提交 end)纯窗口集的 Rust 实现位于共享类型 crate 的 windowset.rs其 Lua 绑定在 windowset_lua.rs完整方法表见 SCRIPTING.md。附案例三步搭出命名 ScratchpadScratchpad便签窗口是需要时召唤、不需要时收走的窗口。官方脚本指南中移植了 xmonad 经典的NamedScratchpad核心思路三步定义 pad名称 匹配条件 启动命令不用时停放在一个你从不查看的隐藏工作区stash 9钩住 spawn 事件窗口第一次出现时决定摆放例如小窗转浮动一键切换未运行则启动 → 在屏则收走 → 在隐藏区则召回并聚焦同时把其他可见的 pad 先收起来。local scratchpad { stash 9, pads {}, order {} } function scratchpad.define(name, spec) scratchpad.pads[name] spec table.insert(scratchpad.order, name) end function scratchpad.toggle(name) return function(ws) local pad scratchpad.pads[name] local window ws:find(pad.match) if not window then os.execute(pad.spawn .. ) -- 未运行启动它 return end if ws:workspace_of(window.id) ws:current() then return ws:shift(window.id, scratchpad.stash) -- 可见收进隐藏区 end ws scratchpad.hide(ws, scratchpad.order) -- 先收起其他可见 pad return ws:shift(window.id, ws:current(), true):focus(window.id) end end -- 绑一个快捷键立即拥有召唤/收起能力 paneru.bind(alt - s, scratchpad.toggle(scratch)) 进阶想让脚本记住某个窗口是哪个 pad用持久化存储paneru.state——它能在热重载和守护进程重启后存活paneru.state.set(pads.term, window_id) -- 存 JSON 形态的值 local id paneru.state.get(pads.term) -- 不存在时返回 nil包含define、hide与 spawn 钩子的完整可运行版本见 SCRIPTING.md 第 7 节 Named Scratchpads 示例。常见坑与 FAQ❓能在脚本顶层查询状态吗不行。paneru.query_state()、paneru.query_active()等只能在事件钩子或按键回调内调用且每次回调最多收集一次状态——不查询的回调零额外开销。❓为什么浮点选项不生效animation_speed、边框width等浮点值必须写小数点12.0而非12。❓改paneru.config会生效吗不会它是只读快照要把改过的表回传给paneru.setup(paneru.config)才生效。❓事件与状态字段的完整契约在哪看见 QUERY_AND_SUBSCRIBE_FORMAT.md脚本内查询与命令行paneru query返回同一份 JSON 文档。参考资料脚本官方指南SCRIPTING.md配置项总览CONFIGURATION.md内嵌 Lua 运行时客户端库crates/lua/API 与运行时实现api.rs、runtime.rs【免费下载链接】paneruA sliding, tiling window manager for MacOS.项目地址: https://gitcode.com/gh_mirrors/pan/paneru创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考