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}