use std::fmt;
use zng_layout::context::DIRECTION_VAR;
use crate::prelude::*;
#[property(LAYOUT, default(0))]
pub fn margin(child: impl UiNode, margin: impl IntoVar<SideOffsets>) -> impl UiNode {
let margin = margin.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&margin);
}
UiNodeOp::Measure { wm, desired_size } => {
let margin = margin.layout();
let size_increment = PxSize::new(margin.horizontal(), margin.vertical());
*desired_size = LAYOUT.with_constraints(LAYOUT.constraints().with_less_size(size_increment), || wm.measure_block(child));
desired_size.width += size_increment.width;
desired_size.height += size_increment.height;
}
UiNodeOp::Layout { wl, final_size } => {
let margin = margin.layout();
let size_increment = PxSize::new(margin.horizontal(), margin.vertical());
*final_size = LAYOUT.with_constraints(LAYOUT.constraints().with_less_size(size_increment), || child.layout(wl));
let mut translate = PxVector::zero();
final_size.width += size_increment.width;
translate.x = margin.left;
final_size.height += size_increment.height;
translate.y = margin.top;
wl.translate(translate);
}
_ => {}
})
}
#[property(LAYOUT, default(Align::FILL))]
pub fn align(child: impl UiNode, alignment: impl IntoVar<Align>) -> impl UiNode {
let alignment = alignment.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&alignment);
}
UiNodeOp::Measure { wm, desired_size } => {
let align = alignment.get();
let child_size = LAYOUT.with_constraints(align.child_constraints(LAYOUT.constraints()), || wm.measure_block(child));
*desired_size = align.measure(child_size, LAYOUT.constraints());
}
UiNodeOp::Layout { wl, final_size } => {
let align = alignment.get();
let child_size = LAYOUT.with_constraints(align.child_constraints(LAYOUT.constraints()), || child.layout(wl));
let (size, offset, baseline) = align.layout(child_size, LAYOUT.constraints(), LAYOUT.direction());
wl.translate(offset);
if baseline {
wl.translate_baseline(true);
}
*final_size = size;
}
_ => {}
})
}
#[property(LAYOUT)]
pub fn is_rtl(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
bind_state(child, DIRECTION_VAR.map(|s| s.is_rtl()), state)
}
#[property(LAYOUT)]
pub fn is_ltr(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
bind_state(child, DIRECTION_VAR.map(|s| s.is_ltr()), state)
}
#[derive(Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum InlineMode {
#[default]
Allow,
Inline,
Block,
}
impl fmt::Debug for InlineMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "InlineMode::")?;
}
match self {
Self::Allow => write!(f, "Allow"),
Self::Inline => write!(f, "Inline"),
Self::Block => write!(f, "Block"),
}
}
}
impl_from_and_into_var! {
fn from(inline: bool) -> InlineMode {
if inline {
InlineMode::Inline
} else {
InlineMode::Block
}
}
}
#[property(WIDGET, default(InlineMode::Allow))]
pub fn inline(child: impl UiNode, mode: impl IntoVar<InlineMode>) -> impl UiNode {
let mode = mode.into_var();
match_node(child, move |child, op| match op {
UiNodeOp::Init => {
WIDGET.sub_var_layout(&mode);
}
UiNodeOp::Measure { wm, desired_size } => {
*desired_size = match mode.get() {
InlineMode::Allow => child.measure(wm),
InlineMode::Inline => {
if LAYOUT.inline_constraints().is_none() {
wm.with_inline_visual(|wm| child.measure(wm))
} else {
child.measure(wm)
}
}
InlineMode::Block => {
wm.measure_block(child)
}
};
}
UiNodeOp::Layout { wl, final_size } => {
*final_size = match mode.get() {
InlineMode::Allow => child.layout(wl),
InlineMode::Inline => {
if LAYOUT.inline_constraints().is_none() {
wl.to_measure(None).with_inline_visual(|wm| child.measure(wm));
wl.with_inline_visual(|wl| child.layout(wl))
} else {
child.layout(wl)
}
}
InlineMode::Block => {
if wl.inline().is_some() {
tracing::error!("inline enabled in `layout` when it signaled disabled in the previous `measure`");
wl.layout_block(child)
} else {
child.layout(wl)
}
}
};
}
_ => {}
})
}
context_var! {
pub static IS_MOBILE_VAR: bool = cfg!(any(target_os = "android", target_os = "ios"));
}
#[property(CONTEXT, default(IS_MOBILE_VAR))]
pub fn force_mobile(child: impl UiNode, is_mobile: impl IntoVar<bool>) -> impl UiNode {
with_context_var(child, IS_MOBILE_VAR, is_mobile)
}
#[property(EVENT)]
pub fn is_mobile(child: impl UiNode, is_mobile: impl IntoVar<bool>) -> impl UiNode {
zng_wgt::node::bind_state(child, IS_MOBILE_VAR, is_mobile)
}