par_term/traits.rs
1//! Shared trait definitions for par-term components.
2//!
3//! These traits document the contracts between major components and enable
4//! mock implementations for unit testing the `app/` module without needing
5//! a live PTY session or GPU context.
6//!
7//! # Status
8//!
9//! [`TerminalAccess`] is **fully implemented** on [`par_term_terminal::TerminalManager`].
10//! See [`crate::traits_impl`] for the concrete `impl` and a `MockTerminal` test helper.
11//!
12//! [`UIElement`] is **fully implemented** on [`crate::tab_bar_ui::TabBarUI`] and
13//! [`crate::status_bar::StatusBarUI`].
14//! See [`crate::traits_impl`] for the concrete impls and a compile-time test.
15//!
16//! `EventHandler` is still deferred — see the comment block at the end of this file.
17//!
18//! # Migration Path for `EventHandler`
19//!
20//! When the `WindowState` decomposition is further advanced:
21//! 1. Define `EventHandler<E>` where `E = winit::event::WindowEvent` and implement it
22//! on each sub-handler struct extracted from `WindowState`.
23//! 2. Add mock implementations in `#[cfg(test)]` blocks.
24
25// ── AUD-040: TerminalAccess ──────────────────────────────────────────────────
26
27/// Provides read-only access to terminal mode and state for input/mouse handlers.
28///
29/// Implemented by `TerminalManager` (and mock types in tests). Decouples
30/// `app/mouse_events/` and `app/input_events/` from the concrete terminal type,
31/// enabling unit tests without a live PTY session.
32///
33/// # Notes
34///
35/// All methods take `&self` — this trait is intentionally read-only. State
36/// mutation goes through `TerminalManager` directly (write, resize, etc.).
37pub trait TerminalAccess {
38 /// Returns `true` if the alternate screen buffer (DECSC/smcup) is active.
39 ///
40 /// Used by mouse handlers to suppress scrollback scrolling while TUI apps
41 /// (vim, htop, etc.) own the display.
42 fn is_alt_screen_active(&self) -> bool;
43
44 /// Returns `true` if mouse motion events should be reported to the PTY.
45 ///
46 /// `button_pressed` — whether a mouse button is currently held. Some
47 /// modes (ButtonEvent) only report motion while a button is pressed.
48 fn should_report_mouse_motion(&self, button_pressed: bool) -> bool;
49
50 /// Returns the current modifyOtherKeys level (0 = off, 1 = basic, 2 = full).
51 ///
52 /// Controls how modifier-key combinations are encoded in key sequences.
53 fn modify_other_keys_mode(&self) -> u8;
54
55 /// Returns `true` if DECCKM (application cursor key) mode is active.
56 ///
57 /// In application mode, cursor keys send `\x1bO[ABCD]` instead of `\x1b[ABCD]`.
58 fn application_cursor(&self) -> bool;
59
60 /// Encode a mouse event into the bytes to send to the PTY.
61 ///
62 /// Parameters match the X10 / SGR / UTF-8 encoding conventions used by
63 /// `par-term-emu-core-rust`:
64 /// - `button` — button index (0-2 for primary/middle/secondary, 64+ for scroll)
65 /// - `col` / `row` — 0-based cell coordinates
66 /// - `pressed` — `true` for press, `false` for release
67 /// - `modifiers` — bitmask: bit 2 = Shift, bit 3 = Alt, bit 4 = Ctrl
68 fn encode_mouse_event(
69 &self,
70 button: u8,
71 col: usize,
72 row: usize,
73 pressed: bool,
74 modifiers: u8,
75 ) -> Vec<u8>;
76}
77
78// ── AUD-041: UIElement ────────────────────────────────────────────────────────
79
80/// Common interface for UI bar/panel components whose height and visibility
81/// depend on per-frame context (configuration, window state).
82///
83/// # Design rationale
84///
85/// The previous stub used zero-argument methods for `height_logical`,
86/// `width_logical`, and `is_visible`. This was incompatible with the concrete
87/// types (`TabBarUI`, `StatusBarUI`) which all require `&Config` and additional
88/// per-frame parameters to compute these values. Forcing them to cache a config
89/// snapshot would introduce a new class of stale-config bugs.
90///
91/// The solution is a GAT (`type Ctx<'a>`) that lets each implementor declare
92/// exactly what context it needs. The caller provides context at the call site;
93/// no component stores a redundant snapshot.
94///
95/// # GAT note
96///
97/// GATs (`type X<'a>`) require Rust 1.65+. They have been stable since late
98/// 2022 and are available in all supported rustc versions for this project.
99///
100/// # Concrete context types
101///
102/// - `TabBarUI::Ctx<'a> = TabBarCtx<'a>` — needs `(&Config, tab_count: usize)`
103/// - `StatusBarUI::Ctx<'a> = StatusBarCtx<'a>` — needs `(&Config, is_fullscreen: bool)`
104///
105/// # Components that are excluded
106///
107/// `TmuxStatusBarUI::height` is a static method, not `&self`, so it cannot
108/// implement this trait without a wrapper. It is documented as out-of-scope.
109pub trait UIElement {
110 /// The per-call context type. Implementors define a concrete `'a`-lifetime
111 /// struct holding the references they need (e.g. `&'a Config`).
112 type Ctx<'a>;
113
114 /// Returns `true` if this element is currently visible.
115 ///
116 /// When not visible the element contributes 0px to the layout.
117 fn is_visible(&self, ctx: Self::Ctx<'_>) -> bool;
118
119 /// Effective height of the element in logical (DPI-independent) pixels.
120 ///
121 /// Returns 0.0 when not visible or when the element is positioned on a
122 /// vertical axis (e.g. a left-side tab bar contributes width, not height).
123 fn height_logical(&self, ctx: Self::Ctx<'_>) -> f32;
124
125 /// Effective width of the element in logical pixels.
126 ///
127 /// Returns 0.0 for horizontally-positioned elements (top/bottom bars).
128 fn width_logical(&self, ctx: Self::Ctx<'_>) -> f32;
129
130 /// Returns `true` if this element is currently capturing keyboard input.
131 ///
132 /// When `true`, key events should not be forwarded to the PTY.
133 /// This method requires no context because it reflects live interaction state
134 /// rather than layout state.
135 fn is_capturing_input(&self) -> bool;
136}
137
138// ── R-10: OverlayComponent ────────────────────────────────────────────────────
139
140/// Common interface for egui overlay UI components that follow the
141/// `show(&mut self, ctx: &egui::Context) -> Self::Action` pattern.
142///
143/// # Design notes
144///
145/// Twelve or more UI dialogs in par-term share the same shape:
146/// - they maintain a `visible: bool` field (or equivalent),
147/// - they expose a `show` method that renders the dialog and returns an action,
148/// - they can be hidden without producing an action.
149///
150/// This trait formalises that contract so callers can be written generically
151/// and components become easier to test in isolation.
152///
153/// # Components that are excluded
154///
155/// The following components have additional required parameters on `show` and
156/// cannot implement this trait without a wrapper:
157/// - `HelpUI::show` — returns `()` (no action type)
158/// - `TmuxSessionPickerUI::show` — requires `tmux_path: &str`
159/// - `InspectorPanel::show` — requires `available_agents: &[AgentConfig]`
160///
161/// These are documented as out-of-scope in `docs/TRAITS.md` (future work).
162pub trait OverlayComponent {
163 /// The action type produced by this component's `show` call.
164 type Action;
165
166 /// Render the overlay and return any action produced by user interaction.
167 ///
168 /// When the component is not visible this must return the "no action"
169 /// variant immediately, without touching `ctx`.
170 fn show(&mut self, ctx: &egui::Context) -> Self::Action;
171
172 /// Returns `true` if the overlay is currently visible.
173 fn is_visible(&self) -> bool;
174
175 /// Show or hide the overlay.
176 ///
177 /// Setting to `false` hides the dialog immediately. Setting to `true`
178 /// is equivalent to calling a parameter-free open method; components
179 /// that require additional state to open (e.g. `show_for_tab`) should
180 /// use their own specific API instead of relying on this method.
181 fn set_visible(&mut self, visible: bool);
182}
183
184// ── AUD-042: EventHandler — REMOVED ──────────────────────────────────────────
185//
186// The `EventHandler` trait was removed because wiring it up to the concrete
187// `WindowState` dispatch chain is a larger structural refactor than can be done
188// in this file alone. The trait will be reintroduced as part of that effort.
189//
190// Proposed future definition (kept here as a design record):
191//
192// pub trait EventHandler {
193// fn handle_event(&mut self, event: winit::event::WindowEvent) -> bool;
194// }
195//
196// To use this trait, each handler extracted from `WindowState` would implement it
197// and `WindowState::on_window_event` would iterate a `Vec<Box<dyn EventHandler>>`.
198// That decomposition is tracked in the `WindowState` refactor issue.