use crate::{
border::BorderBuilder,
core::{pool::Handle, reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*},
decorator::{DecoratorBuilder, DecoratorMessage},
define_constructor,
message::{MessageDirection, UiMessage},
style::{resource::StyleResourceExt, Style},
widget::{Widget, WidgetBuilder, WidgetMessage},
BuildContext, Control, Thickness, UiNode, UserInterface,
};
use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
use std::ops::{Deref, DerefMut};
#[derive(Default, Clone, Visit, Reflect, Debug, TypeUuidProvider, ComponentProvider)]
#[type_uuid(id = "8d8f114d-7fc6-4d7e-8f57-cd4e39958c36")]
pub struct ToggleButton {
pub widget: Widget,
pub decorator: Handle<UiNode>,
pub is_toggled: bool,
pub content: Handle<UiNode>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ToggleButtonMessage {
Toggled(bool),
Content(Handle<UiNode>),
}
impl ToggleButtonMessage {
define_constructor!(
ToggleButtonMessage:Toggled => fn toggled(bool), layout: false
);
define_constructor!(
ToggleButtonMessage:Content => fn content(Handle<UiNode>), layout: false
);
}
impl ToggleButton {
pub const CORNER_RADIUS: &'static str = "ToggleButton.CornerRadius";
pub const BORDER_THICKNESS: &'static str = "ToggleButton.BorderThickness";
pub fn style() -> Style {
Style::default()
.with(Self::CORNER_RADIUS, 4.0f32)
.with(Self::BORDER_THICKNESS, Thickness::uniform(1.0))
}
}
impl ConstructorProvider<UiNode, UserInterface> for ToggleButton {
fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
GraphNodeConstructor::new::<Self>()
.with_variant("ToggleButton", |ui| {
ToggleButtonBuilder::new(WidgetBuilder::new().with_name("ToggleButton"))
.build(&mut ui.build_ctx())
.into()
})
.with_group("Input")
}
}
crate::define_widget_deref!(ToggleButton);
impl Control for ToggleButton {
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.widget.handle_routed_message(ui, message);
if let Some(msg) = message.data::<WidgetMessage>() {
if (message.destination() == self.handle()
|| self.has_descendant(message.destination(), ui))
&& message.direction() == MessageDirection::FromWidget
{
match msg {
WidgetMessage::MouseDown { .. } => {
ui.capture_mouse(self.handle());
}
WidgetMessage::MouseUp { .. } => {
if ui.captured_node() == self.handle() {
let new_state = !self.is_toggled;
ui.send_message(ToggleButtonMessage::toggled(
self.handle(),
MessageDirection::ToWidget,
new_state,
));
ui.release_mouse_capture();
}
}
_ => {}
}
}
} else if let Some(msg) = message.data::<ToggleButtonMessage>() {
if message.destination() == self.handle()
&& message.direction() == MessageDirection::ToWidget
{
match msg {
ToggleButtonMessage::Toggled(value) => {
if self.is_toggled != *value {
self.is_toggled = *value;
ui.send_message(DecoratorMessage::select(
self.decorator,
MessageDirection::ToWidget,
self.is_toggled,
));
ui.send_message(message.reverse());
}
}
ToggleButtonMessage::Content(content) => {
ui.send_message(WidgetMessage::remove(
self.content,
MessageDirection::ToWidget,
));
ui.send_message(WidgetMessage::link(
*content,
MessageDirection::ToWidget,
self.decorator,
));
}
}
}
}
}
}
pub struct ToggleButtonBuilder {
widget_builder: WidgetBuilder,
is_toggled: bool,
content: Handle<UiNode>,
}
impl ToggleButtonBuilder {
pub fn new(widget_builder: WidgetBuilder) -> Self {
Self {
widget_builder,
is_toggled: false,
content: Default::default(),
}
}
pub fn with_toggled(mut self, is_toggled: bool) -> Self {
self.is_toggled = is_toggled;
self
}
pub fn with_content(mut self, content: Handle<UiNode>) -> Self {
self.content = content;
self
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
let decorator = DecoratorBuilder::new(
BorderBuilder::new(WidgetBuilder::new().with_child(self.content))
.with_corner_radius(ctx.style.property(ToggleButton::CORNER_RADIUS))
.with_stroke_thickness(ctx.style.property(ToggleButton::BORDER_THICKNESS))
.with_pad_by_corner_radius(true),
)
.with_pressable(true)
.with_selected_brush(ctx.style.property(Style::BRUSH_BRIGHT_BLUE))
.with_selected(self.is_toggled)
.build(ctx);
let canvas = ToggleButton {
widget: self.widget_builder.with_child(decorator).build(ctx),
decorator,
is_toggled: self.is_toggled,
content: self.content,
};
ctx.add_node(UiNode::new(canvas))
}
}
#[cfg(test)]
mod test {
use crate::{test::test_widget_deletion, toggle::ToggleButtonBuilder, widget::WidgetBuilder};
#[test]
fn test_deletion() {
test_widget_deletion(|ctx| ToggleButtonBuilder::new(WidgetBuilder::new()).build(ctx));
}
}