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 center of the parent.
35    pub const CENTER: Self = Self::new(
36        HorizontalAlignment::CenterHorizontally,
37        VerticalAlignment::CenterVertically,
38    );
39
40    /// Align children to the bottom-end corner.
41    pub const BOTTOM_END: Self = Self::new(HorizontalAlignment::End, VerticalAlignment::Bottom);
42}
43
44/// Alignment along the horizontal axis.
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum HorizontalAlignment {
47    /// Align children to the leading edge.
48    Start,
49    /// Align children to the horizontal center.
50    CenterHorizontally,
51    /// Align children to the trailing edge.
52    End,
53}
54
55impl HorizontalAlignment {
56    /// Compose's bias for this alignment: -1 the start, 0 the middle, 1 the end.
57    pub fn bias(&self) -> f32 {
58        match self {
59            HorizontalAlignment::Start => -1.0,
60            HorizontalAlignment::CenterHorizontally => 0.0,
61            HorizontalAlignment::End => 1.0,
62        }
63    }
64
65    /// The offset of a `child` wide in `available` width, on the device
66    /// pixel grid of `density`: see [`bias_offset`].
67    pub fn align(&self, available: f32, child: f32, density: f32) -> f32 {
68        bias_offset(self.bias(), available, child, density)
69    }
70}
71
72/// Alignment along the vertical axis.
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum VerticalAlignment {
75    /// Align children to the top edge.
76    Top,
77    /// Align children to the vertical center.
78    CenterVertically,
79    /// Align children to the bottom edge.
80    Bottom,
81}
82
83impl VerticalAlignment {
84    /// Compose's bias for this alignment: -1 the top, 0 the middle, 1 the bottom.
85    pub fn bias(&self) -> f32 {
86        match self {
87            VerticalAlignment::Top => -1.0,
88            VerticalAlignment::CenterVertically => 0.0,
89            VerticalAlignment::Bottom => 1.0,
90        }
91    }
92
93    /// The offset of a `child` tall in `available` height, on the device
94    /// pixel grid of `density`: see [`bias_offset`].
95    pub fn align(&self, available: f32, child: f32, density: f32) -> f32 {
96        bias_offset(self.bias(), available, child, density)
97    }
98}
99
100#[cfg(test)]
101#[path = "tests/alignment_tests.rs"]
102mod tests;