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::{
24    check_box::{CheckBoxBuilder, CheckBoxMessage},
25    core::{
26        num_traits::{NumCast, One, Zero},
27        pool::Handle,
28        reflect::prelude::*,
29        type_traits::prelude::*,
30        uuid::uuid,
31        visitor::prelude::*,
32    },
33    define_constructor,
34    message::UiMessage,
35    widget::{Widget, WidgetBuilder},
36    wrap_panel::WrapPanelBuilder,
37    BuildContext, Control, MessageDirection, MouseButton, Orientation, Thickness, UiNode,
38    UserInterface, WidgetMessage,
39};
40use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
41use fyrox_graph::BaseSceneGraph;
42use std::{
43    fmt::Debug,
44    mem,
45    ops::{BitAnd, BitOr, Deref, DerefMut, Not, Shl},
46};
47
48pub trait BitContainer:
49    BitAnd<Output = Self>
50    + BitOr<Output = Self>
51    + Clone
52    + Copy
53    + Default
54    + One
55    + Shl<Output = Self>
56    + NumCast
57    + Not<Output = Self>
58    + Zero
59    + PartialEq
60    + Debug
61    + Reflect
62    + Visit
63    + Send
64    + TypeUuidProvider
65    + 'static
66{
67}
68
69impl<T> BitContainer for T where
70    T: BitAnd<Output = Self>
71        + BitOr<Output = Self>
72        + Clone
73        + Copy
74        + Default
75        + One
76        + Shl<Output = Self>
77        + NumCast
78        + Not<Output = Self>
79        + Zero
80        + PartialEq
81        + Debug
82        + Reflect
83        + Visit
84        + Send
85        + TypeUuidProvider
86        + 'static
87{
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum BitFieldMessage<T: BitContainer> {
92    Value(T),
93}
94
95impl<T: BitContainer> BitFieldMessage<T> {
96    define_constructor!(BitFieldMessage:Value => fn value(T), layout: false);
97}
98
99impl<T: BitContainer> ConstructorProvider<UiNode, UserInterface> for BitField<T> {
100    fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
101        GraphNodeConstructor::new::<Self>()
102            .with_variant(format!("Bit Field<{}>", std::any::type_name::<T>()), |ui| {
103                BitFieldBuilder::<T>::new(WidgetBuilder::new())
104                    .build(&mut ui.build_ctx())
105                    .into()
106            })
107            .with_group("Bit")
108    }
109}
110
111#[derive(Default, Clone, Reflect, Visit, Debug, ComponentProvider)]
112pub struct BitField<T>
113where
114    T: BitContainer,
115{
116    pub widget: Widget,
117    pub value: T,
118    pub bit_switches: Vec<Handle<UiNode>>,
119}
120
121impl<T> Deref for BitField<T>
122where
123    T: BitContainer,
124{
125    type Target = Widget;
126
127    fn deref(&self) -> &Self::Target {
128        &self.widget
129    }
130}
131
132impl<T> DerefMut for BitField<T>
133where
134    T: BitContainer,
135{
136    fn deref_mut(&mut self) -> &mut Self::Target {
137        &mut self.widget
138    }
139}
140
141#[must_use]
142fn set_bit<T: BitContainer>(value: T, index: usize) -> T {
143    value | (T::one() << T::from(index).unwrap_or_default())
144}
145
146#[must_use]
147fn reset_bit<T: BitContainer>(value: T, index: usize) -> T {
148    value & !(T::one() << T::from(index).unwrap_or_default())
149}
150
151#[must_use]
152fn is_bit_set<T: BitContainer>(value: T, index: usize) -> bool {
153    value & (T::one() << T::from(index).unwrap_or_default()) != T::zero()
154}
155
156impl<T> TypeUuidProvider for BitField<T>
157where
158    T: BitContainer,
159{
160    fn type_uuid() -> Uuid {
161        combine_uuids(
162            uuid!("6c19b266-18be-46d2-bfd3-f1dc9cb3f36c"),
163            T::type_uuid(),
164        )
165    }
166}
167
168impl<T> Control for BitField<T>
169where
170    T: BitContainer,
171{
172    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
173        self.widget.handle_routed_message(ui, message);
174
175        if let Some(CheckBoxMessage::Check(Some(value))) = message.data() {
176            if message.direction() == MessageDirection::FromWidget {
177                if let Some(bit_index) = self
178                    .bit_switches
179                    .iter()
180                    .position(|s| *s == message.destination())
181                {
182                    let new_value = if *value {
183                        set_bit(self.value, bit_index)
184                    } else {
185                        reset_bit(self.value, bit_index)
186                    };
187
188                    ui.send_message(BitFieldMessage::value(
189                        self.handle,
190                        MessageDirection::ToWidget,
191                        new_value,
192                    ));
193                }
194            }
195        } else if let Some(BitFieldMessage::Value(value)) = message.data() {
196            if message.destination() == self.handle
197                && message.direction() == MessageDirection::ToWidget
198                && *value != self.value
199            {
200                self.value = *value;
201                self.sync_switches(ui);
202                ui.send_message(message.reverse());
203            }
204        } else if let Some(WidgetMessage::MouseDown { button, .. }) = message.data() {
205            if *button == MouseButton::Right {
206                for (index, bit) in self.bit_switches.iter().cloned().enumerate() {
207                    if ui.node(bit).has_descendant(message.destination(), ui) {
208                        let new_value = if is_bit_set(self.value, index) {
209                            !(T::one() << T::from(index).unwrap_or_default())
210                        } else {
211                            T::one() << T::from(index).unwrap_or_default()
212                        };
213
214                        ui.send_message(BitFieldMessage::value(
215                            self.handle,
216                            MessageDirection::ToWidget,
217                            new_value,
218                        ));
219                    }
220                }
221            }
222        }
223    }
224}
225
226impl<T> BitField<T>
227where
228    T: BitContainer,
229{
230    fn sync_switches(&self, ui: &UserInterface) {
231        for (i, handle) in self.bit_switches.iter().cloned().enumerate() {
232            ui.send_message(CheckBoxMessage::checked(
233                handle,
234                MessageDirection::ToWidget,
235                Some(is_bit_set(self.value, i)),
236            ));
237        }
238    }
239}
240
241pub struct BitFieldBuilder<T>
242where
243    T: BitContainer,
244{
245    widget_builder: WidgetBuilder,
246    value: T,
247}
248
249impl<T> BitFieldBuilder<T>
250where
251    T: BitContainer,
252{
253    pub fn new(widget_builder: WidgetBuilder) -> Self {
254        Self {
255            widget_builder,
256            value: T::default(),
257        }
258    }
259
260    pub fn with_value(mut self, value: T) -> Self {
261        self.value = value;
262        self
263    }
264
265    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
266        let bit_switches = (0..(mem::size_of::<T>() * 8))
267            .map(|i| {
268                CheckBoxBuilder::new(WidgetBuilder::new().with_margin(Thickness::uniform(1.0)))
269                    .checked(Some(is_bit_set(self.value, i)))
270                    .build(ctx)
271            })
272            .collect::<Vec<_>>();
273
274        let panel =
275            WrapPanelBuilder::new(WidgetBuilder::new().with_children(bit_switches.iter().cloned()))
276                .with_orientation(Orientation::Horizontal)
277                .build(ctx);
278
279        let canvas = BitField {
280            widget: self.widget_builder.with_child(panel).build(ctx),
281            value: self.value,
282            bit_switches,
283        };
284        ctx.add_node(UiNode::new(canvas))
285    }
286}
287
288#[cfg(test)]
289mod test {
290    use crate::bit::BitFieldBuilder;
291    use crate::{test::test_widget_deletion, widget::WidgetBuilder};
292
293    #[test]
294    fn test_deletion() {
295        test_widget_deletion(|ctx| BitFieldBuilder::<usize>::new(WidgetBuilder::new()).build(ctx));
296    }
297}