keyset_key/
lib.rs

1//! This crate contains the key and legend types used for describing layouts used internally by
2//! [keyset]. It also contains utility functions for loading KLE layouts
3//!
4//! [keyset]: https://crates.io/crates/keyset
5
6#![warn(
7    missing_docs,
8    clippy::all,
9    clippy::correctness,
10    clippy::suspicious,
11    clippy::style,
12    clippy::complexity,
13    clippy::perf,
14    clippy::pedantic,
15    clippy::cargo,
16    clippy::nursery
17)]
18
19mod legend;
20
21#[cfg(feature = "kle")]
22pub mod kle;
23
24pub use legend::{Legend, Legends};
25
26use geom::{Point, Rect, Size};
27
28use color::Color;
29
30/// The type of homing used on a homing key
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum Homing {
33    /// A scooped homing key, also known as a dished homing key
34    Scoop,
35    /// A key with a homing bar, sometimes called a line
36    Bar,
37    /// A key with a homing bump, also known as a nub, dot, or nipple
38    Bump,
39}
40
41/// The shape of a key
42#[derive(Debug, Clone, Copy)]
43pub enum Shape {
44    /// Not a *key* per se, but only a legend. This is usually used for labels and is the same as a
45    /// decal in KLE
46    None(Size),
47    /// A regular key of the given size
48    Normal(Size),
49    /// A spacebar of the given size
50    Space(Size),
51    /// A homing key with the given homing type. If the homing type is [`None`] the profile's
52    /// default homing type is assumed to be used
53    Homing(Option<Homing>),
54    /// A stepped caps lock key, i.e. a 1.25u key with additional 0.5u step on the right
55    SteppedCaps,
56    /// A vertically-aligned ISO enter, i.e. an ISO enter where legends are aligned within the
57    /// vertical 1.25u &times; 2.0u section of the key
58    IsoVertical,
59    /// A horizontally-aligned ISO enter, i.e. an ISO enter where legends are aligned within the
60    /// horizontal 1.5u top section of the key
61    IsoHorizontal,
62}
63
64impl Shape {
65    /// The outer bounding rectangle of the key shape, i.e. the bounding box of the key shape. The
66    /// inner and outer bounds are the same for regular-shaped keys, but are different for stepped
67    /// keys, L-shaped keys, etc.
68    #[must_use]
69    pub fn outer_rect(self) -> Rect {
70        match self {
71            Self::None(size) | Self::Normal(size) | Self::Space(size) => {
72                Rect::from_origin_size(Point::ORIGIN, size)
73            }
74            Self::Homing(..) => Rect::from_origin_size(Point::ORIGIN, (1.0, 1.0)),
75            Self::SteppedCaps => Rect::from_origin_size(Point::ORIGIN, (1.75, 1.0)),
76            Self::IsoVertical | Self::IsoHorizontal => {
77                Rect::from_origin_size(Point::ORIGIN, (1.5, 2.0))
78            }
79        }
80    }
81
82    /// The inner bounding rectangle of the key shape, i.e. the bounds for the part of the key
83    /// containing the legend. The inner and outer bounds are the same for regular-shaped keys, but
84    /// are different for stepped keys, L-shaped keys, etc.
85    #[must_use]
86    pub fn inner_rect(self) -> Rect {
87        match self {
88            Self::None(size) | Self::Normal(size) | Self::Space(size) => {
89                Rect::from_origin_size(Point::ORIGIN, size)
90            }
91            Self::Homing(..) => Rect::from_origin_size(Point::ORIGIN, (1.0, 1.0)),
92            Self::SteppedCaps => Rect::from_origin_size(Point::ORIGIN, (1.25, 1.0)),
93            Self::IsoVertical => Rect::from_origin_size((0.25, 0.0), (1.25, 2.0)),
94            Self::IsoHorizontal => Rect::from_origin_size(Point::ORIGIN, (1.5, 1.0)),
95        }
96    }
97}
98
99/// A key
100#[derive(Debug, Clone)]
101#[non_exhaustive]
102pub struct Key {
103    /// The position of the key
104    pub position: Point,
105    /// The key's shape
106    pub shape: Shape,
107    /// The key's colour
108    pub color: Color,
109    /// The key's legends
110    pub legends: Legends,
111}
112
113impl Key {
114    /// A new blank key
115    #[must_use]
116    pub fn new() -> Self {
117        Self::default()
118    }
119
120    /// An example non-blank key
121    #[must_use]
122    pub fn example() -> Self {
123        Self {
124            legends: Legends::example(),
125            ..Self::default()
126        }
127    }
128}
129
130impl Default for Key {
131    fn default() -> Self {
132        Self {
133            position: Point::ORIGIN,
134            shape: Shape::Normal(Size::new(1., 1.)),
135            color: Color::new(0.8, 0.8, 0.8),
136            legends: Legends::default(),
137        }
138    }
139}
140
141#[cfg(test)]
142pub mod tests {
143    use assert_matches::assert_matches;
144
145    use super::*;
146
147    #[test]
148    fn shape_outer_size() {
149        assert_eq!(
150            Shape::Normal(Size::new(2.25, 1.)).outer_rect(),
151            Rect::new(0.0, 0.0, 2.25, 1.)
152        );
153        assert_eq!(
154            Shape::IsoVertical.outer_rect(),
155            Rect::new(0.0, 0.0, 1.5, 2.0)
156        );
157        assert_eq!(
158            Shape::IsoHorizontal.outer_rect(),
159            Rect::new(0.0, 0.0, 1.5, 2.0)
160        );
161        assert_eq!(
162            Shape::SteppedCaps.outer_rect(),
163            Rect::new(0.0, 0.0, 1.75, 1.0)
164        );
165    }
166
167    #[test]
168    fn shape_inner_size() {
169        assert_eq!(
170            Shape::Normal(Size::new(2.25, 1.)).inner_rect(),
171            Rect::new(0.0, 0.0, 2.25, 1.)
172        );
173        assert_eq!(
174            Shape::IsoVertical.inner_rect(),
175            Rect::new(0.25, 0.0, 1.5, 2.0)
176        );
177        assert_eq!(
178            Shape::IsoHorizontal.inner_rect(),
179            Rect::new(0.0, 0.0, 1.5, 1.0)
180        );
181        assert_eq!(
182            Shape::SteppedCaps.inner_rect(),
183            Rect::new(0.0, 0.0, 1.25, 1.0)
184        );
185    }
186
187    #[test]
188    fn key_new() {
189        let key = Key::new();
190
191        assert_eq!(key.position, Point::new(0., 0.));
192        assert_matches!(key.shape, Shape::Normal(size) if size == Size::new(1., 1.));
193        assert_eq!(key.color, Color::new(0.8, 0.8, 0.8));
194        for legend in key.legends {
195            assert!(legend.is_none());
196        }
197    }
198
199    #[test]
200    fn key_example() {
201        let key = Key::example();
202        let legend_is_some = [true, false, true, false, false, false, true, false, true];
203
204        assert_eq!(key.position, Point::new(0., 0.));
205        assert_matches!(key.shape, Shape::Normal(size) if size == Size::new(1., 1.));
206        assert_eq!(key.color, Color::new(0.8, 0.8, 0.8));
207        for (legend, is_some) in key.legends.into_iter().zip(legend_is_some) {
208            assert_eq!(legend.is_some(), is_some);
209        }
210    }
211}