pixel_widgets/
widget.rs

1//! Widgets are defined using the [`Widget`](trait.Widget.html) trait.
2//! You can choose to implement widgets yourself, or you can use the built in widgets defined in this module.
3//!
4//! Since the whole [`Component`](../trait.Component.html) every time it is mutated,
5//! most widgets need to keep track of some kind of state across rebuilds.
6//! This is managed automatically by the component, identified by the [`key`](trait.Widget.html#method.key) method.
7//! When implementing custom widgets, you need to make sure that the returned `u64` key is as unique as possible.
8//! You also need to make sure that the custom widgets do not remember absolute layouts.
9//! Widgets like [`Scroll`](scroll/struct.Scroll.html) can change the layout without needing a rebuild of the ui.
10use std::any::Any;
11use std::collections::hash_map::DefaultHasher;
12use std::hash::{Hash, Hasher};
13use std::task::Waker;
14
15use smallvec::SmallVec;
16
17use crate::draw::Primitive;
18use crate::event::Event;
19use crate::layout::*;
20use crate::node::GenericNode;
21use crate::style::*;
22
23/// Prelude widgets
24pub mod prelude {
25    pub use super::button::Button;
26    pub use super::column::Column;
27    pub use super::drag_drop::{Drag, Drop};
28    pub use super::dropdown::Dropdown;
29    pub use super::dummy::Dummy;
30    pub use super::frame::Frame;
31    pub use super::image::Image;
32    pub use super::input::Input;
33    pub use super::layers::Layers;
34    pub use super::menu::Menu;
35    pub use super::panel::Panel;
36    pub use super::progress::Progress;
37    pub use super::row::Row;
38    pub use super::scroll::Scroll;
39    pub use super::slider::Slider;
40    pub use super::spacer::Spacer;
41    pub use super::text::Text;
42    pub use super::toggle::Toggle;
43    pub use super::window::Window;
44
45    pub use super::{StateVec, Widget};
46}
47
48/// A clickable button
49pub mod button;
50/// Layout child widgets vertically
51pub mod column;
52/// Drag and drop zones
53pub mod drag_drop;
54/// Pick an item from a dropdown box
55pub mod dropdown;
56/// Dummy widget that has a custom widget name
57pub mod dummy;
58/// A widget that wraps around a content widget
59pub mod frame;
60/// Just an image
61pub mod image;
62/// Editable text input
63pub mod input;
64/// Stack child widgets on top of each other, while only the topmost receives events.
65pub mod layers;
66/// A context menu with nestable items
67pub mod menu;
68/// A panel with a fixed size and location within it's parent
69pub mod panel;
70/// A bar that fills up according to a value.
71pub mod progress;
72/// Layout child widgets horizontally
73pub mod row;
74/// View a small section of larger widget, with scrollbars.
75pub mod scroll;
76/// A slider for easily picking some number
77pub mod slider;
78/// Empty widget
79pub mod spacer;
80/// Widget that renders a paragraph of text.
81pub mod text;
82/// A clickable button that toggles some `bool`.
83pub mod toggle;
84/// A window with a title and a content widget that can be moved by dragging the title.
85pub mod window;
86
87/// A user interface widget.
88pub trait Widget<'a, Message>: Send {
89    /// The type of state this widget keeps track of.
90    type State: Any + Send + Sync;
91
92    /// The key of this widget, used for resolving state.
93    fn key(&self) -> u64 {
94        let mut hasher = DefaultHasher::new();
95        std::any::type_name::<Self>().hash(&mut hasher);
96        hasher.finish()
97    }
98
99    /// Create a new state
100    fn mount(&self) -> Self::State;
101
102    /// The name of this widget, used to identify widgets of this type in stylesheets.
103    fn widget(&self) -> &'static str;
104
105    /// The state of this widget, used for computing the style.
106    /// If `None` is returned, `Node` will automatically compute a state, such as "hover" and "pressed".
107    fn state(&self, _state: &Self::State) -> StateVec {
108        StateVec::new()
109    }
110
111    /// Should return the amount of children this widget has. Must be consistent with
112    /// [`visit_children()`](#tymethod.visit_children).
113    fn len(&self) -> usize;
114
115    /// Returns whether this children has no children. Must be consistent with
116    /// [`visit_children()`](#tymethod.visit_children).
117    fn is_empty(&self) -> bool {
118        self.len() == 0
119    }
120
121    /// Applies a visitor to all childs of the widget. If an widget fails to visit it's children, the children won't
122    /// be able to resolve their stylesheet, resulting in a panic when calling [`size`](struct.Node.html#method.size),
123    /// [`hit`](struct.Node.html#method.hit), [`event`](struct.Node.html#method.event) or
124    /// [`draw`](struct.Node.html#method.draw).
125    fn visit_children(&mut self, visitor: &mut dyn FnMut(&mut dyn GenericNode<'a, Message>));
126
127    /// Returns the `(width, height)` of this widget.
128    /// The extents are defined as a [`Size`](../layout/struct.Size.html),
129    /// which will later be resolved to actual dimensions.
130    fn size(&self, state: &Self::State, style: &Stylesheet) -> (Size, Size);
131
132    /// Perform a hit detect on the widget. Most widgets are fine with the default implementation, but some
133    /// widgets (like [`Window`](window/struct.Window.html) need to report a _miss_ (`false`) even when the queried
134    /// position is within their layout.
135    ///
136    /// Arguments:
137    /// - `layout`: the layout assigned to the widget
138    /// - `clip`: a clipping rect for mouse events. Mouse events outside of this rect should be considered invalid,
139    /// such as with [`Scroll`](scroll/struct.Scroll.html), where the widget would not be visible outside of the
140    /// currently visible rect.
141    /// - `x`: x mouse coordinate being queried
142    /// - `y`: y mouse coordinate being queried
143    fn hit(
144        &self,
145        _state: &Self::State,
146        layout: Rectangle,
147        clip: Rectangle,
148        _style: &Stylesheet,
149        x: f32,
150        y: f32,
151    ) -> bool {
152        layout.point_inside(x, y) && clip.point_inside(x, y)
153    }
154
155    /// Test the widget for focus exclusivity.
156    /// If the widget or one of it's descendants is in an exclusive focus state, this function should return `true`.
157    /// In all other cases, it should return `false`. When a widget is in an exclusive focus state it is
158    /// the only widget that is allowed to receive events in [`event`](#tymethod.event).
159    /// Widgets that intended to use this behaviour are modal dialogs, dropdown boxes, context menu's, etc.
160    fn focused(&self, _state: &Self::State) -> bool {
161        false
162    }
163
164    /// Handle an event. If an event changes the graphical appearance of an `Widget`,
165    /// [`redraw`](struct.Context.html#method.redraw) should be called to let the [`Ui`](../struct.Ui.html) know that
166    /// the ui should be redrawn.
167    ///
168    /// Arguments:
169    /// - `layout`: the layout assigned to the widget
170    /// - `clip`: a clipping rect for mouse events. Mouse events outside of this rect should be considered invalid,
171    /// such as with [`Scroll`](scroll/struct.Scroll.html), where the widget would not be visible outside of the
172    /// currently visible rect.
173    /// - `event`: the event that needs to be handled
174    /// - `context`: context for submitting messages and requesting redraws of the ui.
175    fn event(
176        &mut self,
177        _state: &mut Self::State,
178        _layout: Rectangle,
179        _clip: Rectangle,
180        _style: &Stylesheet,
181        _event: Event,
182        _context: &mut Context<Message>,
183    ) {
184    }
185
186    /// Draw the widget. Returns a list of [`Primitive`s](../draw/enum.Primitive.html) that should be drawn.
187    ///
188    /// Arguments:
189    /// - `layout`: the layout assigned to the widget
190    /// - `clip`: a clipping rect for use with [`Primitive::PushClip`](../draw/enum.Primitive.html#variant.PushClip).
191    fn draw(
192        &mut self,
193        state: &mut Self::State,
194        layout: Rectangle,
195        clip: Rectangle,
196        style: &Stylesheet,
197    ) -> Vec<Primitive<'a>>;
198}
199
200/// Storage for style states
201pub type StateVec = SmallVec<[StyleState<&'static str>; 3]>;
202
203/// Context for posting messages and requesting redraws of the ui.
204pub struct Context<Message> {
205    cursor: (f32, f32),
206    redraw: bool,
207    poll: bool,
208    messages: Vec<Message>,
209    waker: Waker,
210}
211
212impl<Message> Context<Message> {
213    pub(crate) fn new(redraw: bool, cursor: (f32, f32), waker: Waker) -> Self {
214        Context {
215            cursor,
216            redraw,
217            poll: false,
218            messages: Vec::new(),
219            waker,
220        }
221    }
222
223    pub(crate) fn sub_context<M>(&self) -> Context<M> {
224        Context {
225            cursor: self.cursor,
226            redraw: self.redraw,
227            poll: self.poll,
228            messages: Vec::new(),
229            waker: self.waker.clone(),
230        }
231    }
232
233    /// Push a message to the parnet [`Component`](../component/trait.Component.html).
234    pub fn push(&mut self, message: Message) {
235        self.messages.push(message);
236    }
237
238    /// Push multiple messages to the parent [`Component`](../component/trait.Component.html) using an iterator.
239    pub fn extend<I: IntoIterator<Item = Message>>(&mut self, iter: I) {
240        self.messages.extend(iter);
241    }
242
243    /// Request a redraw of the ui.
244    pub fn redraw(&mut self) {
245        self.redraw = true;
246    }
247
248    /// Returns the redraw flag.
249    pub fn redraw_requested(&self) -> bool {
250        self.redraw
251    }
252
253    /// Returns the cursor position
254    pub fn cursor(&self) -> (f32, f32) {
255        self.cursor
256    }
257
258    pub(crate) fn task_context(&self) -> std::task::Context<'_> {
259        std::task::Context::from_waker(&self.waker)
260    }
261
262    pub(crate) fn into_vec(self) -> Vec<Message> {
263        self.messages
264    }
265}
266
267impl<Message> IntoIterator for Context<Message> {
268    type Item = Message;
269    type IntoIter = std::vec::IntoIter<Message>;
270
271    fn into_iter(self) -> Self::IntoIter {
272        self.messages.into_iter()
273    }
274}