reovim_core/plugin/window.rs
1//! Unified plugin window system
2//!
3//! Provides a single trait for all plugin UI rendering, replacing the previous
4//! separate `WindowProvider`, `OverlayRenderer`, and `RenderContext` systems.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use reovim_core::plugin::{EditorContext, PluginStateRegistry, PluginWindow, WindowConfig, Rect};
10//!
11//! struct MyWindow;
12//!
13//! impl PluginWindow for MyWindow {
14//! fn window_config(
15//! &self,
16//! state: &Arc<PluginStateRegistry>,
17//! ctx: &EditorContext,
18//! ) -> Option<WindowConfig> {
19//! let (x, y, w, h) = ctx.floating(0.8, 0.6);
20//! Some(WindowConfig {
21//! bounds: Rect { x, y, width: w, height: h },
22//! z_order: 200,
23//! visible: true,
24//! })
25//! }
26//!
27//! fn render(
28//! &self,
29//! state: &Arc<PluginStateRegistry>,
30//! ctx: &EditorContext,
31//! buffer: &mut FrameBuffer,
32//! bounds: Rect,
33//! theme: &Theme,
34//! ) {
35//! // Render content into bounds
36//! }
37//! }
38//! ```
39
40use std::sync::Arc;
41
42use crate::{frame::FrameBuffer, highlight::Theme};
43
44use super::{EditorContext, PluginStateRegistry};
45
46/// Simple rectangle for window bounds
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct Rect {
49 /// X position (column)
50 pub x: u16,
51 /// Y position (row)
52 pub y: u16,
53 /// Width in columns
54 pub width: u16,
55 /// Height in rows
56 pub height: u16,
57}
58
59impl Rect {
60 /// Create a new rectangle
61 #[must_use]
62 pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
63 Self {
64 x,
65 y,
66 width,
67 height,
68 }
69 }
70
71 /// Create from tuple (x, y, width, height)
72 #[must_use]
73 pub const fn from_tuple(tuple: (u16, u16, u16, u16)) -> Self {
74 Self {
75 x: tuple.0,
76 y: tuple.1,
77 width: tuple.2,
78 height: tuple.3,
79 }
80 }
81
82 /// Check if the rectangle has zero area
83 #[must_use]
84 pub const fn is_empty(&self) -> bool {
85 self.width == 0 || self.height == 0
86 }
87
88 /// Check if a point is inside the rectangle
89 #[must_use]
90 pub const fn contains(&self, x: u16, y: u16) -> bool {
91 x >= self.x && x < self.x + self.width && y >= self.y && y < self.y + self.height
92 }
93}
94
95/// Window configuration returned by plugins
96#[derive(Debug, Clone, Copy)]
97pub struct WindowConfig {
98 /// Window bounds (position and size)
99 pub bounds: Rect,
100 /// Z-order for rendering (higher = on top)
101 /// Recommended values:
102 /// - 100: Editor windows (buffers)
103 /// - 150: Sidebars (explorer)
104 /// - 200: Dropdowns (completion)
105 /// - 300: Floating (telescope, picker)
106 /// - 400: Modal (settings, dialogs)
107 pub z_order: u32,
108 /// Whether the window is visible
109 pub visible: bool,
110}
111
112impl WindowConfig {
113 /// Create a new window config
114 #[must_use]
115 pub const fn new(bounds: Rect, z_order: u32, visible: bool) -> Self {
116 Self {
117 bounds,
118 z_order,
119 visible,
120 }
121 }
122}
123
124/// Unified trait for plugins that render UI
125///
126/// This trait replaces the previous `WindowProvider`, `OverlayRenderer`, and
127/// `RenderContext` systems with a single unified interface.
128///
129/// # Lifecycle
130///
131/// 1. `window_config()` is called first to get position, size, z-order, visibility
132/// 2. If visible, `render()` is called with the bounds from config
133/// 3. Windows are rendered in z-order (lowest first, highest on top)
134pub trait PluginWindow: Send + Sync {
135 /// Get window configuration (position, size, visibility, z-order)
136 ///
137 /// Return `None` to skip rendering entirely (equivalent to visible=false).
138 /// Return `Some(config)` with `visible=false` to reserve space but not render.
139 fn window_config(
140 &self,
141 state: &Arc<PluginStateRegistry>,
142 ctx: &EditorContext,
143 ) -> Option<WindowConfig>;
144
145 /// Render content into the window bounds
146 ///
147 /// The `bounds` parameter matches `window_config().bounds` and defines
148 /// the area where this window should render. Content outside bounds
149 /// will be clipped.
150 fn render(
151 &self,
152 state: &Arc<PluginStateRegistry>,
153 ctx: &EditorContext,
154 buffer: &mut FrameBuffer,
155 bounds: Rect,
156 theme: &Theme,
157 );
158}