Skip to main content

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::message::MessageData;
24use crate::resources::BITS_ICON;
25use crate::{
26    brush::Brush,
27    core::{
28        algebra::Vector2,
29        color::Color,
30        math::Rect,
31        num_traits::{Euclid, NumCast, One, Zero},
32        pool::Handle,
33        reflect::prelude::*,
34        type_traits::prelude::*,
35        uuid::uuid,
36        visitor::prelude::*,
37    },
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}
104impl<T: BitContainer> MessageData for BitFieldMessage<T> {}
105
106impl<T: BitContainer> ConstructorProvider<UiNode, UserInterface> for BitField<T> {
107    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
108        GraphNodeConstructor::new::<Self>()
109            .with_variant(format!("Bit Field<{}>", std::any::type_name::<T>()), |ui| {
110                BitFieldBuilder::<T>::new(WidgetBuilder::new())
111                    .build(&mut ui.build_ctx())
112                    .to_base()
113                    .into()
114            })
115            .with_group("Bit")
116    }
117}
118
119#[derive(Default, Clone, Reflect, Visit, Debug, ComponentProvider)]
120#[reflect(derived_type = "UiNode")]
121pub struct BitField<T>
122where
123    T: BitContainer,
124{
125    pub widget: Widget,
126    pub value: T,
127    #[visit(skip)]
128    #[reflect(hidden)]
129    current_bit: usize,
130    #[visit(skip)]
131    #[reflect(hidden)]
132    bit_state: BitState,
133    #[visit(skip)]
134    #[reflect(hidden)]
135    current_value: bool,
136}
137
138impl<T> Deref for BitField<T>
139where
140    T: BitContainer,
141{
142    type Target = Widget;
143
144    fn deref(&self) -> &Self::Target {
145        &self.widget
146    }
147}
148
149impl<T> DerefMut for BitField<T>
150where
151    T: BitContainer,
152{
153    fn deref_mut(&mut self) -> &mut Self::Target {
154        &mut self.widget
155    }
156}
157
158#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
159enum BitState {
160    #[default]
161    Normal,
162    Hovered,
163    Pressed,
164}
165
166#[must_use]
167fn set_bit_value<T: BitContainer>(value: T, index: usize, bit_value: bool) -> T {
168    if bit_value {
169        set_bit(value, index)
170    } else {
171        reset_bit(value, index)
172    }
173}
174
175#[must_use]
176fn set_bit<T: BitContainer>(value: T, index: usize) -> T {
177    value | (T::one() << T::from(index).unwrap_or_default())
178}
179
180#[must_use]
181fn reset_bit<T: BitContainer>(value: T, index: usize) -> T {
182    value & !(T::one() << T::from(index).unwrap_or_default())
183}
184
185#[must_use]
186fn is_bit_set<T: BitContainer>(value: T, index: usize) -> bool {
187    value & (T::one() << T::from(index).unwrap_or_default()) != T::zero()
188}
189
190fn byte_width(width: f32) -> usize {
191    let byte_size = BIT_SIZE * 8.0;
192    let col_stride = byte_size + BYTE_GAP;
193    ((width - byte_size) / col_stride).floor().max(0.0) as usize + 1
194}
195
196fn bit_to_rect(index: usize, width: usize) -> Rect<f32> {
197    let (byte_index, bit_index) = index.div_rem_euclid(&8);
198    let (byte_y, byte_x) = byte_index.div_rem_euclid(&width);
199    let row_stride = BIT_SIZE + ROW_GAP;
200    let col_stride = BIT_SIZE * 8.0 + BYTE_GAP;
201    let x = col_stride * byte_x as f32 + BIT_SIZE * bit_index as f32;
202    let y = row_stride * byte_y as f32;
203    Rect::new(x, y, BIT_SIZE, BIT_SIZE)
204}
205
206fn position_to_bit(position: Vector2<f32>, size: usize, width: f32) -> Option<usize> {
207    let byte_width = byte_width(width);
208    (0..size).find(|&i| bit_to_rect(i, byte_width).contains(position))
209}
210
211impl<T> TypeUuidProvider for BitField<T>
212where
213    T: BitContainer,
214{
215    fn type_uuid() -> Uuid {
216        combine_uuids(
217            uuid!("6c19b266-18be-46d2-bfd3-f1dc9cb3f36c"),
218            T::type_uuid(),
219        )
220    }
221}
222
223impl<T> Control for BitField<T>
224where
225    T: BitContainer,
226{
227    fn measure_override(&self, _ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
228        let size = mem::size_of::<T>();
229        let byte_size = BIT_SIZE * 8.0;
230        let width = available_size.x;
231        let byte_width = if width.is_finite() {
232            byte_width(width)
233        } else {
234            2
235        };
236        let (byte_height, rem) = size.div_rem_euclid(&byte_width);
237        let byte_height = byte_height + if rem > 0 { 1 } else { 0 };
238        let byte_height = byte_height.max(1);
239        let byte_width = byte_width.min(size);
240        let width = byte_width as f32 * byte_size + (byte_width - 1) as f32 * BYTE_GAP;
241        let height = byte_height as f32 * BIT_SIZE + (byte_height - 1) as f32 * ROW_GAP;
242        Vector2::new(width, height)
243    }
244    fn draw(&self, ctx: &mut DrawingContext) {
245        let value = self.value;
246        let width = self.actual_local_size().x;
247        let byte_width = byte_width(width);
248        let bit_count = mem::size_of::<T>() * 8;
249        for i in 0..bit_count {
250            if (self.current_bit != i || self.bit_state == BitState::Normal) && is_bit_set(value, i)
251            {
252                self.draw_bit_background(i, byte_width, ctx);
253            }
254        }
255        ctx.commit(
256            self.clip_bounds(),
257            ON_NORMAL,
258            CommandTexture::None,
259            &self.material,
260            None,
261        );
262        for i in 0..bit_count {
263            if (self.current_bit != i || self.bit_state == BitState::Normal)
264                && !is_bit_set(value, i)
265            {
266                self.draw_bit_background(i, byte_width, ctx);
267            }
268        }
269        ctx.commit(
270            self.clip_bounds(),
271            Brush::Solid(Color::TRANSPARENT),
272            CommandTexture::None,
273            &self.material,
274            None,
275        );
276        if self.bit_state != BitState::Normal {
277            let i = self.current_bit;
278            self.draw_bit_background(i, byte_width, ctx);
279            if is_bit_set(value, i) {
280                ctx.commit(
281                    self.clip_bounds(),
282                    ON_HOVER,
283                    CommandTexture::None,
284                    &self.material,
285                    None,
286                );
287            } else {
288                ctx.commit(
289                    self.clip_bounds(),
290                    OFF_HOVER,
291                    CommandTexture::None,
292                    &self.material,
293                    None,
294                );
295            }
296        }
297        for i in 0..bit_count {
298            if is_bit_set(value, i) {
299                self.draw_bit_foreground(i, byte_width, ctx);
300            }
301        }
302        ctx.commit(
303            self.clip_bounds(),
304            Brush::Solid(Color::WHITE),
305            CommandTexture::None,
306            &self.material,
307            None,
308        );
309        for i in 0..bit_count {
310            if is_bit_set(value, i) {
311                self.draw_bit_icon(i, byte_width, ctx);
312            }
313        }
314        ctx.commit(
315            self.clip_bounds(),
316            Brush::Solid(Color::BLACK),
317            CommandTexture::Texture(BITS_ICON.clone().unwrap()),
318            &self.material,
319            None,
320        );
321        for i in 0..bit_count {
322            if !is_bit_set(value, i) {
323                self.draw_bit_icon(i, byte_width, ctx);
324            }
325        }
326        ctx.commit(
327            self.clip_bounds(),
328            Brush::Solid(Color::GRAY),
329            CommandTexture::Texture(BITS_ICON.clone().unwrap()),
330            &self.material,
331            None,
332        );
333    }
334    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
335        self.widget.handle_routed_message(ui, message);
336
337        if let Some(WidgetMessage::MouseMove { pos, state }) = message.data() {
338            if message.destination() == self.handle()
339                && message.direction() == MessageDirection::FromWidget
340            {
341                let pos = self.screen_to_local(*pos);
342                let size = mem::size_of::<T>() * 8;
343                self.bit_state = BitState::Normal;
344                if let Some(bit_index) = position_to_bit(pos, size, self.actual_local_size().x) {
345                    self.current_bit = bit_index;
346                    let mut new_value = self.value;
347                    match state.left {
348                        ButtonState::Pressed => {
349                            new_value = set_bit_value(new_value, bit_index, self.current_value);
350                            self.bit_state = BitState::Pressed;
351                        }
352                        ButtonState::Released => {
353                            self.bit_state = BitState::Hovered;
354                        }
355                    }
356
357                    if new_value != self.value {
358                        ui.send(self.handle, BitFieldMessage::Value(new_value));
359                    }
360                    self.invalidate_visual();
361                }
362            }
363        } else if let Some(WidgetMessage::MouseDown { pos, button }) = message.data() {
364            if message.destination() == self.handle()
365                && message.direction() == MessageDirection::FromWidget
366            {
367                let pos = self.screen_to_local(*pos);
368                let size = mem::size_of::<T>() * 8;
369                self.bit_state = BitState::Normal;
370                if let Some(bit_index) = position_to_bit(pos, size, self.actual_local_size().x) {
371                    self.current_bit = bit_index;
372                    match button {
373                        MouseButton::Left => {
374                            message.set_handled(true);
375                            self.bit_state = BitState::Pressed;
376                            self.current_value = !is_bit_set(self.value, bit_index);
377                            let new_value =
378                                set_bit_value(self.value, bit_index, self.current_value);
379                            self.bit_state = BitState::Pressed;
380
381                            ui.send(self.handle, BitFieldMessage::Value(new_value));
382                        }
383                        MouseButton::Right => {
384                            message.set_handled(true);
385                            self.bit_state = BitState::Hovered;
386                            let new_value = if is_bit_set(self.value, bit_index) {
387                                !(T::one() << T::from(bit_index).unwrap_or_default())
388                            } else {
389                                T::one() << T::from(bit_index).unwrap_or_default()
390                            };
391
392                            ui.send(self.handle, BitFieldMessage::Value(new_value));
393                        }
394                        _ => (),
395                    }
396                }
397            }
398        } else if let Some(WidgetMessage::MouseLeave)
399        | Some(WidgetMessage::MouseUp {
400            button: MouseButton::Left,
401            ..
402        }) = message.data()
403        {
404            if message.destination() == self.handle() {
405                self.bit_state = BitState::Normal;
406                self.invalidate_visual();
407            }
408        } else if let Some(BitFieldMessage::Value(value)) = message.data_for(self.handle) {
409            if *value != self.value {
410                self.value = *value;
411                ui.send_message(message.reverse());
412                self.invalidate_visual();
413            }
414        }
415    }
416}
417
418impl<T> BitField<T>
419where
420    T: BitContainer,
421{
422    fn screen_to_local(&self, position: Vector2<f32>) -> Vector2<f32> {
423        let trans = self.visual_transform();
424        let Some(trans) = trans.try_inverse() else {
425            return position;
426        };
427        trans.transform_point(&position.into()).coords
428    }
429    fn draw_bit_background(&self, index: usize, width: usize, ctx: &mut DrawingContext) {
430        let rect = bit_to_rect(index, width);
431        ctx.push_rect_filled(&rect, None);
432    }
433    fn draw_bit_foreground(&self, index: usize, width: usize, ctx: &mut DrawingContext) {
434        let rect = bit_to_rect(index, width);
435        ctx.push_rect(&rect, 1.0);
436    }
437    fn draw_bit_icon(&self, index: usize, width: usize, ctx: &mut DrawingContext) {
438        let rect = bit_to_rect(index, width);
439        let center = rect.center();
440        let rect = Rect::new(center.x - 4.0, center.y - 4.0, 8.0, 8.0);
441        let i = index % 32;
442        let u = i as f32 / 32.0;
443        let u1 = (i + 1) as f32 / 32.0;
444        let t0 = Vector2::new(u, 0.0);
445        let t1 = Vector2::new(u1, 0.0);
446        let t2 = Vector2::new(u1, 1.0);
447        let t3 = Vector2::new(u, 1.0);
448        ctx.push_rect_filled(&rect, Some(&[t0, t1, t2, t3]));
449    }
450}
451
452pub struct BitFieldBuilder<T>
453where
454    T: BitContainer,
455{
456    widget_builder: WidgetBuilder,
457    value: T,
458}
459
460impl<T> BitFieldBuilder<T>
461where
462    T: BitContainer,
463{
464    pub fn new(widget_builder: WidgetBuilder) -> Self {
465        Self {
466            widget_builder,
467            value: T::default(),
468        }
469    }
470
471    pub fn with_value(mut self, value: T) -> Self {
472        self.value = value;
473        self
474    }
475
476    pub fn build(self, ctx: &mut BuildContext) -> Handle<BitField<T>> {
477        let canvas = BitField {
478            widget: self.widget_builder.build(ctx),
479            value: self.value,
480            current_bit: 0,
481            bit_state: BitState::Normal,
482            current_value: false,
483        };
484        ctx.add(canvas)
485    }
486}
487
488#[cfg(test)]
489mod test {
490    use super::*;
491    use crate::bit::{byte_width, BitFieldBuilder};
492    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
493
494    #[test]
495    fn test_deletion() {
496        test_widget_deletion(|ctx| BitFieldBuilder::<usize>::new(WidgetBuilder::new()).build(ctx));
497    }
498
499    #[test]
500    fn test_byte_width() {
501        assert_eq!(byte_width(0.0), 1);
502        assert_eq!(byte_width(8.0 * BIT_SIZE), 1);
503        assert_eq!(byte_width(16.0 * BIT_SIZE), 1);
504        assert_eq!(byte_width(16.0 * BIT_SIZE + BYTE_GAP), 2);
505        assert_eq!(byte_width(24.0 * BIT_SIZE + 2.0 * BYTE_GAP), 3);
506        assert_eq!(byte_width(32.0 * BIT_SIZE + 2.0 * BYTE_GAP), 3);
507        assert_eq!(byte_width(32.0 * BIT_SIZE + 3.0 * BYTE_GAP), 4);
508        assert_eq!(byte_width(40.0 * BIT_SIZE + 4.0 * BYTE_GAP), 5);
509    }
510}