concinnity_asset/window.rs
1// Application window schema.
2
3use alloc::string::String;
4use alloc::string::ToString;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
7#[serde(rename_all = "camelCase")]
8#[derive(Default)]
9/// How the application window is presented.
10pub enum WindowMode {
11 /// A resizable desktop window.
12 #[default]
13 Windowed,
14 /// Exclusive fullscreen at the display's mode.
15 Fullscreen,
16 /// A borderless window filling the display.
17 Borderless,
18}
19
20/// Declares the application window.
21///
22/// ```json
23/// {
24/// "name": "main_window",
25/// "type": "Window",
26/// "args": {
27/// "title": "Game",
28/// "width": 1280,
29/// "height": 720,
30/// "mode": "windowed",
31/// "resizable": true,
32/// "title_bar": true
33/// }
34/// }
35/// ```
36#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
37#[serde(default)]
38pub struct Window {
39 /// Window title shown in the title bar.
40 pub title: String,
41 /// Initial window width in pixels.
42 pub width: u32,
43 /// Initial window height in pixels.
44 pub height: u32,
45 /// How the window is displayed.
46 pub mode: WindowMode,
47 /// Whether the user can resize the window.
48 pub resizable: bool,
49 /// Whether the title bar is drawn, letting content fill the frame when it
50 /// is not. Only applies to `windowed` mode: `borderless` has no title bar
51 /// by definition, and `fullscreen` leaves the chrome to the OS.
52 ///
53 /// Platforms differ in what survives the title bar. macOS keeps the close /
54 /// minimize / zoom buttons floating over the content, so the window stays
55 /// movable and closable. Windows and Linux draw their controls *in* the
56 /// title bar, so turning it off there also removes them: the window can
57 /// still be resized from its border, but offers no close button and cannot
58 /// be dragged.
59 pub title_bar: bool,
60}
61
62impl Default for Window {
63 fn default() -> Self {
64 Self {
65 title: "Concinnity".to_string(),
66 width: 1024,
67 height: 768,
68 mode: WindowMode::Windowed,
69 resizable: false,
70 title_bar: true,
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn title_bar_defaults_on() {
81 // A world that says nothing about its chrome keeps a title bar: on
82 // Windows and Linux dropping it also drops the close button and the
83 // drag handle, which is not something to hand a world by default.
84 assert!(Window::default().title_bar);
85 let w: Window = serde_json::from_str(r#"{"title":"Game"}"#).unwrap();
86 assert!(w.title_bar);
87 }
88
89 #[test]
90 fn title_bar_round_trips_through_json() {
91 let w: Window = serde_json::from_str(r#"{"title_bar":false}"#).unwrap();
92 assert!(!w.title_bar);
93 // Serializing is the authored-args path (`cn add` writes normalized
94 // args back to world.jsonl), so the key has to survive a round trip.
95 let back: Window = serde_json::from_str(&serde_json::to_string(&w).unwrap()).unwrap();
96 assert!(!back.title_bar);
97 }
98}