use std::sync::Arc;
use super::frame::Frame;
use super::types::LayoutRuntime;
use crate::compiler::CompileResult;
use crate::diff::{self, LayoutDiff, OverlayDiff};
use crate::error::PaneError;
use crate::rect::Rect;
use crate::resolver::{self, CachedLayoutState, ResolvedLayout};
type CachedIndices = Option<CachedLayoutState>;
impl LayoutRuntime {
pub fn resolve(&mut self, width: f32, height: f32) -> Result<Frame, PaneError> {
self.maybe_switch_breakpoint(width)?;
let tree_dirty = self.tree.is_dirty();
let topology_dirty = self.dirty.topology;
let (mut result, cached) = self.compile_tree(tree_dirty, topology_dirty)?;
crate::compiler::compute_layout(&mut result, width, height)?;
let mut layout = self.resolve_layout(&result, cached)?;
self.cached_compile = Some(result);
apply_scroll_offset(&mut layout, self.viewport.scroll_offset);
self.resolve_overlays(&mut layout, width, height);
self.compute_diffs(&layout, topology_dirty);
self.dirty.clear();
let layout = Arc::new(layout);
let prev_arc = self.previous.replace(Arc::clone(&layout));
rotate_buf(&mut self.rects_buf, &mut self.rects_buf_alt);
rotate_vec(&mut self.overlay_rects_buf, &mut self.overlay_rects_buf_alt);
rotate_vec(
&mut self.overlay_failures_buf,
&mut self.overlay_failures_buf_alt,
);
reclaim_buffers(prev_arc, self);
Ok(Frame::new(layout))
}
pub(crate) fn compile_tree(
&mut self,
tree_dirty: bool,
topology_dirty: bool,
) -> Result<(CompileResult, CachedIndices), PaneError> {
let result = match (tree_dirty, self.cached_compile.take()) {
(false, Some(cached)) => cached,
(_, old) => {
self.tree.clear_dirty();
crate::compiler::compile_with_sizes(&self.tree, old, &self.panel_sizes)?
}
};
let cached = match (
topology_dirty,
self.cached_kinds.take(),
self.cached_sorted_kind_keys.take(),
self.cached_panel_kind_indices.take(),
self.cached_decorations.take(),
self.cached_decoration_roles.take(),
self.cached_live_panel_ids.take(),
) {
(
false,
Some(kinds),
Some(sorted_kind_keys),
Some(panel_kind_indices),
Some(decorations),
Some(decoration_roles),
Some(live_panel_ids),
) => Some(CachedLayoutState {
kinds,
sorted_kind_keys,
panel_kind_indices,
decorations,
decoration_roles,
live_panel_ids,
}),
_ => None,
};
Ok((result, cached))
}
pub(crate) fn resolve_layout(
&mut self,
result: &CompileResult,
cached: CachedIndices,
) -> Result<ResolvedLayout, PaneError> {
let layout = match cached {
Some(state) => resolver::resolve_with_cached_kinds(
result,
&self.tree,
state,
&mut self.resolve_scratch,
self.rects_buf.take(),
)?,
None => resolver::resolve_dirty(
result,
&self.tree,
&mut self.resolve_scratch,
self.rects_buf.take(),
)?,
};
self.cached_kinds = Some(Arc::clone(layout.kinds_arc()));
self.cached_sorted_kind_keys = Some(Arc::clone(layout.sorted_kind_keys_arc()));
self.cached_panel_kind_indices = Some(Arc::clone(layout.panel_kind_indices_arc()));
self.cached_decorations = Some(Arc::clone(layout.decorations_arc()));
self.cached_decoration_roles = Some(Arc::clone(layout.decoration_roles_arc()));
self.cached_live_panel_ids = Some(Arc::clone(layout.live_panel_ids_arc()));
Ok(layout)
}
fn resolve_overlays(&mut self, layout: &mut ResolvedLayout, width: f32, height: f32) {
crate::runtime_overlay::resolve_overlays_impl(
&self.overlays,
&mut self.overlay_rects_buf,
&mut self.overlay_failures_buf,
&self.sequence,
layout,
width,
height,
);
}
pub fn last_diff(&self) -> LayoutDiff<'_> {
self.diff_scratch.as_diff()
}
pub fn last_overlay_diff(&self) -> OverlayDiff<'_> {
self.overlay_diff_scratch.as_diff()
}
fn compute_diffs(&mut self, layout: &ResolvedLayout, topology_dirty: bool) {
select_diff(
topology_dirty,
self.previous.as_deref(),
layout,
&mut self.diff_scratch,
);
let curr_rects = layout.overlay_rects_raw();
let curr_failures = layout.overlay_failures();
match self.previous.as_deref() {
Some(prev) => {
diff::diff_overlays(
prev.overlay_rects_raw(),
curr_rects,
curr_failures,
&mut self.overlay_diff_scratch,
);
}
None => {
diff::first_frame_overlays(
curr_rects,
curr_failures,
&mut self.overlay_diff_scratch,
);
}
};
}
}
fn rotate_buf(primary: &mut Option<Vec<Option<Rect>>>, alt: &mut Option<Vec<Option<Rect>>>) {
if let (None, Some(_)) = (primary.as_ref(), alt.as_ref()) {
*primary = alt.take();
}
}
fn rotate_vec<T>(primary: &mut Vec<T>, alt: &mut Vec<T>) {
if primary.is_empty() {
std::mem::swap(primary, alt);
}
}
fn reclaim_buffers(prev_arc: Option<Arc<ResolvedLayout>>, rt: &mut LayoutRuntime) {
let Some(Ok(mut prev_layout)) = prev_arc.map(Arc::try_unwrap) else {
return;
};
let reclaimed_rects = prev_layout.take_rects();
let reclaimed_overlay = prev_layout.take_overlay_rects();
let reclaimed_failures = prev_layout.take_overlay_failures();
let reclaimed_boundaries = prev_layout.take_boundaries();
match rt.rects_buf.is_none() {
true => rt.rects_buf = Some(reclaimed_rects),
false => rt.rects_buf_alt = Some(reclaimed_rects),
}
match rt.overlay_rects_buf_alt.is_empty() {
true => rt.overlay_rects_buf_alt = reclaimed_overlay,
false => {}
}
match rt.overlay_failures_buf_alt.is_empty() {
true => rt.overlay_failures_buf_alt = reclaimed_failures,
false => {}
}
rt.resolve_scratch.donate_boundary_buf(reclaimed_boundaries);
}
fn select_diff(
topology_dirty: bool,
prev: Option<&ResolvedLayout>,
new: &ResolvedLayout,
scratch: &mut diff::PanelScratch,
) {
match (topology_dirty, prev) {
(_, None) => {
diff::first_frame(new, scratch);
}
(false, Some(prev)) => {
diff::diff_same_panels_reuse(prev, new, scratch);
}
(true, Some(prev)) => {
diff::diff_reuse(prev, new, scratch);
}
};
}
fn apply_scroll_offset(layout: &mut ResolvedLayout, offset: f32) {
match offset.abs() < f32::EPSILON {
true => {}
false => layout.shift_x(-offset),
}
}