1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12use zng_wgt::{margin, prelude::*};
13use zng_wgt_access::{AccessRole, access_role};
14
15pub mod hr;
16pub mod vr;
17
18mod collapse;
19pub use collapse::*;
20
21#[widget($crate::RuleLine)]
23pub struct RuleLine(WidgetBase);
24impl RuleLine {
25 fn widget_intrinsic(&mut self) {
26 self.widget_builder().push_build_action(on_build);
27
28 widget_set! {
29 self;
30 access_role = AccessRole::Separator;
31 }
32 }
33
34 widget_impl! {
35 pub margin(margin: impl IntoVar<SideOffsets>);
37 }
38}
39
40#[property(CONTEXT, default(LineOrientation::Horizontal), widget_impl(RuleLine))]
42pub fn orientation(wgt: &mut WidgetBuilding, orientation: impl IntoVar<LineOrientation>) {
43 let _ = orientation;
44 wgt.expect_property_capture();
45}
46
47#[property(CONTEXT, default(rgb(0, 0, 0)), widget_impl(RuleLine))]
49pub fn color(wgt: &mut WidgetBuilding, color: impl IntoVar<Rgba>) {
50 let _ = color;
51 wgt.expect_property_capture();
52}
53
54#[property(CONTEXT, default(1), widget_impl(RuleLine))]
56pub fn stroke_thickness(wgt: &mut WidgetBuilding, thickness: impl IntoVar<Length>) {
57 let _ = thickness;
58 wgt.expect_property_capture();
59}
60
61#[property(CONTEXT, default(Length::Default), widget_impl(RuleLine))]
67pub fn length(wgt: &mut WidgetBuilding, length: impl IntoVar<Length>) {
68 let _ = length;
69 wgt.expect_property_capture();
70}
71
72#[property(CONTEXT, default(LineStyle::Solid), widget_impl(RuleLine))]
74pub fn line_style(wgt: &mut WidgetBuilding, style: impl IntoVar<LineStyle>) {
75 let _ = style;
76 wgt.expect_property_capture();
77}
78
79fn on_build(wgt: &mut WidgetBuilding) {
80 let mut bounds = PxSize::zero();
81
82 let orientation = wgt
83 .capture_var(property_id!(orientation))
84 .unwrap_or_else(|| LineOrientation::Horizontal.into_var());
85
86 let length = wgt.capture_var(property_id!(length)).unwrap_or_else(|| const_var(Length::Default));
87
88 let stroke_thickness = wgt
89 .capture_var(property_id!(stroke_thickness))
90 .unwrap_or_else(|| const_var(Length::from(1)));
91
92 let color = wgt.capture_var(property_id!(color)).unwrap_or_else(|| const_var(rgb(0, 0, 0)));
93
94 let style = wgt
95 .capture_var(property_id!(line_style))
96 .unwrap_or_else(|| LineStyle::Solid.into_var());
97
98 wgt.set_child(match_node_leaf(move |op| match op {
99 UiNodeOp::Init => {
100 WIDGET
101 .sub_var_layout(&stroke_thickness)
102 .sub_var_layout(&orientation)
103 .sub_var_layout(&length)
104 .sub_var_render(&color)
105 .sub_var_render(&style);
106 }
107 UiNodeOp::Info { info } => {
108 info.flag_meta(*COLLAPSABLE_LINE_ID);
109 }
110 UiNodeOp::Measure { desired_size, .. } => {
111 if COLLAPSE_SCOPE.collapse(WIDGET.id()) {
112 *desired_size = PxSize::zero();
113 return;
114 }
115
116 let metrics = LAYOUT.metrics();
117 let default_stroke = Dip::new(1).to_px(metrics.scale_factor());
118
119 *desired_size = match orientation.get() {
120 LineOrientation::Horizontal => PxSize::new(
121 length.layout_dft_x(metrics.constraints().x.fill()),
122 stroke_thickness.layout_dft_y(default_stroke),
123 ),
124 LineOrientation::Vertical => PxSize::new(
125 stroke_thickness.layout_dft_x(default_stroke),
126 length.layout_dft_y(metrics.constraints().y.fill()),
127 ),
128 };
129 }
130 UiNodeOp::Layout { final_size, wl } => {
131 if COLLAPSE_SCOPE.collapse(WIDGET.id()) {
132 wl.collapse();
133 *final_size = PxSize::zero();
134 return;
135 }
136
137 let metrics = LAYOUT.metrics();
138 let default_stroke = Dip::new(1).to_px(metrics.scale_factor());
139
140 let b = match orientation.get() {
141 LineOrientation::Horizontal => PxSize::new(
142 length.layout_dft_x(metrics.constraints().x.fill()),
143 stroke_thickness.layout_dft_y(default_stroke),
144 ),
145 LineOrientation::Vertical => PxSize::new(
146 stroke_thickness.layout_dft_x(default_stroke),
147 length.layout_dft_y(metrics.constraints().y.fill()),
148 ),
149 };
150
151 if b != bounds {
152 bounds = b;
153 WIDGET.render();
154 }
155
156 *final_size = b;
157 }
158 UiNodeOp::Render { frame } => {
159 if bounds.is_empty() {
160 return;
161 }
162 let bounds = PxRect::from_size(bounds);
163 let orientation = orientation.get();
164 let color = color.get();
165 let style = style.get();
166 frame.push_line(bounds, orientation, color, style);
167 }
168 _ => {}
169 }));
170}