Skip to main content

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}