Skip to main content

ratatui_toolkit/master_layout/
interaction_mode.rs

1//! Interaction mode state machine for Layout vs Focus modes
2
3use super::PaneId;
4
5/// Interaction mode - either Layout (navigation) or Focus (interaction)
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum InteractionMode {
8    /// Layout Mode (Command Mode)
9    /// - Navigate panes with hjkl
10    /// - Select pane (visual highlight only)
11    /// - Press Enter to focus selected pane
12    Layout { selected_pane: Option<PaneId> },
13
14    /// Focus Mode (Insert Mode)
15    /// - All input goes to focused pane
16    /// - Press Ctrl-A to exit to Layout Mode
17    Focus { focused_pane: PaneId },
18}
19
20impl InteractionMode {
21    /// Create new Layout Mode with no selection
22    pub fn layout() -> Self {
23        Self::Layout {
24            selected_pane: None,
25        }
26    }
27
28    /// Create new Layout Mode with pane selected
29    pub fn layout_with_selection(pane: PaneId) -> Self {
30        Self::Layout {
31            selected_pane: Some(pane),
32        }
33    }
34
35    /// Create new Focus Mode with pane focused
36    pub fn focus(pane: PaneId) -> Self {
37        Self::Focus { focused_pane: pane }
38    }
39
40    /// Check if in Layout Mode
41    pub fn is_layout(&self) -> bool {
42        matches!(self, Self::Layout { .. })
43    }
44
45    /// Check if in Focus Mode
46    pub fn is_focus(&self) -> bool {
47        matches!(self, Self::Focus { .. })
48    }
49
50    /// Get selected pane if in Layout Mode
51    pub fn selected_pane(&self) -> Option<PaneId> {
52        match self {
53            Self::Layout { selected_pane } => *selected_pane,
54            _ => None,
55        }
56    }
57
58    /// Get focused pane if in Focus Mode
59    pub fn focused_pane(&self) -> Option<PaneId> {
60        match self {
61            Self::Focus { focused_pane } => Some(*focused_pane),
62            _ => None,
63        }
64    }
65
66    /// Transition from Layout to Focus mode
67    pub fn enter_focus(&mut self, pane: PaneId) {
68        *self = Self::Focus { focused_pane: pane };
69    }
70
71    /// Transition from Focus to Layout mode, selecting the previously focused pane
72    pub fn exit_focus(&mut self) {
73        if let Self::Focus { focused_pane } = self {
74            *self = Self::Layout {
75                selected_pane: Some(*focused_pane),
76            };
77        }
78    }
79
80    /// Update selected pane in Layout Mode
81    pub fn select_pane(&mut self, pane: PaneId) {
82        if let Self::Layout { selected_pane } = self {
83            *selected_pane = Some(pane);
84        }
85    }
86}
87
88impl Default for InteractionMode {
89    fn default() -> Self {
90        Self::layout()
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_default_is_layout_mode() {
100        let mode = InteractionMode::default();
101        assert!(mode.is_layout());
102        assert_eq!(mode.selected_pane(), None);
103    }
104
105    #[test]
106    fn test_layout_mode_creation() {
107        let mode = InteractionMode::layout();
108        assert!(mode.is_layout());
109        assert_eq!(mode.selected_pane(), None);
110    }
111
112    #[test]
113    fn test_layout_mode_with_selection() {
114        let pane_id = PaneId::new("test");
115        let mode = InteractionMode::layout_with_selection(pane_id);
116        assert!(mode.is_layout());
117        assert_eq!(mode.selected_pane(), Some(pane_id));
118    }
119
120    #[test]
121    fn test_focus_mode_creation() {
122        let pane_id = PaneId::new("test");
123        let mode = InteractionMode::focus(pane_id);
124        assert!(mode.is_focus());
125        assert_eq!(mode.focused_pane(), Some(pane_id));
126    }
127
128    #[test]
129    fn test_enter_focus_transition() {
130        let mut mode = InteractionMode::layout();
131        let pane_id = PaneId::new("test");
132
133        mode.enter_focus(pane_id);
134
135        assert!(mode.is_focus());
136        assert_eq!(mode.focused_pane(), Some(pane_id));
137    }
138
139    #[test]
140    fn test_exit_focus_transition() {
141        let pane_id = PaneId::new("test");
142        let mut mode = InteractionMode::focus(pane_id);
143
144        mode.exit_focus();
145
146        assert!(mode.is_layout());
147        assert_eq!(mode.selected_pane(), Some(pane_id));
148    }
149
150    #[test]
151    fn test_select_pane_in_layout_mode() {
152        let mut mode = InteractionMode::layout();
153        let pane_id = PaneId::new("test");
154
155        mode.select_pane(pane_id);
156
157        assert_eq!(mode.selected_pane(), Some(pane_id));
158    }
159
160    #[test]
161    fn test_select_pane_in_focus_mode_does_nothing() {
162        let pane1 = PaneId::new("pane1");
163        let pane2 = PaneId::new("pane2");
164        let mut mode = InteractionMode::focus(pane1);
165
166        mode.select_pane(pane2);
167
168        // Should still be in focus mode with pane1
169        assert!(mode.is_focus());
170        assert_eq!(mode.focused_pane(), Some(pane1));
171    }
172}