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}