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