fyrox_ui/
build.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//! Build context is used to decouple explicit UI state modification. See [`BuildContext`] docs for
22//! more info.
23
24use crate::style::resource::StyleResource;
25use crate::{
26    core::pool::Handle, font::FontResource, message::UiMessage, RestrictionEntry, UiNode,
27    UserInterface,
28};
29use fyrox_graph::BaseSceneGraph;
30use std::{
31    ops::{Index, IndexMut},
32    sync::mpsc::Sender,
33};
34
35/// Build context is used to decouple explicit UI state modification. Its main use is in the various widget
36/// builders. Internally, it is just a mutable reference to the UI state. UI can be modified (add nodes, clone,
37/// link, etc.) via build context. This is needed to explicitly highlight that it used to modify the UI
38/// state. It is **not recommended** to use BuildContext for mutable access to widgets at runtime! _Use message
39/// passing_ to modify widgets at runtime, otherwise you will easily break invariant (inner state) of widgets.
40/// The only place where it's allowed to directly mutate widget's state is at build stage (inside `build`
41/// method of your widget builder).
42///
43/// ## Examples
44///
45/// ```rust
46/// # use fyrox_ui::{
47/// #     core::pool::Handle,
48/// #     core::{visitor::prelude::*, reflect::prelude::*, type_traits::prelude::*,},
49/// #     define_widget_deref,
50/// #     message::UiMessage,
51/// #     widget::{Widget, WidgetBuilder},
52/// #     BuildContext, Control, UiNode, UserInterface,
53/// # };
54/// # use std::{
55/// #     any::{Any, TypeId},
56/// #     ops::{Deref, DerefMut},
57/// # };
58/// # use fyrox_core::uuid_provider;
59/// #
60/// #[derive(Clone, Visit, Reflect, Debug, ComponentProvider)]
61/// #[reflect(derived_type = "UiNode")]
62/// struct MyWidget {
63///     widget: Widget,
64/// }
65/// #
66/// # define_widget_deref!(MyWidget);
67/// #
68/// # uuid_provider!(MyWidget = "a93ec1b5-e7c8-4919-ac19-687d8c99f6bd");
69/// #
70/// # impl Control for MyWidget {
71/// #     fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
72/// #         todo!()
73/// #     }
74/// # }
75///
76/// struct MyWidgetBuilder {
77///     widget_builder: WidgetBuilder,
78/// }
79///
80/// impl MyWidgetBuilder {
81///     pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
82///         let my_widget = MyWidget {
83///             widget: self.widget_builder.build(ctx),
84///         };
85///
86///         ctx.add_node(UiNode::new(my_widget))
87///     }
88/// }
89/// ```
90pub struct BuildContext<'a> {
91    ui: &'a mut UserInterface,
92    pub style: StyleResource,
93}
94
95impl Index<Handle<UiNode>> for BuildContext<'_> {
96    type Output = UiNode;
97
98    fn index(&self, index: Handle<UiNode>) -> &Self::Output {
99        &self.ui.nodes[index]
100    }
101}
102
103impl IndexMut<Handle<UiNode>> for BuildContext<'_> {
104    fn index_mut(&mut self, index: Handle<UiNode>) -> &mut Self::Output {
105        &mut self.ui.nodes[index]
106    }
107}
108
109impl<'a> From<&'a mut UserInterface> for BuildContext<'a> {
110    fn from(ui: &'a mut UserInterface) -> Self {
111        Self {
112            style: ui.style.clone(),
113            ui,
114        }
115    }
116}
117
118impl BuildContext<'_> {
119    /// Returns default font instance used by the UI.
120    pub fn default_font(&self) -> FontResource {
121        self.ui.default_font.clone()
122    }
123
124    /// Returns current message sender of the UI, that is used for message passing mechanism. You can
125    /// send messages for your widgets inside your builders, however this has limited use and should
126    /// be avoided in the favor of explicit state modification to not overload message pipeline.
127    pub fn sender(&self) -> Sender<UiMessage> {
128        self.ui.sender()
129    }
130
131    /// Adds a new widget to the UI. See [`UiNode`] docs for more info, [`UiNode::new`] in particular.
132    pub fn add_node(&mut self, node: UiNode) -> Handle<UiNode> {
133        self.ui.add_node(node)
134    }
135
136    /// Links the child widget with the parent widget. Child widget's position and size will be restricted by
137    /// the new parent. When a widget is linked to other widget, its coordinates become relative to it parent.
138    pub fn link(&mut self, child: Handle<UiNode>, parent: Handle<UiNode>) {
139        self.ui.link_nodes(child, parent, false)
140    }
141
142    /// Copies a widget, adds it to the UI, links it to the root node of the UI and returns the handle to it.
143    pub fn copy(&mut self, node: Handle<UiNode>) -> Handle<UiNode> {
144        self.ui.copy_node(node)
145    }
146
147    /// Tries to fetch the node by its handle. Returns `None` if the handle is invalid.
148    pub fn try_get_node(&self, node: Handle<UiNode>) -> Option<&UiNode> {
149        self.ui.try_get_node(node)
150    }
151
152    /// Tries to fetch the node by its handle. Returns `None` if the handle is invalid.
153    pub fn try_get_node_mut(&mut self, node: Handle<UiNode>) -> Option<&mut UiNode> {
154        self.ui.nodes.try_borrow_mut(node)
155    }
156
157    /// Pushes a new picking restriction to the picking-restriction stack. See [`UserInterface::push_picking_restriction`]
158    /// docs for more info.
159    pub fn push_picking_restriction(&mut self, restriction: RestrictionEntry) {
160        self.ui.push_picking_restriction(restriction)
161    }
162
163    /// Explicitly removes picking restriction for the given node from the picking-restriction stack. See
164    /// [`UserInterface::remove_picking_restriction`] docs for more info.
165    pub fn remove_picking_restriction(&mut self, node: Handle<UiNode>) {
166        self.ui.remove_picking_restriction(node)
167    }
168
169    /// Returns an immutable reference to the user interface.
170    pub fn inner(&self) -> &UserInterface {
171        self.ui
172    }
173
174    /// Returns a mutable reference to the user interface.
175    pub fn inner_mut(&mut self) -> &mut UserInterface {
176        self.ui
177    }
178
179    /// Sends a message during build stage. It has quite limited use, but could be unavoidable in
180    /// for cases when you need to do some action that relies on fully performed layout stage. When a
181    /// widget is being built, you can't fetch any layout info of it since it wasn't calculated yet.
182    /// In this case all you can do is to "postpone" your action for later moment in current frame
183    /// by sending a message.
184    pub fn send_message(&self, message: UiMessage) {
185        self.ui.send_message(message);
186    }
187}