#![warn(missing_docs)]
use crate::{
core::{
algebra::Vector2, math::Rect, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
visitor::prelude::*,
},
define_constructor,
message::{MessageDirection, UiMessage},
widget::{Widget, WidgetBuilder},
BuildContext, Control, Orientation, UiNode, UserInterface,
};
use fyrox_core::uuid_provider;
use fyrox_core::variable::InheritableVariable;
use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
use fyrox_graph::BaseSceneGraph;
use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StackPanelMessage {
Orientation(Orientation),
}
impl StackPanelMessage {
define_constructor!(
StackPanelMessage:Orientation => fn orientation(Orientation), layout: false
);
}
#[derive(Default, Clone, Visit, Reflect, Debug, ComponentProvider)]
pub struct StackPanel {
pub widget: Widget,
pub orientation: InheritableVariable<Orientation>,
}
impl ConstructorProvider<UiNode, UserInterface> for StackPanel {
fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
GraphNodeConstructor::new::<Self>()
.with_variant("Stack Panel", |ui| {
StackPanelBuilder::new(WidgetBuilder::new().with_name("Stack Panel"))
.build(&mut ui.build_ctx())
.into()
})
.with_group("Layout")
}
}
crate::define_widget_deref!(StackPanel);
uuid_provider!(StackPanel = "d868f554-a2c5-4280-abfc-396d10a0e1ed");
impl Control for StackPanel {
fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
let mut child_constraint = Vector2::new(f32::INFINITY, f32::INFINITY);
match *self.orientation {
Orientation::Vertical => {
child_constraint.x = available_size.x;
if !self.widget.width().is_nan() {
child_constraint.x = self.widget.width();
}
child_constraint.x = child_constraint.x.clamp(self.min_width(), self.max_width());
}
Orientation::Horizontal => {
child_constraint.y = available_size.y;
if !self.widget.height().is_nan() {
child_constraint.y = self.widget.height();
}
child_constraint.y = child_constraint
.y
.clamp(self.min_height(), self.max_height());
}
}
let mut measured_size = Vector2::default();
for child_handle in self.widget.children() {
ui.measure_node(*child_handle, child_constraint);
let child = ui.node(*child_handle);
let desired = child.desired_size();
match *self.orientation {
Orientation::Vertical => {
if desired.x > measured_size.x {
measured_size.x = desired.x;
}
measured_size.y += desired.y;
}
Orientation::Horizontal => {
measured_size.x += desired.x;
if desired.y > measured_size.y {
measured_size.y = desired.y;
}
}
}
}
measured_size
}
fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
let mut width = final_size.x;
let mut height = final_size.y;
match *self.orientation {
Orientation::Vertical => height = 0.0,
Orientation::Horizontal => width = 0.0,
}
for child_handle in self.widget.children() {
let child = ui.node(*child_handle);
match *self.orientation {
Orientation::Vertical => {
let child_bounds = Rect::new(
0.0,
height,
width.max(child.desired_size().x),
child.desired_size().y,
);
ui.arrange_node(*child_handle, &child_bounds);
width = width.max(child.desired_size().x);
height += child.desired_size().y;
}
Orientation::Horizontal => {
let child_bounds = Rect::new(
width,
0.0,
child.desired_size().x,
height.max(child.desired_size().y),
);
ui.arrange_node(*child_handle, &child_bounds);
width += child.desired_size().x;
height = height.max(child.desired_size().y);
}
}
}
match *self.orientation {
Orientation::Vertical => {
height = height.max(final_size.y);
}
Orientation::Horizontal => {
width = width.max(final_size.x);
}
}
Vector2::new(width, height)
}
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.widget.handle_routed_message(ui, message);
if message.destination() == self.handle && message.direction() == MessageDirection::ToWidget
{
if let Some(StackPanelMessage::Orientation(orientation)) = message.data() {
if *orientation != *self.orientation {
self.orientation.set_value_and_mark_modified(*orientation);
self.invalidate_layout();
}
}
}
}
}
pub struct StackPanelBuilder {
widget_builder: WidgetBuilder,
orientation: Option<Orientation>,
}
impl StackPanelBuilder {
pub fn new(widget_builder: WidgetBuilder) -> Self {
Self {
widget_builder,
orientation: None,
}
}
pub fn with_orientation(mut self, orientation: Orientation) -> Self {
self.orientation = Some(orientation);
self
}
pub fn build_stack_panel(self, ctx: &BuildContext) -> StackPanel {
StackPanel {
widget: self.widget_builder.build(ctx),
orientation: self.orientation.unwrap_or(Orientation::Vertical).into(),
}
}
pub fn build_node(self, ctx: &BuildContext) -> UiNode {
UiNode::new(self.build_stack_panel(ctx))
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
ctx.add_node(self.build_node(ctx))
}
}
#[cfg(test)]
mod test {
use crate::stack_panel::StackPanelBuilder;
use crate::{test::test_widget_deletion, widget::WidgetBuilder};
#[test]
fn test_deletion() {
test_widget_deletion(|ctx| StackPanelBuilder::new(WidgetBuilder::new()).build(ctx));
}
}