use std::any::{Any, type_name};
use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::Deref;
use skia_safe::{Color, Matrix, Point, Rect, Size};
use crate::animators::Animator;
use crate::control::{Control, part, part_mut};
use crate::paint::{CachedObject, PaintCache};
use crate::types::{CacheType, Dirty, IntoProp, LayoutOptions, LockTouch, SkiaGradient, Thickness};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct ControlId {
pub(crate) index: u32,
pub(crate) generation: u32,
}
thread_local! {
static IDS: RefCell<(Vec<u32>, Vec<u32>)> = const { RefCell::new((Vec::new(), Vec::new())) };
}
fn reserve_id() -> ControlId {
IDS.with_borrow_mut(|(generations, free)| {
let index = free.pop().unwrap_or_else(|| {
generations.push(0);
generations.len() as u32 - 1
});
ControlId { index, generation: generations[index as usize] }
})
}
fn release_id(id: ControlId) {
IDS.with_borrow_mut(|(generations, free)| {
generations[id.index as usize] += 1;
free.push(id.index);
})
}
pub struct Handle<T>(ControlId, PhantomData<fn() -> T>);
impl<T> Handle<T> {
pub fn id(self) -> ControlId {
self.0
}
}
impl<T> Clone for Handle<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Handle<T> {}
impl<T> Default for Handle<T> {
fn default() -> Self {
Handle(ControlId { index: u32::MAX, generation: 0 }, PhantomData)
}
}
impl<T> From<Handle<T>> for ControlId {
fn from(h: Handle<T>) -> Self {
h.0
}
}
#[derive(Debug)]
pub struct Base {
pub p: ControlProps,
pub measured: Size,
pub rect: Rect,
pub scale: f32,
pub(crate) need_measure: bool,
pub(crate) need_arrange: bool,
pub(crate) last_constraints: (f32, f32),
pub(crate) last_destination: Rect,
pub(crate) content_epoch: u32,
pub(crate) own_epoch: u32,
pub(crate) dirty: Dirty,
pub content_offset: Point,
pub(crate) tracks_viewport: bool,
pub context_index: Option<usize>,
pub viewport_shift: f32,
pub visual_effects: Vec<Box<dyn crate::effects::SkiaEffect>>,
}
impl Default for Base {
fn default() -> Self {
Self {
p: ControlProps::default(),
measured: Size::default(),
rect: Rect::default(),
scale: 1.0,
need_measure: true,
need_arrange: true,
last_constraints: (f32::NAN, f32::NAN),
last_destination: Rect::default(),
content_epoch: 0,
own_epoch: 0,
dirty: Dirty::NONE,
content_offset: Point::default(),
tracks_viewport: false,
context_index: None,
viewport_shift: 0.0,
visual_effects: Vec::new(),
}
}
}
pub(crate) type Observer = Box<dyn FnMut(Raw<'_>, &dyn Any)>;
pub(crate) type Tapped = Box<dyn FnMut(Raw<'_>, &mut dyn Any, &mut Cx<'_>)>;
#[derive(Default)]
pub(crate) struct Handlers {
pub observers: Vec<Observer>,
pub tapped: Option<Tapped>,
pub input: Option<Box<crate::ui::InputHandlers>>,
}
pub struct Node {
pub(crate) id: ControlId,
pub(crate) parent: Option<ControlId>,
pub(crate) children: Vec<ControlId>,
pub(crate) part: bool,
pub base: Base,
pub(crate) kind: Option<Box<dyn Control>>,
pub(crate) handlers: Handlers,
}
#[derive(Default)]
pub(crate) struct RenderSlot {
pub cache: Option<CachedObject>,
pub cache_epoch: u32,
pub matrix: Option<Matrix>,
pub records: u32,
pub effects: Option<(u32, f32, Thickness)>,
pub paints: Option<Box<PaintCache>>,
pub reads_below: bool,
pub below: Option<u64>,
}
pub(crate) struct Raw<'a> {
pub id: ControlId,
pub control: &'a mut dyn Control,
pub base: &'a mut Base,
pub queue: &'a mut Vec<ControlId>,
}
impl<'a> Raw<'a> {
pub(crate) fn typed<T: Control>(self) -> Mut<'a, T> {
let control = part_mut::<T>(self.control).expect("handler bound to its own control type");
Mut { id: self.id, control, base: self.base, queue: self.queue }
}
pub(crate) fn any(self) -> Mut<'a, dyn Control> {
Mut { id: self.id, control: self.control, base: self.base, queue: self.queue }
}
}
pub struct Mut<'a, T: ?Sized> {
id: ControlId,
control: &'a mut T,
base: &'a mut Base,
queue: &'a mut Vec<ControlId>,
}
impl<T: ?Sized> Mut<'_, T> {
pub fn id(&self) -> ControlId {
self.id
}
pub fn base(&self) -> &Base {
self.base
}
pub(crate) fn base_mut(&mut self) -> &mut Base {
self.base
}
pub fn control_mut(&mut self) -> &mut T {
self.control
}
pub fn mark(&mut self, dirty: Dirty) {
if dirty.is_empty() {
return;
}
if self.base.dirty.is_empty() {
self.queue.push(self.id);
}
self.base.dirty |= dirty;
}
}
impl<T: ?Sized> Deref for Mut<'_, T> {
type Target = T;
fn deref(&self) -> &T {
self.control
}
}
macro_rules! base_props {
($($(#[$doc:meta])* $name:ident / $setter:ident : $ty:ty = $default:expr, $dirty:ident;)*) => {
#[derive(Clone, Debug, PartialEq)]
pub struct ControlProps { $($(#[$doc])* pub $name: $ty,)* }
impl Default for ControlProps {
fn default() -> Self { Self { $($name: $default,)* } }
}
impl<T: Control> Build<T> {
$($(#[$doc])* pub fn $name(mut self, v: impl IntoProp<$ty>) -> Self {
self.base.p.$name = v.into_prop();
self
})*
}
impl<T: ?Sized> Mut<'_, T> {
$(pub fn $setter(&mut self, v: impl IntoProp<$ty>) {
let v = v.into_prop();
if self.base.p.$name != v {
self.base.p.$name = v;
self.mark(Dirty::$dirty);
}
})*
}
};
}
base_props! {
horizontal_options / set_horizontal_options: LayoutOptions = LayoutOptions::Start, MEASURE;
vertical_options / set_vertical_options: LayoutOptions = LayoutOptions::Start, MEASURE;
width_request / set_width_request: f32 = -1.0, MEASURE;
height_request / set_height_request: f32 = -1.0, MEASURE;
minimum_width_request / set_minimum_width_request: f32 = -1.0, MEASURE;
minimum_height_request / set_minimum_height_request: f32 = -1.0, MEASURE;
maximum_width_request / set_maximum_width_request: f32 = -1.0, MEASURE;
maximum_height_request / set_maximum_height_request: f32 = -1.0, MEASURE;
margin / set_margin: Thickness = Thickness::ZERO, MEASURE;
padding / set_padding: Thickness = Thickness::ZERO, MEASURE;
lock_ratio / set_lock_ratio: f32 = 0.0, MEASURE;
horizontal_fill_ratio / set_horizontal_fill_ratio: f32 = 1.0, MEASURE;
vertical_fill_ratio / set_vertical_fill_ratio: f32 = 1.0, MEASURE;
column / set_column: i32 = 0, MEASURE;
row / set_row: i32 = 0, MEASURE;
column_span / set_column_span: i32 = 1, MEASURE;
row_span / set_row_span: i32 = 1, MEASURE;
is_visible / set_is_visible: bool = true, MEASURE;
virtualisation_inflated / set_virtualisation_inflated: f32 = 0.0, MEASURE;
background_color / set_background_color: Option<Color> = None, DRAW;
fill_gradient / set_fill_gradient: Option<Box<SkiaGradient>> = None, DRAW;
use_cache / set_use_cache: CacheType = CacheType::None, DRAW;
is_clipped_to_bounds / set_is_clipped_to_bounds: bool = false, REPAINT;
clip_effects / set_clip_effects: bool = true, REPAINT;
translation_x / set_translation_x: f32 = 0.0, REPAINT;
translation_y / set_translation_y: f32 = 0.0, REPAINT;
rotation / set_rotation: f32 = 0.0, REPAINT;
scale_x / set_scale_x: f32 = 1.0, REPAINT;
scale_y / set_scale_y: f32 = 1.0, REPAINT;
skew_x / set_skew_x: f32 = 0.0, REPAINT;
skew_y / set_skew_y: f32 = 0.0, REPAINT;
anchor_x / set_anchor_x: f32 = 0.5, REPAINT;
anchor_y / set_anchor_y: f32 = 0.5, REPAINT;
opacity / set_opacity: f32 = 1.0, REPAINT;
left / set_left: f32 = 0.0, REPAINT;
top / set_top: f32 = 0.0, REPAINT;
z_index / set_z_index: i32 = 0, REPAINT;
input_transparent / set_input_transparent: bool = false, NONE;
block_gestures_below / set_block_gestures_below: bool = false, NONE;
lock_children_gestures / set_lock_children_gestures: LockTouch = LockTouch::Disabled, NONE;
touch_effect_color / set_touch_effect_color: Color = Color::WHITE, NONE;
animation_tapped / set_animation_tapped: crate::controls::button::SkiaTouchAnimation = crate::controls::button::SkiaTouchAnimation::None, NONE;
animation_tapped_speed / set_animation_tapped_speed: f32 = 0.0, NONE;
tag / set_tag: String = String::new(), NONE;
can_be_focused / set_can_be_focused: bool = false, NONE;
lock_focus / set_lock_focus: bool = false, NONE;
accessibility_role / set_accessibility_role: &'static str = "", NONE;
accessibility_label / set_accessibility_label: String = String::new(), NONE;
accessibility_hint / set_accessibility_hint: String = String::new(), NONE;
accessibility_can_interact / set_accessibility_can_interact: Option<bool> = None, NONE;
accessibility_is_pressed / set_accessibility_is_pressed: Option<bool> = None, NONE;
accessibility_text_selectable / set_accessibility_text_selectable: bool = false, NONE;
accessibility_live / set_accessibility_live: &'static str = "", NONE;
}
impl<T: Control> Build<T> {
pub fn fill(self) -> Self {
self.horizontal_options(LayoutOptions::Fill).vertical_options(LayoutOptions::Fill)
}
pub fn fill_x(self) -> Self {
self.horizontal_options(LayoutOptions::Fill)
}
pub fn fill_y(self) -> Self {
self.vertical_options(LayoutOptions::Fill)
}
pub fn center(self) -> Self {
self.horizontal_options(LayoutOptions::Center).vertical_options(LayoutOptions::Center)
}
pub fn scale(self, v: impl IntoProp<f32>) -> Self {
let v = v.into_prop();
self.scale_x(v).scale_y(v)
}
}
pub struct Build<T: Control> {
id: ControlId,
pub(crate) base: Base,
control: T,
children: Vec<Detached>,
pub(crate) handlers: Handlers,
}
pub struct Detached {
id: ControlId,
base: Base,
control: Box<dyn Control>,
children: Vec<Detached>,
handlers: Handlers,
}
impl<T: Control> From<Build<T>> for Detached {
fn from(b: Build<T>) -> Self {
Detached { id: b.id, base: b.base, control: Box::new(b.control), children: b.children, handlers: b.handlers }
}
}
pub(crate) fn wrong_state<S>() -> ! {
panic!("handler expects app state `{}`", type_name::<S>())
}
impl<T: Control> Build<T> {
pub fn new(control: T) -> Self {
Build { id: reserve_id(), base: Base::default(), control, children: Vec::new(), handlers: Handlers::default() }
}
pub fn id(&self) -> ControlId {
self.id
}
pub fn control_mut(&mut self) -> &mut T {
&mut self.control
}
pub fn assign(self, slot: &mut Handle<T>) -> Self {
*slot = Handle(self.id, PhantomData);
self
}
pub(crate) fn handle(&self) -> Handle<T> {
Handle(self.id, PhantomData)
}
pub fn observe<S: Any>(mut self, mut f: impl FnMut(&mut Mut<'_, T>, &S) + 'static) -> Self {
self.handlers.observers.push(Box::new(move |raw, state| {
let state = state.downcast_ref::<S>().unwrap_or_else(|| wrong_state::<S>());
f(&mut raw.typed(), state)
}));
self
}
pub fn on_tapped<S: Any>(mut self, mut f: impl FnMut(&mut Mut<'_, T>, &mut S, &mut Cx<'_>) + 'static) -> Self {
self.handlers.tapped = Some(Box::new(move |raw, state, cx| {
let state = state.downcast_mut::<S>().unwrap_or_else(|| wrong_state::<S>());
f(&mut raw.typed(), state, cx)
}));
self
}
pub(crate) fn push_child(&mut self, child: impl Into<Detached>) {
self.children.push(child.into());
}
}
pub trait Container: Control {}
impl<T: Container> Build<T> {
pub fn children(mut self, children: impl IntoChildren) -> Self {
children.push_into(&mut self.children);
self
}
}
pub trait IntoChildren {
fn push_into(self, out: &mut Vec<Detached>);
}
impl IntoChildren for Detached {
fn push_into(self, out: &mut Vec<Detached>) {
out.push(self)
}
}
impl<T: Control> IntoChildren for Build<T> {
fn push_into(self, out: &mut Vec<Detached>) {
out.push(self.into())
}
}
impl<C: IntoChildren> IntoChildren for Option<C> {
fn push_into(self, out: &mut Vec<Detached>) {
if let Some(c) = self {
c.push_into(out)
}
}
}
impl<C: IntoChildren> IntoChildren for Vec<C> {
fn push_into(self, out: &mut Vec<Detached>) {
for c in self {
c.push_into(out)
}
}
}
macro_rules! tuple_children {
() => {};
($head:ident $($tail:ident)*) => {
impl<$head: IntoChildren, $($tail: IntoChildren),*> IntoChildren for ($head, $($tail,)*) {
#[allow(non_snake_case)]
fn push_into(self, out: &mut Vec<Detached>) {
let ($head, $($tail,)*) = self;
$head.push_into(out);
$($tail.push_into(out);)*
}
}
tuple_children!($($tail)*);
};
}
tuple_children!(A B C D E F G H I J K L);
#[derive(Default)]
pub struct Tree {
pub(crate) nodes: Vec<Option<Node>>,
pub(crate) render: Vec<RenderSlot>,
pub(crate) root: Option<ControlId>,
pub(crate) queue: Vec<ControlId>,
pub(crate) queue_spare: Vec<ControlId>,
pub(crate) removals: Vec<ControlId>,
pub(crate) drops: Vec<CachedObject>,
pub(crate) needs_frame: bool,
pub(crate) animators: Vec<Animator>,
pub(crate) last_animation: u32,
pub(crate) time_ms: f64,
pub(crate) synthetic_clock: bool,
pub(crate) trackers: Vec<ControlId>,
pub images: crate::images::Images,
pub(crate) assets: crate::assets::Assets,
pub(crate) bakes: crate::bakes::Bakes,
pub(crate) gpu_epoch: u32,
pub(crate) focused: Option<ControlId>,
pub(crate) focus_request: Option<Option<ControlId>>,
pub(crate) gesture: Option<crate::gestures::Gesture>,
pub(crate) gesture_point: Option<skia_safe::Point>,
pub(crate) key_listeners: Vec<ControlId>,
pub(crate) accessibility: Vec<crate::ui::AccessibilityNode>,
pub(crate) accessibility_focused: Option<ControlId>,
pub(crate) clipboard: Option<String>,
pub(crate) paste_requested: bool,
pub(crate) canvas_size: skia_safe::Size,
pub(crate) frame_stats: crate::ui::FrameStats,
pub(crate) open_urls: Vec<String>,
pub(crate) safe_insets: crate::types::Thickness,
pub(crate) mobile_fullscreen: bool,
pub(crate) location_hash: String,
pub(crate) history_ops: Vec<crate::ui::HistoryOp>,
pub(crate) history_enabled: bool,
pub(crate) history_listeners: Vec<ControlId>,
}
impl Tree {
pub fn root(&self) -> Option<ControlId> {
self.root
}
pub(crate) fn node(&self, id: ControlId) -> Option<&Node> {
self.nodes.get(id.index as usize)?.as_ref().filter(|n| n.id == id)
}
pub(crate) fn node_mut(&mut self, id: ControlId) -> Option<&mut Node> {
self.nodes.get_mut(id.index as usize)?.as_mut().filter(|n| n.id == id)
}
pub(crate) fn take(&mut self, id: ControlId) -> Option<Node> {
self.nodes.get_mut(id.index as usize)?.take_if(|n| n.id == id)
}
pub(crate) fn put_back(&mut self, node: Node) {
let index = node.id.index as usize;
self.nodes[index] = Some(node);
}
pub fn mount(&mut self, parent: Option<ControlId>, detached: impl Into<Detached>) -> ControlId {
let d = detached.into();
let index = d.id.index as usize;
if self.nodes.len() <= index {
self.nodes.resize_with(index + 1, || None);
self.render.resize_with(index + 1, RenderSlot::default);
}
if let Some(old) = self.render[index].cache.take() {
self.drops.push(old);
}
self.render[index] = RenderSlot::default();
let mut base = d.base;
base.dirty = Dirty::APPLY;
self.queue.push(d.id);
if d.handlers.input.as_ref().is_some_and(|h| h.listen_keys) {
self.key_listeners.push(d.id);
}
if d.handlers.input.as_ref().is_some_and(|h| h.listen_history) {
self.history_listeners.push(d.id);
}
self.nodes[index] = Some(Node {
id: d.id,
parent,
children: Vec::with_capacity(d.children.len()),
part: false,
base,
kind: Some(d.control),
handlers: d.handlers,
});
match parent.and_then(|p| self.node_mut(p)) {
Some(p) => p.children.push(d.id),
None => self.root = Some(d.id),
}
for child in d.children {
self.mount(Some(d.id), child);
}
self.needs_frame = true;
d.id
}
pub(crate) fn remove_now(&mut self, id: ControlId) {
let Some(node) = self.take(id) else { return };
release_id(id);
if let Some(cache) = self.render[id.index as usize].cache.take() {
self.drops.push(cache);
}
self.render[id.index as usize] = RenderSlot::default();
if node.base.tracks_viewport {
self.trackers.retain(|t| *t != id);
}
if let Some(p) = node.parent.and_then(|p| self.node_mut(p)) {
p.children.retain(|c| *c != id);
}
if self.root == Some(id) {
self.root = None;
}
for child in node.children {
self.remove_now(child);
}
}
pub fn children(&self, id: impl Into<ControlId>) -> &[ControlId] {
self.node(id.into()).map_or(&[], |n| &n.children)
}
pub fn parent(&self, id: impl Into<ControlId>) -> Option<ControlId> {
self.node(id.into())?.parent
}
pub fn base(&self, id: impl Into<ControlId>) -> Option<&Base> {
Some(&self.node(id.into())?.base)
}
pub fn find<T: Control>(&self, id: impl Into<ControlId>) -> Option<&T> {
part(self.node(id.into())?.kind.as_deref()?)
}
pub fn find_mut<T: Control>(&mut self, id: impl Into<ControlId>) -> Option<Mut<'_, T>> {
let id = id.into();
let Tree { nodes, queue, .. } = self;
let node = nodes.get_mut(id.index as usize)?.as_mut().filter(|n| n.id == id)?;
Some(Mut { id, control: part_mut(node.kind.as_deref_mut()?)?, base: &mut node.base, queue })
}
pub fn get_mut<T: Control>(&mut self, handle: Handle<T>) -> Option<Mut<'_, T>> {
self.find_mut(handle)
}
pub fn any_mut(&mut self, id: impl Into<ControlId>) -> Option<Mut<'_, dyn Control>> {
let id = id.into();
let Tree { nodes, queue, .. } = self;
let node = nodes.get_mut(id.index as usize)?.as_mut().filter(|n| n.id == id)?;
Some(Mut { id, control: node.kind.as_deref_mut()?, base: &mut node.base, queue })
}
pub fn cx(&mut self) -> Cx<'_> {
Cx { tree: self }
}
pub fn add_child(&mut self, parent: impl Into<ControlId>, child: impl Into<Detached>) -> ControlId {
Cx { tree: self }.add_child(parent, child)
}
pub fn remove(&mut self, id: impl Into<ControlId>) {
Cx { tree: self }.remove(id)
}
pub fn set_content_offset(&mut self, id: impl Into<ControlId>, offset: Point) {
let id = id.into();
let Some(node) = self.node_mut(id) else { return };
if node.base.content_offset == offset {
return;
}
node.base.content_offset = offset;
self.invalidate(id, Dirty::DRAW);
self.rearrange_trackers(id, None);
}
pub(crate) fn rearrange_trackers(&mut self, ancestor: ControlId, below: Option<ControlId>) {
for i in 0..self.trackers.len() {
let tracker = self.trackers[i];
if !self.is_ancestor(ancestor, tracker) {
continue;
}
let mut current = Some(tracker);
while let Some(node) = current.filter(|c| Some(*c) != below).and_then(|c| self.node_mut(c)) {
node.base.need_arrange = true;
current = node.parent;
}
}
}
pub(crate) fn is_ancestor(&self, ancestor: ControlId, id: ControlId) -> bool {
let mut current = self.parent(id);
while let Some(c) = current {
if c == ancestor {
return true;
}
current = self.parent(c);
}
false
}
pub fn visible_rect(&self, id: impl Into<ControlId>) -> Rect {
let id = id.into();
let Some(node) = self.node(id) else { return Rect::default() };
let mut visible = node.base.rect;
let mut shift = Point::default();
let mut current = node.parent;
while let Some(ancestor) = current.and_then(|c| self.node(c)) {
shift += ancestor.base.content_offset;
if !visible.intersect(ancestor.base.rect.with_offset(-shift)) {
return Rect::default();
}
current = ancestor.parent;
}
visible
}
pub fn invalidate(&mut self, id: impl Into<ControlId>, dirty: Dirty) {
let id = id.into();
let Tree { nodes, queue, .. } = self;
let Some(node) = nodes.get_mut(id.index as usize).and_then(|n| n.as_mut()).filter(|n| n.id == id) else { return };
if dirty.is_empty() {
return;
}
if node.base.dirty.is_empty() {
queue.push(id);
}
node.base.dirty |= dirty;
}
pub(crate) fn run_observers(&mut self, state: &dyn Any) {
let Tree { nodes, queue, .. } = self;
for node in nodes.iter_mut().flatten() {
observe(node, queue, state);
}
}
pub(crate) fn run_observers_under(&mut self, id: ControlId, state: &dyn Any) {
let Tree { nodes, queue, .. } = self;
let mut stack = vec![id];
while let Some(id) = stack.pop() {
let Some(node) = nodes.get_mut(id.index as usize).and_then(|n| n.as_mut()).filter(|n| n.id == id) else {
continue;
};
stack.extend_from_slice(&node.children);
observe(node, queue, state);
}
}
}
fn observe(node: &mut Node, queue: &mut Vec<ControlId>, state: &dyn Any) {
let Some(control) = node.kind.as_deref_mut() else { return };
for observer in &mut node.handlers.observers {
observer(Raw { id: node.id, control: &mut *control, base: &mut node.base, queue }, state);
}
}
pub struct Cx<'a> {
pub(crate) tree: &'a mut Tree,
}
impl Cx<'_> {
pub fn get_mut<T: Control>(&mut self, handle: Handle<T>) -> Option<Mut<'_, T>> {
self.tree.get_mut(handle)
}
pub fn find<T: Control>(&self, id: impl Into<ControlId>) -> Option<&T> {
self.tree.find(id)
}
pub fn any_mut(&mut self, id: impl Into<ControlId>) -> Option<Mut<'_, dyn Control>> {
self.tree.any_mut(id)
}
pub fn base(&self, id: impl Into<ControlId>) -> Option<&Base> {
self.tree.base(id)
}
pub fn add_child(&mut self, parent: impl Into<ControlId>, child: impl Into<Detached>) -> ControlId {
let parent = parent.into();
let id = self.tree.mount(Some(parent), child);
self.tree.invalidate(parent, Dirty::MEASURE);
id
}
pub fn set_content_offset(&mut self, id: impl Into<ControlId>, offset: Point) {
self.tree.set_content_offset(id, offset)
}
pub fn visible_rect(&self, id: impl Into<ControlId>) -> Rect {
self.tree.visible_rect(id)
}
pub fn remove(&mut self, id: impl Into<ControlId>) {
self.tree.removals.push(id.into());
self.tree.needs_frame = true;
}
pub fn insert_child(&mut self, parent: impl Into<ControlId>, index: usize, child: impl Into<Detached>) -> ControlId {
let parent = parent.into();
let id = self.add_child(parent, child);
let mut order = self.app_children(parent);
order.retain(|c| *c != id);
order.insert(index.min(order.len()), id);
self.reorder(parent, &order);
id
}
pub fn replace_child(&mut self, old: impl Into<ControlId>, child: impl Into<Detached>) -> Option<ControlId> {
let old = old.into();
let parent = self.tree.parent(old)?;
let id = self.add_child(parent, child);
let mut order = self.app_children(parent);
order.retain(|c| *c != id);
let at = order.iter().position(|c| *c == old)?;
order[at] = id;
order.push(old);
self.reorder(parent, &order);
self.remove(old);
Some(id)
}
pub fn move_child(&mut self, parent: impl Into<ControlId>, from: usize, to: usize) {
let parent = parent.into();
let mut order = self.app_children(parent);
if from >= order.len() {
return;
}
let id = order.remove(from);
order.insert(to.min(order.len()), id);
self.reorder(parent, &order);
}
pub fn clear_children(&mut self, parent: impl Into<ControlId>) {
for child in self.app_children(parent.into()) {
self.remove(child);
}
}
pub fn app_children(&self, parent: impl Into<ControlId>) -> Vec<ControlId> {
let tree = &*self.tree;
let own = |c: &ControlId| tree.node(*c).is_some_and(|n| n.part);
tree.children(parent).iter().copied().filter(|c| !own(c) && !tree.removals.contains(c)).collect()
}
fn reorder(&mut self, parent: ControlId, order: &[ControlId]) {
let Some(node) = self.tree.node_mut(parent) else { return };
let mut next = order.iter();
for slot in node.children.iter_mut() {
if order.contains(slot)
&& let Some(id) = next.next()
{
*slot = *id;
}
}
self.tree.invalidate(parent, Dirty::MEASURE);
}
pub fn invalidate(&mut self, id: impl Into<ControlId>, dirty: Dirty) {
self.tree.invalidate(id, dirty)
}
}