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