1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//! Which thing has the keyboard. P0: the tree rail or the active pane. Later:
//! `Picker` / `Palette` / `Prompt` overlays steal focus while open, and `Pane`
//! gains a pane-id once splits exist.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Focus {
Tree,
/// The currently-active pane (per `App::layout` / `App::active`).
Pane,
/// The right-side panel (outline / diagnostics / grep / …). Only
/// reachable when `App::right_panel_visible` is true. Ctrl+E cycles
/// through this when present. keyboard-round-7 SEV-2 #1 —
/// previously the right panel had no keyboard focus path.
RightPanel,
/// 2026-08-07 — bottom panel (dockable panes Phase 1). Only
/// reachable when `App::bottom_panel_visible`. Ctrl+E cycle
/// visits it after RightPanel when present.
BottomPanel,
// Picker, Palette, Prompt, // overlay tracks
}
impl Focus {
/// `Ctrl+E` cycle order. Tree → Pane → RightPanel → Tree.
/// Skips panels that aren't currently reachable.
pub fn next(self, has_pane: bool, has_right_panel: bool) -> Focus {
match self {
Focus::Tree => {
if has_pane {
Focus::Pane
} else if has_right_panel {
Focus::RightPanel
} else {
Focus::Tree
}
}
Focus::Pane => {
if has_right_panel {
Focus::RightPanel
} else {
Focus::Tree
}
}
Focus::RightPanel => Focus::Tree,
Focus::BottomPanel => Focus::Tree,
}
}
}