本文记录在 Hyprland 的 scrolling 布局下,为当前窗口的下方开启一个新终端(ghostty)的方案。核心难点在于 scrolling 布局与 dwindle 布局对"分割方向"的处理完全不同。
背景
Hyprland 支持几种平铺布局(dwindle / master / scrolling)。我日常使用 scrolling 布局,窗口按"列"排列在一条无限延伸的纸带上,每个新窗口默认在最右侧新建一列。想要一个"在当前窗口正下方打开终端"的快捷键,实现时却踩了坑。
0.55 Lua 配置模式踩坑(关键)
从 Hyprland 0.55 起配置全面转向 Lua(DVC 模式),hyprctl dispatch 的参数会被包装成 hl.dispatch(...) 求值。这导致旧的 hyprctl dispatch layoutmsg consume_or_expel prev 写法报 Lua 语法错误而失效(实测返回类似 error: hl.dispatch(layoutmsg consume_or_expel...) 的 Lua 语法错误),表现为终端直接开成新列,看起来像"直接打开 ghostty"而没有出现在窗口下方。
0.55/DVC 模式需直接传 Lua 形式:
| |
实测通过:新终端正常合并回前一列出现在下方;无相邻列时仅产生 warning(无副作用)。脚本中已采用此写法。
踩坑:split:bottom 不生效
dwindle 布局可以用 hyprctl dispatch split:bottom 预设下一个新窗口的分割方向,让它开在当前窗口下方:
| |
但实测在 scrolling 布局下完全无效——新开的终端仍然出现在右侧新列。
原因:split: 派发器是 dwindle 布局专用的,它把"当前焦点节点"沿指定方向一分为二。而 scrolling 布局是纯列式模型,没有"节点分割"的概念,新窗口永远作为一个新列添加,不存在往下塞进当前窗口下面的原生操作。
| |
思路:用 layoutmsg 的 consume 把新列合并回当前列
scrolling 布局虽然不支持原生"按方向分割",但它提供了 layoutmsg 系列命令。关键思路是:
- 先创建一个新窗口(默认会在右侧新开一列)
- 用
layoutmsg consume_or_expel prev把当前焦点窗口(即刚开的那个)合并进相邻的前一列
合并后,新窗口会出现在前一列的下方(因为列内支持垂直堆叠多个窗口),于是视觉上就是"当前窗口下方"。
| |
注意(0.55 Lua 配置模式):从 Hyprland 0.55 开始配置全面转向 Lua,
hyprctl dispatch layoutmsg consume_or_expel prev的参数会被包装成hl.dispatch(...)求值,导致报 Lua 语法错误而失效(表现为终端开成新列,看起来像"直接打开 ghostty")。0.55/DVC 模式需直接传 Lua 形式:
1hyprctl dispatch 'hl.dsp.layout("consume_or_expel prev")'该写法实测通过,无相邻列时仅产生 warning 无副作用。
完整脚本
由于终端需要等待新窗口成为焦点后,再由滚动布局合并,写成一个脚本更稳妥:
| |
几个要点:
- 记录
FOCUSED_BEFORE是为了准确识别"新开的窗口",避免被其它弹窗干扰 - 用循环轮询
hyprctl -j activewindow等待焦点切到新窗口,比固定sleep更可靠 - 脚本本身放在 nix 配置仓库,通过 mkOutOfStoreSymlink 引用,修改即生效,无需 rebuild(见 mkOutOfStoreSymlink 说明)
绑定快捷键
在 hyprland.conf 中加入:
| |
为什么能"下方"堆叠
scrolling 布局中,同一个列可以垂直堆叠多个窗口(列内也可以有多行)。consume_or_expel prev 会把窗口并入当前列的下方槽位,从而实现视觉上的"下方打开"。