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 pub version: Option<u8>,
11 pub width: f32,
13 pub height: f32,
15 pub elements: Vec<BoardElement>,
16}
17
18#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema)]
19#[serde(tag = "__type")]
20pub 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 pub id: u32,
62 pub boundaries: Vec<SerializablePoint>,
65 pub text_position: SerializablePoint,
68 pub key_codes: Vec<u32>,
69 pub text: String,
70 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 pub id: u32,
80 pub boundaries: Vec<SerializablePoint>,
83 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 pub id: u32,
229 pub location: SerializablePoint,
231 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}