kas_core/
dir.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Direction types
7
8use std::fmt;
9
10/// Trait over directional types
11///
12/// This trait has a variable implementation, [`Direction`], and several fixed
13/// implementations, [`Right`], [`Down`], [`Left`] and [`Up`].
14///
15/// Using a generic `<D: Directional>` allows compile-time substitution of
16/// direction information when parametrised with fixed implementations.
17pub trait Directional: Copy + Sized + std::fmt::Debug + 'static {
18    /// Direction flipped over diagonal (i.e. Down ↔ Right)
19    ///
20    /// This allows compile-time selection of the flipped direction.
21    type Flipped: Directional;
22
23    /// Direction reversed along axis (i.e. Left ↔ Right)
24    ///
25    /// This allows compile-time selection of the reversed direction.
26    type Reversed: Directional;
27
28    /// Flip over diagonal (i.e. Down ↔ Right)
29    #[must_use = "method does not modify self but returns a new value"]
30    fn flipped(self) -> Self::Flipped;
31
32    /// Reverse along axis (i.e. Left ↔ Right)
33    #[must_use = "method does not modify self but returns a new value"]
34    fn reversed(self) -> Self::Reversed;
35
36    /// Convert to the [`Direction`] enum
37    #[must_use = "method does not modify self but returns a new value"]
38    fn as_direction(self) -> Direction;
39
40    /// Up or Down
41    #[inline]
42    fn is_vertical(self) -> bool {
43        ((self.as_direction() as u32) & 1) == 1
44    }
45
46    /// Left or Right
47    #[inline]
48    fn is_horizontal(self) -> bool {
49        ((self.as_direction() as u32) & 1) == 0
50    }
51
52    /// Left or Up
53    #[inline]
54    fn is_reversed(self) -> bool {
55        ((self.as_direction() as u32) & 2) == 2
56    }
57}
58
59macro_rules! fixed {
60    ($d:ident, $df:ident, $dr:ident) => {
61        /// Zero-sized instantiation of [`Directional`]
62        #[derive(Copy, Clone, Default, Debug)]
63        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64        pub struct $d;
65        impl Directional for $d {
66            type Flipped = $df;
67            type Reversed = $dr;
68            #[inline]
69            fn flipped(self) -> Self::Flipped {
70                $df
71            }
72            #[inline]
73            fn reversed(self) -> Self::Reversed {
74                $dr
75            }
76            #[inline]
77            fn as_direction(self) -> Direction {
78                Direction::$d
79            }
80        }
81    };
82}
83fixed!(Left, Up, Right);
84fixed!(Right, Down, Left);
85fixed!(Up, Left, Down);
86fixed!(Down, Right, Up);
87
88/// Axis-aligned directions
89///
90/// This is a variable instantiation of [`Directional`].
91///
92/// A default direction is provided, though somewhat arbitrary: `Right`.
93#[crate::impl_default(Direction::Right)]
94#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96pub enum Direction {
97    Right = 0,
98    Down = 1,
99    Left = 2,
100    Up = 3,
101}
102
103impl fmt::Display for Direction {
104    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
105        write!(f, "{}", match self {
106            Direction::Right => "Right",
107            Direction::Down => "Down",
108            Direction::Left => "Left",
109            Direction::Up => "Up",
110        })
111    }
112}
113
114impl Directional for Direction {
115    type Flipped = Self;
116    type Reversed = Self;
117
118    fn flipped(self) -> Self::Flipped {
119        use Direction::*;
120        match self {
121            Right => Down,
122            Down => Right,
123            Left => Up,
124            Up => Left,
125        }
126    }
127
128    fn reversed(self) -> Self::Reversed {
129        use Direction::*;
130        match self {
131            Right => Left,
132            Down => Up,
133            Left => Right,
134            Up => Down,
135        }
136    }
137
138    #[inline]
139    fn as_direction(self) -> Direction {
140        self
141    }
142}
143
144bitflags! {
145    /// Multi-direction selector
146    pub struct Directions: u8 {
147        const LEFT = 0b0001;
148        const RIGHT = 0b0010;
149        const UP = 0b0100;
150        const DOWN = 0b1000;
151    }
152}
153
154#[cfg(feature = "accesskit")]
155impl From<Direction> for accesskit::Orientation {
156    #[inline]
157    fn from(dir: Direction) -> Self {
158        match dir {
159            Direction::Right | Direction::Left => accesskit::Orientation::Horizontal,
160            Direction::Down | Direction::Up => accesskit::Orientation::Vertical,
161        }
162    }
163}
164
165#[cfg(test)]
166mod test {
167    use super::*;
168    use std::mem::size_of;
169
170    #[test]
171    fn size() {
172        assert_eq!(size_of::<Left>(), 0);
173        assert_eq!(size_of::<Right>(), 0);
174        assert_eq!(size_of::<Up>(), 0);
175        assert_eq!(size_of::<Down>(), 0);
176        assert_eq!(size_of::<Direction>(), 1);
177    }
178}