zng_wgt/
layout_props.rs
1use std::fmt;
2
3use zng_layout::context::DIRECTION_VAR;
4
5use crate::prelude::*;
6
7#[property(LAYOUT, default(0))]
14pub fn margin(child: impl UiNode, margin: impl IntoVar<SideOffsets>) -> impl UiNode {
15 let margin = margin.into_var();
16 match_node(child, move |child, op| match op {
17 UiNodeOp::Init => {
18 WIDGET.sub_var_layout(&margin);
19 }
20 UiNodeOp::Measure { wm, desired_size } => {
21 let margin = margin.layout();
22 let size_increment = PxSize::new(margin.horizontal(), margin.vertical());
23 *desired_size = LAYOUT.with_constraints(LAYOUT.constraints().with_less_size(size_increment), || wm.measure_block(child));
24 desired_size.width += size_increment.width;
25 desired_size.height += size_increment.height;
26 }
27 UiNodeOp::Layout { wl, final_size } => {
28 let margin = margin.layout();
29 let size_increment = PxSize::new(margin.horizontal(), margin.vertical());
30
31 *final_size = LAYOUT.with_constraints(LAYOUT.constraints().with_less_size(size_increment), || child.layout(wl));
32 let mut translate = PxVector::zero();
33 final_size.width += size_increment.width;
34 translate.x = margin.left;
35 final_size.height += size_increment.height;
36 translate.y = margin.top;
37 wl.translate(translate);
38 }
39 _ => {}
40 })
41}
42
43#[property(LAYOUT, default(Align::FILL))]
51pub fn align(child: impl UiNode, alignment: impl IntoVar<Align>) -> impl UiNode {
52 let alignment = alignment.into_var();
53 match_node(child, move |child, op| match op {
54 UiNodeOp::Init => {
55 WIDGET.sub_var_layout(&alignment);
56 }
57 UiNodeOp::Measure { wm, desired_size } => {
58 let align = alignment.get();
59 let child_size = LAYOUT.with_constraints(align.child_constraints(LAYOUT.constraints()), || wm.measure_block(child));
60 *desired_size = align.measure(child_size, LAYOUT.constraints());
61 }
62 UiNodeOp::Layout { wl, final_size } => {
63 let align = alignment.get();
64 let child_size = LAYOUT.with_constraints(align.child_constraints(LAYOUT.constraints()), || child.layout(wl));
65 let (size, offset, baseline) = align.layout(child_size, LAYOUT.constraints(), LAYOUT.direction());
66 wl.translate(offset);
67 if baseline {
68 wl.translate_baseline(true);
69 }
70 *final_size = size;
71 }
72 _ => {}
73 })
74}
75
76#[property(LAYOUT)]
82pub fn is_rtl(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
83 bind_state(child, DIRECTION_VAR.map(|s| s.is_rtl()), state)
84}
85
86#[property(LAYOUT)]
92pub fn is_ltr(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
93 bind_state(child, DIRECTION_VAR.map(|s| s.is_ltr()), state)
94}
95
96#[derive(Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
98pub enum InlineMode {
99 #[default]
103 Allow,
104 Inline,
112 Block,
117}
118impl fmt::Debug for InlineMode {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 if f.alternate() {
121 write!(f, "InlineMode::")?;
122 }
123 match self {
124 Self::Allow => write!(f, "Allow"),
125 Self::Inline => write!(f, "Inline"),
126 Self::Block => write!(f, "Block"),
127 }
128 }
129}
130impl_from_and_into_var! {
131 fn from(inline: bool) -> InlineMode {
132 if inline {
133 InlineMode::Inline
134 } else {
135 InlineMode::Block
136 }
137 }
138}
139
140#[property(WIDGET, default(InlineMode::Allow))]
144pub fn inline(child: impl UiNode, mode: impl IntoVar<InlineMode>) -> impl UiNode {
145 let mode = mode.into_var();
146 match_node(child, move |child, op| match op {
147 UiNodeOp::Init => {
148 WIDGET.sub_var_layout(&mode);
149 }
150 UiNodeOp::Measure { wm, desired_size } => {
151 *desired_size = match mode.get() {
152 InlineMode::Allow => child.measure(wm),
153 InlineMode::Inline => {
154 if LAYOUT.inline_constraints().is_none() {
155 wm.with_inline_visual(|wm| child.measure(wm))
157 } else {
158 child.measure(wm)
160 }
161 }
162 InlineMode::Block => {
163 wm.measure_block(child)
165 }
166 };
167 }
168 UiNodeOp::Layout { wl, final_size } => {
169 *final_size = match mode.get() {
170 InlineMode::Allow => child.layout(wl),
171 InlineMode::Inline => {
172 if LAYOUT.inline_constraints().is_none() {
173 wl.to_measure(None).with_inline_visual(|wm| child.measure(wm));
174 wl.with_inline_visual(|wl| child.layout(wl))
175 } else {
176 child.layout(wl)
178 }
179 }
180 InlineMode::Block => {
181 if wl.inline().is_some() {
182 tracing::error!("inline enabled in `layout` when it signaled disabled in the previous `measure`");
183 wl.layout_block(child)
184 } else {
185 child.layout(wl)
186 }
187 }
188 };
189 }
190 _ => {}
191 })
192}
193
194context_var! {
195 pub static IS_MOBILE_VAR: bool = cfg!(any(target_os = "android", target_os = "ios"));
199}
200
201#[property(CONTEXT, default(IS_MOBILE_VAR))]
205pub fn force_mobile(child: impl UiNode, is_mobile: impl IntoVar<bool>) -> impl UiNode {
206 with_context_var(child, IS_MOBILE_VAR, is_mobile)
207}
208
209#[property(EVENT)]
211pub fn is_mobile(child: impl UiNode, is_mobile: impl IntoVar<bool>) -> impl UiNode {
212 zng_wgt::node::bind_state(child, IS_MOBILE_VAR, is_mobile)
213}