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