1use crate::{
22 border::BorderBuilder,
23 core::{pool::Handle, reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*},
24 decorator::{DecoratorBuilder, DecoratorMessage},
25 message::{MessageDirection, UiMessage},
26 style::{resource::StyleResourceExt, Style},
27 widget::{Widget, WidgetBuilder, WidgetMessage},
28 BuildContext, Control, Thickness, UiNode, UserInterface,
29};
30use fyrox_core::pool::ObjectOrVariant;
31
32use crate::message::MessageData;
33use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
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}
51impl MessageData for ToggleButtonMessage {}
52
53impl ToggleButton {
54 pub const CORNER_RADIUS: &'static str = "ToggleButton.CornerRadius";
56 pub const BORDER_THICKNESS: &'static str = "ToggleButton.BorderThickness";
58
59 pub fn style() -> Style {
61 Style::default()
62 .with(Self::CORNER_RADIUS, 4.0f32)
63 .with(Self::BORDER_THICKNESS, Thickness::uniform(1.0))
64 }
65}
66
67impl ConstructorProvider<UiNode, UserInterface> for ToggleButton {
68 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
69 GraphNodeConstructor::new::<Self>()
70 .with_variant("ToggleButton", |ui| {
71 ToggleButtonBuilder::new(WidgetBuilder::new().with_name("ToggleButton"))
72 .build(&mut ui.build_ctx())
73 .to_base()
74 .into()
75 })
76 .with_group("Input")
77 }
78}
79
80crate::define_widget_deref!(ToggleButton);
81
82impl Control for ToggleButton {
83 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
84 self.widget.handle_routed_message(ui, message);
85
86 if let Some(msg) = message.data::<WidgetMessage>() {
87 if (message.destination() == self.handle()
88 || self.has_descendant(message.destination(), ui))
89 && message.direction() == MessageDirection::FromWidget
90 {
91 match msg {
92 WidgetMessage::MouseDown { .. } => {
93 ui.capture_mouse(self.handle());
94 }
95 WidgetMessage::MouseUp { .. } => {
96 if ui.captured_node() == self.handle() {
97 let new_state = !self.is_toggled;
98
99 ui.send(self.handle(), ToggleButtonMessage::Toggled(new_state));
100
101 ui.release_mouse_capture();
102 }
103 }
104 _ => {}
105 }
106 }
107 } else if let Some(msg) = message.data_for::<ToggleButtonMessage>(self.handle()) {
108 match msg {
109 ToggleButtonMessage::Toggled(value) => {
110 if self.is_toggled != *value {
111 self.is_toggled = *value;
112 ui.send(self.decorator, DecoratorMessage::Select(self.is_toggled));
113 ui.send_message(message.reverse());
114 }
115 }
116 ToggleButtonMessage::Content(content) => {
117 ui.send(self.content, WidgetMessage::Remove);
118 ui.send(*content, WidgetMessage::LinkWith(self.decorator));
119 }
120 }
121 }
122 }
123}
124
125pub struct ToggleButtonBuilder {
126 widget_builder: WidgetBuilder,
127 is_toggled: bool,
128 content: Handle<UiNode>,
129}
130
131impl ToggleButtonBuilder {
132 pub fn new(widget_builder: WidgetBuilder) -> Self {
133 Self {
134 widget_builder,
135 is_toggled: false,
136 content: Default::default(),
137 }
138 }
139
140 pub fn with_toggled(mut self, is_toggled: bool) -> Self {
141 self.is_toggled = is_toggled;
142 self
143 }
144
145 pub fn with_content(mut self, content: Handle<impl ObjectOrVariant<UiNode>>) -> Self {
146 self.content = content.to_base();
147 self
148 }
149
150 pub fn build(self, ctx: &mut BuildContext) -> Handle<ToggleButton> {
151 let decorator = DecoratorBuilder::new(
152 BorderBuilder::new(WidgetBuilder::new().with_child(self.content))
153 .with_corner_radius(ctx.style.property(ToggleButton::CORNER_RADIUS))
154 .with_stroke_thickness(ctx.style.property(ToggleButton::BORDER_THICKNESS))
155 .with_pad_by_corner_radius(true),
156 )
157 .with_pressable(true)
158 .with_selected_brush(ctx.style.property(Style::BRUSH_BRIGHT_BLUE))
159 .with_selected(self.is_toggled)
160 .build(ctx)
161 .to_base();
162
163 let canvas = ToggleButton {
164 widget: self.widget_builder.with_child(decorator).build(ctx),
165 decorator,
166 is_toggled: self.is_toggled,
167 content: self.content,
168 };
169 ctx.add(canvas)
170 }
171}
172
173#[cfg(test)]
174mod test {
175 use crate::{test::test_widget_deletion, toggle::ToggleButtonBuilder, widget::WidgetBuilder};
176
177 #[test]
178 fn test_deletion() {
179 test_widget_deletion(|ctx| ToggleButtonBuilder::new(WidgetBuilder::new()).build(ctx));
180 }
181}