#![warn(missing_docs)]
use crate::style::resource::StyleResourceExt;
use crate::style::{Style, StyledProperty};
use crate::widget::WidgetBuilder;
use crate::{
border::{Border, BorderBuilder},
brush::Brush,
core::{
algebra::Vector2, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
visitor::prelude::*,
},
define_constructor,
draw::DrawingContext,
message::{MessageDirection, UiMessage},
widget::{Widget, WidgetMessage},
BuildContext, Control, UiNode, UserInterface,
};
use fyrox_core::uuid_provider;
use fyrox_core::variable::InheritableVariable;
use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone, PartialEq)]
pub enum DecoratorMessage {
Select(bool),
HoverBrush(StyledProperty<Brush>),
NormalBrush(StyledProperty<Brush>),
PressedBrush(StyledProperty<Brush>),
SelectedBrush(StyledProperty<Brush>),
}
impl DecoratorMessage {
define_constructor!(
DecoratorMessage:Select => fn select(bool), layout: false
);
define_constructor!(
DecoratorMessage:HoverBrush => fn hover_brush(StyledProperty<Brush>), layout: false
);
define_constructor!(
DecoratorMessage:NormalBrush => fn normal_brush(StyledProperty<Brush>), layout: false
);
define_constructor!(
DecoratorMessage:PressedBrush => fn pressed_brush(StyledProperty<Brush>), layout: false
);
define_constructor!(
DecoratorMessage:SelectedBrush => fn selected_brush(StyledProperty<Brush>), layout: false
);
}
#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
pub struct Decorator {
#[component(include)]
pub border: Border,
pub normal_brush: InheritableVariable<StyledProperty<Brush>>,
pub hover_brush: InheritableVariable<StyledProperty<Brush>>,
pub pressed_brush: InheritableVariable<StyledProperty<Brush>>,
pub selected_brush: InheritableVariable<StyledProperty<Brush>>,
pub is_selected: InheritableVariable<bool>,
pub is_pressable: InheritableVariable<bool>,
}
impl ConstructorProvider<UiNode, UserInterface> for Decorator {
fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
GraphNodeConstructor::new::<Self>()
.with_variant("Decorator", |ui| {
DecoratorBuilder::new(BorderBuilder::new(
WidgetBuilder::new().with_name("Decorator"),
))
.build(&mut ui.build_ctx())
.into()
})
.with_group("Visual")
}
}
impl Deref for Decorator {
type Target = Widget;
fn deref(&self) -> &Self::Target {
&self.border
}
}
impl DerefMut for Decorator {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.border
}
}
uuid_provider!(Decorator = "bb4b60aa-c657-4ed6-8db6-d7f374397c73");
impl Control for Decorator {
fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
self.border.measure_override(ui, available_size)
}
fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
self.border.arrange_override(ui, final_size)
}
fn draw(&self, drawing_context: &mut DrawingContext) {
self.border.draw(drawing_context)
}
fn update(&mut self, dt: f32, ui: &mut UserInterface) {
self.border.update(dt, ui)
}
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.border.handle_routed_message(ui, message);
if let Some(msg) = message.data::<DecoratorMessage>() {
match msg {
&DecoratorMessage::Select(value) => {
if *self.is_selected != value {
self.is_selected.set_value_and_mark_modified(value);
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
if *self.is_selected {
(*self.selected_brush).clone()
} else {
(*self.normal_brush).clone()
},
));
}
}
DecoratorMessage::HoverBrush(brush) => {
self.hover_brush.set_value_and_mark_modified(brush.clone());
if self.is_mouse_directly_over {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
(*self.hover_brush).clone(),
));
}
}
DecoratorMessage::NormalBrush(brush) => {
self.normal_brush.set_value_and_mark_modified(brush.clone());
if !*self.is_selected && !self.is_mouse_directly_over {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
(*self.normal_brush).clone(),
));
}
}
DecoratorMessage::PressedBrush(brush) => {
self.pressed_brush
.set_value_and_mark_modified(brush.clone());
}
DecoratorMessage::SelectedBrush(brush) => {
self.selected_brush
.set_value_and_mark_modified(brush.clone());
if *self.is_selected {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
(*self.selected_brush).clone(),
));
}
}
}
} else if let Some(msg) = message.data::<WidgetMessage>() {
if message.destination() == self.handle()
|| self.has_descendant(message.destination(), ui)
{
match msg {
WidgetMessage::MouseLeave => {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
if *self.is_selected {
(*self.selected_brush).clone()
} else {
(*self.normal_brush).clone()
},
));
}
WidgetMessage::MouseEnter => {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
if *self.is_selected {
(*self.selected_brush).clone()
} else {
(*self.hover_brush).clone()
},
));
}
WidgetMessage::MouseDown { .. } if *self.is_pressable => {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
(*self.pressed_brush).clone(),
));
}
WidgetMessage::MouseUp { .. } => {
if *self.is_selected {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
(*self.selected_brush).clone(),
));
} else {
ui.send_message(WidgetMessage::background(
self.handle(),
MessageDirection::ToWidget,
(*self.normal_brush).clone(),
));
}
}
_ => {}
}
}
if message.destination() == self.handle() {
if let WidgetMessage::Style(style) = msg {
self.normal_brush.update(style);
self.hover_brush.update(style);
self.pressed_brush.update(style);
self.selected_brush.update(style);
}
}
}
}
}
pub struct DecoratorBuilder {
border_builder: BorderBuilder,
normal_brush: Option<StyledProperty<Brush>>,
hover_brush: Option<StyledProperty<Brush>>,
pressed_brush: Option<StyledProperty<Brush>>,
selected_brush: Option<StyledProperty<Brush>>,
pressable: bool,
selected: bool,
}
impl DecoratorBuilder {
pub fn new(border_builder: BorderBuilder) -> Self {
Self {
normal_brush: None,
hover_brush: None,
pressed_brush: None,
selected_brush: None,
pressable: true,
selected: false,
border_builder,
}
}
pub fn with_normal_brush(mut self, brush: StyledProperty<Brush>) -> Self {
self.normal_brush = Some(brush);
self
}
pub fn with_hover_brush(mut self, brush: StyledProperty<Brush>) -> Self {
self.hover_brush = Some(brush);
self
}
pub fn with_pressed_brush(mut self, brush: StyledProperty<Brush>) -> Self {
self.pressed_brush = Some(brush);
self
}
pub fn with_selected_brush(mut self, brush: StyledProperty<Brush>) -> Self {
self.selected_brush = Some(brush);
self
}
pub fn with_pressable(mut self, pressable: bool) -> Self {
self.pressable = pressable;
self
}
pub fn with_selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
pub fn build(mut self, ctx: &mut BuildContext) -> Handle<UiNode> {
let normal_brush = self
.normal_brush
.unwrap_or_else(|| ctx.style.property::<Brush>(Style::BRUSH_LIGHT));
let hover_brush = self
.hover_brush
.unwrap_or_else(|| ctx.style.property::<Brush>(Style::BRUSH_LIGHTER));
let pressed_brush = self
.pressed_brush
.unwrap_or_else(|| ctx.style.property::<Brush>(Style::BRUSH_LIGHTEST));
let selected_brush = self
.selected_brush
.unwrap_or_else(|| ctx.style.property::<Brush>(Style::BRUSH_BRIGHT));
if self.border_builder.widget_builder.foreground.is_none() {
let brush = ctx.style.property(Style::BRUSH_DARKER);
self.border_builder.widget_builder.foreground = Some(brush);
}
let mut border = self.border_builder.build_border(ctx);
if self.selected {
*border.background = selected_brush.clone();
} else {
*border.background = normal_brush.clone();
}
let node = UiNode::new(Decorator {
border,
normal_brush: normal_brush.into(),
hover_brush: hover_brush.into(),
pressed_brush: pressed_brush.into(),
selected_brush: selected_brush.into(),
is_selected: self.selected.into(),
is_pressable: self.pressable.into(),
});
ctx.add_node(node)
}
}
#[cfg(test)]
mod test {
use crate::border::BorderBuilder;
use crate::decorator::DecoratorBuilder;
use crate::{test::test_widget_deletion, widget::WidgetBuilder};
#[test]
fn test_deletion() {
test_widget_deletion(|ctx| {
DecoratorBuilder::new(BorderBuilder::new(WidgetBuilder::new())).build(ctx)
});
}
}