docs: 明确 sticky 窗口规则并修正 core 头文件注释
This commit is contained in:
@@ -300,10 +300,10 @@ workspace 的 =layout_id= 字段指向当前活动的布局(列表中的一个
|
||||
5. =geometry_mode= 只负责几何模式,不负责 z-order。
|
||||
6. =stack_order[]= 只负责 z-order,不负责几何模式。
|
||||
7. =floating= 是二值状态:窗口要么由 layout 决定基础几何,要么由 =float_rect= 决定基础几何。
|
||||
8. =sticky= 只影响可见性,不改变窗口归属工作区。
|
||||
9. floating 窗口可以跨多个输出显示,但归属工作区仍然只有一个。
|
||||
10. 多输出下 floating 窗口是否迁移工作区,由锚点和跨输出策略决定,不由覆盖面积决定。
|
||||
11. layout 的 name 字段同时用于状态栏显示,应为 1-2 个字符的简短标识符(如 "T", "M")。
|
||||
8. =sticky => floating= ;=sticky= 只放宽 workspace 可见性,不改变窗口归属工作区。
|
||||
9. floating / sticky 窗口都可以跨多个输出显示,但归属工作区仍然只有一个。
|
||||
10. 多输出下 floating / sticky 窗口是否迁移工作区,由锚点和跨输出策略决定,不由覆盖面积决定。
|
||||
11. layout 的 =symbol= 字段用于状态栏显示,应为 1-2 个字符的简短标识符(如 "T", "M");=name= 应保持稳定、可读(如 "tile", "monocle")。
|
||||
12. 窗口标题、类名等元数据存储在 =wm_window_t= 中,与控制状态一同管理。
|
||||
13. 工作区名称存储在 =wm_workspace_t= 中,核心算法不应依赖此字段。
|
||||
14. 每个 workspace 的可用布局列表在启动时分配,运行时固定,只能通过 =CYCLE_LAYOUT= 或 =SET_LAYOUT= 命令切换当前活动布局。
|
||||
@@ -347,7 +347,8 @@ workspace 的 =layout_id= 字段指向当前活动的布局(列表中的一个
|
||||
|
||||
4. =wm_command.h=
|
||||
强类型语义命令。
|
||||
命令语义补充见 [[file:WM_COMMAND_RULES.org][WM_COMMAND_RULES.org]] 。
|
||||
命令语义补充见 [[file:WM_COMMAND_RULES.org][WM_COMMAND_RULES.org]] ;
|
||||
=sticky= 多显示器与跨屏行为补充见 [[file:WM_STICKY_WINDOW_RULES.org][WM_STICKY_WINDOW_RULES.org]] 。
|
||||
|
||||
5. =wm_plan.h=
|
||||
脏标记和后端副作用。
|
||||
@@ -373,10 +374,13 @@ workspace 的 =layout_id= 字段指向当前活动的布局(列表中的一个
|
||||
12. [[file:WM_COMMAND_RULES.org][WM_COMMAND_RULES.org]]
|
||||
关键命令的前置条件、状态转移、可见性和副作用规则。
|
||||
|
||||
13. [[file:WM_POLICY_APPLY_COMMAND_SKELETON.org][WM_POLICY_APPLY_COMMAND_SKELETON.org]]
|
||||
13. [[file:WM_STICKY_WINDOW_RULES.org][WM_STICKY_WINDOW_RULES.org]]
|
||||
=sticky= 窗口在 workspace、taskbar、多显示器和跨 output 拖动下的补充规则。
|
||||
|
||||
14. [[file:WM_POLICY_APPLY_COMMAND_SKELETON.org][WM_POLICY_APPLY_COMMAND_SKELETON.org]]
|
||||
=wm_policy_apply_command()= 的伪代码骨架和实现顺序建议。
|
||||
|
||||
14. [[file:WORKSPACE_CONFIG_EXAMPLES.org][WORKSPACE_CONFIG_EXAMPLES.org]]
|
||||
15. [[file:WORKSPACE_CONFIG_EXAMPLES.org][WORKSPACE_CONFIG_EXAMPLES.org]]
|
||||
Workspace-Output 配置示例和最佳实践。
|
||||
|
||||
* 下一步实施顺序
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
这份文档定义 =wm_policy_apply_command()= 应遵守的最小语义。目标不是覆盖所有未来扩展,而是先把最关键的状态转移规则固定下来,避免后续实现时在 =floating=、=sticky=、=geometry_mode= 和 =stack_order[]= 上出现分叉语义。
|
||||
|
||||
=sticky= 在多显示器、taskbar 和跨 output 拖动下的补充语义见
|
||||
[[file:WM_STICKY_WINDOW_RULES.org][WM_STICKY_WINDOW_RULES.org]] 。
|
||||
|
||||
* 通用规则
|
||||
|
||||
1. 找不到目标 =window_id=、=workspace_id= 或 =output_id= 时,命令失败,不修改状态。
|
||||
@@ -9,13 +12,14 @@
|
||||
3. 命令成功且造成状态变化时,递增 =state.generation= ,并设置相应 =dirty_flags= 。
|
||||
4. =stack_order[]= 是全局 z-order 真相,=raise/lower/focus= 只能通过修改这个数组影响堆叠。
|
||||
5. =geometry_mode= 只表示几何显示模式,不表示窗口是否在最上层。
|
||||
6. =sticky= 只影响可见性,不改变 =workspace_id= 。
|
||||
7. =floating= 决定基础几何来源:
|
||||
6. =sticky => floating= 。
|
||||
7. =sticky= 只放宽 workspace 可见性,不改变 =workspace_id= 。
|
||||
8. =floating= 决定基础几何来源:
|
||||
=floating = false= 时,基础几何来自 layout 结果。
|
||||
=floating = true= 时,基础几何来自 =float_rect= 。
|
||||
8. 文档里出现的可选行为,应优先固化到 =wm_policy_config_t= ,而不是继续留在 prose 里。
|
||||
9. =frame_rect= 和 =float_rect= 都表示包含边框后的外框矩形。
|
||||
10. =border_width= 不是 =wm_window_t= 真状态;runtime 在提交
|
||||
9. 文档里出现的可选行为,应优先固化到 =wm_policy_config_t= ,而不是继续留在 prose 里。
|
||||
10. =frame_rect= 和 =float_rect= 都表示包含边框后的外框矩形。
|
||||
11. =border_width= 不是 =wm_window_t= 真状态;runtime 在提交
|
||||
=WM_EFFECT_CONFIGURE_WINDOW= 时按全局配置解析它。
|
||||
|
||||
* 管理类命令
|
||||
@@ -32,9 +36,10 @@
|
||||
1. 向 =state.windows[]= 添加一个新窗口。
|
||||
2. 设定其 =workspace_id= 。
|
||||
3. 初始 =floating= 、=sticky= 、=urgent= 、=geometry_mode= 由 =command.as.manage_window.initial_state= 显式给出。
|
||||
4. 若 =has_initial_float_rect == true= ,将其复制到 =float_rect= 。
|
||||
5. 将窗口追加到 =stack_order[]= 顶部。
|
||||
6. 是否将该窗口设为所属 workspace 的 =focused_window_id= ,由 =initial_state.set_focus= 或 =policy.manage_sets_focus= 决定。
|
||||
4. 若 =initial_state.sticky == true= ,则结果状态中必须满足 =floating == true= 。
|
||||
5. 若 =has_initial_float_rect == true= ,将其复制到 =float_rect= 。
|
||||
6. 将窗口追加到 =stack_order[]= 顶部。
|
||||
7. 是否将该窗口设为所属 workspace 的 =focused_window_id= ,由 =initial_state.set_focus= 或 =policy.manage_sets_focus= 决定。
|
||||
|
||||
副作用:
|
||||
|
||||
@@ -215,6 +220,11 @@
|
||||
|
||||
** =WM_COMMAND_TOGGLE_FLOATING=
|
||||
|
||||
前置条件:
|
||||
|
||||
1. 目标窗口存在。
|
||||
2. 若目标窗口当前 =sticky == true= ,命令失败。
|
||||
|
||||
状态变化:
|
||||
|
||||
1. 若原来 =floating == false= :
|
||||
@@ -235,10 +245,14 @@
|
||||
|
||||
状态变化:
|
||||
|
||||
1. 切换目标窗口的 =sticky= 状态。
|
||||
2. 不改变 =workspace_id= 。
|
||||
3. 不改变 =floating= 。
|
||||
4. 不改变 =geometry_mode= 。
|
||||
1. 若原来 =sticky == false= :
|
||||
若当前 =floating == false= ,先将当前 =frame_rect= 复制到 =float_rect= ,再设 =floating = true= 。
|
||||
然后将 =sticky= 设为 =true= 。
|
||||
2. 若原来 =sticky == true= :
|
||||
将 =sticky= 设为 =false= 。
|
||||
3. 不改变 =workspace_id= 。
|
||||
4. 关闭 =sticky= 时不自动恢复平铺;=floating= 保持原值不变。
|
||||
5. 不改变 =geometry_mode= 。
|
||||
|
||||
副作用:
|
||||
|
||||
@@ -318,6 +332,7 @@
|
||||
3. 若 =commit_workspace_change == true= :
|
||||
依据 =anchor= 找到目标输出。
|
||||
再按 =policy= 决定是否修改 =workspace_id= 。
|
||||
该规则对普通 =floating= 窗口和 =sticky= 窗口一致适用。
|
||||
4. 不改变 =geometry_mode= 。
|
||||
5. =float_rect= 是外框矩形,因此移动的是包含边框后的最终窗口外框。
|
||||
|
||||
@@ -335,7 +350,7 @@
|
||||
|
||||
状态变化:
|
||||
|
||||
1. 按 =dw/dh= 修改 =float_rect.w/h= 。
|
||||
1. 按 =dw/dh= 修改 =float_rect.width/height= 。
|
||||
2. 应对宽高做最小值约束,避免得到零或负值。
|
||||
3. 不改变 =workspace_id= 。
|
||||
4. 不改变 =geometry_mode= 。
|
||||
@@ -414,11 +429,15 @@ bool wm_state_window_should_be_visible_on_output(const wm_state_t *state,
|
||||
const wm_window_t *window,
|
||||
const wm_output_t *output) {
|
||||
if (!wm_state_window_should_be_visible(state, window)) return false;
|
||||
if (window->sticky) return true;
|
||||
return window->workspace_id == output->current_workspace_id;
|
||||
return wm_rect_intersects(window->frame_rect, output->geometry);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
若后续存在按 output 渲染的 taskbar ,建议在下列条件同时满足时显示窗口条目:
|
||||
|
||||
1. =window->skip_taskbar == false=
|
||||
2. =wm_state_window_should_be_visible_on_output(...) == true=
|
||||
|
||||
* 几何解析优先级
|
||||
|
||||
窗口最终矩形建议按下面的优先级解析:
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
这份文档给出 =wm_policy_apply_command()= 的最小实现骨架。目标是把实际代码的控制流固定住,使其和 [[file:WM_COMMAND_RULES.org][WM_COMMAND_RULES.org]] 中定义的命令规则一一对应。
|
||||
|
||||
=sticky= 在多显示器、taskbar 和跨 output 拖动下的补充语义见
|
||||
[[file:WM_STICKY_WINDOW_RULES.org][WM_STICKY_WINDOW_RULES.org]] 。
|
||||
|
||||
* 函数职责
|
||||
|
||||
=wm_policy_apply_command()= 只做四件事:
|
||||
@@ -187,6 +190,10 @@ static bool apply_manage_window(wm_state_t *state,
|
||||
.urgent = command->as.manage_window.initial_state.urgent,
|
||||
};
|
||||
|
||||
if (win.sticky) {
|
||||
win.floating = true;
|
||||
}
|
||||
|
||||
if (command->as.manage_window.has_initial_float_rect) {
|
||||
win.float_rect = command->as.manage_window.initial_float_rect;
|
||||
}
|
||||
@@ -300,13 +307,26 @@ static bool apply_toggle_sticky(wm_state_t *state,
|
||||
wm_window_t *win = require_window(state, command->as.toggle_sticky.window_id);
|
||||
if (!win) return false;
|
||||
|
||||
win->sticky = !win->sticky;
|
||||
if (!win->sticky) {
|
||||
if (!win->floating) {
|
||||
win->float_rect = win->frame_rect;
|
||||
win->floating = true;
|
||||
}
|
||||
win->sticky = true;
|
||||
} else {
|
||||
win->sticky = false;
|
||||
/* 关闭 sticky 时保持 floating 不变,不自动恢复平铺。 */
|
||||
}
|
||||
|
||||
plan->dirty_flags |= WM_DIRTY_LAYOUT | WM_DIRTY_RENDER;
|
||||
push_render_all_outputs(state, plan);
|
||||
return true;
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
实际实现中,若关闭 =sticky= 后窗口对当前正在查看的 workspace 不再可见,
|
||||
且它曾是相关 workspace 的焦点窗口,则应重选该 workspace 的焦点。
|
||||
|
||||
** =apply_focus_direction()=
|
||||
|
||||
#+BEGIN_SRC c
|
||||
@@ -438,6 +458,7 @@ static bool apply_toggle_floating(wm_state_t *state,
|
||||
wm_plan_t *plan) {
|
||||
wm_window_t *win = require_window(state, command->as.toggle_floating.window_id);
|
||||
if (!win) return false;
|
||||
if (win->sticky) return false;
|
||||
|
||||
if (!win->floating) {
|
||||
win->floating = true;
|
||||
@@ -529,6 +550,8 @@ static bool apply_move_floating_window(wm_state_t *state,
|
||||
require_window(state, command->as.move_floating_window.window_id);
|
||||
if (!win || !win->floating) return false;
|
||||
|
||||
wm_workspace_id_t old_ws_id = win->workspace_id;
|
||||
|
||||
win->float_rect.x += command->as.move_floating_window.dx;
|
||||
win->float_rect.y += command->as.move_floating_window.dy;
|
||||
|
||||
@@ -539,6 +562,12 @@ static bool apply_move_floating_window(wm_state_t *state,
|
||||
command->as.move_floating_window.policy ==
|
||||
WM_CROSS_OUTPUT_MOVE_TO_TARGET_WORKSPACE) {
|
||||
win->workspace_id = out->current_workspace_id;
|
||||
if (win->workspace_id != old_ws_id) {
|
||||
wm_workspace_t *src = require_workspace(state, old_ws_id);
|
||||
if (src && src->focused_window_id == win->id) {
|
||||
maybe_refocus_workspace(state, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,12 +587,12 @@ static bool apply_resize_floating_window(wm_state_t *state,
|
||||
require_window(state, command->as.resize_floating_window.window_id);
|
||||
if (!win || !win->floating) return false;
|
||||
|
||||
win->float_rect.w =
|
||||
clamp_u16((int)win->float_rect.w + command->as.resize_floating_window.dw,
|
||||
WM_MIN_WINDOW_W, UINT16_MAX);
|
||||
win->float_rect.h =
|
||||
clamp_u16((int)win->float_rect.h + command->as.resize_floating_window.dh,
|
||||
WM_MIN_WINDOW_H, UINT16_MAX);
|
||||
win->float_rect.width =
|
||||
clamp_i32(win->float_rect.width + command->as.resize_floating_window.dw,
|
||||
WM_MIN_WINDOW_WIDTH, INT32_MAX);
|
||||
win->float_rect.height =
|
||||
clamp_i32(win->float_rect.height + command->as.resize_floating_window.dh,
|
||||
WM_MIN_WINDOW_HEIGHT, INT32_MAX);
|
||||
|
||||
plan->dirty_flags |= WM_DIRTY_LAYOUT | WM_DIRTY_RENDER;
|
||||
push_render_all_outputs(state, plan);
|
||||
@@ -581,7 +610,9 @@ static bool apply_set_layout(wm_state_t *state,
|
||||
wm_workspace_t *ws =
|
||||
require_workspace(state, command->as.set_layout.workspace_id);
|
||||
if (!ws) return false;
|
||||
if (!wm_layout_lookup(layouts, command->as.set_layout.layout_id)) return false;
|
||||
if (!wm_layout_slot_get(layouts, command->as.set_layout.layout_id)) {
|
||||
return false;
|
||||
}
|
||||
if (ws->layout_id == command->as.set_layout.layout_id) return false;
|
||||
|
||||
ws->layout_id = command->as.set_layout.layout_id;
|
||||
@@ -663,6 +694,7 @@ static bool apply_quit(wm_state_t *state,
|
||||
|
||||
1. 如果有 =WM_DIRTY_LAYOUT= ,则对每个 output 的当前 workspace 重新计算 layout。
|
||||
2. 根据窗口的 =geometry_mode= 、=floating= 和 layout 结果解析最终矩形。
|
||||
=sticky= 窗口由于满足 =sticky => floating= ,因此同样走 =float_rect= 路径。
|
||||
3. 生成 =WM_EFFECT_CONFIGURE_WINDOW= 。
|
||||
4. 若有 =WM_DIRTY_STACK= ,生成 =WM_EFFECT_RESTACK_WINDOWS= 。
|
||||
5. 若有 =WM_DIRTY_RENDER= ,生成 =WM_EFFECT_RENDER_OUTPUT= 。
|
||||
|
||||
200
docs/min_core_draft/WM_STICKY_WINDOW_RULES.org
Normal file
200
docs/min_core_draft/WM_STICKY_WINDOW_RULES.org
Normal file
@@ -0,0 +1,200 @@
|
||||
* Sticky 窗口规则
|
||||
|
||||
这份文档补充 =sticky= 窗口在最小核心中的约束,重点覆盖以下问题:
|
||||
|
||||
1. =sticky= 与 =floating= 的关系
|
||||
2. =sticky= 窗口是否参与自动布局
|
||||
3. =sticky= 窗口在 taskbar 中的显示规则
|
||||
4. 多显示器下 =sticky= 窗口的可见性与跨屏移动语义
|
||||
|
||||
* 核心结论
|
||||
|
||||
1. =sticky => floating=
|
||||
2. =sticky= 窗口不参与任何自动布局
|
||||
3. =sticky= 只放宽 workspace 可见性,不改变窗口的唯一归属 =workspace_id=
|
||||
4. =sticky= 窗口不复制到每个 output;是否出现在某个 output 上,只由其最终矩形是否覆盖该 output 决定
|
||||
5. 拖动 =sticky= 或普通 =floating= 窗口跨 output 时,默认只修改 =float_rect= ,不隐式修改 =workspace_id=
|
||||
6. 只有显式提交跨 output 迁移时,才允许把窗口归属 workspace 改到目标 output 当前显示的 workspace
|
||||
|
||||
* 状态约束
|
||||
|
||||
** =sticky= 与 =floating=
|
||||
|
||||
最小核心中,=sticky= 不是独立于 =floating= 的另一套几何模式,而是附加在 floating 语义之上的可见性扩展。
|
||||
|
||||
因此规定:
|
||||
|
||||
1. 若窗口进入 =sticky= 状态,则必须满足 =floating == true=
|
||||
2. 若窗口原本是平铺窗口,开启 =sticky= 时,应先把当前最终外框矩形写入 =float_rect= ,再设 =floating = true= ,最后设 =sticky = true=
|
||||
3. 关闭 =sticky= 时,只清除 =sticky= 标记,不自动恢复到平铺模式
|
||||
|
||||
不自动恢复平铺的原因是:否则核心必须额外保存“进入 sticky 前是否为 floating”的历史状态,复杂度不值得。
|
||||
|
||||
** 唯一归属 workspace
|
||||
|
||||
每个窗口始终只有一个归属 =workspace_id= 。
|
||||
|
||||
=sticky= 的作用只是:
|
||||
|
||||
- 让窗口在非归属 workspace 上也可见
|
||||
|
||||
而不是:
|
||||
|
||||
- 把窗口复制到多个 workspace
|
||||
- 让窗口同时归属于多个 workspace
|
||||
|
||||
因此:
|
||||
|
||||
1. =workspace_id= 仍然是窗口的唯一归属
|
||||
2. layout、focus 历史、命令目标和后续迁移规则都仍以该归属 workspace 为准
|
||||
|
||||
* 布局规则
|
||||
|
||||
** 不参与自动布局
|
||||
|
||||
=sticky= 窗口不参与任何自动布局。
|
||||
|
||||
这条规则在单显示器和多显示器下都成立:
|
||||
|
||||
1. 不进入任何 workspace 的平铺输入
|
||||
2. 不进入任何 output 的布局输入
|
||||
3. 几何始终来自 =float_rect=
|
||||
|
||||
原因:
|
||||
|
||||
1. 自动布局应是 workspace-local / output-local 的
|
||||
2. =sticky= 如果参与布局,会让同一个窗口对多个 workspace 或多个 output 的布局结果产生耦合
|
||||
3. 同一个窗口不能同时接受多份独立 layout 结果
|
||||
|
||||
** =fn == NULL= 的 floating 布局槽位
|
||||
|
||||
若当前 workspace 的活动布局槽位满足 =fn == NULL= ,则该布局表示 =floating= 布局。
|
||||
|
||||
这只表示当前 workspace 上的普通窗口不进行平铺计算,不改变 =sticky= 窗口的特殊规则:
|
||||
|
||||
1. 普通 floating 窗口:几何来自各自的 =float_rect=
|
||||
2. =sticky= 窗口:同样几何来自 =float_rect=
|
||||
3. 二者都不参与 layout 函数计算
|
||||
|
||||
* Taskbar 规则
|
||||
|
||||
** 基本规则
|
||||
|
||||
如果 taskbar 表达的是“当前 workspace 上用户可见的窗口列表”,则 =sticky= 窗口应被视为可见窗口,因此应出现在 taskbar 中。
|
||||
|
||||
推荐的基础判断是:
|
||||
|
||||
1. =skip_taskbar == false=
|
||||
2. 窗口在当前上下文中实际可见
|
||||
|
||||
** 单显示器
|
||||
|
||||
单显示器下,若当前 workspace 与窗口归属 workspace 不同,只要窗口是 =sticky= 且当前几何可见,仍应出现在 taskbar 中。
|
||||
|
||||
** 多显示器 / 每 output 一条 taskbar
|
||||
|
||||
若系统为每个 output 提供独立 taskbar ,推荐规则是:
|
||||
|
||||
1. 只在窗口实际可见的 output 上显示该窗口
|
||||
2. 不在所有 output 的 taskbar 中无条件复制显示
|
||||
|
||||
也就是说:
|
||||
|
||||
1. 窗口只落在左屏,则只显示在左屏 taskbar
|
||||
2. 窗口只落在右屏,则只显示在右屏 taskbar
|
||||
3. 窗口跨越两个 output ,则可同时出现在两个 output 的 taskbar
|
||||
|
||||
这样 taskbar 与屏幕上的真实可见内容保持一致。
|
||||
|
||||
* 多显示器规则
|
||||
|
||||
** 坐标空间
|
||||
|
||||
=float_rect= 和 =frame_rect= 使用全局桌面坐标,而不是某个 output 的局部坐标。
|
||||
|
||||
因此:
|
||||
|
||||
1. 一个 floating/sticky 窗口可以自然跨越多个 output
|
||||
2. output 可见性可以通过“窗口最终矩形是否与 output 矩形相交”来判断
|
||||
|
||||
** 可见性
|
||||
|
||||
=sticky= 只放宽 workspace 可见性,不提供 output 复制语义。
|
||||
|
||||
因此:
|
||||
|
||||
1. 窗口不会因为 =sticky= 自动在所有 output 上都出现
|
||||
2. 它只会出现在最终矩形实际覆盖到的 output 上
|
||||
3. 若窗口完全移到另一块屏幕,则旧屏幕上不再可见
|
||||
|
||||
* 跨 output 移动规则
|
||||
|
||||
** 拖动过程中
|
||||
|
||||
无论窗口是普通 =floating= 还是 =sticky= ,拖动过程中都只更新 =float_rect= 。
|
||||
|
||||
不应在拖动过程中频繁修改 =workspace_id= ,否则归属语义会抖动,也不利于测试。
|
||||
|
||||
** 拖动结束后的归属处理
|
||||
|
||||
拖动结束后,是否迁移窗口归属 workspace,应由显式策略控制,而不是隐式发生。
|
||||
|
||||
推荐沿用以下策略:
|
||||
|
||||
1. =WM_CROSS_OUTPUT_KEEP_WORKSPACE=
|
||||
- 只更新 =float_rect=
|
||||
- 不修改 =workspace_id=
|
||||
2. =WM_CROSS_OUTPUT_MOVE_TO_TARGET_WORKSPACE=
|
||||
- 使用稳定锚点确定目标 output
|
||||
- 若锚点落在另一个 output 上,则把 =workspace_id= 改为该 output 当前显示的 workspace
|
||||
|
||||
推荐锚点:
|
||||
|
||||
1. 窗口中心点
|
||||
2. 或命令中显式提供的 =anchor=
|
||||
|
||||
不建议按覆盖面积决定归属,因为该规则在窗口跨屏时不稳定,也不直观。
|
||||
|
||||
** =sticky= 下的跨 output 移动
|
||||
|
||||
=sticky= 窗口跨屏拖动时,默认也不自动修改 =workspace_id= 。
|
||||
|
||||
原因:
|
||||
|
||||
1. =sticky= 本来就可以跨 workspace 可见
|
||||
2. 若移动几何时顺便修改归属,会让“移动窗口”和“迁移窗口”两个动作发生耦合
|
||||
|
||||
因此推荐:
|
||||
|
||||
1. 默认只更新 =float_rect=
|
||||
2. 若用户明确要求迁移,则再执行归属 workspace 切换
|
||||
|
||||
* 关闭 Sticky 的语义
|
||||
|
||||
关闭 =sticky= 时:
|
||||
|
||||
1. 只清除 =sticky= 标记
|
||||
2. 不自动修改 =workspace_id=
|
||||
3. 不自动修改 =floating=
|
||||
4. 不自动恢复平铺
|
||||
|
||||
若当前查看的 workspace 不是窗口归属的 =workspace_id= ,则关闭 =sticky= 后窗口应立即从当前视图消失。
|
||||
|
||||
如果该窗口在关闭 =sticky= 前是当前焦点窗口,则需要重新选择当前 workspace 的焦点窗口。
|
||||
|
||||
* 设计原则总结
|
||||
|
||||
这套规则的目标是把几个概念彻底拆开:
|
||||
|
||||
1. =floating= 管几何来源
|
||||
2. =sticky= 管跨 workspace 可见性
|
||||
3. =workspace_id= 管唯一归属
|
||||
4. 跨 output 拖动只改几何
|
||||
5. workspace 迁移必须显式提交
|
||||
|
||||
这样可以避免以下几类混乱语义:
|
||||
|
||||
1. 拖动窗口时归属 workspace 频繁跳变
|
||||
2. =sticky= 窗口同时参与多个布局
|
||||
3. taskbar 显示内容与实际可见窗口不一致
|
||||
4. 关闭 =sticky= 时隐式触发额外状态回滚
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/state.h"
|
||||
|
||||
typedef struct wm_runtime_t {
|
||||
|
||||
@@ -50,7 +50,6 @@ typedef struct wm_output_t {
|
||||
} wm_output_t;
|
||||
|
||||
typedef struct wm_state_t {
|
||||
/* 内部实现(不透明结构体,实现文件中定义) */
|
||||
wm_workspace_t *workspaces;
|
||||
size_t workspace_count;
|
||||
size_t workspace_capacity;
|
||||
|
||||
Reference in New Issue
Block a user