use crate::effects::Effects;
use crate::layout::NodeId;
use crate::style::{Computed, Constraints, ScrollbarStyle};
use crate::{AccessibleInfo, Context};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub struct Node(pub NodeId);
impl std::ops::Deref for Node {
type Target = NodeId;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Node {
pub fn id(&self) -> u64 {
self.0.index as u64
}
pub fn raw(&self) -> NodeId {
self.0
}
pub fn from_raw(raw: NodeId) -> Self {
Node(raw)
}
#[inline(always)]
pub fn to_accesskit_id(&self) -> accesskit::NodeId {
crate::accessibility::to_accesskit_id(*self)
}
#[inline(always)]
pub fn from_accesskit_id(id: accesskit::NodeId) -> Self {
crate::accessibility::from_accesskit_id(id)
}
pub fn get_invalid() -> Node {
Node(NodeId::INVALID)
}
pub fn prepend(&self, ctxt: &mut Context, child: Node) -> bool {
ctxt.layout.prepend(self.0, child.0)
}
pub fn set_dirty(&self, ctxt: &mut Context) {
ctxt.layout.set_dirty(self.0);
}
pub fn remove(&self, ctxt: &mut Context) -> bool {
ctxt.layout.remove(self.0)
}
pub fn put_after(&self, ctxt: &mut Context, sibling: Node) -> bool {
ctxt.layout.put_after(sibling.0, self.0)
}
pub fn put_before(&self, ctxt: &mut Context, sibling: Node) -> bool {
ctxt.layout.put_before(sibling.0, self.0)
}
pub fn is_valid(&self) -> bool {
self.0.is_valid()
}
pub fn parent(&self, ctxt: &Context) -> Option<Node> {
ctxt.layout.parent(self.0).map(Node)
}
pub fn first_child(&self, ctxt: &Context) -> Option<Node> {
ctxt.layout.first_child(self.0).map(Node)
}
pub fn last_child(&self, ctxt: &Context) -> Option<Node> {
ctxt.layout.last_child(self.0).map(Node)
}
pub fn next_sibling(&self, ctxt: &Context) -> Option<Node> {
ctxt.layout.next_sibling(self.0).map(Node)
}
pub fn prev_sibling(&self, ctxt: &Context) -> Option<Node> {
ctxt.layout.prev_sibling(self.0).map(Node)
}
pub fn is_descendant_of(&self, ctxt: &Context, ancestor: Node) -> bool {
ctxt.layout.is_descendant_of(self.0, ancestor.0)
}
pub fn children_iter<'a>(&self, ctxt: &'a Context) -> impl Iterator<Item = Node> + 'a {
let mut curr = self.first_child(ctxt);
std::iter::from_fn(move || {
let next = curr?;
curr = next.next_sibling(ctxt);
Some(next)
})
}
pub fn set_accessible(&self, ctxt: &mut Context, info: AccessibleInfo) {
ctxt.set_accessible(*self, info);
}
pub fn get_accessible<'a>(&self, ctxt: &'a Context) -> Option<&'a AccessibleInfo> {
ctxt.get_accessible(*self)
}
pub fn remove_accessible(&self, ctxt: &mut Context) -> Option<AccessibleInfo> {
ctxt.remove_accessible(*self)
}
pub fn append(&self, ctxt: &mut Context, child: Node) -> bool {
ctxt.layout.append(self.0, child.0)
}
pub fn set_constraints(&self, ctxt: &mut Context, constraints: Constraints) {
ctxt.layout.set_constraints(self.0, constraints);
}
pub fn get_constraints(&self, ctxt: &Context) -> Option<Constraints> {
ctxt.layout.get_constraints(self.0).copied()
}
pub fn update_constraints<F>(&self, ctxt: &mut Context, update_fn: F)
where
F: FnOnce(&mut Constraints),
{
let existing = self.get_constraints(ctxt);
let old_constraints = existing.unwrap_or_default();
let mut new_constraints = old_constraints.clone();
update_fn(&mut new_constraints);
if existing.is_none() || old_constraints != new_constraints {
self.set_constraints(ctxt, new_constraints);
}
}
pub fn with_constraints(self, ctxt: &mut Context, constraints: Constraints) -> Self {
self.set_constraints(ctxt, constraints);
self
}
pub fn set_effects(&self, ctxt: &mut Context, effects: Effects) {
ctxt.effects.insert(*self, effects);
ctxt.dirty_effects.insert(*self);
}
pub fn get_effects(&self, ctxt: &Context) -> Option<Effects> {
ctxt.effects.get(self).cloned()
}
pub fn update_effects<F>(&self, ctxt: &mut Context, update_fn: F)
where
F: FnOnce(&mut Effects),
{
if let Some(effects) = ctxt.effects.get_mut(self) {
update_fn(effects);
ctxt.dirty_effects.insert(*self);
} else {
let mut effects = Effects::default();
update_fn(&mut effects);
ctxt.effects.insert(*self, effects);
ctxt.dirty_effects.insert(*self);
}
}
pub fn with_effects(self, ctxt: &mut Context, effects: Effects) -> Self {
self.set_effects(ctxt, effects);
self
}
pub fn set_scrollbar_style(&self, ctxt: &mut Context, style: ScrollbarStyle) {
ctxt.scrollbars.insert(*self, style);
}
pub fn get_scrollbar_style(&self, ctxt: &Context) -> Option<ScrollbarStyle> {
ctxt.scrollbars.get(self).cloned()
}
pub fn update_scrollbar_style<F>(&self, ctxt: &mut Context, update_fn: F)
where
F: FnOnce(&mut ScrollbarStyle),
{
if let Some(sb) = ctxt.scrollbars.get_mut(self) {
update_fn(sb);
} else {
let mut sb = ScrollbarStyle::default();
update_fn(&mut sb);
ctxt.scrollbars.insert(*self, sb);
}
}
pub fn with_scrollbar_style(self, ctxt: &mut Context, style: ScrollbarStyle) -> Self {
self.set_scrollbar_style(ctxt, style);
self
}
pub fn get_computed(&self, ctxt: &Context) -> Option<Computed> {
ctxt.layout.get_computed(self.0).copied()
}
pub fn children(&self, ctxt: &Context) -> Vec<Node> {
ctxt.layout.children(self.0).into_iter().map(Node).collect()
}
pub fn compute_content_height(&self, ctxt: &Context) -> f32 {
let computed = match self.get_computed(ctxt) {
Some(c) => c,
None => return 0.0,
};
computed.content_h.max(computed.h)
}
pub fn set_text(&self, ctxt: &mut Context, text: &str) {
let existing_userdata = ctxt
.layout
.texts
.get_mut(self.0)
.and_then(|t| t.userdata.take());
ctxt.layout
.set_text(self.0, text.to_string(), existing_userdata);
}
pub fn set_text_with_userdata<T: 'static>(&self, ctxt: &mut Context, text: &str, userdata: T) {
ctxt.layout
.set_text(self.0, text.to_string(), Some(Box::new(userdata)));
}
pub fn unset_text(&self, ctxt: &mut Context) {
ctxt.layout.unset_text(self.0);
}
pub fn get_text<'a>(&self, ctxt: &'a Context) -> Option<&'a str> {
ctxt.layout.get_text(self.0)
}
pub fn get_text_userdata<'a, T: 'static>(&self, ctxt: &'a Context) -> Option<&'a T> {
ctxt.layout.get_text_userdata::<T>(self.0)
}
pub fn get_text_userdata_mut<'a, T: 'static>(
&self,
ctxt: &'a mut Context,
) -> Option<&'a mut T> {
ctxt.layout.get_text_userdata_mut::<T>(self.0)
}
pub fn get_text_range_geometry(
&self,
ctx: &Context,
range: std::ops::Range<usize>,
) -> Vec<[f32; 4]> {
let Some(text) = self.get_text(ctx) else {
return Vec::new();
};
let default_style = crate::TextStyle::default();
let (style, spans) =
if let Some(info) = self.get_text_userdata::<crate::TextRenderInfo>(ctx) {
(&info.style, &info.spans[..])
} else if let Some(style) = self.get_text_userdata::<crate::TextStyle>(ctx) {
(style, &[][..])
} else {
(&default_style, &[][..])
};
let computed = self.get_computed(ctx).unwrap_or_default();
let constraints = self.get_constraints(ctx).unwrap_or_default();
let inner_w = (computed.w - constraints.padding.left - constraints.padding.right).max(0.0);
let inner_h = (computed.h - constraints.padding.top - constraints.padding.bottom).max(0.0);
crate::text::get_range_geometry(
text,
style,
inner_w,
inner_h,
range,
&ctx.text_context,
spans,
)
}
}