Skip to main content

nuhxboard_types/
layout.rs

1use geo::Coord;
2pub use ordered_float::OrderedFloat;
3use schemars::{json_schema, JsonSchema};
4use serde::{Deserialize, Serialize};
5
6#[derive(Serialize, Deserialize, Default, Debug, JsonSchema)]
7#[serde(rename_all = "PascalCase")]
8pub struct Layout {
9    /// No actual meaning. Kept for parity with NohBoard layout files.
10    pub version: Option<u8>,
11    /// Width of the window in pixels
12    pub width: f32,
13    /// Height of the window in pixels
14    pub height: f32,
15    pub elements: Vec<BoardElement>,
16}
17
18#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
19#[serde(tag = "__type")]
20/// Union for different element types
21pub enum BoardElement {
22    KeyboardKey(KeyboardKeyDefinition),
23    MouseKey(CommonDefinition),
24    MouseScroll(CommonDefinition),
25    MouseSpeedIndicator(MouseSpeedIndicatorDefinition),
26}
27
28impl BoardElement {
29    pub fn id(&self) -> u32 {
30        if let Ok(def) = CommonDefinitionRef::try_from(self) {
31            *def.id
32        } else if let Self::MouseSpeedIndicator(def) = self {
33            def.id
34        } else {
35            unreachable!()
36        }
37    }
38
39    pub fn translate(&mut self, delta: Coord<f32>, move_text: bool) {
40        match self {
41            BoardElement::MouseSpeedIndicator(key) => {
42                key.location += delta;
43            }
44            _ => {
45                let common = CommonDefinitionMut::try_from(self).unwrap();
46                for boundary in common.boundaries {
47                    *boundary += delta;
48                }
49                if move_text {
50                    *common.text_position += delta;
51                }
52            }
53        }
54    }
55}
56
57#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
58#[serde(rename_all = "PascalCase")]
59pub struct KeyboardKeyDefinition {
60    /// Unique id of the element. Used by style files.
61    pub id: u32,
62    /// Vertices of the element. Used to draw a polygon for the background if no image is
63    /// supplied, and always used for graphical editing.
64    pub boundaries: Vec<SerializablePoint>,
65    /// The position of the top-left corner of the text. **Window-relative, not
66    /// element-relative**.
67    pub text_position: SerializablePoint,
68    pub key_codes: Vec<u32>,
69    pub text: String,
70    /// Text to display when Shift is held.
71    pub shift_text: String,
72    pub change_on_caps: bool,
73}
74
75#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
76#[serde(rename_all = "PascalCase")]
77pub struct CommonDefinition {
78    /// Unique id of the element. Used by style files.
79    pub id: u32,
80    /// Vertices of the element. Used to draw a polygon for the background if no image is
81    /// supplied, and always used for graphical editing.
82    pub boundaries: Vec<SerializablePoint>,
83    /// The position of the top-left corner of the text. **Window-relative, not
84    /// element-relative**.
85    pub text_position: SerializablePoint,
86    pub key_codes: Vec<u32>,
87    pub text: String,
88}
89
90impl CommonDefinition {
91    pub fn translate_face(&mut self, face: usize, delta: Coord<f32>) {
92        self.boundaries[face] += delta;
93        if face == self.boundaries.len() - 1 {
94            self.boundaries[0] += delta;
95        } else {
96            self.boundaries[face + 1] += delta;
97        }
98    }
99}
100
101impl From<KeyboardKeyDefinition> for CommonDefinition {
102    fn from(val: KeyboardKeyDefinition) -> Self {
103        CommonDefinition {
104            id: val.id,
105            text: val.text,
106            text_position: val.text_position,
107            boundaries: val.boundaries,
108            key_codes: val.key_codes,
109        }
110    }
111}
112
113impl TryFrom<BoardElement> for CommonDefinition {
114    type Error = MouseSpeedIndicatorDefinition;
115
116    fn try_from(value: BoardElement) -> Result<Self, Self::Error> {
117        match value {
118            BoardElement::KeyboardKey(key) => Ok(key.into()),
119            BoardElement::MouseKey(key) | BoardElement::MouseScroll(key) => Ok(key),
120            BoardElement::MouseSpeedIndicator(key) => Err(key),
121        }
122    }
123}
124
125pub struct CommonDefinitionRef<'a> {
126    pub id: &'a u32,
127    pub text: &'a String,
128    pub text_position: &'a SerializablePoint,
129    pub boundaries: &'a Vec<SerializablePoint>,
130    pub key_codes: &'a Vec<u32>,
131}
132
133impl<'a> From<&'a KeyboardKeyDefinition> for CommonDefinitionRef<'a> {
134    fn from(val: &'a KeyboardKeyDefinition) -> Self {
135        CommonDefinitionRef {
136            id: &val.id,
137            text: &val.text,
138            text_position: &val.text_position,
139            boundaries: &val.boundaries,
140            key_codes: &val.key_codes,
141        }
142    }
143}
144
145impl<'a> From<&'a CommonDefinition> for CommonDefinitionRef<'a> {
146    fn from(val: &'a CommonDefinition) -> Self {
147        CommonDefinitionRef {
148            id: &val.id,
149            text: &val.text,
150            text_position: &val.text_position,
151            boundaries: &val.boundaries,
152            key_codes: &val.key_codes,
153        }
154    }
155}
156
157impl<'a> TryFrom<&'a BoardElement> for CommonDefinitionRef<'a> {
158    type Error = &'a MouseSpeedIndicatorDefinition;
159
160    fn try_from(value: &'a BoardElement) -> Result<Self, Self::Error> {
161        match value {
162            BoardElement::KeyboardKey(key) => Ok(key.into()),
163            BoardElement::MouseKey(key) | BoardElement::MouseScroll(key) => Ok(key.into()),
164            BoardElement::MouseSpeedIndicator(def) => Err(def),
165        }
166    }
167}
168
169pub struct CommonDefinitionMut<'a> {
170    pub id: &'a mut u32,
171    pub text: &'a mut String,
172    pub text_position: &'a mut SerializablePoint,
173    pub boundaries: &'a mut Vec<SerializablePoint>,
174    pub key_codes: &'a mut Vec<u32>,
175}
176
177impl CommonDefinitionMut<'_> {
178    pub fn translate_face(&mut self, face: usize, delta: Coord<f32>) {
179        self.boundaries[face] += delta;
180        if face == self.boundaries.len() - 1 {
181            self.boundaries[0] += delta;
182        } else {
183            self.boundaries[face + 1] += delta;
184        }
185    }
186}
187
188impl<'a> From<&'a mut KeyboardKeyDefinition> for CommonDefinitionMut<'a> {
189    fn from(val: &'a mut KeyboardKeyDefinition) -> Self {
190        CommonDefinitionMut {
191            id: &mut val.id,
192            text: &mut val.text,
193            text_position: &mut val.text_position,
194            boundaries: &mut val.boundaries,
195            key_codes: &mut val.key_codes,
196        }
197    }
198}
199
200impl<'a> From<&'a mut CommonDefinition> for CommonDefinitionMut<'a> {
201    fn from(val: &'a mut CommonDefinition) -> Self {
202        CommonDefinitionMut {
203            id: &mut val.id,
204            text: &mut val.text,
205            text_position: &mut val.text_position,
206            boundaries: &mut val.boundaries,
207            key_codes: &mut val.key_codes,
208        }
209    }
210}
211
212impl<'a> TryFrom<&'a mut BoardElement> for CommonDefinitionMut<'a> {
213    type Error = &'a mut MouseSpeedIndicatorDefinition;
214
215    fn try_from(value: &'a mut BoardElement) -> Result<Self, Self::Error> {
216        match value {
217            BoardElement::KeyboardKey(key) => Ok(key.into()),
218            BoardElement::MouseKey(key) | BoardElement::MouseScroll(key) => Ok(key.into()),
219            BoardElement::MouseSpeedIndicator(key) => Err(key),
220        }
221    }
222}
223
224#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
225#[serde(rename_all = "PascalCase")]
226pub struct MouseSpeedIndicatorDefinition {
227    /// Unique id of the element. Used by style files.
228    pub id: u32,
229    /// Position of the center of the indicator.
230    pub location: SerializablePoint,
231    /// Radius of the outer ring.
232    pub radius: f32,
233}
234
235#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
236#[serde(rename_all = "PascalCase")]
237pub struct SerializablePoint {
238    pub x: OrderedFloat<f32>,
239    pub y: OrderedFloat<f32>,
240}
241
242impl JsonSchema for SerializablePoint {
243    fn schema_name() -> std::borrow::Cow<'static, str> {
244        "SerializablePoint".into()
245    }
246
247    fn schema_id() -> std::borrow::Cow<'static, str> {
248        "nuhxboard_types::layout::SerializablePoint".into()
249    }
250
251    fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
252        json_schema!({
253            "$schema": "https://json-schema.org/draft/2029-12/schema",
254            "properties": {
255                "X": {
256                    "format": "float",
257                    "type": "number"
258                },
259                "Y": {
260                    "format": "float",
261                    "type": "number"
262                }
263            },
264            "required": ["X", "Y"],
265            "title": "SerializablePoint",
266            "type": "object"
267        })
268    }
269}
270
271impl From<SerializablePoint> for iced::Point {
272    fn from(point: SerializablePoint) -> Self {
273        iced::Point::new(*point.x, *point.y)
274    }
275}
276
277impl From<iced::Point> for SerializablePoint {
278    fn from(point: iced::Point) -> Self {
279        Self {
280            x: OrderedFloat(point.x),
281            y: OrderedFloat(point.y),
282        }
283    }
284}
285
286impl From<SerializablePoint> for Coord<f32> {
287    fn from(value: SerializablePoint) -> Self {
288        Self {
289            x: *value.x,
290            y: *value.y,
291        }
292    }
293}
294
295impl From<Coord<f32>> for SerializablePoint {
296    fn from(value: Coord<f32>) -> Self {
297        Self {
298            x: OrderedFloat(value.x),
299            y: OrderedFloat(value.y),
300        }
301    }
302}
303
304impl std::ops::AddAssign<Coord<f32>> for SerializablePoint {
305    fn add_assign(&mut self, rhs: Coord<f32>) {
306        self.x += rhs.x;
307        self.y += rhs.y;
308    }
309}
310
311impl std::ops::SubAssign<Coord<f32>> for SerializablePoint {
312    fn sub_assign(&mut self, rhs: Coord<f32>) {
313        self.x -= rhs.x;
314        self.y -= rhs.y;
315    }
316}
317
318impl std::fmt::Display for SerializablePoint {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        write!(f, "({}, {})", self.x, self.y)
321    }
322}