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