1use crate::{
22 border::BorderBuilder,
23 core::{pool::Handle, reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*},
24 decorator::{DecoratorBuilder, DecoratorMessage},
25 define_constructor,
26 message::{MessageDirection, UiMessage},
27 style::{resource::StyleResourceExt, Style},
28 widget::{Widget, WidgetBuilder, WidgetMessage},
29 BuildContext, Control, Thickness, UiNode, UserInterface,
30};
31use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
32use std::ops::{Deref, DerefMut};
33
34#[derive(Default, Clone, Visit, Reflect, Debug, TypeUuidProvider, ComponentProvider)]
35#[type_uuid(id = "8d8f114d-7fc6-4d7e-8f57-cd4e39958c36")]
36pub struct ToggleButton {
37 pub widget: Widget,
38 pub decorator: Handle<UiNode>,
39 pub is_toggled: bool,
40 pub content: Handle<UiNode>,
41}
42
43#[derive(Debug, Clone, PartialEq)]
45pub enum ToggleButtonMessage {
46 Toggled(bool),
47 Content(Handle<UiNode>),
48}
49
50impl ToggleButtonMessage {
51 define_constructor!(
52 ToggleButtonMessage:Toggled => fn toggled(bool), layout: false
53 );
54 define_constructor!(
55 ToggleButtonMessage:Content => fn content(Handle<UiNode>), layout: false
56 );
57}
58
59impl ToggleButton {
60 pub const CORNER_RADIUS: &'static str = "ToggleButton.CornerRadius";
62 pub const BORDER_THICKNESS: &'static str = "ToggleButton.BorderThickness";
64
65 pub fn style() -> Style {
67 Style::default()
68 .with(Self::CORNER_RADIUS, 4.0f32)
69 .with(Self::BORDER_THICKNESS, Thickness::uniform(1.0))
70 }
71}
72
73impl ConstructorProvider<UiNode, UserInterface> for ToggleButton {
74 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
75 GraphNodeConstructor::new::<Self>()
76 .with_variant("ToggleButton", |ui| {
77 ToggleButtonBuilder::new(WidgetBuilder::new().with_name("ToggleButton"))
78 .build(&mut ui.build_ctx())
79 .into()
80 })
81 .with_group("Input")
82 }
83}
84
85crate::define_widget_deref!(ToggleButton);
86
87impl Control for ToggleButton {
88 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
89 self.widget.handle_routed_message(ui, message);
90
91 if let Some(msg) = message.data::<WidgetMessage>() {
92 if (message.destination() == self.handle()
93 || self.has_descendant(message.destination(), ui))
94 && message.direction() == MessageDirection::FromWidget
95 {
96 match msg {
97 WidgetMessage::MouseDown { .. } => {
98 ui.capture_mouse(self.handle());
99 }
100 WidgetMessage::MouseUp { .. } => {
101 if ui.captured_node() == self.handle() {
102 let new_state = !self.is_toggled;
103
104 ui.send_message(ToggleButtonMessage::toggled(
105 self.handle(),
106 MessageDirection::ToWidget,
107 new_state,
108 ));
109
110 ui.release_mouse_capture();
111 }
112 }
113 _ => {}
114 }
115 }
116 } else if let Some(msg) = message.data::<ToggleButtonMessage>() {
117 if message.destination() == self.handle()
118 && message.direction() == MessageDirection::ToWidget
119 {
120 match msg {
121 ToggleButtonMessage::Toggled(value) => {
122 if self.is_toggled != *value {
123 self.is_toggled = *value;
124
125 ui.send_message(DecoratorMessage::select(
126 self.decorator,
127 MessageDirection::ToWidget,
128 self.is_toggled,
129 ));
130
131 ui.send_message(message.reverse());
132 }
133 }
134 ToggleButtonMessage::Content(content) => {
135 ui.send_message(WidgetMessage::remove(
136 self.content,
137 MessageDirection::ToWidget,
138 ));
139 ui.send_message(WidgetMessage::link(
140 *content,
141 MessageDirection::ToWidget,
142 self.decorator,
143 ));
144 }
145 }
146 }
147 }
148 }
149}
150
151pub struct ToggleButtonBuilder {
152 widget_builder: WidgetBuilder,
153 is_toggled: bool,
154 content: Handle<UiNode>,
155}
156
157impl ToggleButtonBuilder {
158 pub fn new(widget_builder: WidgetBuilder) -> Self {
159 Self {
160 widget_builder,
161 is_toggled: false,
162 content: Default::default(),
163 }
164 }
165
166 pub fn with_toggled(mut self, is_toggled: bool) -> Self {
167 self.is_toggled = is_toggled;
168 self
169 }
170
171 pub fn with_content(mut self, content: Handle<UiNode>) -> Self {
172 self.content = content;
173 self
174 }
175
176 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
177 let decorator = DecoratorBuilder::new(
178 BorderBuilder::new(WidgetBuilder::new().with_child(self.content))
179 .with_corner_radius(ctx.style.property(ToggleButton::CORNER_RADIUS))
180 .with_stroke_thickness(ctx.style.property(ToggleButton::BORDER_THICKNESS))
181 .with_pad_by_corner_radius(true),
182 )
183 .with_pressable(true)
184 .with_selected_brush(ctx.style.property(Style::BRUSH_BRIGHT_BLUE))
185 .with_selected(self.is_toggled)
186 .build(ctx);
187
188 let canvas = ToggleButton {
189 widget: self.widget_builder.with_child(decorator).build(ctx),
190 decorator,
191 is_toggled: self.is_toggled,
192 content: self.content,
193 };
194 ctx.add_node(UiNode::new(canvas))
195 }
196}
197
198#[cfg(test)]
199mod test {
200 use crate::{test::test_widget_deletion, toggle::ToggleButtonBuilder, widget::WidgetBuilder};
201
202 #[test]
203 fn test_deletion() {
204 test_widget_deletion(|ctx| ToggleButtonBuilder::new(WidgetBuilder::new()).build(ctx));
205 }
206}