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}