1use std::sync::Arc;
2
3use crate::compiler::{CompileResult, compile, compute_layout};
4use crate::diff::{self, LayoutDiff};
5use crate::error::PaneError;
6use crate::layout::Layout;
7use crate::node::PanelId;
8use crate::panel::fixed;
9use crate::rect::Rect;
10use crate::resolver::{self, ResolveScratch, ResolvedLayout};
11use crate::sequence::PanelSequence;
12use crate::strategy::StrategyKind;
13use crate::tree::LayoutTree;
14use crate::viewport::ViewportState;
15
16pub struct Frame {
18 layout: Arc<ResolvedLayout>,
19 diff: LayoutDiff,
20}
21
22impl Frame {
23 pub fn layout(&self) -> &ResolvedLayout {
25 &self.layout
26 }
27
28 pub fn diff(&self) -> &LayoutDiff {
30 &self.diff
31 }
32}
33
34pub struct LayoutRuntime {
36 tree: LayoutTree,
37 viewport: ViewportState,
38 previous: Option<Arc<ResolvedLayout>>,
39 cached_compile: Option<CompileResult>,
40 cached_kinds: Option<resolver::KindIndex>,
41 rects_buf: Option<Vec<Option<Rect>>>,
42 diff_scratch: diff::DiffScratch,
43 resolve_scratch: ResolveScratch,
44 strategy: Option<StrategyKind>,
45 sequence: PanelSequence,
46}
47
48impl LayoutRuntime {
49 pub fn new(tree: LayoutTree) -> Self {
51 Self {
52 tree,
53 viewport: ViewportState::default(),
54 previous: None,
55 cached_compile: None,
56 cached_kinds: None,
57 rects_buf: None,
58 diff_scratch: diff::DiffScratch::default(),
59 resolve_scratch: ResolveScratch::default(),
60 strategy: None,
61 sequence: PanelSequence::default(),
62 }
63 }
64
65 pub fn from_strategy(strategy: StrategyKind, kinds: &[Arc<str>]) -> Result<Self, PaneError> {
67 let mut sequence = PanelSequence::default();
68 let mut viewport = ViewportState::default();
69 let tree = crate::strategy::build_initial(&strategy, kinds, &mut sequence, &mut viewport)?;
70 Ok(Self {
71 tree,
72 viewport,
73 previous: None,
74 cached_compile: None,
75 cached_kinds: None,
76 rects_buf: None,
77 diff_scratch: diff::DiffScratch::default(),
78 resolve_scratch: ResolveScratch::default(),
79 strategy: Some(strategy),
80 sequence,
81 })
82 }
83
84 pub fn from_tree_and_strategy(
87 tree: LayoutTree,
88 strategy: StrategyKind,
89 kinds: &[Arc<str>],
90 ) -> Result<Self, PaneError> {
91 let mut sequence = PanelSequence::default();
92 for kind in kinds {
93 for &pid in tree.panels_by_kind(kind) {
94 sequence.push(pid);
95 }
96 }
97 let focus = sequence.get(0);
98 Ok(Self {
99 tree,
100 viewport: ViewportState {
101 focus,
102 ..ViewportState::default()
103 },
104 previous: None,
105 cached_compile: None,
106 cached_kinds: None,
107 rects_buf: None,
108 diff_scratch: diff::DiffScratch::default(),
109 resolve_scratch: ResolveScratch::default(),
110 strategy: Some(strategy),
111 sequence,
112 })
113 }
114
115 pub fn tree(&self) -> &LayoutTree {
117 &self.tree
118 }
119
120 pub fn tree_mut(&mut self) -> &mut LayoutTree {
122 &mut self.tree
123 }
124
125 pub fn viewport(&self) -> &ViewportState {
127 &self.viewport
128 }
129
130 pub fn toggle_collapsed(&mut self, pid: PanelId) -> Result<(), PaneError> {
135 match self.viewport.collapsed.contains(&pid) {
136 true => {
137 let saved = self
138 .viewport
139 .saved_constraints
140 .remove(&pid)
141 .ok_or_else(|| {
142 PaneError::InvalidViewport(
143 format!("no saved constraints for panel {pid}").into(),
144 )
145 })?;
146 self.tree.set_constraints(pid, saved)?;
147 self.viewport.collapsed.remove(&pid);
148 Ok(())
149 }
150 false => {
151 let current = self.tree.panel_constraints(pid)?;
152 self.viewport.saved_constraints.insert(pid, current);
153 self.tree.set_constraints(pid, fixed(0.0))?;
154 self.viewport.collapsed.insert(pid);
155 Ok(())
156 }
157 }
158 }
159
160 pub fn scroll_by(&mut self, delta: f32) {
162 self.viewport.scroll_offset += delta;
163 }
164
165 pub fn scroll_to(&mut self, offset: f32) {
167 self.viewport.scroll_offset = offset;
168 }
169
170 pub fn set_active(&mut self, pid: PanelId) {
172 self.viewport.focus = Some(pid);
173 }
174
175 pub fn active_panel(&self) -> Option<PanelId> {
177 self.viewport.focus
178 }
179
180 pub fn strategy(&self) -> Option<&StrategyKind> {
182 self.strategy.as_ref()
183 }
184
185 pub fn sequence(&self) -> &PanelSequence {
187 &self.sequence
188 }
189
190 pub fn focused(&self) -> Option<PanelId> {
192 self.viewport.focus
193 }
194
195 pub fn focused_kind(&self) -> Option<&str> {
197 let pid = self.viewport.focus?;
198 self.tree.panel_kind(pid).ok()
199 }
200
201 pub fn is_decoration_for(&self, pid: PanelId, content_pid: PanelId) -> bool {
203 let (Ok(dec_kind), Ok(content_kind)) =
204 (self.tree.panel_kind(pid), self.tree.panel_kind(content_pid))
205 else {
206 return false;
207 };
208 let base = dec_kind
209 .strip_suffix("_tab")
210 .or_else(|| dec_kind.strip_suffix("_title"));
211 matches!(base, Some(b) if b == content_kind)
212 }
213
214 pub fn add_panel(&mut self, kind: Arc<str>) -> Result<PanelId, PaneError> {
216 let strategy = self
217 .strategy
218 .as_ref()
219 .ok_or_else(|| PaneError::InvalidMutation("no strategy set".into()))?
220 .clone();
221 crate::strategy::apply_add(
222 &strategy,
223 &mut self.tree,
224 &mut self.sequence,
225 &mut self.viewport,
226 kind,
227 )
228 }
229
230 pub fn remove_panel(&mut self, pid: PanelId) -> Result<Option<PanelId>, PaneError> {
232 let strategy = self
233 .strategy
234 .as_ref()
235 .ok_or_else(|| PaneError::InvalidMutation("no strategy set".into()))?
236 .clone();
237 crate::strategy::apply_remove(
238 &strategy,
239 &mut self.tree,
240 &mut self.sequence,
241 &mut self.viewport,
242 pid,
243 )
244 }
245
246 pub fn move_panel(&mut self, pid: PanelId, new_index: usize) -> Result<PanelId, PaneError> {
248 let strategy = self
249 .strategy
250 .as_ref()
251 .ok_or_else(|| PaneError::InvalidMutation("no strategy set".into()))?
252 .clone();
253 crate::strategy::apply_move(
254 &strategy,
255 &mut self.tree,
256 &mut self.sequence,
257 &mut self.viewport,
258 pid,
259 new_index,
260 )
261 }
262
263 pub fn focus(&mut self, pid: PanelId) -> Result<(), PaneError> {
265 match &self.strategy {
266 Some(strategy) => {
267 let strategy = strategy.clone();
268 crate::strategy::apply_focus(
269 &strategy,
270 &mut self.tree,
271 &mut self.sequence,
272 &mut self.viewport,
273 pid,
274 )
275 }
276 None => {
277 self.viewport.focus = Some(pid);
278 Ok(())
279 }
280 }
281 }
282
283 pub fn focus_next(&mut self) -> Result<(), PaneError> {
285 let next = match self.viewport.focus {
286 Some(current) => {
287 let idx = self.sequence.index_of(current).unwrap_or(0);
288 let next_idx = (idx + 1) % self.sequence.len().max(1);
289 self.sequence.get(next_idx)
290 }
291 None => self.sequence.get(0),
292 };
293 match next {
294 Some(pid) => self.focus(pid),
295 None => Ok(()),
296 }
297 }
298
299 pub fn focus_prev(&mut self) -> Result<(), PaneError> {
301 let prev = match self.viewport.focus {
302 Some(current) => {
303 let len = self.sequence.len().max(1);
304 let idx = self.sequence.index_of(current).unwrap_or(0);
305 let prev_idx = (idx + len - 1) % len;
306 self.sequence.get(prev_idx)
307 }
308 None => self.sequence.get(0),
309 };
310 match prev {
311 Some(pid) => self.focus(pid),
312 None => Ok(()),
313 }
314 }
315
316 pub fn resolve(&mut self, width: f32, height: f32) -> Result<Frame, PaneError> {
318 let tree_dirty = self.tree.is_dirty();
319
320 let mut result = match (tree_dirty, self.cached_compile.take()) {
321 (false, Some(cached)) => cached,
322 _ => {
323 self.tree.clear_dirty();
324 compile(&self.tree)?
325 }
326 };
327
328 compute_layout(&mut result, width, height)?;
329
330 let mut layout = match (tree_dirty, self.cached_kinds.take()) {
331 (false, Some(kinds)) => resolver::resolve_with_cached_kinds(
332 &result,
333 &self.tree,
334 kinds,
335 &mut self.resolve_scratch,
336 self.rects_buf.take(),
337 )?,
338 _ => resolver::resolve(&result, &self.tree)?,
339 };
340
341 self.cached_kinds = Some(Arc::clone(layout.kinds_arc()));
342 self.cached_compile = Some(result);
343
344 apply_scroll_offset(&mut layout, self.viewport.scroll_offset);
345
346 let prev_arc = self.previous.take();
347 let diff = match (tree_dirty, prev_arc.as_deref()) {
348 (_, None) => diff::first_frame(&layout),
349 (false, Some(prev)) => diff::diff_same_panels(prev, &layout),
350 (true, Some(prev)) => diff::diff_reuse(prev, &layout, &mut self.diff_scratch),
351 };
352
353 let layout = Arc::new(layout);
354 self.previous = Some(Arc::clone(&layout));
355
356 if let Some(Ok(mut prev_layout)) = prev_arc.map(Arc::try_unwrap) {
358 self.rects_buf = Some(prev_layout.take_rects());
359 }
360
361 Ok(Frame { layout, diff })
362 }
363}
364
365impl From<LayoutTree> for LayoutRuntime {
366 fn from(tree: LayoutTree) -> Self {
367 Self::new(tree)
368 }
369}
370
371fn apply_scroll_offset(layout: &mut ResolvedLayout, offset: f32) {
373 match offset.abs() < f32::EPSILON {
374 true => {}
375 false => layout.shift_x(-offset),
376 }
377}
378
379impl From<Layout> for LayoutRuntime {
380 fn from(layout: Layout) -> Self {
381 Self::new(LayoutTree::from(layout))
382 }
383}