1#![warn(missing_docs)]
25#![allow(clippy::reversed_empty_ranges)]
26
27use crate::{
28 core::{
29 algebra::Vector2, math::Rect, pool::Handle, reflect::prelude::*, type_traits::prelude::*,
30 visitor::prelude::*,
31 },
32 define_constructor,
33 message::{MessageDirection, UiMessage},
34 widget::{Widget, WidgetBuilder},
35 BuildContext, Control, Orientation, UiNode, UserInterface,
36};
37
38use core::f32;
39use fyrox_core::uuid_provider;
40use fyrox_core::variable::InheritableVariable;
41use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
42use fyrox_graph::BaseSceneGraph;
43use std::{
44 cell::RefCell,
45 ops::{Deref, DerefMut, Range},
46};
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum WrapPanelMessage {
51 Orientation(Orientation),
53}
54
55impl WrapPanelMessage {
56 define_constructor!(
57 WrapPanelMessage:Orientation => fn orientation(Orientation), layout: false
59 );
60}
61
62#[derive(Default, Clone, Debug, Visit, Reflect, ComponentProvider)]
91#[reflect(derived_type = "UiNode")]
92pub struct WrapPanel {
93 pub widget: Widget,
95 pub orientation: InheritableVariable<Orientation>,
97 #[visit(skip)]
99 #[reflect(hidden)]
100 pub lines: RefCell<Vec<Line>>,
101}
102
103impl ConstructorProvider<UiNode, UserInterface> for WrapPanel {
104 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
105 GraphNodeConstructor::new::<Self>()
106 .with_variant("Wrap Panel", |ui| {
107 WrapPanelBuilder::new(WidgetBuilder::new().with_name("Wrap Panel"))
108 .build(&mut ui.build_ctx())
109 .into()
110 })
111 .with_group("Layout")
112 }
113}
114
115crate::define_widget_deref!(WrapPanel);
116
117#[derive(Clone, Debug)]
119pub struct Line {
120 pub children: Range<usize>,
122 pub bounds: Rect<f32>,
124}
125
126impl Default for Line {
127 fn default() -> Self {
128 Self {
129 children: 0..0,
130 bounds: Default::default(),
131 }
132 }
133}
134
135uuid_provider!(WrapPanel = "f488ab8e-8f8b-473c-a450-5ac33f1afb39");
136
137impl Control for WrapPanel {
138 fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
139 let mut measured_size: Vector2<f32> = Vector2::default();
140 let mut line_size = Vector2::default();
141 for child_handle in self.widget.children() {
142 let child = ui.node(*child_handle);
143 ui.measure_node(*child_handle, Vector2::new(f32::INFINITY, f32::INFINITY));
144 let desired = child.desired_size();
145 match *self.orientation {
146 Orientation::Vertical => {
147 if line_size.y + desired.y > available_size.y {
148 measured_size.y = measured_size.y.max(line_size.y);
150 measured_size.x += line_size.x;
151 line_size = Vector2::default();
152 }
153 line_size.x = line_size.x.max(desired.x);
154 line_size.y += desired.y;
155 }
156 Orientation::Horizontal => {
157 if line_size.x + desired.x > available_size.x {
158 measured_size.x = measured_size.x.max(line_size.x);
160 measured_size.y += line_size.y;
161 line_size = Vector2::default();
162 }
163 line_size.x += desired.x;
164 line_size.y = line_size.y.max(desired.y);
165 }
166 }
167 }
168
169 match *self.orientation {
171 Orientation::Vertical => {
172 measured_size.y = measured_size.y.max(line_size.y);
173 measured_size.x += line_size.x;
174 }
175 Orientation::Horizontal => {
176 measured_size.x = measured_size.x.max(line_size.x);
177 measured_size.y += line_size.y;
178 }
179 }
180
181 measured_size
182 }
183
184 fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
185 let mut lines = self.lines.borrow_mut();
187 lines.clear();
188 let mut line = Line::default();
189 for child_handle in self.widget.children() {
190 let child = ui.node(*child_handle);
191 let desired = child.desired_size();
192 match *self.orientation {
193 Orientation::Vertical => {
194 if line.bounds.h() + desired.y > final_size.y {
195 lines.push(line.clone());
197 line.bounds.position.x += line.bounds.w();
199 line.bounds.position.y = 0.0;
200 line.bounds.size.x = desired.x;
201 line.bounds.size.y = desired.y;
202 line.children.start = line.children.end;
204 line.children.end = line.children.start + 1;
205 } else {
206 line.bounds.size.y += desired.y;
207 line.bounds.size.x = line.bounds.w().max(desired.x);
208 line.children.end += 1;
209 }
210 }
211 Orientation::Horizontal => {
212 if line.bounds.w() + desired.x > final_size.x {
213 lines.push(line.clone());
215 line.bounds.position.x = 0.0;
217 line.bounds.position.y += line.bounds.h();
218 line.bounds.size.x = desired.x;
219 line.bounds.size.y = desired.y;
220 line.children.start = line.children.end;
222 line.children.end = line.children.start + 1;
223 } else {
224 line.bounds.size.x += desired.x;
225 line.bounds.size.y = line.bounds.h().max(desired.y);
226 line.children.end += 1;
227 }
228 }
229 }
230 }
231
232 lines.push(line);
234
235 let mut full_size = Vector2::default();
237 for line in lines.iter() {
238 let mut cursor = line.bounds.position;
239 for child_index in line.children.clone() {
240 let child_handle = self.children()[child_index];
241 let child = ui.node(child_handle);
242 let desired = child.desired_size();
243 match *self.orientation {
244 Orientation::Vertical => {
245 let child_bounds =
246 Rect::new(line.bounds.x(), cursor.y, line.bounds.w(), desired.y);
247 ui.arrange_node(child_handle, &child_bounds);
248 cursor.y += desired.y;
249 }
250 Orientation::Horizontal => {
251 let child_bounds =
252 Rect::new(cursor.x, line.bounds.y(), desired.x, line.bounds.h());
253 ui.arrange_node(child_handle, &child_bounds);
254 cursor.x += desired.x;
255 }
256 }
257 }
258 match *self.orientation {
259 Orientation::Vertical => {
260 full_size.x += line.bounds.w();
261 full_size.y = final_size.y.max(line.bounds.h());
262 }
263 Orientation::Horizontal => {
264 full_size.x = final_size.x.max(line.bounds.w());
265 full_size.y += line.bounds.h();
266 }
267 }
268 }
269
270 full_size
271 }
272
273 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
274 self.widget.handle_routed_message(ui, message);
275
276 if message.destination() == self.handle && message.direction() == MessageDirection::ToWidget
277 {
278 if let Some(WrapPanelMessage::Orientation(orientation)) = message.data() {
279 if *orientation != *self.orientation {
280 self.orientation.set_value_and_mark_modified(*orientation);
281 self.invalidate_layout();
282 }
283 }
284 }
285 }
286}
287
288pub struct WrapPanelBuilder {
290 widget_builder: WidgetBuilder,
291 orientation: Option<Orientation>,
292}
293
294impl WrapPanelBuilder {
295 pub fn new(widget_builder: WidgetBuilder) -> Self {
297 Self {
298 widget_builder,
299 orientation: None,
300 }
301 }
302
303 pub fn with_orientation(mut self, orientation: Orientation) -> Self {
305 self.orientation = Some(orientation);
306 self
307 }
308
309 pub fn build_node(self, ctx: &BuildContext) -> UiNode {
311 let stack_panel = WrapPanel {
312 widget: self.widget_builder.build(ctx),
313 orientation: self.orientation.unwrap_or(Orientation::Vertical).into(),
314 lines: Default::default(),
315 };
316
317 UiNode::new(stack_panel)
318 }
319
320 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
322 ctx.add_node(self.build_node(ctx))
323 }
324}
325
326#[cfg(test)]
327mod test {
328 use crate::wrap_panel::WrapPanelBuilder;
329 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
330
331 #[test]
332 fn test_deletion() {
333 test_widget_deletion(|ctx| WrapPanelBuilder::new(WidgetBuilder::new()).build(ctx));
334 }
335}