#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
#![warn(unused_extern_crates)]
#![warn(missing_docs)]
use zng_wgt::prelude::*;
#[property(LAYOUT, default((0, 0)))]
pub fn offset(child: impl UiNode, offset: impl IntoVar<Vector>) -> impl UiNode {
let offset = offset.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&offset);
}
UiNodeOp::Layout { wl, final_size } => {
let size = child.layout(wl);
let offset = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(LAYOUT.constraints().fill_size().max(size)), || {
offset.layout()
});
wl.translate(offset);
*final_size = size;
}
_ => {}
})
}
#[property(LAYOUT, default(0))]
pub fn x(child: impl UiNode, x: impl IntoVar<Length>) -> impl UiNode {
let x = x.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&x);
}
UiNodeOp::Layout { wl, final_size } => {
let size = child.layout(wl);
let x = with_fill_metrics(LAYOUT.constraints(), |_| x.layout_x());
wl.translate(PxVector::new(x, Px(0)));
*final_size = size;
}
_ => {}
})
}
#[property(LAYOUT, default(0))]
pub fn y(child: impl UiNode, y: impl IntoVar<Length>) -> impl UiNode {
let y = y.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&y);
}
UiNodeOp::Layout { wl, final_size } => {
let size = child.layout(wl);
let y = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(LAYOUT.constraints().fill_size().max(size)), || {
y.layout_y()
});
wl.translate(PxVector::new(Px(0), y));
*final_size = size;
}
_ => {}
})
}
#[property(SIZE-2, default((0, 0)))]
pub fn min_size(child: impl UiNode, min_size: impl IntoVar<Size>) -> impl UiNode {
let min_size = min_size.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&min_size);
}
UiNodeOp::Measure { wm, desired_size } => {
let c = LAYOUT.constraints();
let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_size.layout());
let size = LAYOUT.with_constraints(c.with_min_size(min), || wm.measure_block(child));
*desired_size = size.max(min);
}
UiNodeOp::Layout { wl, final_size } => {
let c = LAYOUT.constraints();
let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_size.layout());
let size = LAYOUT.with_constraints(c.with_min_size(min), || child.layout(wl));
*final_size = size.max(min);
}
_ => {}
})
}
#[property(SIZE-2, default(0))]
pub fn min_width(child: impl UiNode, min_width: impl IntoVar<Length>) -> impl UiNode {
let min_width = min_width.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&min_width);
}
UiNodeOp::Measure { wm, desired_size } => {
let c = LAYOUT.constraints();
let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_width.layout_x());
let mut size = LAYOUT.with_constraints(c.with_min_x(min), || wm.measure_block(child));
size.width = size.width.max(min);
*desired_size = size;
}
UiNodeOp::Layout { wl, final_size } => {
let c = LAYOUT.constraints();
let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_width.layout_x());
let mut size = LAYOUT.with_constraints(c.with_min_x(min), || child.layout(wl));
size.width = size.width.max(min);
*final_size = size;
}
_ => {}
})
}
#[property(SIZE-2, default(0))]
pub fn min_height(child: impl UiNode, min_height: impl IntoVar<Length>) -> impl UiNode {
let min_height = min_height.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&min_height);
}
UiNodeOp::Measure { wm, desired_size } => {
let c = LAYOUT.constraints();
let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_height.layout_y());
let mut size = LAYOUT.with_constraints(c.with_min_y(min), || wm.measure_block(child));
size.height = size.height.max(min);
*desired_size = size;
}
UiNodeOp::Layout { wl, final_size } => {
let c = LAYOUT.constraints();
let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_height.layout_y());
let mut size = LAYOUT.with_constraints(c.with_min_y(min), || child.layout(wl));
size.height = size.height.max(min);
*final_size = size;
}
_ => {}
})
}
#[property(SIZE-1, default(PxSize::splat(Px::MAX)))]
pub fn max_size(child: impl UiNode, max_size: impl IntoVar<Size>) -> impl UiNode {
let max_size = max_size.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&max_size);
}
UiNodeOp::Measure { wm, desired_size } => {
let parent_constraints = LAYOUT.constraints();
let max = with_fill_metrics(parent_constraints, |d| max_size.layout_dft(d));
let size = LAYOUT.with_constraints(parent_constraints.with_max_size(max), || wm.measure_block(child));
*desired_size = size.min(max);
*desired_size = Align::TOP_LEFT.measure(*desired_size, parent_constraints);
}
UiNodeOp::Layout { wl, final_size } => {
let parent_constraints = LAYOUT.constraints();
let max = with_fill_metrics(parent_constraints, |d| max_size.layout_dft(d));
let size = LAYOUT.with_constraints(parent_constraints.with_max_size(max), || child.layout(wl));
*final_size = Align::TOP_LEFT.measure(size.min(max), parent_constraints);
}
_ => {}
})
}
#[property(SIZE-1, default(Px::MAX))]
pub fn max_width(child: impl UiNode, max_width: impl IntoVar<Length>) -> impl UiNode {
let max_width = max_width.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&max_width);
}
UiNodeOp::Measure { wm, desired_size } => {
let parent_constraints = LAYOUT.constraints();
let max = with_fill_metrics(parent_constraints, |d| max_width.layout_dft_x(d.width));
let mut size = LAYOUT.with_constraints(parent_constraints.with_max_x(max), || wm.measure_block(child));
size.width = size.width.min(max);
*desired_size = size;
desired_size.width = Align::TOP_LEFT.measure_x(desired_size.width, parent_constraints.x);
}
UiNodeOp::Layout { wl, final_size } => {
let parent_constraints = LAYOUT.constraints();
let max = with_fill_metrics(parent_constraints, |d| max_width.layout_dft_x(d.width));
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_max_x(max), || child.layout(wl));
size.width = size.width.min(max);
*final_size = size;
final_size.width = Align::TOP_LEFT.measure_x(final_size.width, parent_constraints.x);
}
_ => {}
})
}
#[property(SIZE-1, default(Px::MAX))]
pub fn max_height(child: impl UiNode, max_height: impl IntoVar<Length>) -> impl UiNode {
let max_height = max_height.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&max_height);
}
UiNodeOp::Measure { wm, desired_size } => {
let parent_constraints = LAYOUT.constraints();
let max = with_fill_metrics(parent_constraints, |d| max_height.layout_dft_y(d.height));
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_max_y(max), || wm.measure_block(child));
size.height = size.height.min(max);
*desired_size = size;
desired_size.height = Align::TOP_LEFT.measure_y(desired_size.height, parent_constraints.y);
}
UiNodeOp::Layout { wl, final_size } => {
let parent_constraints = LAYOUT.constraints();
let max = with_fill_metrics(parent_constraints, |d| max_height.layout_dft_y(d.height));
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_max_y(max), || child.layout(wl));
size.height = size.height.min(max);
*final_size = size;
final_size.height = Align::TOP_LEFT.measure_y(final_size.height, parent_constraints.y);
}
_ => {}
})
}
#[property(SIZE, default(Size::default()))]
pub fn size(child: impl UiNode, size: impl IntoVar<Size>) -> impl UiNode {
let size = size.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&size);
child.init();
size.with(|l| WIDGET_SIZE.set(l));
}
UiNodeOp::Update { updates } => {
child.update(updates);
size.with_new(|s| {
WIDGET_SIZE.set(s);
WIDGET.layout();
});
}
UiNodeOp::Measure { wm, desired_size } => {
child.delegated();
wm.measure_block(&mut NilUiNode); let parent_constraints = LAYOUT.constraints();
*desired_size = with_fill_metrics(parent_constraints.with_new_min(Px(0), Px(0)), |d| size.layout_dft(d));
*desired_size = Align::TOP_LEFT.measure(*desired_size, parent_constraints);
}
UiNodeOp::Layout { wl, final_size } => {
let parent_constraints = LAYOUT.constraints();
let constraints = parent_constraints.with_new_min(Px(0), Px(0));
let size = with_fill_metrics(constraints, |d| size.layout_dft(d));
let size = constraints.clamp_size(size);
LAYOUT.with_constraints(PxConstraints2d::new_exact_size(size), || child.layout(wl));
*final_size = Align::TOP_LEFT.measure(size, parent_constraints);
}
_ => {}
})
}
#[property(SIZE, default(Length::Default))]
pub fn width(child: impl UiNode, width: impl IntoVar<Length>) -> impl UiNode {
let width = width.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&width);
child.init();
width.with(|s| WIDGET_SIZE.set_width(s));
}
UiNodeOp::Update { updates } => {
child.update(updates);
width.with_new(|w| {
WIDGET_SIZE.set_width(w);
WIDGET.layout();
});
}
UiNodeOp::Measure { wm, desired_size } => {
let parent_constraints = LAYOUT.constraints();
let constraints = parent_constraints.with_new_min_x(Px(0));
let width = with_fill_metrics(constraints, |d| width.layout_dft_x(d.width));
let width = constraints.x.clamp(width);
*desired_size = LAYOUT.with_constraints(constraints.with_exact_x(width), || wm.measure_block(child));
desired_size.width = Align::TOP_LEFT.measure_x(width, parent_constraints.x);
}
UiNodeOp::Layout { wl, final_size } => {
let parent_constraints = LAYOUT.constraints();
let constraints = parent_constraints.with_new_min_x(Px(0));
let width = with_fill_metrics(constraints, |d| width.layout_dft_x(d.width));
let width = constraints.x.clamp(width);
*final_size = LAYOUT.with_constraints(constraints.with_exact_x(width), || child.layout(wl));
final_size.width = Align::TOP_LEFT.measure_x(width, parent_constraints.x);
}
_ => {}
})
}
#[property(SIZE, default(Length::Default))]
pub fn height(child: impl UiNode, height: impl IntoVar<Length>) -> impl UiNode {
let height = height.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&height);
child.init();
height.with(|s| WIDGET_SIZE.set_height(s));
}
UiNodeOp::Update { updates } => {
child.update(updates);
height.with_new(|h| {
WIDGET_SIZE.set_height(h);
WIDGET.layout();
});
}
UiNodeOp::Measure { wm, desired_size } => {
let parent_constraints = LAYOUT.constraints();
let constraints = parent_constraints.with_new_min_y(Px(0));
let height = with_fill_metrics(constraints, |d| height.layout_dft_x(d.height));
let height = constraints.x.clamp(height);
*desired_size = LAYOUT.with_constraints(constraints.with_exact_y(height), || wm.measure_block(child));
desired_size.height = Align::TOP_LEFT.measure_y(height, parent_constraints.y);
}
UiNodeOp::Layout { wl, final_size } => {
let parent_constraints = LAYOUT.constraints();
let constraints = parent_constraints.with_new_min_y(Px(0));
let height = with_fill_metrics(constraints, |d| height.layout_dft_y(d.height));
let height = constraints.y.clamp(height);
*final_size = LAYOUT.with_constraints(constraints.with_exact_y(height), || child.layout(wl));
final_size.height = Align::TOP_LEFT.measure_y(height, parent_constraints.y);
}
_ => {}
})
}
#[property(SIZE, default(Size::default()))]
pub fn force_size(child: impl UiNode, size: impl IntoVar<Size>) -> impl UiNode {
let size = size.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&size);
child.init();
size.with(|l| WIDGET_SIZE.set(l));
}
UiNodeOp::Update { updates } => {
child.update(updates);
size.with_new(|s| {
WIDGET_SIZE.set(s);
WIDGET.layout();
});
}
UiNodeOp::Measure { wm, desired_size } => {
child.delegated();
let c = LAYOUT.constraints().with_new_min(Px(0), Px(0)).with_fill(false, false);
let size = with_fill_metrics(c, |d| size.layout_dft(d));
wm.measure_block(&mut NilUiNode);
*desired_size = Align::TOP_LEFT.measure(size, c);
}
UiNodeOp::Layout { wl, final_size } => {
let c = LAYOUT.constraints().with_new_min(Px(0), Px(0)).with_fill(false, false);
let size = with_fill_metrics(c, |d| size.layout_dft(d));
LAYOUT.with_constraints(PxConstraints2d::new_exact_size(size), || child.layout(wl));
*final_size = Align::TOP_LEFT.measure(size, c);
}
_ => {}
})
}
#[property(SIZE, default(Length::Default))]
pub fn force_width(child: impl UiNode, width: impl IntoVar<Length>) -> impl UiNode {
let width = width.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&width);
child.init();
width.with(|s| WIDGET_SIZE.set_width(s));
}
UiNodeOp::Update { updates } => {
child.update(updates);
width.with_new(|w| {
WIDGET_SIZE.set_width(w);
WIDGET.layout();
});
}
UiNodeOp::Measure { wm, desired_size } => {
let c = LAYOUT.constraints().with_new_min_x(Px(0)).with_fill_x(false);
let width = with_fill_metrics(c, |d| width.layout_dft_x(d.width));
let mut size = LAYOUT.with_constraints(c.with_unbounded_x().with_exact_x(width), || wm.measure_block(child));
size.width = Align::TOP_LEFT.measure_x(width, c.x);
*desired_size = size;
}
UiNodeOp::Layout { wl, final_size } => {
let c = LAYOUT.constraints().with_new_min_x(Px(0)).with_fill_x(false);
let width = with_fill_metrics(c, |d| width.layout_dft_x(d.width));
let mut size = LAYOUT.with_constraints(c.with_unbounded_x().with_exact_x(width), || child.layout(wl));
size.width = Align::TOP_LEFT.measure_x(width, c.x);
*final_size = size;
}
_ => {}
})
}
#[property(SIZE, default(Length::Default))]
pub fn force_height(child: impl UiNode, height: impl IntoVar<Length>) -> impl UiNode {
let height = height.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&height);
child.init();
height.with(|s| WIDGET_SIZE.set_height(s));
}
UiNodeOp::Update { updates } => {
child.update(updates);
height.with_new(|h| {
WIDGET_SIZE.set_height(h);
WIDGET.layout();
});
}
UiNodeOp::Measure { wm, desired_size } => {
let c = LAYOUT.constraints().with_new_min_y(Px(0)).with_fill_y(false);
let height = with_fill_metrics(c, |d| height.layout_dft_y(d.height));
let mut size = LAYOUT.with_constraints(c.with_unbounded_y().with_exact_y(height), || wm.measure_block(child));
size.height = Align::TOP_LEFT.measure_y(height, c.y);
*desired_size = size;
}
UiNodeOp::Layout { wl, final_size } => {
let c = LAYOUT.constraints().with_new_min_y(Px(0)).with_fill_y(false);
let height = with_fill_metrics(c, |d| height.layout_dft_y(d.height));
let mut size = LAYOUT.with_constraints(c.with_unbounded_y().with_exact_y(height), || child.layout(wl));
size.height = Align::TOP_LEFT.measure_y(height, c.y);
*final_size = size;
}
_ => {}
})
}
fn with_fill_metrics<R>(c: PxConstraints2d, f: impl FnOnce(PxSize) -> R) -> R {
let dft = c.fill_size();
LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || f(dft))
}
#[property(BORDER, default(Length::Default))]
pub fn baseline(child: impl UiNode, baseline: impl IntoVar<Length>) -> impl UiNode {
let baseline = baseline.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&baseline);
}
UiNodeOp::Layout { wl, final_size } => {
let size = child.layout(wl);
let bounds = WIDGET.bounds();
let inner_size = bounds.inner_size();
let default = bounds.baseline();
let baseline = LAYOUT.with_constraints(LAYOUT.constraints().with_max_size(inner_size).with_fill(true, true), || {
baseline.layout_dft_y(default)
});
wl.set_baseline(baseline);
*final_size = size;
}
_ => {}
})
}
#[property(SIZE, default(false))]
pub fn sticky_width(child: impl UiNode, sticky: impl IntoVar<bool>) -> impl UiNode {
let sticky = sticky.into_var();
let mut sticky_after_layout = false;
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&sticky);
}
UiNodeOp::Deinit => {
sticky_after_layout = false;
}
UiNodeOp::Update { .. } => {
if sticky.is_new() {
sticky_after_layout = false;
}
}
UiNodeOp::Measure { wm, desired_size } => {
if sticky_after_layout && sticky.get() {
let min = WIDGET.bounds().inner_size().width;
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_x(min), || wm.measure_block(child));
size.width = size.width.max(min);
*desired_size = size;
}
}
UiNodeOp::Layout { wl, final_size } => {
let sticky = sticky.get();
if sticky_after_layout && sticky {
let min = WIDGET.bounds().inner_size().width;
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_x(min), || child.layout(wl));
size.width = size.width.max(min);
*final_size = size;
}
sticky_after_layout = sticky;
}
_ => {}
})
}
#[property(SIZE, default(false))]
pub fn sticky_height(child: impl UiNode, sticky: impl IntoVar<bool>) -> impl UiNode {
let sticky = sticky.into_var();
let mut sticky_after_layout = false;
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&sticky);
}
UiNodeOp::Deinit => {
sticky_after_layout = false;
}
UiNodeOp::Update { .. } => {
if sticky.is_new() {
sticky_after_layout = false;
}
}
UiNodeOp::Measure { wm, desired_size } => {
if sticky_after_layout && sticky.get() {
let min = WIDGET.bounds().inner_size().height;
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_y(min), || wm.measure_block(child));
size.height = size.height.max(min);
*desired_size = size;
}
}
UiNodeOp::Layout { wl, final_size } => {
let sticky = sticky.get();
if sticky_after_layout && sticky {
let min = WIDGET.bounds().inner_size().height;
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_y(min), || child.layout(wl));
size.height = size.height.max(min);
*final_size = size;
}
sticky_after_layout = sticky;
}
_ => {}
})
}
#[property(SIZE, default(false))]
pub fn sticky_size(child: impl UiNode, sticky: impl IntoVar<bool>) -> impl UiNode {
let sticky = sticky.into_var();
let mut sticky_after_layout = false;
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var(&sticky);
}
UiNodeOp::Deinit => {
sticky_after_layout = false;
}
UiNodeOp::Update { .. } => {
if sticky.is_new() {
sticky_after_layout = false;
}
}
UiNodeOp::Measure { wm, desired_size } => {
if sticky_after_layout && sticky.get() {
let min = WIDGET.bounds().inner_size();
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_size(min), || wm.measure_block(child));
size = size.max(min);
*desired_size = size;
}
}
UiNodeOp::Layout { wl, final_size } => {
let sticky = sticky.get();
if sticky_after_layout && sticky {
let min = WIDGET.bounds().inner_size();
let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_size(min), || child.layout(wl));
size = size.max(min);
*final_size = size;
}
sticky_after_layout = sticky;
}
_ => {}
})
}
#[expect(non_camel_case_types)]
pub struct WIDGET_SIZE;
impl WIDGET_SIZE {
pub fn set_width(&self, width: &Length) {
WIDGET.with_state_mut(|mut state| {
let width = width.into();
match state.entry(*WIDGET_SIZE_ID) {
state_map::StateMapEntry::Occupied(mut e) => e.get_mut().width = width,
state_map::StateMapEntry::Vacant(e) => {
e.insert(euclid::size2(width, WidgetLength::Default));
}
}
});
}
pub fn set_height(&self, height: &Length) {
WIDGET.with_state_mut(|mut state| {
let height = height.into();
match state.entry(*WIDGET_SIZE_ID) {
state_map::StateMapEntry::Occupied(mut e) => e.get_mut().height = height,
state_map::StateMapEntry::Vacant(e) => {
e.insert(euclid::size2(WidgetLength::Default, height));
}
}
})
}
pub fn set(&self, size: &Size) {
WIDGET.set_state(*WIDGET_SIZE_ID, euclid::size2((&size.width).into(), (&size.height).into()));
}
pub fn get(&self) -> euclid::Size2D<WidgetLength, ()> {
WIDGET.get_state(*WIDGET_SIZE_ID).unwrap_or_default()
}
pub fn get_wgt(&self, wgt: &mut impl UiNode) -> euclid::Size2D<WidgetLength, ()> {
wgt.with_context(WidgetUpdateMode::Ignore, || self.get()).unwrap_or_default()
}
}
static_id! {
static ref WIDGET_SIZE_ID: StateId<euclid::Size2D<WidgetLength, ()>>;
}
#[property(LAYOUT)]
pub fn actual_size(child: impl UiNode, size: impl IntoVar<DipSize>) -> impl UiNode {
let size = size.into_var();
match_node(child, move |c, op| match op {
UiNodeOp::Render { frame } => {
c.render(frame);
let f = frame.scale_factor();
let s = WIDGET.info().bounds_info().inner_size().to_dip(f);
if size.get() != s {
let _ = size.set(s);
}
}
UiNodeOp::RenderUpdate { update } => {
c.render_update(update);
let info = WIDGET.info();
let f = info.tree().scale_factor();
let s = info.bounds_info().inner_size().to_dip(f);
if size.get() != s {
let _ = size.set(s);
}
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn actual_width(child: impl UiNode, width: impl IntoVar<Dip>) -> impl UiNode {
let width = width.into_var();
match_node(child, move |c, op| match op {
UiNodeOp::Render { frame } => {
c.render(frame);
let f = frame.scale_factor();
let w = WIDGET.info().bounds_info().inner_size().width.to_dip(f);
if width.get() != w {
let _ = width.set(w);
}
}
UiNodeOp::RenderUpdate { update } => {
c.render_update(update);
let info = WIDGET.info();
let f = info.tree().scale_factor();
let w = info.bounds_info().inner_size().width.to_dip(f);
if width.get() != w {
let _ = width.set(w);
}
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn actual_height(child: impl UiNode, height: impl IntoVar<Dip>) -> impl UiNode {
let height = height.into_var();
match_node(child, move |c, op| match op {
UiNodeOp::Render { frame } => {
c.render(frame);
let f = frame.scale_factor();
let h = WIDGET.info().bounds_info().inner_size().height.to_dip(f);
if height.get() != h {
let _ = height.set(h);
}
}
UiNodeOp::RenderUpdate { update } => {
c.render_update(update);
let info = WIDGET.info();
let f = info.tree().scale_factor();
let h = info.bounds_info().inner_size().height.to_dip(f);
if height.get() != h {
let _ = height.set(h);
}
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn actual_size_px(child: impl UiNode, size: impl IntoVar<PxSize>) -> impl UiNode {
let size = size.into_var();
match_node(child, move |c, op| match &op {
UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
c.op(op);
let s = WIDGET.info().bounds_info().inner_size();
if size.get() != s {
let _ = size.set(s);
}
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn actual_width_px(child: impl UiNode, width: impl IntoVar<Px>) -> impl UiNode {
let width = width.into_var();
match_node(child, move |c, op| match &op {
UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
c.op(op);
let w = WIDGET.info().bounds_info().inner_size().width;
if width.get() != w {
let _ = width.set(w);
}
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn actual_height_px(child: impl UiNode, height: impl IntoVar<Px>) -> impl UiNode {
let height = height.into_var();
match_node(child, move |c, op| match &op {
UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
c.op(op);
let h = WIDGET.info().bounds_info().inner_size().height;
if height.get() != h {
let _ = height.set(h);
}
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn actual_transform(child: impl UiNode, transform: impl IntoVar<PxTransform>) -> impl UiNode {
let transform = transform.into_var();
match_node(child, move |c, op| match &op {
UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
c.op(op);
let t = WIDGET.info().bounds_info().inner_transform();
if transform.get() != t {
let _ = transform.set(t);
}
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn actual_bounds(child: impl UiNode, bounds: impl IntoVar<PxRect>) -> impl UiNode {
let bounds = bounds.into_var();
match_node(child, move |c, op| match &op {
UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
c.op(op);
let t = WIDGET.info().bounds_info().inner_bounds();
if bounds.get() != t {
let _ = bounds.set(t);
}
}
_ => {}
})
}
#[derive(Debug, Clone, Copy, PartialEq, Default, serde::Serialize, serde::Deserialize)]
pub enum WidgetLength {
#[default]
Default,
Leftover(Factor),
Exact,
}
impl From<&Length> for WidgetLength {
fn from(value: &Length) -> Self {
match value {
Length::Default => WidgetLength::Default,
Length::Leftover(f) => WidgetLength::Leftover(*f),
_ => WidgetLength::Exact,
}
}
}