conrod_core/ui.rs
1use color::Color;
2use cursor;
3use event;
4use fnv;
5use graph::{self, Graph};
6use input;
7use position::{self, Align, Dimensions, Direction, Padding, Point, Position, Range, Rect, Scalar};
8use render;
9use std;
10use std::sync::atomic::{self, AtomicUsize};
11use text;
12use theme::Theme;
13use utils;
14use widget::{self, Widget};
15
16/// A constructor type for building a `Ui` instance with a set of optional parameters.
17pub struct UiBuilder {
18 /// The initial dimensions of the window in which the `Ui` exists.
19 pub window_dimensions: Dimensions,
20 /// The theme used to set default styling for widgets.
21 ///
22 /// If this field is `None` when `build` is called, `Theme::default` will be used.
23 pub maybe_theme: Option<Theme>,
24 /// An estimation of the maximum number of widgets that will be used with this `Ui` instance.
25 ///
26 /// This value is used to determine the size with which various collections should be
27 /// reserved. This may make the first cycle of widget instantiations more efficient as the
28 /// collections will not be required to grow dynamically. These collections include:
29 ///
30 /// - the widget graph node and edge `Vec`s
31 /// - the `HashSet` used to track updated widgets
32 /// - the widget `DepthOrder` (a kind of toposort describing the order of widgets in their
33 /// rendering order).
34 ///
35 /// If this field is `None` when `build` is called, these collections will be initialised with
36 /// no pre-reserved size and will instead grow organically as needed.
37 pub maybe_widgets_capacity: Option<usize>,
38}
39
40/// `Ui` is the most important type within Conrod and is necessary for rendering and maintaining
41/// widget state.
42/// # Ui Handles the following:
43/// * Contains the state of all widgets which can be indexed via their widget::Id.
44/// * Stores rendering state for each widget until the end of each render cycle.
45/// * Contains the theme used for default styling of the widgets.
46/// * Maintains the latest user input state (for mouse and keyboard).
47/// * Maintains the latest window dimensions.
48#[derive(Debug)]
49pub struct Ui {
50 /// The theme used to set default styling for widgets.
51 pub theme: Theme,
52 /// An index into the root widget of the graph, representing the entire window.
53 pub window: widget::Id,
54 /// Handles aggregation of events and providing them to Widgets
55 global_input: input::Global,
56 /// Manages all fonts that have been loaded by the user.
57 pub fonts: text::font::Map,
58 /// The Widget cache, storing state for all widgets.
59 widget_graph: Graph,
60 /// The widget::Id of the widget that was last updated/set.
61 maybe_prev_widget_id: Option<widget::Id>,
62 /// The widget::Id of the last widget used as a parent for another widget.
63 maybe_current_parent_id: Option<widget::Id>,
64 /// The number of frames that will be used for the `redraw_count` when `need_redraw` is
65 /// triggered.
66 num_redraw_frames: u8,
67 /// Whether or not the `Ui` needs to be re-drawn to screen.
68 redraw_count: AtomicUsize,
69 /// A background color to clear the screen with before drawing if one was given.
70 maybe_background_color: Option<Color>,
71 /// The order in which widgets from the `widget_graph` are drawn.
72 depth_order: graph::DepthOrder,
73 /// The set of widgets that have been updated since the beginning of the `set_widgets` stage.
74 updated_widgets: fnv::FnvHashSet<widget::Id>,
75 /// The `updated_widgets` for the previous `set_widgets` stage.
76 ///
77 /// We use this to compare against the newly generated `updated_widgets` to see whether or not
78 /// we require re-drawing.
79 prev_updated_widgets: fnv::FnvHashSet<widget::Id>,
80 /// Scroll events that have been emitted during a call to `Ui::set_widgets`. These are usually
81 /// emitted by some widget like the `Scrollbar`.
82 ///
83 /// These events will be drained and pushed onto the end of the `global_input` event buffer at
84 /// the end of the `Ui::set_widgets` method. This ensures that the events are received by the
85 /// target widgets during the next call to `Ui::set_widgets`.
86 pending_scroll_events: Vec<event::Ui>,
87 /// Mouse cursor
88 mouse_cursor: cursor::MouseCursor,
89
90 // TODO: Remove the following fields as they should now be handled by `input::Global`.
91 /// Window width.
92 pub win_w: f64,
93 /// Window height.
94 pub win_h: f64,
95}
96
97/// A wrapper around the `Ui` that restricts the user from mutating the `Ui` in certain ways while
98/// in the scope of the `Ui::set_widgets` function and within `Widget`s' `update` methods. Using
99/// the `UiCell`, users may access the `Ui` immutably (via `Deref`) however they wish, however they
100/// may only mutate the `Ui` via the `&mut self` methods provided by the `UiCell`.
101///
102/// The name came from its likening to a "jail cell for the `Ui`", as it restricts a user's access
103/// to it. However, we realise that the name may also cause ambiguity with the std `Cell` and
104/// `RefCell` types (which `UiCell` has nothing to do with). Thus, if you have a better name for
105/// this type in mind, please let us know at the github repo via an issue or PR sometime before we
106/// hit 1.0.0!
107#[derive(Debug)]
108pub struct UiCell<'a> {
109 /// A mutable reference to a **Ui**.
110 ui: &'a mut Ui,
111}
112
113/// Each time conrod is required to redraw the GUI, it must draw for at least the next three frames
114/// to ensure that, in the case that graphics buffers are being swapped, we have filled each
115/// buffer. Otherwise if we don't draw into each buffer, we will probably be subject to flickering.
116pub const SAFE_REDRAW_COUNT: u8 = 3;
117
118impl UiBuilder {
119 /// Begin building a new `Ui` instance.
120 ///
121 /// Give the initial dimensions of the window within which the `Ui` will be instantiated as a
122 /// `Scalar` (DPI agnostic) value.
123 pub fn new(window_dimensions: Dimensions) -> Self {
124 UiBuilder {
125 window_dimensions: window_dimensions,
126 maybe_theme: None,
127 maybe_widgets_capacity: None,
128 }
129 }
130
131 /// The theme used to set default styling for widgets.
132 ///
133 /// If this field is `None` when `build` is called, `Theme::default` will be used.
134 pub fn theme(mut self, value: Theme) -> Self {
135 self.maybe_theme = Some(value);
136 self
137 }
138
139 /// An estimation of the maximum number of widgets that will be used with this `Ui` instance.
140 ///
141 /// This value is used to determine the size with which various collections should be
142 /// reserved. This may make the first cycle of widget instantiations more efficient as the
143 /// collections will not be required to grow dynamically. These collections include:
144 ///
145 /// - the widget graph node and edge `Vec`s
146 /// - the `HashSet` used to track updated widgets
147 /// - the widget `DepthOrder` (a kind of toposort describing the order of widgets in their
148 /// rendering order).
149 ///
150 /// If this field is `None` when `build` is called, these collections will be initialised with
151 /// no pre-reserved size and will instead grow organically as required.
152 pub fn widgets_capacity(mut self, value: usize) -> Self {
153 self.maybe_widgets_capacity = Some(value);
154 self
155 }
156
157 /// Build **Ui** from the given builder
158 pub fn build(self) -> Ui {
159 Ui::new(self)
160 }
161}
162
163impl Ui {
164 /// A new, empty **Ui**.
165 fn new(builder: UiBuilder) -> Self {
166 let UiBuilder {
167 window_dimensions,
168 maybe_widgets_capacity,
169 maybe_theme,
170 } = builder;
171
172 let (mut widget_graph, depth_order, updated_widgets) = maybe_widgets_capacity.map_or_else(
173 || {
174 (
175 Graph::new(),
176 graph::DepthOrder::new(),
177 fnv::FnvHashSet::default(),
178 )
179 },
180 |n| {
181 (
182 Graph::with_node_capacity(n),
183 graph::DepthOrder::with_node_capacity(n),
184 std::collections::HashSet::with_capacity_and_hasher(
185 n,
186 fnv::FnvBuildHasher::default(),
187 ),
188 )
189 },
190 );
191
192 let window = widget_graph.add_placeholder();
193 let prev_updated_widgets = updated_widgets.clone();
194 Ui {
195 widget_graph: widget_graph,
196 theme: maybe_theme.unwrap_or_else(|| Theme::default()),
197 fonts: text::font::Map::new(),
198 window: window,
199 win_w: window_dimensions[0],
200 win_h: window_dimensions[1],
201 maybe_prev_widget_id: None,
202 maybe_current_parent_id: None,
203 num_redraw_frames: SAFE_REDRAW_COUNT,
204 redraw_count: AtomicUsize::new(SAFE_REDRAW_COUNT as usize),
205 maybe_background_color: None,
206 depth_order: depth_order,
207 updated_widgets: updated_widgets,
208 prev_updated_widgets: prev_updated_widgets,
209 global_input: input::Global::new(),
210 pending_scroll_events: Vec::new(),
211 mouse_cursor: cursor::MouseCursor::Arrow,
212 }
213 }
214
215 /// Returns a `input::Widget` for the given widget
216 pub fn widget_input(&self, widget: widget::Id) -> input::Widget {
217 // If there's no rectangle for a given widget, then we use one with zero area.
218 // This means that the resulting `input::Widget` will not include any mouse events
219 // unless it has captured the mouse, since none will have occured over that area.
220 let rect = self.rect_of(widget).unwrap_or_else(|| {
221 let right_edge = self.win_w / 2.0;
222 let bottom_edge = self.win_h / 2.0;
223 Rect::from_xy_dim([right_edge, bottom_edge], [0.0, 0.0])
224 });
225 input::Widget::for_widget(widget, rect, &self.global_input)
226 }
227
228 /// The **Rect** for the widget at the given index.
229 ///
230 /// Returns `None` if there is no widget for the given index.
231 pub fn rect_of(&self, id: widget::Id) -> Option<Rect> {
232 self.widget_graph.widget(id).map(|widget| widget.rect)
233 }
234
235 /// The absolute width of the widget at the given index.
236 ///
237 /// Returns `None` if there is no widget for the given index.
238 pub fn w_of(&self, id: widget::Id) -> Option<Scalar> {
239 self.rect_of(id).map(|rect| rect.w())
240 }
241
242 /// The absolute height of the widget at the given index.
243 ///
244 /// Returns `None` if there is no widget for the given index.
245 pub fn h_of(&self, id: widget::Id) -> Option<Scalar> {
246 self.rect_of(id).map(|rect| rect.h())
247 }
248
249 /// The absolute dimensions for the widget at the given index.
250 ///
251 /// Returns `None` if there is no widget for the given index.
252 pub fn wh_of(&self, id: widget::Id) -> Option<Dimensions> {
253 self.rect_of(id).map(|rect| rect.dim())
254 }
255
256 /// The coordinates for the widget at the given index.
257 ///
258 /// Returns `None` if there is no widget for the given index.
259 pub fn xy_of(&self, id: widget::Id) -> Option<Point> {
260 self.rect_of(id).map(|rect| rect.xy())
261 }
262
263 /// The `kid_area` of the widget at the given index.
264 ///
265 /// Returns `None` if there is no widget for the given index.
266 pub fn kid_area_of(&self, id: widget::Id) -> Option<Rect> {
267 self.widget_graph
268 .widget(id)
269 .map(|widget| widget.kid_area.rect.padding(widget.kid_area.pad))
270 }
271
272 /// An index to the previously updated widget if there is one.
273 pub fn maybe_prev_widget(&self) -> Option<widget::Id> {
274 self.maybe_prev_widget_id
275 }
276
277 /// Borrow the **Ui**'s `widget_graph`.
278 pub fn widget_graph(&self) -> &Graph {
279 &self.widget_graph
280 }
281
282 /// Borrow the **Ui**'s set of updated widgets.
283 ///
284 /// This set indicates which widgets have been instantiated since the beginning of the most
285 /// recent `Ui::set_widgets` call.
286 pub fn updated_widgets(&self) -> &fnv::FnvHashSet<widget::Id> {
287 &self.updated_widgets
288 }
289
290 /// Borrow the **Ui**'s set of updated widgets.
291 ///
292 /// This set indicates which widgets have were instantiated during the previous call to
293 /// `Ui::set_widgets`.
294 pub fn prev_updated_widgets(&self) -> &fnv::FnvHashSet<widget::Id> {
295 &self.prev_updated_widgets
296 }
297
298 /// Produces a type that may be used to generate new unique `widget::Id`s.
299 ///
300 /// See the [**widget::id::Generator**](../widget/id/struct.Generator.html) docs for details on
301 /// how to use this correctly.
302 pub fn widget_id_generator(&mut self) -> widget::id::Generator {
303 widget::id::Generator::new(&mut self.widget_graph)
304 }
305
306 /// Scroll the widget at the given index by the given offset amount.
307 ///
308 /// The produced `Scroll` event will be applied upon the next call to `Ui::set_widgets`.
309 pub fn scroll_widget(&mut self, widget_id: widget::Id, offset: [Scalar; 2]) {
310 let (x, y) = (offset[0], offset[1]);
311
312 if x != 0.0 || y != 0.0 {
313 let event = event::Ui::Scroll(
314 Some(widget_id),
315 event::Scroll {
316 x: x,
317 y: y,
318 modifiers: self.global_input.current.modifiers,
319 },
320 )
321 .into();
322 self.global_input.push_event(event);
323 }
324 }
325
326 /// Determines which widget is currently under the mouse and sets it within the `Ui`'s
327 /// `input::Global`'s `input::State`.
328 ///
329 /// If the `widget_under_mouse` has changed, this function will also update the
330 /// `widget_capturing_mouse`.
331 ///
332 /// If the left mouse button is up, we assume that the widget directly under the
333 /// mouse cursor captures all input from the mouse.
334 ///
335 /// If the left mouse button is down, we assume that the widget that was clicked
336 /// remains "pinned" and will continue to capture the mouse until it is
337 /// released.
338 ///
339 /// Note: This function expects that `ui.global_input.current.mouse.xy` is up-to-date.
340 fn track_widget_under_mouse_and_update_capturing(&mut self) {
341 self.global_input.current.widget_under_mouse = graph::algo::pick_widgets(
342 &self.depth_order.indices,
343 self.global_input.current.mouse.xy,
344 )
345 .next(&self.widget_graph, &self.depth_order.indices, &self.theme);
346
347 // If MouseButton::Left is up and `widget_under_mouse` has changed, capture new widget
348 // under mouse.
349 if self.global_input.current.mouse.buttons.left().is_up() {
350 let widget_under_mouse = self.global_input.current.widget_under_mouse;
351
352 // Check to see if we need to uncapture a widget.
353 if let Some(idx) = self.global_input.current.widget_capturing_mouse {
354 if widget_under_mouse != Some(idx) {
355 let source = input::Source::Mouse;
356 let event = event::Ui::WidgetUncapturesInputSource(idx, source).into();
357 self.global_input.push_event(event);
358 self.global_input.current.widget_capturing_mouse = None;
359 }
360 }
361
362 // Check to see if there is a new widget capturing the mouse.
363 if self.global_input.current.widget_capturing_mouse.is_none() {
364 if let Some(idx) = widget_under_mouse {
365 let source = input::Source::Mouse;
366 let event = event::Ui::WidgetCapturesInputSource(idx, source).into();
367 self.global_input.push_event(event);
368 self.global_input.current.widget_capturing_mouse = Some(idx);
369 }
370 }
371 }
372 }
373
374 /// Handle raw window events and update the `Ui` state accordingly.
375 ///
376 /// This occurs within several stages:
377 ///
378 /// 1. Convert the user's given `event` to a `RawEvent` so that the `Ui` may use it.
379 /// 2. Interpret the `RawEvent` for higher-level `Event`s such as `DoubleClick`,
380 /// `WidgetCapturesKeyboard`, etc.
381 /// 3. Update the `Ui`'s `global_input` `State` accordingly, depending on the `RawEvent`.
382 /// 4. Store newly produced `event::Ui`s within the `global_input` so that they may be filtered
383 /// and fed to `Widget`s next time `Ui::set_widget` is called.
384 ///
385 /// This method *drives* the `Ui` forward, and is what allows for using conrod's `Ui` with any
386 /// window event stream.
387 ///
388 /// The given `event` must implement the **ToRawEvent** trait so that it can be converted to a
389 /// `RawEvent` that can be used by the `Ui`.
390 pub fn handle_event(&mut self, event: event::Input) {
391 use event::Input;
392 use input::state::mouse::Button as MouseButton;
393 use input::{Button, Key, ModifierKey, Motion};
394
395 // A function for filtering `ModifierKey`s.
396 fn filter_modifier(key: Key) -> Option<ModifierKey> {
397 match key {
398 Key::LCtrl | Key::RCtrl => Some(ModifierKey::CTRL),
399 Key::LShift | Key::RShift => Some(ModifierKey::SHIFT),
400 Key::LAlt | Key::RAlt => Some(ModifierKey::ALT),
401 Key::LGui | Key::RGui => Some(ModifierKey::GUI),
402 _ => None,
403 }
404 }
405
406 // Here we handle all user input given to conrod.
407 //
408 // Not only do we store the `Input` event as an `Event::Raw`, we also use them to
409 // interpret higher level events such as `Click` or `Drag`.
410 //
411 // Finally, we also ensure that the `current_state` is up-to-date.
412 self.global_input.push_event(event.clone().into());
413 match event {
414 // Some button was pressed, whether keyboard, mouse or some other device.
415 Input::Press(button_type) => match button_type {
416 // Check to see whether we need to (un)capture the keyboard or mouse.
417 Button::Mouse(mouse_button) => {
418 // Create a mouse `Press` event.
419 let mouse_xy = self.global_input.current.mouse.xy;
420 let press = event::Press {
421 button: event::Button::Mouse(mouse_button, mouse_xy),
422 modifiers: self.global_input.current.modifiers,
423 };
424 let widget = self.global_input.current.widget_capturing_mouse;
425 let press_event = event::Ui::Press(widget, press).into();
426 self.global_input.push_event(press_event);
427
428 if let MouseButton::Left = mouse_button {
429 // Check to see if we need to uncapture the keyboard.
430 if let Some(idx) = self.global_input.current.widget_capturing_keyboard {
431 if Some(idx) != self.global_input.current.widget_under_mouse {
432 let source = input::Source::Keyboard;
433 let event = event::Ui::WidgetUncapturesInputSource(idx, source);
434 self.global_input.push_event(event.into());
435 self.global_input.current.widget_capturing_keyboard = None;
436 }
437 }
438
439 // Check to see if we need to capture the keyboard.
440 if let Some(idx) = self.global_input.current.widget_under_mouse {
441 let source = input::Source::Keyboard;
442 let event = event::Ui::WidgetCapturesInputSource(idx, source);
443 self.global_input.push_event(event.into());
444 self.global_input.current.widget_capturing_keyboard = Some(idx);
445 }
446 }
447
448 // Keep track of pressed buttons in the current input::State.
449 let xy = self.global_input.current.mouse.xy;
450 let widget = self.global_input.current.widget_under_mouse;
451 self.global_input
452 .current
453 .mouse
454 .buttons
455 .press(mouse_button, xy, widget);
456 }
457
458 Button::Keyboard(key) => {
459 // Create a keyboard `Press` event.
460 let press = event::Press {
461 button: event::Button::Keyboard(key),
462 modifiers: self.global_input.current.modifiers,
463 };
464 let widget = self.global_input.current.widget_capturing_keyboard;
465 let press_event = event::Ui::Press(widget, press).into();
466 self.global_input.push_event(press_event);
467
468 // If some modifier key was pressed, add it to the current modifiers.
469 if let Some(modifier) = filter_modifier(key) {
470 self.global_input.current.modifiers.insert(modifier);
471 }
472
473 // If `Esc` was pressed, check to see if we need to cancel a `Drag` or
474 // uncapture a widget.
475 if let Key::Escape = key {
476 // TODO:
477 // 1. Cancel `Drag` if currently under way.
478 // 2. If mouse is captured due to pinning widget with left mouse button,
479 // cancel capturing.
480 }
481 }
482
483 _ => {}
484 },
485
486 // Some button was released.
487 //
488 // Checks for events in the following order:
489 // 1. Click
490 // 2. DoubleClick
491 // 2. WidgetUncapturesMouse
492 Input::Release(button_type) => match button_type {
493 Button::Mouse(mouse_button) => {
494 // Create a `Release` event.
495 let mouse_xy = self.global_input.current.mouse.xy;
496 let release = event::Release {
497 button: event::Button::Mouse(mouse_button, mouse_xy),
498 modifiers: self.global_input.current.modifiers,
499 };
500 let widget = self.global_input.current.widget_capturing_mouse;
501 let release_event = event::Ui::Release(widget, release).into();
502 self.global_input.push_event(release_event);
503
504 // Check for `Click` and `DoubleClick` events.
505 let down = self.global_input.current.mouse.buttons[mouse_button].if_down();
506 if let Some((_, widget)) = down {
507 // The widget that's being clicked.
508 let clicked_widget =
509 self.global_input
510 .current
511 .widget_under_mouse
512 .and_then(|released| {
513 widget.and_then(|pressed| {
514 if pressed == released {
515 Some(released)
516 } else {
517 None
518 }
519 })
520 });
521
522 let click = event::Click {
523 button: mouse_button,
524 xy: self.global_input.current.mouse.xy,
525 modifiers: self.global_input.current.modifiers,
526 };
527
528 let click_event = event::Ui::Click(clicked_widget, click).into();
529 self.global_input.push_event(click_event);
530
531 let now = instant::Instant::now();
532 let double_click =
533 self.global_input
534 .last_click
535 .and_then(|(last_time, last_click)| {
536 // If the button of this click is different to the button
537 // of last click, don't create a `DoubleClick`.
538 if click.button != last_click.button {
539 return None;
540 }
541
542 // If the mouse has moved since the last click, don't
543 // create a `DoubleClick`.
544 if click.xy != last_click.xy {
545 return None;
546 }
547
548 // If the duration since the last click is longer than the
549 // double_click_threshold, don't create a `DoubleClick`.
550 let duration = now.duration_since(last_time);
551 // TODO: Work out how to get this threshold from the user's
552 // system preferences.
553 let threshold = self.theme.double_click_threshold;
554 if duration >= threshold {
555 return None;
556 }
557
558 Some(event::DoubleClick {
559 button: click.button,
560 xy: click.xy,
561 modifiers: click.modifiers,
562 })
563 });
564
565 if let Some(double_click) = double_click {
566 // Reset the `last_click` to `None`, as to not register another
567 // `DoubleClick` on the next consecutive `Click`.
568 self.global_input.last_click = None;
569 let double_click_event =
570 event::Ui::DoubleClick(clicked_widget, double_click).into();
571 self.global_input.push_event(double_click_event);
572 } else {
573 // Set the `Click` that we just stored as the `last_click`.
574 self.global_input.last_click = Some((now, click));
575 }
576 }
577
578 // Uncapture widget capturing mouse if MouseButton::Left is down and
579 // widget_under_mouse != capturing widget.
580 if let MouseButton::Left = mouse_button {
581 if let Some(idx) = self.global_input.current.widget_capturing_mouse {
582 if Some(idx) != self.global_input.current.widget_under_mouse {
583 let source = input::Source::Mouse;
584 let event = event::Ui::WidgetUncapturesInputSource(idx, source);
585 self.global_input.push_event(event.into());
586 self.global_input.current.widget_capturing_mouse = None;
587 }
588 }
589 }
590
591 // Release the given mouse_button from the input::State.
592 self.global_input
593 .current
594 .mouse
595 .buttons
596 .release(mouse_button);
597 }
598
599 Button::Keyboard(key) => {
600 // Create a `Release` event.
601 let release = event::Release {
602 button: event::Button::Keyboard(key),
603 modifiers: self.global_input.current.modifiers,
604 };
605 let widget = self.global_input.current.widget_capturing_keyboard;
606 let release_event = event::Ui::Release(widget, release).into();
607 self.global_input.push_event(release_event);
608
609 // If a modifier key was released, remove it from the current set.
610 if let Some(modifier) = filter_modifier(key) {
611 self.global_input.current.modifiers.remove(modifier);
612 }
613 }
614
615 _ => (),
616 },
617
618 // The window was resized.
619 Input::Resize(w, h) => {
620 // Create a `WindowResized` event.
621 let (w, h) = (w as Scalar, h as Scalar);
622 let window_resized = event::Ui::WindowResized([w, h]).into();
623 self.global_input.push_event(window_resized);
624
625 self.win_w = w;
626 self.win_h = h;
627 self.needs_redraw();
628 self.track_widget_under_mouse_and_update_capturing();
629 }
630
631 // The mouse cursor was moved to a new position.
632 //
633 // Checks for events in the following order:
634 // 1. `Drag`
635 // 2. `WidgetUncapturesMouse`
636 // 3. `WidgetCapturesMouse`
637 Input::Motion(motion) => {
638 // Create a `Motion` event.
639 let move_ = event::Motion {
640 motion: motion,
641 modifiers: self.global_input.current.modifiers,
642 };
643 let widget = self.global_input.current.widget_capturing_mouse;
644 let move_event = event::Ui::Motion(widget, move_).into();
645 self.global_input.push_event(move_event);
646
647 match motion {
648 Motion::MouseCursor { x, y } => {
649 // Check for drag events.
650 let last_mouse_xy = self.global_input.current.mouse.xy;
651 let mouse_xy = [x, y];
652 let delta_xy = utils::vec2_sub(mouse_xy, last_mouse_xy);
653 // For each button that is down, trigger a drag event.
654 let buttons = self.global_input.current.mouse.buttons.clone();
655 for (btn, btn_xy, widget) in buttons.pressed() {
656 let total_delta_xy = utils::vec2_sub(mouse_xy, btn_xy);
657 let distance = (total_delta_xy[0] + total_delta_xy[1]).abs().sqrt();
658 if distance > self.theme.mouse_drag_threshold {
659 let event = event::Ui::Drag(
660 widget,
661 event::Drag {
662 button: btn,
663 origin: btn_xy,
664 from: last_mouse_xy,
665 to: mouse_xy,
666 delta_xy: delta_xy,
667 total_delta_xy: total_delta_xy,
668 modifiers: self.global_input.current.modifiers,
669 },
670 )
671 .into();
672 self.global_input.push_event(event);
673 }
674 }
675
676 // Update the position of the mouse within the global_input's
677 // input::State.
678 self.global_input.current.mouse.xy = mouse_xy;
679
680 self.track_widget_under_mouse_and_update_capturing();
681 }
682
683 // Some scrolling occurred (e.g. mouse scroll wheel).
684 Motion::Scroll { x, y } => {
685 let mut scrollable_widgets = {
686 let depth_order = &self.depth_order.indices;
687 let mouse_xy = self.global_input.current.mouse.xy;
688 graph::algo::pick_scrollable_widgets(depth_order, mouse_xy)
689 };
690
691 // Iterate through the scrollable widgets from top to bottom.
692 //
693 // A scroll event will be created for the first scrollable widget
694 // that hasn't already reached the bound of the scroll event's
695 // direction.
696 while let Some(idx) = scrollable_widgets.next(
697 &self.widget_graph,
698 &self.depth_order.indices,
699 &self.theme,
700 ) {
701 let (kid_area, maybe_x_scroll, maybe_y_scroll) =
702 match self.widget_graph.widget(idx) {
703 Some(widget) => (
704 widget.kid_area,
705 widget.maybe_x_scroll_state,
706 widget.maybe_y_scroll_state,
707 ),
708 None => continue,
709 };
710
711 fn offset_is_at_bound<A>(
712 scroll: &widget::scroll::State<A>,
713 additional_offset: Scalar,
714 ) -> bool {
715 fn approx_eq(a: Scalar, b: Scalar) -> bool {
716 (a - b).abs() < 0.000001
717 }
718
719 if additional_offset.is_sign_positive() {
720 let max = utils::partial_max(
721 scroll.offset_bounds.start,
722 scroll.offset_bounds.end,
723 );
724 approx_eq(scroll.offset, max)
725 } else {
726 let min = utils::partial_min(
727 scroll.offset_bounds.start,
728 scroll.offset_bounds.end,
729 );
730 approx_eq(scroll.offset, min)
731 }
732 }
733
734 let mut scroll_x = false;
735 let mut scroll_y = false;
736
737 // Check whether the x axis is scrollable.
738 if x != 0.0 {
739 let new_scroll = widget::scroll::State::update(
740 self,
741 idx,
742 &kid_area,
743 maybe_x_scroll,
744 x,
745 );
746 if let Some(prev_scroll) = maybe_x_scroll {
747 let (prev_is_at_bound, new_is_at_bound) = (
748 offset_is_at_bound(&prev_scroll, x),
749 offset_is_at_bound(&new_scroll, x),
750 );
751 scroll_x = !prev_is_at_bound || !new_is_at_bound;
752 }
753 }
754
755 // Check whether the y axis is scrollable.
756 if y != 0.0 {
757 let new_scroll = widget::scroll::State::update(
758 self,
759 idx,
760 &kid_area,
761 maybe_y_scroll,
762 y,
763 );
764 if let Some(prev_scroll) = maybe_y_scroll {
765 let (prev_is_at_bound, new_is_at_bound) = (
766 offset_is_at_bound(&prev_scroll, y),
767 offset_is_at_bound(&new_scroll, y),
768 );
769 scroll_y = !prev_is_at_bound || !new_is_at_bound;
770 }
771 }
772
773 // Create a `Scroll` event if either axis is scrollable.
774 if scroll_x || scroll_y {
775 let event = event::Ui::Scroll(
776 Some(idx),
777 event::Scroll {
778 x: x,
779 y: y,
780 modifiers: self.global_input.current.modifiers,
781 },
782 )
783 .into();
784 self.global_input.push_event(event);
785
786 // Now that we've scrolled the top, scrollable widget,
787 // we're done with the loop.
788 break;
789 }
790 }
791
792 // If no scrollable widgets could be scrolled, emit the event to
793 // the widget that currently captures the mouse.
794 if x != 0.0 || y != 0.0 {
795 let widget = self.global_input.current.widget_capturing_mouse;
796 if let Some(idx) = widget {
797 if let Some(widget) = self.widget_graph.widget(idx) {
798 // Only create the event if the widget is not
799 // scrollable, as the event would have already been
800 // created within the above loop.
801 if widget.maybe_x_scroll_state.is_none()
802 && widget.maybe_y_scroll_state.is_none()
803 {
804 let scroll = event::Scroll {
805 x: x,
806 y: y,
807 modifiers: self.global_input.current.modifiers,
808 };
809 let event = event::Ui::Scroll(Some(idx), scroll);
810 self.global_input.push_event(event.into());
811 }
812 }
813 }
814 }
815
816 // Now that there might be a different widget under the mouse, we
817 // must update the capturing state.
818 self.track_widget_under_mouse_and_update_capturing();
819 }
820
821 _ => (),
822 }
823 }
824
825 Input::Text(string) => {
826 // Create a `Text` event.
827 let text = event::Text {
828 string: string,
829 modifiers: self.global_input.current.modifiers,
830 };
831 let widget = self.global_input.current.widget_capturing_keyboard;
832 let text_event = event::Ui::Text(widget, text).into();
833 self.global_input.push_event(text_event);
834 }
835
836 Input::Touch(touch) => match touch.phase {
837 input::touch::Phase::Start => {
838 // Find the widget under the touch.
839 let widget_under_touch =
840 graph::algo::pick_widgets(&self.depth_order.indices, touch.xy).next(
841 &self.widget_graph,
842 &self.depth_order.indices,
843 &self.theme,
844 );
845
846 // The start of the touch interaction state to be stored.
847 let start = input::state::touch::Start {
848 time: instant::Instant::now(),
849 xy: touch.xy,
850 widget: widget_under_touch,
851 };
852
853 // The touch interaction state to be stored in the map.
854 let state = input::state::touch::Touch {
855 start: start,
856 xy: touch.xy,
857 widget: widget_under_touch,
858 };
859
860 // Insert the touch state into the map.
861 self.global_input.current.touch.insert(touch.id, state);
862
863 // Push touch event.
864 let event = event::Ui::Touch(widget_under_touch, touch);
865 self.global_input.push_event(event.into());
866
867 // Push capture event.
868 if let Some(widget) = widget_under_touch {
869 let source = input::Source::Touch(touch.id);
870 let event = event::Ui::WidgetCapturesInputSource(widget, source);
871 self.global_input.push_event(event.into());
872 }
873 }
874
875 input::touch::Phase::Move => {
876 // Update the widget under the touch and return the widget capturing the touch.
877 let widget = match self.global_input.current.touch.get_mut(&touch.id) {
878 Some(touch_state) => {
879 touch_state.widget =
880 graph::algo::pick_widgets(&self.depth_order.indices, touch.xy)
881 .next(
882 &self.widget_graph,
883 &self.depth_order.indices,
884 &self.theme,
885 );
886 touch_state.xy = touch.xy;
887 touch_state.start.widget
888 }
889 None => None,
890 };
891 let event = event::Ui::Touch(widget, touch);
892 self.global_input.push_event(event.into());
893 }
894
895 input::touch::Phase::Cancel => {
896 let widget = self
897 .global_input
898 .current
899 .touch
900 .remove(&touch.id)
901 .and_then(|t| t.start.widget);
902 let event = event::Ui::Touch(widget, touch);
903 self.global_input.push_event(event.into());
904
905 // Generate an "uncaptures" event if necessary.
906 if let Some(widget) = widget {
907 let source = input::Source::Touch(touch.id);
908 let event = event::Ui::WidgetUncapturesInputSource(widget, source);
909 self.global_input.push_event(event.into());
910 }
911 }
912
913 input::touch::Phase::End => {
914 let old_touch = self
915 .global_input
916 .current
917 .touch
918 .remove(&touch.id)
919 .map(|touch| touch);
920 let widget_capturing = old_touch.as_ref().and_then(|touch| touch.start.widget);
921 let event = event::Ui::Touch(widget_capturing, touch);
922 self.global_input.push_event(event.into());
923
924 // Create a `Tap` event.
925 //
926 // If the widget at the end of the touch is the same as the widget at the start
927 // of the touch, that widget receives the `Tap`.
928 let tapped_widget =
929 graph::algo::pick_widgets(&self.depth_order.indices, touch.xy)
930 .next(&self.widget_graph, &self.depth_order.indices, &self.theme)
931 .and_then(|widget| match Some(widget) == widget_capturing {
932 true => Some(widget),
933 false => None,
934 });
935 let tap = event::Tap {
936 id: touch.id,
937 xy: touch.xy,
938 };
939 let event = event::Ui::Tap(tapped_widget, tap);
940 self.global_input.push_event(event.into());
941
942 // Generate an "uncaptures" event if necessary.
943 if let Some(widget) = widget_capturing {
944 let source = input::Source::Touch(touch.id);
945 let event = event::Ui::WidgetUncapturesInputSource(widget, source);
946 self.global_input.push_event(event.into());
947 }
948 }
949 },
950
951 Input::Focus(focused) if focused == true => self.needs_redraw(),
952 Input::Focus(_focused) => (),
953
954 Input::Redraw => self.needs_redraw(),
955 }
956 }
957
958 /// Get an immutable reference to global input. Handles aggregation of events and providing them to Widgets
959 ///
960 /// Can be used to access the current input state, e.g. which widgets are currently capturing inputs.
961 pub fn global_input(&self) -> &input::Global {
962 &self.global_input
963 }
964
965 /// Set keyboard capturing widget
966 pub fn keyboard_capture(&mut self, idx: widget::Id) {
967 let source = input::Source::Keyboard;
968
969 if self
970 .global_input
971 .current
972 .widget_capturing_keyboard
973 .is_some()
974 {
975 let event = event::Ui::WidgetUncapturesInputSource(idx, source);
976 self.global_input.push_event(event.into());
977 self.global_input.current.widget_capturing_keyboard = None;
978 }
979
980 let event = event::Ui::WidgetCapturesInputSource(idx, source).into();
981 self.global_input.push_event(event);
982 self.global_input.current.widget_capturing_keyboard = Some(idx);
983 }
984
985 /// Get the centred xy coords for some given `Dimension`s, `Position` and alignment.
986 ///
987 /// If getting the xy for a specific widget, its `widget::Id` should be specified so that we
988 /// can also consider the scroll offset of the scrollable parent widgets.
989 ///
990 /// The `place_on_kid_area` argument specifies whether or not **Place** **Position** variants
991 /// should target a **Widget**'s `kid_area`, or simply the **Widget**'s total area.
992 pub fn calc_xy(
993 &self,
994 maybe_id: Option<widget::Id>,
995 maybe_parent_id: Option<widget::Id>,
996 x_position: Position,
997 y_position: Position,
998 dim: Dimensions,
999 place_on_kid_area: bool,
1000 ) -> Point {
1001 use utils::vec2_add;
1002
1003 // Retrieves the absolute **Scalar** position from the given position for a single axis.
1004 //
1005 // The axis used is specified by the given range_from_rect function which, given some
1006 // **Rect**, returns the relevant **Range**.
1007 fn abs_from_position<R, P>(
1008 ui: &Ui,
1009 maybe_parent_id: Option<widget::Id>,
1010 position: Position,
1011 dim: Scalar,
1012 place_on_kid_area: bool,
1013 range_from_rect: R,
1014 start_and_end_pad: P,
1015 ) -> Scalar
1016 where
1017 R: FnOnce(Rect) -> Range,
1018 P: FnOnce(Padding) -> Range,
1019 {
1020 let (relative, maybe_id) = match position {
1021 Position::Absolute(abs) => return abs,
1022 Position::Relative(relative, maybe_id) => (relative, maybe_id),
1023 };
1024
1025 match relative {
1026 position::Relative::Scalar(scalar) => maybe_id
1027 .or(ui.maybe_prev_widget_id)
1028 .or(Some(ui.window.into()))
1029 .and_then(|idx| ui.rect_of(idx).map(range_from_rect))
1030 .map(|other_range| other_range.middle() + scalar)
1031 .unwrap_or(scalar),
1032
1033 position::Relative::Direction(direction, amt) => maybe_id
1034 .or(ui.maybe_prev_widget_id)
1035 .and_then(|idx| ui.rect_of(idx).map(range_from_rect))
1036 .map(|other_range| {
1037 let range = Range::from_pos_and_len(0.0, dim);
1038 match direction {
1039 Direction::Forwards => range.align_after(other_range).middle() + amt,
1040 Direction::Backwards => range.align_before(other_range).middle() - amt,
1041 }
1042 })
1043 .unwrap_or_else(|| match direction {
1044 Direction::Forwards => amt,
1045 Direction::Backwards => -amt,
1046 }),
1047
1048 position::Relative::Align(align) => maybe_id
1049 .or(ui.maybe_prev_widget_id)
1050 .or(Some(ui.window.into()))
1051 .and_then(|idx| ui.rect_of(idx).map(range_from_rect))
1052 .map(|other_range| {
1053 let range = Range::from_pos_and_len(0.0, dim);
1054 match align {
1055 Align::Start => range.align_start_of(other_range).middle(),
1056 Align::Middle => other_range.middle(),
1057 Align::End => range.align_end_of(other_range).middle(),
1058 }
1059 })
1060 .unwrap_or(0.0),
1061
1062 position::Relative::Place(place) => {
1063 let parent_id = maybe_id
1064 .or(maybe_parent_id)
1065 .or(ui.maybe_current_parent_id)
1066 .unwrap_or(ui.window.into());
1067 let maybe_area = match place_on_kid_area {
1068 true => ui
1069 .widget_graph
1070 .widget(parent_id)
1071 .map(|w| w.kid_area)
1072 .map(|k| (range_from_rect(k.rect), start_and_end_pad(k.pad))),
1073 false => ui
1074 .rect_of(parent_id)
1075 .map(|rect| (range_from_rect(rect), Range::new(0.0, 0.0))),
1076 };
1077 maybe_area
1078 .map(|(parent_range, pad)| {
1079 let range = Range::from_pos_and_len(0.0, dim);
1080 let parent_range = parent_range.pad_start(pad.start).pad_end(pad.end);
1081 match place {
1082 position::Place::Start(maybe_mgn) => {
1083 range.align_start_of(parent_range).middle()
1084 + maybe_mgn.unwrap_or(0.0)
1085 }
1086 position::Place::Middle => parent_range.middle(),
1087 position::Place::End(maybe_mgn) => {
1088 range.align_end_of(parent_range).middle()
1089 - maybe_mgn.unwrap_or(0.0)
1090 }
1091 }
1092 })
1093 .unwrap_or(0.0)
1094 }
1095 }
1096 }
1097
1098 fn x_range(rect: Rect) -> Range {
1099 rect.x
1100 }
1101 fn y_range(rect: Rect) -> Range {
1102 rect.y
1103 }
1104 fn x_pad(pad: Padding) -> Range {
1105 pad.x
1106 }
1107 fn y_pad(pad: Padding) -> Range {
1108 pad.y
1109 }
1110 let x = abs_from_position(
1111 self,
1112 maybe_parent_id,
1113 x_position,
1114 dim[0],
1115 place_on_kid_area,
1116 x_range,
1117 x_pad,
1118 );
1119 let y = abs_from_position(
1120 self,
1121 maybe_parent_id,
1122 y_position,
1123 dim[1],
1124 place_on_kid_area,
1125 y_range,
1126 y_pad,
1127 );
1128 let xy = [x, y];
1129
1130 // Add the widget's parents' total combined scroll offset to the given xy.
1131 maybe_id
1132 .map(|idx| vec2_add(xy, graph::algo::scroll_offset(&self.widget_graph, idx)))
1133 .unwrap_or(xy)
1134 }
1135
1136 /// A function within which all widgets are instantiated by the user, normally situated within
1137 /// the "update" stage of an event loop.
1138 pub fn set_widgets(&mut self) -> UiCell {
1139 self.maybe_prev_widget_id = None;
1140 self.maybe_current_parent_id = None;
1141
1142 // Move the previous `updated_widgets` to `prev_updated_widgets` and clear
1143 // `updated_widgets` so that we're ready to store the newly updated widgets.
1144 {
1145 let Ui {
1146 ref mut updated_widgets,
1147 ref mut prev_updated_widgets,
1148 ..
1149 } = *self;
1150 std::mem::swap(updated_widgets, prev_updated_widgets);
1151 updated_widgets.clear();
1152 }
1153
1154 let mut ui_cell = UiCell { ui: self };
1155
1156 // Instantiate the root `Window` `Widget`.
1157 //
1158 // This widget acts as the parent-most widget and root node for the Ui's `widget_graph`,
1159 // upon which all other widgets are placed.
1160 {
1161 use {color, Borderable, Colorable, Positionable};
1162 type Window = widget::BorderedRectangle;
1163 Window::new([ui_cell.win_w, ui_cell.win_h])
1164 .no_parent()
1165 .x_y(0.0, 0.0)
1166 .border(0.0)
1167 .border_color(color::BLACK.alpha(0.0))
1168 .color(
1169 ui_cell
1170 .maybe_background_color
1171 .unwrap_or(color::BLACK.alpha(0.0)),
1172 )
1173 .set(ui_cell.window, &mut ui_cell);
1174 }
1175
1176 ui_cell.ui.maybe_current_parent_id = Some(ui_cell.window.into());
1177
1178 ui_cell.set_mouse_cursor(cursor::MouseCursor::Arrow);
1179
1180 ui_cell
1181 }
1182
1183 /// Set the number of frames that the `Ui` should draw in the case that `needs_redraw` is
1184 /// called. The default is `3` (see the SAFE_REDRAW_COUNT docs for details).
1185 pub fn set_num_redraw_frames(&mut self, num_frames: u8) {
1186 self.num_redraw_frames = num_frames;
1187 }
1188
1189 /// Tells the `Ui` that it needs to re-draw everything. It does this by setting the redraw
1190 /// count to `num_redraw_frames`. See the docs for `set_num_redraw_frames`, SAFE_REDRAW_COUNT
1191 /// or `draw_if_changed` for more info on how/why the redraw count is used.
1192 pub fn needs_redraw(&self) {
1193 self.redraw_count
1194 .store(self.num_redraw_frames as usize, atomic::Ordering::Relaxed);
1195 }
1196
1197 /// The first of the `Primitives` yielded by `Ui::draw` or `Ui::draw_if_changed` will always
1198 /// be a `Rectangle` the size of the window in which conrod is hosted.
1199 ///
1200 /// This method sets the colour with which this `Rectangle` is drawn (the default being
1201 /// `conrod::color::TRANSPARENT`.
1202 pub fn clear_with(&mut self, color: Color) {
1203 self.maybe_background_color = Some(color);
1204 }
1205
1206 /// Draw the `Ui` in it's current state.
1207 ///
1208 /// NOTE: If you don't need to redraw your conrod GUI every frame, it is recommended to use the
1209 /// `Ui::draw_if_changed` method instead.
1210 pub fn draw(&self) -> render::Primitives {
1211 let Ui {
1212 ref redraw_count,
1213 ref widget_graph,
1214 ref depth_order,
1215 ref theme,
1216 ref fonts,
1217 win_w,
1218 win_h,
1219 ..
1220 } = *self;
1221
1222 // Use the depth_order indices as the order for drawing.
1223 let indices = &depth_order.indices;
1224
1225 // We're about to draw everything, so take one from the redraw count.
1226 let remaining_redraws = redraw_count.load(atomic::Ordering::Relaxed);
1227 if remaining_redraws > 0 {
1228 redraw_count.store(remaining_redraws - 1, atomic::Ordering::Relaxed);
1229 }
1230
1231 render::Primitives::new(widget_graph, indices, theme, fonts, [win_w, win_h])
1232 }
1233
1234 /// Same as the `Ui::draw` method, but *only* draws if the `redraw_count` is greater than 0.
1235 ///
1236 /// The `redraw_count` is set to `SAFE_REDRAW_COUNT` whenever a `Widget` indicates that it
1237 /// needs to be re-drawn.
1238 ///
1239 /// It can also be triggered manually by the user using the `Ui::needs_redraw` method.
1240 ///
1241 /// This method is generally preferred over `Ui::draw` as it requires far less CPU usage, only
1242 /// redrawing to the screen if necessary.
1243 ///
1244 /// Note that when `Ui::needs_redraw` is triggered, it sets the `redraw_count` to 3 by default.
1245 /// This ensures that conrod is drawn to each buffer in the case that there is buffer swapping
1246 /// happening. Let us know if you need finer control over this and we'll expose a way for you
1247 /// to set the redraw count manually.
1248 pub fn draw_if_changed(&self) -> Option<render::Primitives> {
1249 if self.has_changed() {
1250 return Some(self.draw());
1251 }
1252
1253 None
1254 }
1255
1256 /// Returns if the redraw_count is greater than 0 and thus draw_if_changed would draw
1257 /// See `Ui::draw_if_changed` for when this is triggered
1258 pub fn has_changed(&self) -> bool {
1259 self.redraw_count.load(atomic::Ordering::Relaxed) > 0
1260 }
1261
1262 /// The **Rect** that bounds the kids of the widget with the given index.
1263 pub fn kids_bounding_box(&self, id: widget::Id) -> Option<Rect> {
1264 graph::algo::kids_bounding_box(&self.widget_graph, &self.prev_updated_widgets, id)
1265 }
1266
1267 /// The **Rect** that represents the maximum fully visible area for the widget with the given
1268 /// index, including consideration of cropped scroll area.
1269 ///
1270 /// Otherwise, return None if the widget is not visible.
1271 pub fn visible_area(&self, id: widget::Id) -> Option<Rect> {
1272 graph::algo::cropped_area_of_widget(&self.widget_graph, id)
1273 }
1274
1275 /// Get mouse cursor state.
1276 pub fn mouse_cursor(&self) -> cursor::MouseCursor {
1277 self.mouse_cursor
1278 }
1279}
1280
1281impl<'a> UiCell<'a> {
1282 /// A reference to the `Theme` that is currently active within the `Ui`.
1283 pub fn theme(&self) -> &Theme {
1284 &self.ui.theme
1285 }
1286
1287 /// A convenience method for borrowing the `Font` for the given `Id` if it exists.
1288 pub fn font(&self, id: text::font::Id) -> Option<&text::Font> {
1289 self.ui.fonts.get(id)
1290 }
1291
1292 /// Returns the dimensions of the window
1293 pub fn window_dim(&self) -> Dimensions {
1294 [self.ui.win_w, self.ui.win_h]
1295 }
1296
1297 /// Returns an immutable reference to the `input::Global` of the `Ui`.
1298 ///
1299 /// All coordinates here will be relative to the center of the window.
1300 pub fn global_input(&self) -> &input::Global {
1301 &self.ui.global_input
1302 }
1303
1304 /// Returns a `input::Widget` with input events for the widget.
1305 ///
1306 /// All coordinates in the `input::Widget` will be relative to the widget at the given index.
1307 pub fn widget_input(&self, id: widget::Id) -> input::Widget {
1308 self.ui.widget_input(id)
1309 }
1310
1311 /// Produces a type that may be used to generate new unique `widget::Id`s.
1312 ///
1313 /// See the [**widget::id::Generator**](../widget/id/struct.Generator.html) docs for details on
1314 /// how to use this correctly.
1315 pub fn widget_id_generator(&mut self) -> widget::id::Generator {
1316 self.ui.widget_id_generator()
1317 }
1318
1319 /// The **Rect** that bounds the kids of the widget with the given index.
1320 ///
1321 /// Returns `None` if the widget has no children or if there's is no widget for the given index.
1322 pub fn kids_bounding_box(&self, id: widget::Id) -> Option<Rect> {
1323 self.ui.kids_bounding_box(id)
1324 }
1325
1326 /// Scroll the widget at the given index by the given offset amount.
1327 ///
1328 /// The produced `Scroll` event will be pushed to the `pending_scroll_events` and will be
1329 /// applied to the widget during the next call to `Ui::set_widgets`.
1330 pub fn scroll_widget(&mut self, id: widget::Id, offset: [Scalar; 2]) {
1331 let (x, y) = (offset[0], offset[1]);
1332
1333 if x != 0.0 || y != 0.0 {
1334 let event = event::Ui::Scroll(
1335 Some(id),
1336 event::Scroll {
1337 x: x,
1338 y: y,
1339 modifiers: self.ui.global_input.current.modifiers,
1340 },
1341 );
1342 self.ui.pending_scroll_events.push(event);
1343 }
1344 }
1345
1346 /// Sets the mouse cursor
1347 pub fn set_mouse_cursor(&mut self, cursor: cursor::MouseCursor) {
1348 self.ui.mouse_cursor = cursor;
1349 }
1350}
1351
1352impl<'a> Drop for UiCell<'a> {
1353 fn drop(&mut self) {
1354 // We'll need to re-draw if we have gained or lost widgets.
1355 let changed = self.ui.updated_widgets != self.ui.prev_updated_widgets;
1356 if changed {
1357 self.ui.needs_redraw();
1358 }
1359
1360 // Update the **DepthOrder** so that it reflects the **Graph**'s current state.
1361 {
1362 let Ui {
1363 ref widget_graph,
1364 ref mut depth_order,
1365 window,
1366 ref updated_widgets,
1367 ..
1368 } = *self.ui;
1369
1370 depth_order.update(widget_graph, window, updated_widgets);
1371 }
1372
1373 // Reset the global input state. Note that this is the **only** time this should be called.
1374 self.ui.global_input.clear_events_and_update_start_state();
1375
1376 // Update which widget is under the cursor.
1377 if changed {
1378 self.ui.track_widget_under_mouse_and_update_capturing();
1379 }
1380
1381 // Move all pending `Scroll` events that have been produced since the start of this method
1382 // into the `global_input` event buffer.
1383 for scroll_event in self.ui.pending_scroll_events.drain(0..) {
1384 self.ui.global_input.push_event(scroll_event.into());
1385 }
1386 }
1387}
1388
1389impl<'a> ::std::ops::Deref for UiCell<'a> {
1390 type Target = Ui;
1391 fn deref(&self) -> &Ui {
1392 self.ui
1393 }
1394}
1395
1396impl<'a> AsRef<Ui> for UiCell<'a> {
1397 fn as_ref(&self) -> &Ui {
1398 &self.ui
1399 }
1400}
1401
1402/// A function for retrieving the `&mut Ui<B>` from a `UiCell<B>`.
1403///
1404/// This function is only for internal use to allow for some `Ui` type acrobatics in order to
1405/// provide a nice *safe* API for the user.
1406pub fn ref_mut_from_ui_cell<'a, 'b: 'a>(ui_cell: &'a mut UiCell<'b>) -> &'a mut Ui {
1407 ui_cell.ui
1408}
1409
1410/// A mutable reference to the given `Ui`'s widget `Graph`.
1411pub fn widget_graph_mut(ui: &mut Ui) -> &mut Graph {
1412 &mut ui.widget_graph
1413}
1414
1415/// Infer a widget's `Depth` parent by examining it's *x* and *y* `Position`s.
1416///
1417/// When a different parent may be inferred from either `Position`, the *x* `Position` is favoured.
1418pub fn infer_parent_from_position(ui: &Ui, x: Position, y: Position) -> Option<widget::Id> {
1419 use position::Relative::{Align, Direction, Place, Scalar};
1420 use Position::Relative;
1421 match (x, y) {
1422 (Relative(Place(_), maybe_parent_id), _) | (_, Relative(Place(_), maybe_parent_id)) => {
1423 maybe_parent_id
1424 }
1425 (Relative(Direction(_, _), maybe_id), _)
1426 | (_, Relative(Direction(_, _), maybe_id))
1427 | (Relative(Align(_), maybe_id), _)
1428 | (_, Relative(Align(_), maybe_id))
1429 | (Relative(Scalar(_), maybe_id), _)
1430 | (_, Relative(Scalar(_), maybe_id)) => maybe_id
1431 .or(ui.maybe_prev_widget_id)
1432 .and_then(|idx| ui.widget_graph.depth_parent(idx)),
1433 _ => None,
1434 }
1435}
1436
1437/// Attempts to infer the parent of a widget from its *x*/*y* `Position`s and the current state of
1438/// the `Ui`.
1439///
1440/// If no parent can be inferred via the `Position`s, the `maybe_current_parent_id` will be used.
1441///
1442/// If `maybe_current_parent_id` is `None`, the `Ui`'s `window` widget will be used.
1443///
1444/// **Note:** This function does not check whether or not using the `window` widget would cause a
1445/// cycle.
1446pub fn infer_parent_unchecked(ui: &Ui, x_pos: Position, y_pos: Position) -> widget::Id {
1447 infer_parent_from_position(ui, x_pos, y_pos)
1448 .or(ui.maybe_current_parent_id)
1449 .unwrap_or(ui.window.into())
1450}
1451
1452/// Cache some `PreUpdateCache` widget data into the widget graph.
1453/// Set the widget that is being cached as the new `prev_widget`.
1454/// Set the widget's parent as the new `current_parent`.
1455pub fn pre_update_cache(ui: &mut Ui, widget: widget::PreUpdateCache) {
1456 ui.maybe_prev_widget_id = Some(widget.id);
1457 ui.maybe_current_parent_id = widget.maybe_parent_id;
1458 let widget_id = widget.id;
1459 ui.widget_graph
1460 .pre_update_cache(ui.window, widget, ui.updated_widgets.len());
1461
1462 // Add the widget's `widget::Id` to the set of updated widgets.
1463 ui.updated_widgets.insert(widget_id);
1464}
1465
1466/// Cache some `PostUpdateCache` widget data into the widget graph.
1467/// Set the widget that is being cached as the new `prev_widget`.
1468/// Set the widget's parent as the new `current_parent`.
1469pub fn post_update_cache<W>(ui: &mut Ui, widget: widget::PostUpdateCache<W>)
1470where
1471 W: Widget,
1472 W::State: 'static,
1473 W::Style: 'static,
1474{
1475 ui.maybe_prev_widget_id = Some(widget.id);
1476 ui.maybe_current_parent_id = widget.maybe_parent_id;
1477 ui.widget_graph.post_update_cache(widget);
1478}