zng_wgt_rule_line/
lib.rs

1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3//!
4//! Rule line widgets.
5//!
6//! # Crate
7//!
8#![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
15/// Draws a horizontal or vertical rule line.
16#[widget($crate::RuleLine)]
17pub struct RuleLine(WidgetBase);
18impl RuleLine {
19    fn widget_intrinsic(&mut self) {
20        self.widget_builder().push_build_action(on_build);
21
22        widget_set! {
23            self;
24            access_role = AccessRole::Separator;
25        }
26    }
27
28    widget_impl! {
29        /// Margin around the line.
30        pub margin(margin: impl IntoVar<SideOffsets>);
31    }
32}
33
34/// Line orientation.
35#[property(CONTEXT, capture, default(LineOrientation::Horizontal), widget_impl(RuleLine))]
36pub fn orientation(orientation: impl IntoVar<LineOrientation>) {}
37
38/// Line color.
39#[property(CONTEXT, capture, default(rgb(0, 0, 0)), widget_impl(RuleLine))]
40pub fn color(color: impl IntoVar<Rgba>) {}
41
42/// Line stroke thickness.
43#[property(CONTEXT, capture, default(1), widget_impl(RuleLine))]
44pub fn stroke_thickness(thickness: impl IntoVar<Length>) {}
45
46/// Line length.
47///
48/// Set to [`Default`] to fill available length without requesting any length.
49///
50/// [`Default`]: Length::Default
51#[property(CONTEXT, capture, default(Length::Default), widget_impl(RuleLine))]
52pub fn length(length: impl IntoVar<Length>) {}
53
54/// Line style.
55#[property(CONTEXT, capture, default(LineStyle::Solid), widget_impl(RuleLine))]
56pub fn line_style(style: impl IntoVar<LineStyle>) {}
57
58fn on_build(wgt: &mut WidgetBuilding) {
59    let mut bounds = PxSize::zero();
60
61    let orientation = wgt
62        .capture_var(property_id!(orientation))
63        .unwrap_or_else(|| LineOrientation::Horizontal.into_var().boxed());
64
65    let length = wgt
66        .capture_var(property_id!(length))
67        .unwrap_or_else(|| LocalVar(Length::Default).boxed());
68
69    let stroke_thickness = wgt
70        .capture_var(property_id!(stroke_thickness))
71        .unwrap_or_else(|| LocalVar(Length::from(1)).boxed());
72
73    let color = wgt
74        .capture_var(property_id!(color))
75        .unwrap_or_else(|| LocalVar(rgb(0, 0, 0)).boxed());
76
77    let style = wgt
78        .capture_var(property_id!(line_style))
79        .unwrap_or_else(|| LineStyle::Solid.into_var().boxed());
80
81    wgt.set_child(match_node_leaf(move |op| match op {
82        UiNodeOp::Init => {
83            WIDGET
84                .sub_var_layout(&stroke_thickness)
85                .sub_var_layout(&orientation)
86                .sub_var_layout(&length)
87                .sub_var_render(&color)
88                .sub_var_render(&style);
89        }
90        UiNodeOp::Measure { desired_size, .. } => {
91            let metrics = LAYOUT.metrics();
92            let default_stroke = Dip::new(1).to_px(metrics.scale_factor());
93
94            *desired_size = match orientation.get() {
95                LineOrientation::Horizontal => PxSize::new(
96                    length.layout_dft_x(metrics.constraints().x.fill()),
97                    stroke_thickness.layout_dft_y(default_stroke),
98                ),
99                LineOrientation::Vertical => PxSize::new(
100                    stroke_thickness.layout_dft_x(default_stroke),
101                    length.layout_dft_y(metrics.constraints().y.fill()),
102                ),
103            };
104        }
105        UiNodeOp::Layout { final_size, .. } => {
106            let metrics = LAYOUT.metrics();
107            let default_stroke = Dip::new(1).to_px(metrics.scale_factor());
108
109            let b = match orientation.get() {
110                LineOrientation::Horizontal => PxSize::new(
111                    length.layout_dft_x(metrics.constraints().x.fill()),
112                    stroke_thickness.layout_dft_y(default_stroke),
113                ),
114                LineOrientation::Vertical => PxSize::new(
115                    stroke_thickness.layout_dft_x(default_stroke),
116                    length.layout_dft_y(metrics.constraints().y.fill()),
117                ),
118            };
119
120            if b != bounds {
121                bounds = b;
122                WIDGET.render();
123            }
124
125            *final_size = b;
126        }
127        UiNodeOp::Render { frame } => {
128            let bounds = PxRect::from_size(bounds);
129            let orientation = orientation.get();
130            let color = color.get();
131            let style = style.get();
132            frame.push_line(bounds, orientation, color, style);
133        }
134        _ => {}
135    }));
136}
137
138/// Horizontal rule line.
139pub mod hr {
140    use zng_wgt::prelude::*;
141
142    /// Draws an horizontal [`RuleLine!`](struct@crate::RuleLine).
143    #[widget($crate::hr::Hr)]
144    pub struct Hr(super::RuleLine);
145    impl Hr {
146        fn widget_intrinsic(&mut self) {
147            widget_set! {
148                self;
149                orientation = LineOrientation::Horizontal;
150                color = COLOR_VAR;
151                stroke_thickness = STROKE_THICKNESS_VAR;
152                line_style = LINE_STYLE_VAR;
153                margin = MARGIN_VAR;
154            }
155        }
156    }
157
158    context_var! {
159        /// Line color, inherits from [`FONT_COLOR_VAR`].
160        ///
161        /// [`FONT_COLOR_VAR`]: zng_wgt_text::FONT_COLOR_VAR
162        pub static COLOR_VAR: Rgba = zng_wgt_text::FONT_COLOR_VAR.map(|c| c.with_alpha(30.pct()));
163
164        /// Line stroke thickness, default is `1.dip()`
165        pub static STROKE_THICKNESS_VAR: Length = 1.dip();
166
167        /// Line style, default is `Solid`.
168        pub static LINE_STYLE_VAR: LineStyle = LineStyle::Solid;
169
170        /// Margin around line.
171        ///
172        /// Is `(4, 0)` by default, 4 top-bottom, 0 left-right.
173        pub static MARGIN_VAR: SideOffsets = (4, 0);
174    }
175
176    /// Sets the [`COLOR_VAR`] that affects all horizontal rules inside the widget.
177    #[property(CONTEXT, default(COLOR_VAR))]
178    pub fn color(child: impl UiNode, color: impl IntoVar<Rgba>) -> impl UiNode {
179        with_context_var(child, COLOR_VAR, color)
180    }
181
182    /// Sets the [`STROKE_THICKNESS_VAR`] that affects all horizontal rules inside the widget.
183    #[property(CONTEXT, default(STROKE_THICKNESS_VAR))]
184    pub fn stroke_thickness(child: impl UiNode, thickness: impl IntoVar<Length>) -> impl UiNode {
185        with_context_var(child, STROKE_THICKNESS_VAR, thickness)
186    }
187
188    /// Sets the [`LINE_STYLE_VAR`] that affects all horizontal rules inside the widget.
189    #[property(CONTEXT, default(LINE_STYLE_VAR))]
190    pub fn line_style(child: impl UiNode, style: impl IntoVar<LineStyle>) -> impl UiNode {
191        with_context_var(child, LINE_STYLE_VAR, style)
192    }
193
194    /// Sets the [`MARGIN_VAR`] that affects all horizontal rules inside the widget.
195    #[property(CONTEXT, default(MARGIN_VAR))]
196    pub fn margin(child: impl UiNode, margin: impl IntoVar<SideOffsets>) -> impl UiNode {
197        with_context_var(child, MARGIN_VAR, margin)
198    }
199}
200
201/// Vertical rule line.
202pub mod vr {
203    use zng_wgt::prelude::*;
204
205    /// Draws a vertical [`RuleLine!`](struct@crate::RuleLine).
206    #[widget($crate::vr::Vr)]
207    pub struct Vr(super::RuleLine);
208    impl Vr {
209        fn widget_intrinsic(&mut self) {
210            widget_set! {
211                self;
212                orientation = LineOrientation::Vertical;
213                color = COLOR_VAR;
214                stroke_thickness = STROKE_THICKNESS_VAR;
215                line_style = LINE_STYLE_VAR;
216                margin = MARGIN_VAR;
217            }
218        }
219    }
220
221    context_var! {
222        /// Line color, inherits from [`FONT_COLOR_VAR`].
223        ///
224        /// [`FONT_COLOR_VAR`]: zng_wgt_text::FONT_COLOR_VAR
225        pub static COLOR_VAR: Rgba = zng_wgt_text::FONT_COLOR_VAR.map(|c| c.with_alpha(30.pct()));
226
227        /// Line stroke thickness, default is `1.dip()`
228        pub static STROKE_THICKNESS_VAR: Length = 1.dip();
229
230        /// Line style, default is `Solid`.
231        pub static LINE_STYLE_VAR: LineStyle = LineStyle::Solid;
232
233        /// Margin around line.
234        ///
235        /// Is `(0, 4)` by default, 0 top-bottom, 4 left-right.
236        pub static MARGIN_VAR: SideOffsets = (0, 4);
237    }
238
239    /// Sets the [`COLOR_VAR`] that affects all vertical rules inside the widget.
240    #[property(CONTEXT, default(COLOR_VAR))]
241    pub fn color(child: impl UiNode, color: impl IntoVar<Rgba>) -> impl UiNode {
242        with_context_var(child, COLOR_VAR, color)
243    }
244
245    /// Sets the [`STROKE_THICKNESS_VAR`] that affects all vertical rules inside the widget.
246    #[property(CONTEXT, default(STROKE_THICKNESS_VAR))]
247    pub fn stroke_thickness(child: impl UiNode, thickness: impl IntoVar<Length>) -> impl UiNode {
248        with_context_var(child, STROKE_THICKNESS_VAR, thickness)
249    }
250
251    /// Sets the [`LINE_STYLE_VAR`] that affects all vertical rules inside the widget.
252    #[property(CONTEXT, default(LINE_STYLE_VAR))]
253    pub fn line_style(child: impl UiNode, style: impl IntoVar<LineStyle>) -> impl UiNode {
254        with_context_var(child, LINE_STYLE_VAR, style)
255    }
256
257    /// Sets the [`MARGIN_VAR`] that affects all vertical rules inside the widget.
258    #[property(CONTEXT, default(MARGIN_VAR))]
259    pub fn margin(child: impl UiNode, margin: impl IntoVar<SideOffsets>) -> impl UiNode {
260        with_context_var(child, MARGIN_VAR, margin)
261    }
262}