matrix_gui/
widget_state.rs1use core::cell::Cell;
2use enum_iterator::Sequence;
3
4use crate::ui::{GuiError, GuiResult};
5
6pub trait WidgetId: Copy + Sequence + PartialEq + Default {
7 fn id(&self) -> usize;
8}
9
10#[derive(Clone, Copy, Debug, PartialEq)]
11#[non_exhaustive]
12#[repr(u8)]
13pub enum RenderStatus {
14 NeedsRedraw = 0,
15 Rendered = 1,
16 Normal = 2,
17 Dragging = 3,
18 Pressed = 4,
19 Released = 5,
20 Focused = 6,
21 Defocused = 7,
22 Triggered = 8,
23 Inactive = 9,
24 UserStatus0 = 10,
25 UserStatus1 = 11,
26 UserStatus2 = 12,
27 UserStatus3 = 13,
28 Unknown = 127,
29}
30
31impl From<u8> for RenderStatus {
32 fn from(val: u8) -> Self {
33 match val {
34 0 => Self::NeedsRedraw,
35 1 => Self::Rendered,
36 2 => Self::Normal,
37 3 => Self::Dragging,
38 4 => Self::Pressed,
39 5 => Self::Released,
40 6 => Self::Focused,
41 7 => Self::Defocused,
42 8 => Self::Triggered,
43 9 => Self::Inactive,
44 10 => Self::UserStatus0,
45 11 => Self::UserStatus1,
46 12 => Self::UserStatus2,
47 13 => Self::UserStatus3,
48 _ => Self::Unknown,
49 }
50 }
51}
52
53#[derive(Clone, Debug)]
54pub struct RenderState(Cell<u8>);
55
56#[cfg(not(feature = "interaction"))]
57impl RenderState {
58 #[inline]
59 const fn raw_val(&self) -> u8 {
60 self.0.get()
61 }
62
63 #[inline]
64 const fn interactive(&self) -> u8 {
65 0
66 }
67
68 pub fn set_status(&self, val: RenderStatus) {
69 self.0.set(val as u8);
70 }
71}
72
73#[cfg(feature = "interaction")]
74impl RenderState {
75 const MASK_VALUE: u8 = 0x7F; const MASK_INTERACTIVE: u8 = 0x80; #[inline]
79 const fn raw_val(&self) -> u8 {
80 self.0.get() & Self::MASK_VALUE
81 }
82
83 #[inline]
84 const fn interactive(&self) -> u8 {
85 self.0.get() & Self::MASK_INTERACTIVE
86 }
87
88 #[inline]
89 pub fn mark_as_interact(&self) {
90 self.0.set(self.0.get() | Self::MASK_INTERACTIVE);
91 }
92
93 pub fn set_status(&self, val: RenderStatus) {
94 self.0.set(self.interactive() | val as u8);
95 }
96}
97
98impl RenderState {
99 pub const fn new(status: RenderStatus) -> Self {
100 Self(Cell::new(status as u8))
101 }
102
103 pub fn new_array<const N: usize>() -> [Self; N] {
104 core::array::from_fn(|_| RenderState::needs_redraw())
105 }
106
107 pub const fn needs_redraw() -> Self {
108 Self(Cell::new(RenderStatus::NeedsRedraw as u8))
109 }
110
111 pub fn status(&self) -> RenderStatus {
112 self.raw_val().into()
113 }
114
115 #[inline]
116 pub const fn is_interact(&self) -> bool {
117 self.interactive() != 0
118 }
119
120 #[inline]
121 pub const fn is_static(&self) -> bool {
122 !self.is_interact()
123 }
124
125 pub fn force_redraw(&self) {
126 self.0.set(RenderStatus::NeedsRedraw as u8);
127 }
128
129 pub fn compare_set(&self, other: RenderStatus) -> bool {
130 let result = self.raw_val() == other as u8;
131 if !result {
132 self.set_status(other);
133 }
134 result
135 }
136
137 pub fn compare(&self, other: RenderStatus) -> bool {
138 self.raw_val() == other as u8
139 }
140}
141
142impl PartialEq for RenderState {
143 fn eq(&self, other: &Self) -> bool {
144 self.0 == other.0
145 }
146}
147
148#[derive(Debug)]
149pub struct WidgetStates<'a> {
150 states: &'a [RenderState],
151 #[cfg(feature = "animation")]
152 anim_status: &'a [crate::prelude::AnimStatus],
153 #[cfg(feature = "popup")]
154 modal_active: Cell<bool>,
155}
156
157impl<'a> WidgetStates<'a> {
158 #[cfg(not(feature = "animation"))]
159 pub const fn new(states: &'a [RenderState]) -> Self {
160 WidgetStates {
161 states,
162 #[cfg(feature = "popup")]
163 modal_active: Cell::new(false),
164 }
165 }
166
167 pub fn get_state<ID: WidgetId>(&self, widget_id: ID) -> GuiResult<&RenderState> {
168 #[cfg(feature = "popup")]
169 if self.is_modal_active() {
170 return Err(GuiError::ModalActive);
171 }
172 self.states
173 .get(widget_id.id())
174 .ok_or(GuiError::InvalidWidgetId)
175 }
176
177 pub fn set_status<ID: WidgetId>(&self, widget_id: ID, status: RenderStatus) -> bool {
178 if let Some(state) = self.states.get(widget_id.id()) {
179 state.set_status(status);
180 true
181 } else {
182 false
183 }
184 }
185
186 pub fn compare_set<ID: WidgetId>(&self, widget_id: ID, value: RenderStatus) -> bool {
187 if let Some(state) = self.states.get(widget_id.id()) {
188 state.compare_set(value)
189 } else {
190 false
191 }
192 }
193
194 pub fn force_redraw_all(&self) {
195 #[cfg(feature = "popup")]
196 {
197 self.set_modal_active(false);
198 }
199
200 for state in self.states.iter() {
201 state.force_redraw();
202 }
203 }
204
205 pub fn force_redraw_range<ID: WidgetId>(&self, start: ID, end: ID) {
206 let mut curr = start;
207 self.force_redraw(curr);
208 while let Some(next) = curr.next() {
209 self.force_redraw(next);
210
211 if next == end {
212 break;
213 }
214 curr = next;
215 }
216 }
217
218 pub fn force_redraw_multi<ID: WidgetId>(&self, widget_ids: &[ID]) {
219 for widget_id in widget_ids {
220 self.force_redraw(*widget_id);
221 }
222 }
223
224 pub fn force_redraw<ID: WidgetId>(&self, widget_id: ID) {
225 if let Some(state) = self.states.get(widget_id.id()) {
226 state.force_redraw();
227 }
228 }
229}
230
231#[cfg(feature = "popup")]
232impl<'a> WidgetStates<'a> {
233 pub fn set_modal_active(&self, modal_status: bool) {
234 self.modal_active.set(modal_status);
235 }
236
237 pub const fn is_modal_active(&self) -> bool {
238 self.modal_active.get()
239 }
240}
241
242#[cfg(feature = "animation")]
243impl<'a> WidgetStates<'a> {
244 pub const fn new(
245 states: &'a [RenderState],
246 anim_status: &'a [crate::prelude::AnimStatus],
247 ) -> Self {
248 WidgetStates {
249 states,
250 anim_status,
251 #[cfg(feature = "popup")]
252 modal_active: Cell::new(false),
253 }
254 }
255
256 pub fn take_anim_status(&self, anim_id: crate::prelude::AnimId) -> GuiResult<Option<i32>> {
257 self.anim_status
258 .get(anim_id as usize)
259 .map_or(Err(GuiError::InvalidAnimId), |s| Ok(s.take()))
260 }
261
262 pub fn get_anim_status(&self, anim_id: crate::prelude::AnimId) -> GuiResult<Option<i32>> {
263 self.anim_status
264 .get(anim_id as usize)
265 .map_or(Err(GuiError::InvalidAnimId), |s| Ok(s.get()))
266 }
267}
268
269impl<'a> WidgetStates<'a> {
270 #[cfg(feature = "interaction")]
271 pub(crate) fn mark_as_interact<ID: WidgetId>(&self, widget_id: ID) {
272 if let Some(state) = self.states.get(widget_id.id()) {
273 state.mark_as_interact();
274 }
275 }
276
277 pub fn should_redraw<ID: WidgetId>(&self, widget_id: ID) -> bool {
278 if let Ok(state) = self.get_state(widget_id) {
279 state.compare(RenderStatus::NeedsRedraw)
280 } else {
281 false
282 }
283 }
284
285 pub fn should_redraw_multi<ID: WidgetId>(&self, widget_ids: &[ID]) -> bool {
286 for widget_id in widget_ids {
287 if let Ok(state) = self.get_state(*widget_id) {
288 if state.compare(RenderStatus::NeedsRedraw) {
289 return true;
290 }
291 }
292 }
293
294 false
295 }
296
297 pub fn should_redraw_static(&self) -> bool {
298 for state in self.states {
299 if state.is_static() && state.compare(RenderStatus::NeedsRedraw) {
300 return true;
301 }
302 }
303
304 false
305 }
306
307 pub fn should_redraw_any(&self) -> bool {
308 for state in self.states {
309 if state.compare(RenderStatus::NeedsRedraw) {
310 return true;
311 }
312 }
313
314 false
315 }
316}