fyrox_ui/
bit.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! A widget that shows numeric value as a set of individual bits allowing switching separate bits.
22
23use crate::resources::BITS_ICON;
24use crate::{
25    brush::Brush,
26    core::{
27        algebra::Vector2,
28        color::Color,
29        math::Rect,
30        num_traits::{Euclid, NumCast, One, Zero},
31        pool::Handle,
32        reflect::prelude::*,
33        type_traits::prelude::*,
34        uuid::uuid,
35        visitor::prelude::*,
36    },
37    define_constructor,
38    draw::{CommandTexture, Draw, DrawingContext},
39    message::{ButtonState, UiMessage},
40    widget::{Widget, WidgetBuilder},
41    BuildContext, Control, MessageDirection, MouseButton, UiNode, UserInterface, WidgetMessage,
42};
43use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
44use std::{
45    fmt::Debug,
46    mem,
47    ops::{BitAnd, BitOr, Deref, DerefMut, Not, Shl},
48};
49
50const BIT_SIZE: f32 = 16.0;
51const BYTE_GAP: f32 = 8.0;
52const ROW_GAP: f32 = 4.0;
53
54const ON_NORMAL: Brush = Brush::Solid(Color::DARK_GRAY);
55const ON_HOVER: Brush = Brush::Solid(Color::LIGHT_BLUE);
56const OFF_HOVER: Brush = Brush::Solid(Color::DARK_SLATE_BLUE);
57
58pub trait BitContainer:
59    BitAnd<Output = Self>
60    + BitOr<Output = Self>
61    + Clone
62    + Copy
63    + Default
64    + One
65    + Shl<Output = Self>
66    + NumCast
67    + Not<Output = Self>
68    + Zero
69    + PartialEq
70    + Debug
71    + Reflect
72    + Visit
73    + Send
74    + TypeUuidProvider
75    + 'static
76{
77}
78
79impl<T> BitContainer for T where
80    T: BitAnd<Output = Self>
81        + BitOr<Output = Self>
82        + Clone
83        + Copy
84        + Default
85        + One
86        + Shl<Output = Self>
87        + NumCast
88        + Not<Output = Self>
89        + Zero
90        + PartialEq
91        + Debug
92        + Reflect
93        + Visit
94        + Send
95        + TypeUuidProvider
96        + 'static
97{
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub enum BitFieldMessage<T: BitContainer> {
102    Value(T),
103}
104
105impl<T: BitContainer> BitFieldMessage<T> {
106    define_constructor!(BitFieldMessage:Value => fn value(T), layout: false);
107}
108
109impl<T: BitContainer> ConstructorProvider<UiNode, UserInterface> for BitField<T> {
110    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
111        GraphNodeConstructor::new::<Self>()
112            .with_variant(format!("Bit Field<{}>", std::any::type_name::<T>()), |ui| {
113                BitFieldBuilder::<T>::new(WidgetBuilder::new())
114                    .build(&mut ui.build_ctx())
115                    .into()
116            })
117            .with_group("Bit")
118    }
119}
120
121#[derive(Default, Clone, Reflect, Visit, Debug, ComponentProvider)]
122#[reflect(derived_type = "UiNode")]
123pub struct BitField<T>
124where
125    T: BitContainer,
126{
127    pub widget: Widget,
128    pub value: T,
129    #[visit(skip)]
130    #[reflect(hidden)]
131    current_bit: usize,
132    #[visit(skip)]
133    #[reflect(hidden)]
134    bit_state: BitState,
135    #[visit(skip)]
136    #[reflect(hidden)]
137    current_value: bool,
138}
139
140impl<T> Deref for BitField<T>
141where
142    T: BitContainer,
143{
144    type Target = Widget;
145
146    fn deref(&self) -> &Self::Target {
147        &self.widget
148    }
149}
150
151impl<T> DerefMut for BitField<T>
152where
153    T: BitContainer,
154{
155    fn deref_mut(&mut self) -> &mut Self::Target {
156        &mut self.widget
157    }
158}
159
160#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
161enum BitState {
162    #[default]
163    Normal,
164    Hovered,
165    Pressed,
166}
167
168#[must_use]
169fn set_bit_value<T: BitContainer>(value: T, index: usize, bit_value: bool) -> T {
170    if bit_value {
171        set_bit(value, index)
172    } else {
173        reset_bit(value, index)
174    }
175}
176
177#[must_use]
178fn set_bit<T: BitContainer>(value: T, index: usize) -> T {
179    value | (T::one() << T::from(index).unwrap_or_default())
180}
181
182#[must_use]
183fn reset_bit<T: BitContainer>(value: T, index: usize) -> T {
184    value & !(T::one() << T::from(index).unwrap_or_default())
185}
186
187#[must_use]
188fn is_bit_set<T: BitContainer>(value: T, index: usize) -> bool {
189    value & (T::one() << T::from(index).unwrap_or_default()) != T::zero()
190}
191
192fn byte_width(width: f32) -> usize {
193    let byte_size = BIT_SIZE * 8.0;
194    let col_stride = byte_size + BYTE_GAP;
195    ((width - byte_size) / col_stride).floor().max(0.0) as usize + 1
196}
197
198fn bit_to_rect(index: usize, width: usize) -> Rect<f32> {
199    let (byte_index, bit_index) = index.div_rem_euclid(&8);
200    let (byte_y, byte_x) = byte_index.div_rem_euclid(&width);
201    let row_stride = BIT_SIZE + ROW_GAP;
202    let col_stride = BIT_SIZE * 8.0 + BYTE_GAP;
203    let x = col_stride * byte_x as f32 + BIT_SIZE * bit_index as f32;
204    let y = row_stride * byte_y as f32;
205    Rect::new(x, y, BIT_SIZE, BIT_SIZE)
206}
207
208fn position_to_bit(position: Vector2<f32>, size: usize, width: f32) -> Option<usize> {
209    let byte_width = byte_width(width);
210    (0..size).find(|&i| bit_to_rect(i, byte_width).contains(position))
211}
212
213impl<T> TypeUuidProvider for BitField<T>
214where
215    T: BitContainer,
216{
217    fn type_uuid() -> Uuid {
218        combine_uuids(
219            uuid!("6c19b266-18be-46d2-bfd3-f1dc9cb3f36c"),
220            T::type_uuid(),
221        )
222    }
223}
224
225impl<T> Control for BitField<T>
226where
227    T: BitContainer,
228{
229    fn measure_override(&self, _ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
230        let size = mem::size_of::<T>();
231        let byte_size = BIT_SIZE * 8.0;
232        let width = available_size.x;
233        let byte_width = if width.is_finite() {
234            byte_width(width)
235        } else {
236            2
237        };
238        let (byte_height, rem) = size.div_rem_euclid(&byte_width);
239        let byte_height = byte_height + if rem > 0 { 1 } else { 0 };
240        let byte_height = byte_height.max(1);
241        let byte_width = byte_width.min(size);
242        let width = byte_width as f32 * byte_size + (byte_width - 1) as f32 * BYTE_GAP;
243        let height = byte_height as f32 * BIT_SIZE + (byte_height - 1) as f32 * ROW_GAP;
244        Vector2::new(width, height)
245    }
246    fn draw(&self, ctx: &mut DrawingContext) {
247        let value = self.value;
248        let width = self.actual_local_size().x;
249        let byte_width = byte_width(width);
250        let bit_count = mem::size_of::<T>() * 8;
251        for i in 0..bit_count {
252            if (self.current_bit != i || self.bit_state == BitState::Normal) && is_bit_set(value, i)
253            {
254                self.draw_bit_background(i, byte_width, ctx);
255            }
256        }
257        ctx.commit(
258            self.clip_bounds(),
259            ON_NORMAL,
260            CommandTexture::None,
261            &self.material,
262            None,
263        );
264        for i in 0..bit_count {
265            if (self.current_bit != i || self.bit_state == BitState::Normal)
266                && !is_bit_set(value, i)
267            {
268                self.draw_bit_background(i, byte_width, ctx);
269            }
270        }
271        ctx.commit(
272            self.clip_bounds(),
273            Brush::Solid(Color::TRANSPARENT),
274            CommandTexture::None,
275            &self.material,
276            None,
277        );
278        if self.bit_state != BitState::Normal {
279            let i = self.current_bit;
280            self.draw_bit_background(i, byte_width, ctx);
281            if is_bit_set(value, i) {
282                ctx.commit(
283                    self.clip_bounds(),
284                    ON_HOVER,
285                    CommandTexture::None,
286                    &self.material,
287                    None,
288                );
289            } else {
290                ctx.commit(
291                    self.clip_bounds(),
292                    OFF_HOVER,
293                    CommandTexture::None,
294                    &self.material,
295                    None,
296                );
297            }
298        }
299        for i in 0..bit_count {
300            if is_bit_set(value, i) {
301                self.draw_bit_foreground(i, byte_width, ctx);
302            }
303        }
304        ctx.commit(
305            self.clip_bounds(),
306            Brush::Solid(Color::WHITE),
307            CommandTexture::None,
308            &self.material,
309            None,
310        );
311        for i in 0..bit_count {
312            if is_bit_set(value, i) {
313                self.draw_bit_icon(i, byte_width, ctx);
314            }
315        }
316        ctx.commit(
317            self.clip_bounds(),
318            Brush::Solid(Color::BLACK),
319            CommandTexture::Texture(BITS_ICON.clone().unwrap()),
320            &self.material,
321            None,
322        );
323        for i in 0..bit_count {
324            if !is_bit_set(value, i) {
325                self.draw_bit_icon(i, byte_width, ctx);
326            }
327        }
328        ctx.commit(
329            self.clip_bounds(),
330            Brush::Solid(Color::GRAY),
331            CommandTexture::Texture(BITS_ICON.clone().unwrap()),
332            &self.material,
333            None,
334        );
335    }
336    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
337        self.widget.handle_routed_message(ui, message);
338
339        if let Some(WidgetMessage::MouseMove { pos, state }) = message.data() {
340            if message.destination() == self.handle()
341                && message.direction() == MessageDirection::FromWidget
342            {
343                let pos = self.screen_to_local(*pos);
344                let size = mem::size_of::<T>() * 8;
345                self.bit_state = BitState::Normal;
346                if let Some(bit_index) = position_to_bit(pos, size, self.actual_local_size().x) {
347                    self.current_bit = bit_index;
348                    let mut new_value = self.value;
349                    match state.left {
350                        ButtonState::Pressed => {
351                            new_value = set_bit_value(new_value, bit_index, self.current_value);
352                            self.bit_state = BitState::Pressed;
353                        }
354                        ButtonState::Released => {
355                            self.bit_state = BitState::Hovered;
356                        }
357                    }
358
359                    if new_value != self.value {
360                        ui.send_message(BitFieldMessage::value(
361                            self.handle,
362                            MessageDirection::ToWidget,
363                            new_value,
364                        ));
365                    }
366                }
367            }
368        } else if let Some(WidgetMessage::MouseDown { pos, button }) = message.data() {
369            if message.destination() == self.handle()
370                && message.direction() == MessageDirection::FromWidget
371            {
372                let pos = self.screen_to_local(*pos);
373                let size = mem::size_of::<T>() * 8;
374                self.bit_state = BitState::Normal;
375                if let Some(bit_index) = position_to_bit(pos, size, self.actual_local_size().x) {
376                    self.current_bit = bit_index;
377                    match button {
378                        MouseButton::Left => {
379                            message.set_handled(true);
380                            self.bit_state = BitState::Pressed;
381                            self.current_value = !is_bit_set(self.value, bit_index);
382                            let new_value =
383                                set_bit_value(self.value, bit_index, self.current_value);
384                            self.bit_state = BitState::Pressed;
385
386                            ui.send_message(BitFieldMessage::value(
387                                self.handle,
388                                MessageDirection::ToWidget,
389                                new_value,
390                            ));
391                        }
392                        MouseButton::Right => {
393                            message.set_handled(true);
394                            self.bit_state = BitState::Hovered;
395                            let new_value = if is_bit_set(self.value, bit_index) {
396                                !(T::one() << T::from(bit_index).unwrap_or_default())
397                            } else {
398                                T::one() << T::from(bit_index).unwrap_or_default()
399                            };
400
401                            ui.send_message(BitFieldMessage::value(
402                                self.handle,
403                                MessageDirection::ToWidget,
404                                new_value,
405                            ));
406                        }
407                        _ => (),
408                    }
409                }
410            }
411        } else if let Some(WidgetMessage::MouseLeave)
412        | Some(WidgetMessage::MouseUp {
413            button: MouseButton::Left,
414            ..
415        }) = message.data()
416        {
417            if message.destination() == self.handle() {
418                self.bit_state = BitState::Normal;
419            }
420        } else if let Some(BitFieldMessage::Value(value)) = message.data() {
421            if message.destination() == self.handle
422                && message.direction() == MessageDirection::ToWidget
423                && *value != self.value
424            {
425                self.value = *value;
426                ui.send_message(message.reverse());
427            }
428        }
429    }
430}
431
432impl<T> BitField<T>
433where
434    T: BitContainer,
435{
436    fn screen_to_local(&self, position: Vector2<f32>) -> Vector2<f32> {
437        let trans = self.visual_transform();
438        let Some(trans) = trans.try_inverse() else {
439            return position;
440        };
441        trans.transform_point(&position.into()).coords
442    }
443    fn draw_bit_background(&self, index: usize, width: usize, ctx: &mut DrawingContext) {
444        let rect = bit_to_rect(index, width);
445        ctx.push_rect_filled(&rect, None);
446    }
447    fn draw_bit_foreground(&self, index: usize, width: usize, ctx: &mut DrawingContext) {
448        let rect = bit_to_rect(index, width);
449        ctx.push_rect(&rect, 1.0);
450    }
451    fn draw_bit_icon(&self, index: usize, width: usize, ctx: &mut DrawingContext) {
452        let rect = bit_to_rect(index, width);
453        let center = rect.center();
454        let rect = Rect::new(center.x - 4.0, center.y - 4.0, 8.0, 8.0);
455        let i = index % 32;
456        let u = i as f32 / 32.0;
457        let u1 = (i + 1) as f32 / 32.0;
458        let t0 = Vector2::new(u, 0.0);
459        let t1 = Vector2::new(u1, 0.0);
460        let t2 = Vector2::new(u1, 1.0);
461        let t3 = Vector2::new(u, 1.0);
462        ctx.push_rect_filled(&rect, Some(&[t0, t1, t2, t3]));
463    }
464}
465
466pub struct BitFieldBuilder<T>
467where
468    T: BitContainer,
469{
470    widget_builder: WidgetBuilder,
471    value: T,
472}
473
474impl<T> BitFieldBuilder<T>
475where
476    T: BitContainer,
477{
478    pub fn new(widget_builder: WidgetBuilder) -> Self {
479        Self {
480            widget_builder,
481            value: T::default(),
482        }
483    }
484
485    pub fn with_value(mut self, value: T) -> Self {
486        self.value = value;
487        self
488    }
489
490    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
491        let canvas = BitField {
492            widget: self.widget_builder.build(ctx),
493            value: self.value,
494            current_bit: 0,
495            bit_state: BitState::Normal,
496            current_value: false,
497        };
498        ctx.add_node(UiNode::new(canvas))
499    }
500}
501
502#[cfg(test)]
503mod test {
504    use super::*;
505    use crate::bit::{byte_width, BitFieldBuilder};
506    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
507
508    #[test]
509    fn test_deletion() {
510        test_widget_deletion(|ctx| BitFieldBuilder::<usize>::new(WidgetBuilder::new()).build(ctx));
511    }
512
513    #[test]
514    fn test_byte_width() {
515        assert_eq!(byte_width(0.0), 1);
516        assert_eq!(byte_width(8.0 * BIT_SIZE), 1);
517        assert_eq!(byte_width(16.0 * BIT_SIZE), 1);
518        assert_eq!(byte_width(16.0 * BIT_SIZE + BYTE_GAP), 2);
519        assert_eq!(byte_width(24.0 * BIT_SIZE + 2.0 * BYTE_GAP), 3);
520        assert_eq!(byte_width(32.0 * BIT_SIZE + 2.0 * BYTE_GAP), 3);
521        assert_eq!(byte_width(32.0 * BIT_SIZE + 3.0 * BYTE_GAP), 4);
522        assert_eq!(byte_width(40.0 * BIT_SIZE + 4.0 * BYTE_GAP), 5);
523    }
524}