1use zng_wgt::prelude::*;
4use zng_wgt_input::{focus::FocusableMix, pointer_capture::capture_pointer};
5use zng_wgt_style::{Style, StyleMix, impl_style_fn};
6
7use crate::{SLIDER_DIRECTION_VAR, SliderDirection, ThumbValue};
8
9#[widget($crate::thumb::Thumb {
11 ($value:expr) => {
12 value = $value;
13 }
14})]
15pub struct Thumb(FocusableMix<StyleMix<WidgetBase>>);
16impl Thumb {
17 fn widget_intrinsic(&mut self) {
18 self.style_intrinsic(STYLE_FN_VAR, property_id!(self::style_fn));
19
20 self.widget_builder()
21 .push_build_action(|wgt| match wgt.capture_var::<ThumbValue>(property_id!(Self::value)) {
22 Some(v) => {
23 wgt.push_intrinsic(NestGroup::LAYOUT, "event-layout", move |c| thumb_event_layout_node(c, v));
24 }
25 None => tracing::error!("missing required `slider::Thumb::value` property"),
26 });
27
28 widget_set! {
29 self;
30 capture_pointer = true;
33 }
34 }
35}
36impl_style_fn!(Thumb, DefaultStyle);
37
38#[widget($crate::thumb::DefaultStyle)]
40pub struct DefaultStyle(Style);
41impl DefaultStyle {
42 fn widget_intrinsic(&mut self) {
43 widget_set! {
44 self;
45 zng_wgt::border = 3, LightDark::new(colors::BLACK, colors::WHITE).rgba_into();
46 zng_wgt_size_offset::force_size = 10 + 3 + 3;
47 zng_wgt::corner_radius = 16;
48 zng_wgt_fill::background_color = colors::ACCENT_COLOR_VAR.rgba();
49
50 when #{crate::SLIDER_DIRECTION_VAR}.is_horizontal() {
51 zng_wgt_size_offset::offset = (-3 - 10 / 2, -3 - 5 / 2); }
53 when #{crate::SLIDER_DIRECTION_VAR}.is_vertical() {
54 zng_wgt_size_offset::offset = (-3 - 5 / 2, -3 - 10 / 2);
55 }
56
57 #[easing(150.ms())]
58 zng_wgt_transform::scale = 100.pct();
59 when *#zng_wgt_input::is_cap_hovered {
60 #[easing(0.ms())]
61 zng_wgt_transform::scale = 120.pct();
62 }
63 }
64 }
65}
66
67#[property(CONTEXT, widget_impl(Thumb))]
69pub fn value(wgt: &mut WidgetBuilding, value: impl IntoVar<ThumbValue>) {
70 let _ = value;
71 wgt.expect_property_capture();
72}
73
74fn thumb_event_layout_node(child: impl IntoUiNode, value: impl IntoVar<ThumbValue>) -> UiNode {
78 let value = value.into_var();
79 match_node(child, move |c, op| match op {
80 UiNodeOp::Init => {
81 WIDGET.sub_var_layout(&value);
82 }
83 UiNodeOp::Layout { wl, final_size } => {
84 *final_size = c.layout(wl);
85 let layout_direction = LAYOUT.direction();
86
87 let c = LAYOUT.constraints();
89 let track_size = c.with_fill_vector(c.is_bounded()).fill_size();
90 let track_orientation = SLIDER_DIRECTION_VAR.get();
91 let offset = value.get().offset;
92
93 let offset = match track_orientation.layout(layout_direction) {
94 SliderDirection::LeftToRight => track_size.width * offset,
95 SliderDirection::RightToLeft => track_size.width - (track_size.width * offset),
96 SliderDirection::BottomToTop => track_size.height - (track_size.height * offset),
97 SliderDirection::TopToBottom => track_size.height * offset,
98 _ => unreachable!(),
99 };
100 let offset = if track_orientation.is_horizontal() {
101 PxVector::new(offset, Px(0))
102 } else {
103 PxVector::new(Px(0), offset)
104 };
105 wl.translate(offset);
106 }
107 _ => {}
109 })
110}