1use crate::{
22 core::{
23 algebra::Vector2, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
24 visitor::prelude::*,
25 },
26 define_constructor,
27 message::{ButtonState, MessageDirection, MouseButton, UiMessage},
28 widget::{Widget, WidgetBuilder, WidgetMessage},
29 BuildContext, Control, UiNode, UserInterface,
30};
31use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
32use std::ops::{Deref, DerefMut};
33
34#[derive(Debug, Clone, PartialEq)]
35pub enum ThumbMessage {
36 DragStarted { position: Vector2<f32> },
37 DragDelta { offset: Vector2<f32> },
38 DragCompleted { position: Vector2<f32> },
39}
40
41impl ThumbMessage {
42 define_constructor!(ThumbMessage:DragStarted => fn drag_started(position: Vector2<f32>), layout: false);
43 define_constructor!(ThumbMessage:DragDelta => fn drag_delta(offset: Vector2<f32>), layout: false);
44 define_constructor!(ThumbMessage:DragCompleted => fn drag_completed(position: Vector2<f32>), layout: false);
45}
46
47#[derive(Default, Clone, Visit, Reflect, Debug, TypeUuidProvider, ComponentProvider)]
48#[type_uuid(id = "71ad2ff4-6e9e-461d-b7c2-867bd4039684")]
49pub struct Thumb {
50 pub widget: Widget,
51 pub click_pos: Vector2<f32>,
52}
53
54impl ConstructorProvider<UiNode, UserInterface> for Thumb {
55 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
56 GraphNodeConstructor::new::<Self>()
57 .with_variant("Thumb", |ui| {
58 ThumbBuilder::new(WidgetBuilder::new().with_name("Thumb"))
59 .build(&mut ui.build_ctx())
60 .into()
61 })
62 .with_group("Input")
63 }
64}
65
66crate::define_widget_deref!(Thumb);
67
68impl Control for Thumb {
69 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
70 self.widget.handle_routed_message(ui, message);
71
72 if let Some(msg) = message.data::<WidgetMessage>() {
73 match msg {
74 WidgetMessage::MouseDown { pos, button } => {
75 if !message.handled() && *button == MouseButton::Left {
76 ui.capture_mouse(self.handle);
77 message.set_handled(true);
78 self.click_pos = *pos;
79 ui.send_message(ThumbMessage::drag_started(
80 self.handle,
81 MessageDirection::FromWidget,
82 self.actual_local_position(),
83 ));
84 }
85 }
86 WidgetMessage::MouseUp { button, .. } => {
87 if ui.captured_node() == self.handle && *button == MouseButton::Left {
88 ui.send_message(ThumbMessage::drag_completed(
89 self.handle,
90 MessageDirection::FromWidget,
91 self.actual_local_position(),
92 ));
93
94 ui.release_mouse_capture();
95 }
96 }
97 WidgetMessage::MouseMove { pos, state } => {
98 if ui.captured_node() == self.handle && state.left == ButtonState::Pressed {
99 ui.send_message(ThumbMessage::drag_delta(
100 self.handle,
101 MessageDirection::FromWidget,
102 self.visual_transform()
103 .try_inverse()
104 .unwrap_or_default()
105 .transform_vector(&(*pos - self.click_pos)),
106 ));
107 }
108 }
109 _ => (),
110 }
111 }
112 }
113}
114
115pub struct ThumbBuilder {
116 widget_builder: WidgetBuilder,
117}
118
119impl ThumbBuilder {
120 pub fn new(widget_builder: WidgetBuilder) -> Self {
121 Self { widget_builder }
122 }
123
124 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
125 let thumb = Thumb {
126 widget: self.widget_builder.build(ctx),
127 click_pos: Default::default(),
128 };
129 ctx.add_node(UiNode::new(thumb))
130 }
131}
132
133#[cfg(test)]
134mod test {
135 use crate::thumb::ThumbBuilder;
136 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
137
138 #[test]
139 fn test_deletion() {
140 test_widget_deletion(|ctx| ThumbBuilder::new(WidgetBuilder::new()).build(ctx));
141 }
142}