Skip to main content

cranpose_ui_layout/
alignment.rs

1//! Alignment utilities for positioning content
2
3use crate::round_to_px;
4
5/// Compose's `BiasAlignment`: the offset that puts `child` at `bias` of
6/// `available` (-1 the start, 0 the middle, 1 the end), on a whole device
7/// pixel of `density` as Kotlin's `roundToInt` rounds it, and before the
8/// start when the child is the larger.
9pub fn bias_offset(bias: f32, available: f32, child: f32, density: f32) -> f32 {
10    round_to_px((available - child) / 2.0 * (1.0 + bias), density)
11}
12
13/// Alignment across both axes used for positioning content within a box.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct Alignment {
16    /// Horizontal alignment component.
17    pub horizontal: HorizontalAlignment,
18    /// Vertical alignment component.
19    pub vertical: VerticalAlignment,
20}
21
22impl Alignment {
23    /// Creates a new [`Alignment`] from explicit horizontal and vertical components.
24    pub const fn new(horizontal: HorizontalAlignment, vertical: VerticalAlignment) -> Self {
25        Self {
26            horizontal,
27            vertical,
28        }
29    }
30
31    /// Align children to the top-start corner.
32    pub const TOP_START: Self = Self::new(HorizontalAlignment::Start, VerticalAlignment::Top);
33
34    /// Align children to the middle of the top edge.
35    pub const TOP_CENTER: Self = Self::new(
36        HorizontalAlignment::CenterHorizontally,
37        VerticalAlignment::Top,
38    );
39
40    /// Align children to the top-end corner.
41    pub const TOP_END: Self = Self::new(HorizontalAlignment::End, VerticalAlignment::Top);
42
43    /// Align children to the middle of the start edge.
44    pub const CENTER_START: Self = Self::new(
45        HorizontalAlignment::Start,
46        VerticalAlignment::CenterVertically,
47    );
48
49    /// Align children to the center of the parent.
50    pub const CENTER: Self = Self::new(
51        HorizontalAlignment::CenterHorizontally,
52        VerticalAlignment::CenterVertically,
53    );
54
55    /// Align children to the middle of the end edge.
56    pub const CENTER_END: Self = Self::new(
57        HorizontalAlignment::End,
58        VerticalAlignment::CenterVertically,
59    );
60
61    /// Align children to the bottom-start corner.
62    pub const BOTTOM_START: Self = Self::new(HorizontalAlignment::Start, VerticalAlignment::Bottom);
63
64    /// Align children to the middle of the bottom edge.
65    pub const BOTTOM_CENTER: Self = Self::new(
66        HorizontalAlignment::CenterHorizontally,
67        VerticalAlignment::Bottom,
68    );
69
70    /// Align children to the bottom-end corner.
71    pub const BOTTOM_END: Self = Self::new(HorizontalAlignment::End, VerticalAlignment::Bottom);
72}
73
74/// Alignment along the horizontal axis.
75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76pub enum HorizontalAlignment {
77    /// Align children to the leading edge.
78    Start,
79    /// Align children to the horizontal center.
80    CenterHorizontally,
81    /// Align children to the trailing edge.
82    End,
83}
84
85impl HorizontalAlignment {
86    /// Compose's bias for this alignment: -1 the start, 0 the middle, 1 the end.
87    pub fn bias(&self) -> f32 {
88        match self {
89            HorizontalAlignment::Start => -1.0,
90            HorizontalAlignment::CenterHorizontally => 0.0,
91            HorizontalAlignment::End => 1.0,
92        }
93    }
94
95    /// The offset of a `child` wide in `available` width, on the device
96    /// pixel grid of `density`: see [`bias_offset`].
97    pub fn align(&self, available: f32, child: f32, density: f32) -> f32 {
98        bias_offset(self.bias(), available, child, density)
99    }
100}
101
102/// Alignment along the vertical axis.
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum VerticalAlignment {
105    /// Align children to the top edge.
106    Top,
107    /// Align children to the vertical center.
108    CenterVertically,
109    /// Align children to the bottom edge.
110    Bottom,
111}
112
113impl VerticalAlignment {
114    /// Compose's bias for this alignment: -1 the top, 0 the middle, 1 the bottom.
115    pub fn bias(&self) -> f32 {
116        match self {
117            VerticalAlignment::Top => -1.0,
118            VerticalAlignment::CenterVertically => 0.0,
119            VerticalAlignment::Bottom => 1.0,
120        }
121    }
122
123    /// The offset of a `child` tall in `available` height, on the device
124    /// pixel grid of `density`: see [`bias_offset`].
125    pub fn align(&self, available: f32, child: f32, density: f32) -> f32 {
126        bias_offset(self.bias(), available, child, density)
127    }
128}
129
130#[cfg(test)]
131#[path = "tests/alignment_tests.rs"]
132mod tests;