bevy_lunex/
layouts.rs

1use crate::*;
2
3// Exported prelude
4pub mod prelude {
5    // All standard exports
6    pub use super::{
7        Align,
8        Scaling,
9    };
10}
11
12// #============================#
13// #=== MULTIPURPOSE STRUCTS ===#
14
15/// **Rectangle 2D** - Contains computed values from node layout.
16#[derive(Debug, Default, Clone, Copy, PartialEq, Reflect)]
17pub struct Rectangle2D {
18    pub pos : Vec2,
19    pub size: Vec2,
20}
21impl Rectangle2D {
22    pub fn lerp(self, rhs: Self, lerp: f32) -> Self {
23        Rectangle2D {
24            pos: self.pos.lerp(rhs.pos, lerp),
25            size: self.size.lerp(rhs.size, lerp),
26        }
27    }
28}
29impl Rectangle2D {
30    /// A new empty [`Rectangle2D`]. Has `0` size.
31    pub const EMPTY: Rectangle2D = Rectangle2D { pos : Vec2::ZERO, size: Vec2::ZERO };
32    /// Creates new empty Window layout.
33    pub const fn new() -> Self {
34        Rectangle2D::EMPTY
35    }
36    /// Replaces the position with the new value.
37    pub fn with_pos(mut self, pos: impl Into<Vec2>) -> Self {
38        self.pos = pos.into();
39        self
40    }
41    /// Replaces the x position with the new value.
42    pub fn with_x(mut self, width: f32) -> Self {
43        self.pos.x = width;
44        self
45    }
46    /// Replaces the y position with the new value.
47    pub fn with_y(mut self, height: f32) -> Self {
48        self.pos.y = height;
49        self
50    }
51    /// Replaces the size with the new value.
52    pub fn with_size(mut self, size: impl Into<Vec2>) -> Self {
53        self.size = size.into();
54        self
55    }
56    /// Replaces the width with the new value.
57    pub fn with_width(mut self, width: f32) -> Self {
58        self.size.x = width;
59        self
60    }
61    /// Replaces the height with the new value.
62    pub fn with_height(mut self, height: f32) -> Self {
63        self.size.y = height;
64        self
65    }
66}
67
68/// **Align** - A type used to define alignment in a node layout.
69/// ## 🛠️ Example
70/// ```
71/// # use bevy_lunex::*;
72/// let align: Align = Align::START; // -> -1.0
73/// let align: Align = Align(-1.0);  // -> -1.0
74/// let align: Align = (-1.0).into();  // -> -1.0
75/// ```
76/// The expected range is `-1.0` to `1.0`, but you can extrapolate.
77#[derive(Debug, Default, Clone, Copy, PartialEq, Reflect)]
78pub struct Align (pub f32);
79impl Align {
80    pub const START: Align = Align(-1.0);
81    pub const LEFT: Align = Align(-1.0);
82    pub const CENTER: Align = Align(0.0);
83    pub const MIDDLE: Align = Align(0.0);
84    pub const END: Align = Align(1.0);
85    pub const RIGHT: Align = Align(1.0);
86}
87impl From<f32> for Align {
88    fn from(val: f32) -> Self {
89        Align(val)
90    }
91}
92
93
94/// **Scaling** - A type used to define how should a Solid node layout scale relative to a parent.
95/// ## 🛠️ Example
96/// ```
97/// # use bevy_lunex::*;
98/// let scaling: Scaling = Scaling::HorFill; // -> always cover the horizontal axis
99/// let scaling: Scaling = Scaling::VerFill; // -> always cover the vertical axis
100/// let scaling: Scaling = Scaling::Fit;  // -> always fit inside
101/// let scaling: Scaling = Scaling::Fill; // -> always cover all
102/// ```
103#[derive(Debug, Default, Clone, Copy, PartialEq, Reflect)]
104pub enum Scaling {
105    /// Node layout should always cover the horizontal axis of the parent node.
106    HorFill,
107    /// Node layout should always cover the vertical axis of the parent node.
108    VerFill,
109    /// Node layout should always fit inside the parent node.
110    #[default] Fit,
111    /// Node layout should always cover all of the parent node.
112    Fill,
113}
114
115
116// #====================#
117// #=== LAYOUT TYPES ===#
118
119/// **Ui Layout Type** - Enum holding all UI layout variants.
120#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
121pub enum UiLayoutType {
122    Boundary(UiLayoutTypeBoundary),
123    Window(UiLayoutTypeWindow),
124    Solid(UiLayoutTypeSolid),
125}
126impl UiLayoutType {
127    /// Computes the layout based on given parameters.
128    pub(crate) fn compute(&self, parent: &Rectangle2D, absolute_scale: f32, viewport_size: Vec2, font_size: f32) -> Rectangle2D {
129        match self {
130            UiLayoutType::Boundary(layout) => layout.compute(parent, absolute_scale, viewport_size, font_size),
131            UiLayoutType::Window(layout) => layout.compute(parent, absolute_scale, viewport_size, font_size),
132            UiLayoutType::Solid(layout) => layout.compute(parent, absolute_scale, viewport_size, font_size),
133        }
134    }
135}
136impl From<UiLayoutTypeBoundary> for UiLayoutType {
137    fn from(value: UiLayoutTypeBoundary) -> Self {
138        UiLayoutType::Boundary(value)
139    }
140}
141impl From<UiLayoutTypeWindow> for UiLayoutType {
142    fn from(value: UiLayoutTypeWindow) -> Self {
143        UiLayoutType::Window(value)
144    }
145}
146impl From<UiLayoutTypeSolid> for UiLayoutType {
147    fn from(value: UiLayoutTypeSolid) -> Self {
148        UiLayoutType::Solid(value)
149    }
150}
151
152
153/// **Boundary** - Declarative layout type that is defined by its top-left corner and bottom-right corner.
154#[derive(Debug, Default, Clone, Copy, PartialEq, Reflect)]
155pub struct UiLayoutTypeBoundary {
156    /// Position of the top-left corner.
157    pub pos1: UiValue<Vec2>,
158    /// Position of the bottom-right corner.
159    pub pos2: UiValue<Vec2>,
160}
161impl UiLayoutTypeBoundary {
162    /// Creates new empty Boundary node layout.
163    pub const fn new() -> Self {
164        Self {
165            pos1: UiValue::new(),
166            pos2: UiValue::new(),
167        }
168    }
169    /// Replaces the position of the top-left corner with a new value.
170    pub fn pos1(mut self, pos: impl Into<UiValue<Vec2>>) -> Self {
171        self.pos1 = pos.into();
172        self
173    }
174    /// Replaces the position of the bottom-right corner with a new value.
175    pub fn pos2(mut self, pos: impl Into<UiValue<Vec2>>) -> Self {
176        self.pos2 = pos.into();
177        self
178    }
179    /// Replaces the x position of the top-left corner with a new value.
180    pub fn x1(mut self, x: impl Into<UiValue<f32>>) -> Self {
181        self.pos1.set_x(x);
182        self
183    }
184    /// Replaces the y position of the top-left corner with a new value.
185    pub fn y1(mut self, y: impl Into<UiValue<f32>>) -> Self {
186        self.pos1.set_y(y);
187        self
188    }
189    /// Replaces the x position of the bottom-right corner with a new value.
190    pub fn x2(mut self, x: impl Into<UiValue<f32>>) -> Self {
191        self.pos2.set_x(x);
192        self
193    }
194    /// Replaces the y position of the bottom-right corner with a new value.
195    pub fn y2(mut self, y: impl Into<UiValue<f32>>) -> Self {
196        self.pos2.set_y(y);
197        self
198    }
199    /// Sets the position of the top-left corner to a new value.
200    pub fn set_pos1(&mut self, pos: impl Into<UiValue<Vec2>>) {
201        self.pos1 = pos.into();
202    }
203    /// Sets the position of the bottom-right corner to a new value.
204    pub fn set_pos2(&mut self, pos: impl Into<UiValue<Vec2>>) {
205        self.pos2 = pos.into();
206    }
207    /// Sets the x position of the top-left corner to a new value.
208    pub fn set_x1(&mut self, x: impl Into<UiValue<f32>>) {
209        self.pos1.set_x(x);
210    }
211    /// Sets the y position of the top-left corner to a new value.
212    pub fn set_y1(&mut self, y: impl Into<UiValue<f32>>) {
213        self.pos1.set_y(y);
214    }
215    /// Sets the x position of the bottom-right corner to a new value.
216    pub fn set_x2(&mut self, x: impl Into<UiValue<f32>>) {
217        self.pos2.set_x(x);
218    }
219    /// Sets the y position of the bottom-right corner to a new value.
220    pub fn set_y2(&mut self, y: impl Into<UiValue<f32>>) {
221        self.pos2.set_y(y);
222    }
223    /// Pack the layout type into UiLayout
224    pub fn pack(self) -> UiLayout {
225        UiLayout::from(self)
226    }
227    /// Wrap the layout type into UiLayout
228    pub fn wrap(self) -> UiLayoutType {
229        UiLayoutType::from(self)
230    }
231    /// Computes the layout based on given parameters.
232    pub(crate) fn compute(&self, parent: &Rectangle2D, absolute_scale: f32, viewport_size: Vec2, font_size: f32) -> Rectangle2D {
233        let pos1 = self.pos1.evaluate(Vec2::splat(absolute_scale), parent.size, viewport_size, Vec2::splat(font_size));
234        let pos2 = self.pos2.evaluate(Vec2::splat(absolute_scale), parent.size, viewport_size, Vec2::splat(font_size));
235        let size = pos2 - pos1;
236        Rectangle2D {
237            pos: -parent.size / 2.0 + pos1 + size/2.0,
238            size,
239        }
240    }
241}
242
243/// **Window** - Declarative layout type that is defined by its size and position.
244#[derive(Debug, Default, Clone, Copy, PartialEq, Reflect)]
245pub struct UiLayoutTypeWindow {
246    /// Position of the node.
247    pub pos : UiValue<Vec2>,
248    /// Decides where position should be applied at.
249    pub anchor: Anchor,
250    /// Size of the node layout.
251    pub size: UiValue<Vec2>,
252}
253impl UiLayoutTypeWindow {
254    /// Creates new empty Window node layout.
255    pub const fn new() -> Self {
256        Self {
257            pos: UiValue::new(),
258            anchor: Anchor::TOP_LEFT,
259            size: UiValue::new(),
260        }
261    }
262    /// Replaces the size to make the window fully cover the parent.
263    pub fn full(self) -> Self {
264        self.size(Rl(100.0))
265    }
266    /// Replaces the position with a new value.
267    pub fn pos(mut self, pos: impl Into<UiValue<Vec2>>) -> Self {
268        self.pos = pos.into();
269        self
270    }
271    /// Replaces the x position with a new value.
272    pub fn x(mut self, x: impl Into<UiValue<f32>>) -> Self {
273        self.pos.set_x(x);
274        self
275    }
276    /// Replaces the y position with a new value.
277    pub fn y(mut self, y: impl Into<UiValue<f32>>) -> Self {
278        self.pos.set_y(y);
279        self
280    }
281    /// Replaces the size with a new value.
282    pub fn size(mut self, size: impl Into<UiValue<Vec2>>) -> Self {
283        self.size = size.into();
284        self
285    }
286    /// Replaces the width with a new value.
287    pub fn width(mut self, width: impl Into<UiValue<f32>>) -> Self {
288        self.size.set_x(width);
289        self
290    }
291    /// Replaces the height with a new value.
292    pub fn height(mut self, height: impl Into<UiValue<f32>>) -> Self {
293        self.size.set_y(height);
294        self
295    }
296    /// Replaces the anchor with a new value.
297    pub fn anchor(mut self, anchor: impl Into<Anchor>) -> Self {
298        self.anchor = anchor.into();
299        self
300    }
301    /// Sets the position to a new value.
302    pub fn set_pos(&mut self, pos: impl Into<UiValue<Vec2>>){
303        self.pos = pos.into();
304    }
305    /// Sets the x position to a new value.
306    pub fn set_x(&mut self, x: impl Into<UiValue<f32>>){
307        self.pos.set_x(x);
308    }
309    /// Sets the y position to a new value.
310    pub fn set_y(&mut self, y: impl Into<UiValue<f32>>){
311        self.pos.set_y(y);
312    }
313    /// Sets the size to a new value.
314    pub fn set_size(&mut self, size: impl Into<UiValue<Vec2>>){
315        self.size = size.into();
316    }
317    /// Sets the width to a new value.
318    pub fn set_width(&mut self, width: impl Into<UiValue<f32>>){
319        self.size.set_x(width);
320    }
321    /// Sets the height to a new value.
322    pub fn set_height(&mut self, height: impl Into<UiValue<f32>>){
323        self.size.set_y(height);
324    }
325    /// Sets the anchor to a new value.
326    pub fn set_anchor(&mut self, anchor: impl Into<Anchor>){
327        self.anchor = anchor.into();
328    }
329    /// Pack the layout type into UiLayout
330    pub fn pack(self) -> UiLayout {
331        UiLayout::from(self)
332    }
333    /// Wrap the layout type into UiLayout
334    pub fn wrap(self) -> UiLayoutType {
335        UiLayoutType::from(self)
336    }
337    /// Computes the layout based on given parameters.
338    pub(crate) fn compute(&self, parent: &Rectangle2D, absolute_scale: f32, viewport_size: Vec2, font_size: f32) -> Rectangle2D {
339        let pos = self.pos.evaluate(Vec2::splat(absolute_scale), parent.size, viewport_size, Vec2::splat(font_size));
340        let size = self.size.evaluate(Vec2::splat(absolute_scale), parent.size, viewport_size, Vec2::splat(font_size));
341        let mut anchor = self.anchor.as_vec();
342        anchor.y *= -1.0;
343        Rectangle2D {
344            pos: -parent.size / 2.0 + pos - size * (anchor),
345            size,
346        }
347    }
348}
349
350/// **Solid** - Declarative layout type that is defined by its width and height ratio.
351#[derive(Debug, Default, Clone, Copy, PartialEq, Reflect)]
352pub struct UiLayoutTypeSolid {
353    /// Aspect ratio of the width and height. `1:1 == 10:10 == 100:100`.
354    pub size: UiValue<Vec2>,
355    /// Horizontal alignment within parent.
356    pub align_x: Align,
357    /// Vertical alignment within parent.
358    pub align_y: Align,
359    /// Specifies container scaling.
360    pub scaling: Scaling,
361}
362impl UiLayoutTypeSolid {
363    /// Creates new empty Solid node layout.
364    pub fn new() -> Self {
365        Self {
366            size: Ab(Vec2::ONE).into(),
367            align_x: Align::CENTER,
368            align_y: Align::CENTER,
369            scaling: Scaling::Fit,
370        }
371    }
372    /// Replaces the size with a new value.
373    pub fn size(mut self, size: impl Into<UiValue<Vec2>>) -> Self {
374        self.size = size.into();
375        self
376    }
377    /// Replaces the width with a new value.
378    pub fn width(mut self, width: impl Into<UiValue<f32>>) -> Self {
379        self.size.set_x(width);
380        self
381    }
382    /// Replaces the height with a new value.
383    pub fn height(mut self, height: impl Into<UiValue<f32>>) -> Self {
384        self.size.set_y(height);
385        self
386    }
387    /// Replaces the x alignment with a new value.
388    pub fn align_x(mut self, align: impl Into<Align>) -> Self {
389        self.align_x = align.into();
390        self
391    }
392    /// Replaces the y alignment with a new value.
393    pub fn align_y(mut self, align: impl Into<Align>) -> Self {
394        self.align_y = align.into();
395        self
396    }
397    /// Replaces the scaling mode with a new value.
398    pub fn scaling(mut self, scaling: Scaling) -> Self {
399        self.scaling = scaling;
400        self
401    }
402    /// Sets the size to a new value.
403    pub fn set_size(&mut self, size: impl Into<UiValue<Vec2>>) {
404        self.size = size.into();
405    }
406    /// Sets the width to a new value.
407    pub fn set_width(&mut self, width: impl Into<UiValue<f32>>) {
408        self.size.set_x(width);
409    }
410    /// Sets the height to a new value.
411    pub fn set_height(&mut self, height: impl Into<UiValue<f32>>) {
412        self.size.set_y(height);
413    }
414    /// Sets the x alignment to a new value.
415    pub fn set_align_x(&mut self, align: impl Into<Align>) {
416        self.align_x = align.into();
417    }
418    /// Sets the y alignment to a new value.
419    pub fn set_align_y(&mut self, align: impl Into<Align>) {
420        self.align_y = align.into();
421    }
422    /// Sets the scaling mode to a new value.
423    pub fn set_scaling(&mut self, scaling: Scaling) {
424        self.scaling = scaling;
425    }
426    /// Pack the layout type into UiLayout
427    pub fn pack(self) -> UiLayout {
428        UiLayout::from(self)
429    }
430    /// Wrap the layout type into UiLayout
431    pub fn wrap(self) -> UiLayoutType {
432        UiLayoutType::from(self)
433    }
434    /// Computes the layout based on given parameters.
435    pub(crate) fn compute(&self, parent: &Rectangle2D, absolute_scale: f32, viewport_size: Vec2, font_size: f32) -> Rectangle2D {
436
437        let size = self.size.evaluate(Vec2::splat(absolute_scale), parent.size, viewport_size, Vec2::splat(font_size));
438
439        let scale = match self.scaling {
440            Scaling::HorFill => parent.size.x / size.x,
441            Scaling::VerFill => parent.size.y / size.y,
442            Scaling::Fit => f32::min(parent.size.x / size.x, parent.size.y / size.y),
443            Scaling::Fill => f32::max(parent.size.x / size.x, parent.size.y / size.y),
444        };
445
446        let center_point = parent.size / 2.0;
447
448        let computed_width = size.x * scale;
449        let computed_height = size.y * scale;
450        let computed_point = Vec2::new(center_point.x - computed_width / 2.0, center_point.y - computed_height / 2.0);
451
452        Rectangle2D {
453            pos: Vec2::new(
454                computed_point.x * self.align_x.0,
455                computed_point.y * self.align_y.0,
456            ),
457            size: (computed_width, computed_height).into(),
458        }
459    }
460}