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/// Reserved for a partial-redraw path, as upstream reserves it:
24/// [`Renderer::draw`](crate::Renderer::draw) reads none of these bits today
25/// and repaints the whole decoration whatever is set.
26///
27/// A bitmask rather than a bool so a caller can already record *what*
28/// changed: redrawing a title bar because the pointer moved over a button
29/// needs only a rounded-rect fill, while redrawing it because the geometry
30/// changed needs a blur, and a bool could not tell the two apart.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct Dirty(u32);
33
34impl Dirty {
35    /// Nothing changed.
36    pub const NONE: Self = Self(0);
37    /// The title text.
38    pub const TITLE: Self = Self(1 << 0);
39    /// Focus.
40    pub const FOCUS: Self = Self(1 << 1);
41    /// Which button is hovered.
42    pub const HOVER: Self = Self(1 << 2);
43    /// The window's size or position.
44    pub const GEOMETRY: Self = Self(1 << 3);
45    /// An animation advanced.
46    pub const ANIMATION: Self = Self(1 << 4);
47    /// Everything. What a freshly created state carries, since nothing has
48    /// been drawn yet and every part of it is new.
49    pub const ALL: Self = Self(u32::MAX);
50
51    /// Whether any of `other`'s bits are set here.
52    #[must_use]
53    pub const fn intersects(self, other: Self) -> bool {
54        self.0 & other.0 != 0
55    }
56
57    /// Both sets of bits.
58    #[must_use]
59    pub const fn union(self, other: Self) -> Self {
60        Self(self.0 | other.0)
61    }
62
63    /// Whether nothing is set.
64    #[must_use]
65    pub const fn is_empty(self) -> bool {
66        self.0 == 0
67    }
68}
69
70/// The progress value a state carries before an animator has written one.
71///
72/// Out of the `0..=1` range on purpose: a caller can tell "not yet animated"
73/// from "animated to zero", which are different things — the first means draw
74/// it settled, the second means draw it mid-transition at the start.
75pub const PROGRESS_UNSET: f32 = -1.0;
76
77/// A decorated window, as the renderer sees it.
78#[derive(Debug, Clone, PartialEq)]
79#[allow(
80    clippy::exhaustive_structs,
81    reason = "built by callers with `..Default::default()`, which a new field does not break"
82)]
83pub struct WindowState {
84    /// The title text.
85    pub title: String,
86    /// Whether the window has focus.
87    pub focused: bool,
88    /// Which button is hovered.
89    pub hover: HoverButton,
90    /// What has changed since the last draw. Not yet read by the renderer;
91    /// see [`Dirty`].
92    pub dirty: Dirty,
93    /// How far through the focus transition, or [`PROGRESS_UNSET`].
94    pub focus_progress: f32,
95    /// How far through the hover transition, or [`PROGRESS_UNSET`].
96    pub hover_progress: f32,
97}
98
99impl Default for WindowState {
100    fn default() -> Self {
101        Self {
102            title: String::new(),
103            focused: false,
104            hover: HoverButton::None,
105            dirty: Dirty::ALL,
106            focus_progress: PROGRESS_UNSET,
107            hover_progress: PROGRESS_UNSET,
108        }
109    }
110}