29.9 重组窗口

当删除窗口 W 的最后一个同级窗口时,其父窗口也会被删除,W 会在窗口树中替代它。这意味着 W 必须与其父窗口的同级窗口重组,以形成新的窗口组合(see 窗口与框架)。在某些情况下,删除一个活动窗口甚至可能导致两个内部窗口被删除。

     ______________________________________
    | ______  ____________________________ |
    ||      || __________________________ ||
    ||      ||| ___________  ___________ |||
    ||      ||||           ||           ||||
    ||      ||||____W6_____||_____W7____||||
    ||      |||____________W4____________|||
    ||      || __________________________ ||
    ||      |||                          |||
    ||      |||                          |||
    ||      |||____________W5____________|||
    ||__W2__||_____________W3_____________ |
    |__________________W1__________________|

在该布局中删除 W5 通常会导致 W3W4 被删除。剩余的活动窗口 W2W6W7 会被重组,形成一个以 W1 为父窗口的新水平组合。

不过,有时不删除像 W4 这样的父窗口是合理的。特别是当父窗口被用于保留嵌入在同类型组合中的组合时,不应移除该父窗口。这种嵌入方式可以确保,当你拆分一个窗口并随后删除新窗口时,Emacs 会恢复对应框架在拆分前的布局。

考虑一个初始场景:有两个活动窗口 W2W3,以及它们的父窗口 W1

     ______________________________________
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||_________________W2_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W3_________________||
    |__________________W1__________________|

如下拆分 W2 以创建新窗口 W4

     ______________________________________
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W2_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W4_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W3_________________||
    |__________________W1__________________|

现在,当垂直放大窗口时,Emacs 会尝试从其下方的同级窗口获取相应空间(如果该窗口存在)。在本场景中,放大 W4 会从 W3 占用空间。

     ______________________________________
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W2_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||_________________W4_________________||
    | ____________________________________ |
    ||_________________W3_________________||
    |__________________W1__________________|

删除 W4 会将其全部空间归还给 W2,包括之前从 W3 占用的空间。

     ______________________________________
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||_________________W2_________________||
    | ____________________________________ |
    ||_________________W3_________________||
    |__________________W1__________________|

这可能不符合直觉,特别是当 W4 仅被临时用于显示缓冲区时(see 临时显示),而你希望继续使用初始布局。

通过在拆分 W2 时创建新的父窗口,可以修复该行为。下面描述的变量支持这一设置。

User Option: window-combination-limit

该变量控制拆分窗口时是否创建新的父窗口。支持以下值:

nil

表示如果存在现有父窗口,且拆分方向与现有窗口组合方向一致,则新活动窗口可以共享该父窗口(否则仍会创建新的内部窗口)。

window-size

表示当 display-buffer 拆分窗口并在 alist 参数中传入 window-heightwindow-width 条目时,会创建新的父窗口(see 缓冲区显示动作函数)。其他情况下,窗口拆分行为与值为 nil 时一致。

temp-buffer-resize

表示当 with-temp-buffer-window 拆分窗口且启用了 temp-buffer-resize-mode 时,会创建新的父窗口(see 临时显示)。其他情况下,行为与 nil 一致。

temp-buffer

表示 with-temp-buffer-window 在拆分现有窗口时始终创建新的父窗口(see 临时显示)。其他情况下,行为与 nil 一致。

display-buffer

表示当 display-buffer(see 为显示缓冲区选择窗口)拆分窗口时,始终创建新的父窗口。其他情况下,行为与 nil 一致。

t

表示拆分窗口时始终创建新的父窗口。因此,如果该变量的值始终为 t,则任何窗口树都是二叉树(即除根窗口外,每个窗口都恰好有一个同级窗口)。

默认值为 window-size。其他值保留供将来使用。

如果由于该变量的设置,split-window 创建了新的父窗口,它还会对新创建的内部窗口调用 set-window-combination-limit(见下文)。这会影响删除子窗口时窗口树的重排方式(见下文)。

如果 window-combination-limitt,在本场景的初始布局中拆分 W2 会产生如下结果:

     ______________________________________
    | ____________________________________ |
    || __________________________________ ||
    |||                                  |||
    |||________________W2________________|||
    || __________________________________ ||
    |||                                  |||
    |||________________W4________________|||
    ||_________________W5_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W3_________________||
    |__________________W1__________________|

创建了一个新的内部窗口 W5;其子窗口为 W2 和新的活动窗口 W4。现在 W2W4 的唯一同级窗口,因此放大 W4 会尝试缩小 W2,而 W3 不受影响。可以看到,W5 表示嵌入在垂直组合 W1 中的两个窗口的垂直组合。

Function: set-window-combination-limit window limit

该函数将窗口 window组合限制 设置为 limit。该值可通过函数 window-combination-limit 获取。其效果见下文;注意该值仅对内部窗口有意义。当调用 split-window 时,如果变量 window-combination-limitt,它会自动调用该函数,并将 t 作为 limit 传入。

Function: window-combination-limit window

该函数返回 window 的组合限制。

组合限制仅对内部窗口有意义。如果为 nil,则 Emacs 可以在删除窗口时自动删除 window,以便将 window 的子窗口与其同级窗口分组,形成新的窗口组合。如果组合限制为 t,则 window 的子窗口永远不会与其同级窗口自动重组。

在本节开头所示的布局中,如果 W4W6W7 的父窗口)的组合限制为 t,则删除 W5 不会同时隐式删除 W4

另外,每当同一组合中的某个窗口被拆分或删除时,通过始终调整该组合中所有窗口的大小,可以避免上述问题。这也允许拆分原本过小而无法执行该操作的窗口。

User Option: window-combination-resize

如果该变量为 nil,则 split-window 仅能在窗口(记为 window)的屏幕区域足够容纳自身和新窗口时拆分它。

如果该变量为 t,则 split-window 会尝试调整与 window 属于同一组合的所有窗口的大小,以容纳新窗口。特别地,即使 window 是固定尺寸窗口或通常过小无法拆分,该设置也可能使 split-window 成功执行。此外,后续调整或删除 window 可能会调整其组合中所有其他窗口的大小。

默认值为 nil。其他值保留供将来使用。如果某个拆分操作受非 nilwindow-combination-limit 值影响,则可能忽略该变量的值。

为说明 window-combination-resize 的效果,考虑以下框架布局。

     ______________________________________
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||_________________W2_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||_________________W3_________________||
    |__________________W1__________________|

如果 window-combination-resizenil,拆分窗口 W3 不会改变 W2 的大小:

     ______________________________________
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||                                    ||
    ||_________________W2_________________||
    | ____________________________________ |
    ||                                    ||
    ||_________________W3_________________||
    | ____________________________________ |
    ||                                    ||
    ||_________________W4_________________||
    |__________________W1__________________|

如果 window-combination-resizet,拆分 W3 会使三个活动窗口的高度大致相等:

     ______________________________________
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W2_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W3_________________||
    | ____________________________________ |
    ||                                    ||
    ||                                    ||
    ||_________________W4_________________||
    |__________________W1__________________|

删除活动窗口 W2W3W4 中的任意一个,都会将其空间按比例分配给剩余的两个活动窗口。