Skip to main content

drmkit_csd/
state.rs

1// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
2// SPDX-License-Identifier: MIT
3
4//! What a decorated window currently is.
5
6/// Which window button the pointer is over.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
8#[non_exhaustive]
9pub enum HoverButton {
10    /// None of them.
11    #[default]
12    None,
13    /// Close.
14    Close,
15    /// Minimize.
16    Minimize,
17    /// Maximize.
18    Maximize,
19}
20
21/// What changed since the decoration was last drawn.
22///
23/// A bitmask rather than a bool: redrawing a title bar because the pointer
24/// moved over a button costs a rounded-rect fill; redrawing it because the
25/// geometry changed costs a blur. A caller that cannot tell them apart pays
26/// the second price for the first event.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct Dirty(u32);
29
30impl Dirty {
31    /// Nothing changed.
32    pub const NONE: Self = Self(0);
33    /// The title text.
34    pub const TITLE: Self = Self(1 << 0);
35    /// Focus.
36    pub const FOCUS: Self = Self(1 << 1);
37    /// Which button is hovered.
38    pub const HOVER: Self = Self(1 << 2);
39    /// The window's size or position.
40    pub const GEOMETRY: Self = Self(1 << 3);
41    /// An animation advanced.
42    pub const ANIMATION: Self = Self(1 << 4);
43    /// Everything. What a freshly created state carries, since nothing has
44    /// been drawn yet and every part of it is new.
45    pub const ALL: Self = Self(u32::MAX);
46
47    /// Whether any of `other`'s bits are set here.
48    #[must_use]
49    pub const fn intersects(self, other: Self) -> bool {
50        self.0 & other.0 != 0
51    }
52
53    /// Both sets of bits.
54    #[must_use]
55    pub const fn union(self, other: Self) -> Self {
56        Self(self.0 | other.0)
57    }
58
59    /// Whether nothing is set.
60    #[must_use]
61    pub const fn is_empty(self) -> bool {
62        self.0 == 0
63    }
64}
65
66/// The progress value a state carries before an animator has written one.
67///
68/// Out of the `0..=1` range on purpose: a caller can tell "not yet animated"
69/// from "animated to zero", which are different things — the first means draw
70/// it settled, the second means draw it mid-transition at the start.
71pub const PROGRESS_UNSET: f32 = -1.0;
72
73/// A decorated window, as the renderer sees it.
74#[derive(Debug, Clone, PartialEq)]
75#[allow(
76    clippy::exhaustive_structs,
77    reason = "built by callers with `..Default::default()`, which a new field does not break"
78)]
79pub struct WindowState {
80    /// The title text.
81    pub title: String,
82    /// Whether the window has focus.
83    pub focused: bool,
84    /// Which button is hovered.
85    pub hover: HoverButton,
86    /// What has changed since the last draw.
87    pub dirty: Dirty,
88    /// How far through the focus transition, or [`PROGRESS_UNSET`].
89    pub focus_progress: f32,
90    /// How far through the hover transition, or [`PROGRESS_UNSET`].
91    pub hover_progress: f32,
92}
93
94impl Default for WindowState {
95    fn default() -> Self {
96        Self {
97            title: String::new(),
98            focused: false,
99            hover: HoverButton::None,
100            dirty: Dirty::ALL,
101            focus_progress: PROGRESS_UNSET,
102            hover_progress: PROGRESS_UNSET,
103        }
104    }
105}