use color::Color;
use event;
use graph::{self, Graph};
use input;
use position::{self, Align, Direction, Dimensions, Padding, Point, Position, Range, Rect, Scalar};
use render;
use std;
use std::sync::atomic::{self, AtomicUsize};
use fnv;
use text;
use theme::Theme;
use utils;
use widget::{self, Widget};
use cursor;
pub struct UiBuilder {
pub window_dimensions: Dimensions,
pub maybe_theme: Option<Theme>,
pub maybe_widgets_capacity: Option<usize>
}
#[derive(Debug)]
pub struct Ui {
pub theme: Theme,
pub window: widget::Id,
global_input: input::Global,
pub fonts: text::font::Map,
widget_graph: Graph,
maybe_prev_widget_id: Option<widget::Id>,
maybe_current_parent_id: Option<widget::Id>,
num_redraw_frames: u8,
redraw_count: AtomicUsize,
maybe_background_color: Option<Color>,
depth_order: graph::DepthOrder,
updated_widgets: fnv::FnvHashSet<widget::Id>,
prev_updated_widgets: fnv::FnvHashSet<widget::Id>,
pending_scroll_events: Vec<event::Ui>,
mouse_cursor: cursor::MouseCursor,
pub win_w: f64,
pub win_h: f64,
}
#[derive(Debug)]
pub struct UiCell<'a> {
ui: &'a mut Ui,
}
pub const SAFE_REDRAW_COUNT: u8 = 3;
impl UiBuilder {
pub fn new(window_dimensions: Dimensions) -> Self {
UiBuilder {
window_dimensions: window_dimensions,
maybe_theme: None,
maybe_widgets_capacity: None
}
}
pub fn theme(mut self, value: Theme) -> Self {
self.maybe_theme = Some(value);
self
}
pub fn widgets_capacity(mut self, value: usize) -> Self {
self.maybe_widgets_capacity = Some(value);
self
}
pub fn build(self) -> Ui {
Ui::new(self)
}
}
impl Ui {
fn new(builder: UiBuilder) -> Self {
let UiBuilder {
window_dimensions,
maybe_widgets_capacity,
maybe_theme,
} = builder;
let (mut widget_graph, depth_order, updated_widgets) =
maybe_widgets_capacity.map_or_else(
|| (Graph::new(),
graph::DepthOrder::new(),
fnv::FnvHashSet::default()),
|n| (Graph::with_node_capacity(n),
graph::DepthOrder::with_node_capacity(n),
std::collections::HashSet::with_capacity_and_hasher(n,
fnv::FnvBuildHasher::default())));
let window = widget_graph.add_placeholder();
let prev_updated_widgets = updated_widgets.clone();
Ui {
widget_graph: widget_graph,
theme: maybe_theme.unwrap_or_else(|| Theme::default()),
fonts: text::font::Map::new(),
window: window,
win_w: window_dimensions[0],
win_h: window_dimensions[1],
maybe_prev_widget_id: None,
maybe_current_parent_id: None,
num_redraw_frames: SAFE_REDRAW_COUNT,
redraw_count: AtomicUsize::new(SAFE_REDRAW_COUNT as usize),
maybe_background_color: None,
depth_order: depth_order,
updated_widgets: updated_widgets,
prev_updated_widgets: prev_updated_widgets,
global_input: input::Global::new(),
pending_scroll_events: Vec::new(),
mouse_cursor: cursor::MouseCursor::Arrow,
}
}
pub fn widget_input(&self, widget: widget::Id) -> input::Widget {
let rect = self.rect_of(widget).unwrap_or_else(|| {
let right_edge = self.win_w / 2.0;
let bottom_edge = self.win_h / 2.0;
Rect::from_xy_dim([right_edge, bottom_edge], [0.0, 0.0])
});
input::Widget::for_widget(widget, rect, &self.global_input)
}
pub fn rect_of(&self, id: widget::Id) -> Option<Rect> {
self.widget_graph.widget(id).map(|widget| widget.rect)
}
pub fn w_of(&self, id: widget::Id) -> Option<Scalar> {
self.rect_of(id).map(|rect| rect.w())
}
pub fn h_of(&self, id: widget::Id) -> Option<Scalar> {
self.rect_of(id).map(|rect| rect.h())
}
pub fn wh_of(&self, id: widget::Id) -> Option<Dimensions> {
self.rect_of(id).map(|rect| rect.dim())
}
pub fn xy_of(&self, id: widget::Id) -> Option<Point> {
self.rect_of(id).map(|rect| rect.xy())
}
pub fn kid_area_of(&self, id: widget::Id) -> Option<Rect> {
self.widget_graph.widget(id).map(|widget| {
widget.kid_area.rect.padding(widget.kid_area.pad)
})
}
pub fn maybe_prev_widget(&self) -> Option<widget::Id> {
self.maybe_prev_widget_id
}
pub fn widget_graph(&self) -> &Graph {
&self.widget_graph
}
pub fn updated_widgets(&self) -> &fnv::FnvHashSet<widget::Id> {
&self.updated_widgets
}
pub fn prev_updated_widgets(&self) -> &fnv::FnvHashSet<widget::Id> {
&self.prev_updated_widgets
}
pub fn widget_id_generator(&mut self) -> widget::id::Generator {
widget::id::Generator::new(&mut self.widget_graph)
}
pub fn scroll_widget(&mut self, widget_id: widget::Id, offset: [Scalar; 2]) {
let (x, y) = (offset[0], offset[1]);
if x != 0.0 || y != 0.0 {
let event = event::Ui::Scroll(Some(widget_id), event::Scroll {
x: x,
y: y,
modifiers: self.global_input.current.modifiers,
}).into();
self.global_input.push_event(event);
}
}
fn track_widget_under_mouse_and_update_capturing(&mut self) {
self.global_input.current.widget_under_mouse =
graph::algo::pick_widgets(&self.depth_order.indices,
self.global_input.current.mouse.xy)
.next(&self.widget_graph,
&self.depth_order.indices,
&self.theme);
if self.global_input.current.mouse.buttons.left().is_up() {
let widget_under_mouse = self.global_input.current.widget_under_mouse;
if let Some(idx) = self.global_input.current.widget_capturing_mouse {
if widget_under_mouse != Some(idx) {
let source = input::Source::Mouse;
let event = event::Ui::WidgetUncapturesInputSource(idx, source).into();
self.global_input.push_event(event);
self.global_input.current.widget_capturing_mouse = None;
}
}
if self.global_input.current.widget_capturing_mouse.is_none() {
if let Some(idx) = widget_under_mouse {
let source = input::Source::Mouse;
let event = event::Ui::WidgetCapturesInputSource(idx, source).into();
self.global_input.push_event(event);
self.global_input.current.widget_capturing_mouse = Some(idx);
}
}
}
}
pub fn handle_event(&mut self, event: event::Input) {
use event::Input;
use input::{Button, Key, ModifierKey, Motion};
use input::state::mouse::Button as MouseButton;
fn filter_modifier(key: Key) -> Option<ModifierKey> {
match key {
Key::LCtrl | Key::RCtrl => Some(ModifierKey::CTRL),
Key::LShift | Key::RShift => Some(ModifierKey::SHIFT),
Key::LAlt | Key::RAlt => Some(ModifierKey::ALT),
Key::LGui | Key::RGui => Some(ModifierKey::GUI),
_ => None
}
}
self.global_input.push_event(event.clone().into());
match event {
Input::Press(button_type) => match button_type {
Button::Mouse(mouse_button) => {
let mouse_xy = self.global_input.current.mouse.xy;
let press = event::Press {
button: event::Button::Mouse(mouse_button, mouse_xy),
modifiers: self.global_input.current.modifiers,
};
let widget = self.global_input.current.widget_capturing_mouse;
let press_event = event::Ui::Press(widget, press).into();
self.global_input.push_event(press_event);
if let MouseButton::Left = mouse_button {
if let Some(idx) = self.global_input.current.widget_capturing_keyboard {
if Some(idx) != self.global_input.current.widget_under_mouse {
let source = input::Source::Keyboard;
let event = event::Ui::WidgetUncapturesInputSource(idx, source);
self.global_input.push_event(event.into());
self.global_input.current.widget_capturing_keyboard = None;
}
}
if let Some(idx) = self.global_input.current.widget_under_mouse {
let source = input::Source::Keyboard;
let event = event::Ui::WidgetCapturesInputSource(idx, source);
self.global_input.push_event(event.into());
self.global_input.current.widget_capturing_keyboard = Some(idx);
}
}
let xy = self.global_input.current.mouse.xy;
let widget = self.global_input.current.widget_under_mouse;
self.global_input.current.mouse.buttons.press(mouse_button, xy, widget);
},
Button::Keyboard(key) => {
let press = event::Press {
button: event::Button::Keyboard(key),
modifiers: self.global_input.current.modifiers,
};
let widget = self.global_input.current.widget_capturing_keyboard;
let press_event = event::Ui::Press(widget, press).into();
self.global_input.push_event(press_event);
if let Some(modifier) = filter_modifier(key) {
self.global_input.current.modifiers.insert(modifier);
}
if let Key::Escape = key {
}
},
_ => {}
},
Input::Release(button_type) => match button_type {
Button::Mouse(mouse_button) => {
let mouse_xy = self.global_input.current.mouse.xy;
let release = event::Release {
button: event::Button::Mouse(mouse_button, mouse_xy),
modifiers: self.global_input.current.modifiers,
};
let widget = self.global_input.current.widget_capturing_mouse;
let release_event = event::Ui::Release(widget, release).into();
self.global_input.push_event(release_event);
let down = self.global_input.current.mouse.buttons[mouse_button].if_down();
if let Some((_, widget)) = down {
let clicked_widget = self.global_input.current.widget_under_mouse
.and_then(|released| widget.and_then(|pressed| {
if pressed == released { Some(released) } else { None }
}));
let click = event::Click {
button: mouse_button,
xy: self.global_input.current.mouse.xy,
modifiers: self.global_input.current.modifiers,
};
let click_event = event::Ui::Click(clicked_widget, click).into();
self.global_input.push_event(click_event);
let now = instant::Instant::now();
let double_click = self.global_input.last_click
.and_then(|(last_time, last_click)| {
if click.button != last_click.button {
return None;
}
if click.xy != last_click.xy {
return None;
}
let duration = now.duration_since(last_time);
let threshold = self.theme.double_click_threshold;
if duration >= threshold {
return None;
}
Some(event::DoubleClick {
button: click.button,
xy: click.xy,
modifiers: click.modifiers,
})
});
if let Some(double_click) = double_click {
self.global_input.last_click = None;
let double_click_event =
event::Ui::DoubleClick(clicked_widget, double_click).into();
self.global_input.push_event(double_click_event);
} else {
self.global_input.last_click = Some((now, click));
}
}
if let MouseButton::Left = mouse_button {
if let Some(idx) = self.global_input.current.widget_capturing_mouse {
if Some(idx) != self.global_input.current.widget_under_mouse {
let source = input::Source::Mouse;
let event = event::Ui::WidgetUncapturesInputSource(idx, source);
self.global_input.push_event(event.into());
self.global_input.current.widget_capturing_mouse = None;
}
}
}
self.global_input.current.mouse.buttons.release(mouse_button);
},
Button::Keyboard(key) => {
let release = event::Release {
button: event::Button::Keyboard(key),
modifiers: self.global_input.current.modifiers,
};
let widget = self.global_input.current.widget_capturing_keyboard;
let release_event = event::Ui::Release(widget, release).into();
self.global_input.push_event(release_event);
if let Some(modifier) = filter_modifier(key) {
self.global_input.current.modifiers.remove(modifier);
}
},
_ => (),
},
Input::Resize(w, h) => {
let (w, h) = (w as Scalar, h as Scalar);
let window_resized = event::Ui::WindowResized([w, h]).into();
self.global_input.push_event(window_resized);
self.win_w = w;
self.win_h = h;
self.needs_redraw();
self.track_widget_under_mouse_and_update_capturing();
},
Input::Motion(motion) => {
let move_ = event::Motion {
motion: motion,
modifiers: self.global_input.current.modifiers,
};
let widget = self.global_input.current.widget_capturing_mouse;
let move_event = event::Ui::Motion(widget, move_).into();
self.global_input.push_event(move_event);
match motion {
Motion::MouseCursor { x, y } => {
let last_mouse_xy = self.global_input.current.mouse.xy;
let mouse_xy = [x, y];
let delta_xy = utils::vec2_sub(mouse_xy, last_mouse_xy);
let buttons = self.global_input.current.mouse.buttons.clone();
for (btn, btn_xy, widget) in buttons.pressed() {
let total_delta_xy = utils::vec2_sub(mouse_xy, btn_xy);
let distance = (total_delta_xy[0] + total_delta_xy[1]).abs().sqrt();
if distance > self.theme.mouse_drag_threshold {
let event = event::Ui::Drag(
widget,
event::Drag {
button: btn,
origin: btn_xy,
from: last_mouse_xy,
to: mouse_xy,
delta_xy: delta_xy,
total_delta_xy: total_delta_xy,
modifiers: self.global_input.current.modifiers,
},
)
.into();
self.global_input.push_event(event);
}
}
self.global_input.current.mouse.xy = mouse_xy;
self.track_widget_under_mouse_and_update_capturing();
},
Motion::Scroll { x, y } => {
let mut scrollable_widgets = {
let depth_order = &self.depth_order.indices;
let mouse_xy = self.global_input.current.mouse.xy;
graph::algo::pick_scrollable_widgets(depth_order, mouse_xy)
};
while let Some(idx) =
scrollable_widgets.next(&self.widget_graph,
&self.depth_order.indices,
&self.theme)
{
let (kid_area, maybe_x_scroll, maybe_y_scroll) =
match self.widget_graph.widget(idx) {
Some(widget) => {
(widget.kid_area,
widget.maybe_x_scroll_state,
widget.maybe_y_scroll_state)
},
None => continue,
};
fn offset_is_at_bound<A>(scroll: &widget::scroll::State<A>,
additional_offset: Scalar) -> bool
{
fn approx_eq(a: Scalar, b: Scalar) -> bool {
(a - b).abs() < 0.000001
}
if additional_offset.is_sign_positive() {
let max = utils::partial_max(scroll.offset_bounds.start,
scroll.offset_bounds.end);
approx_eq(scroll.offset, max)
} else {
let min = utils::partial_min(scroll.offset_bounds.start,
scroll.offset_bounds.end);
approx_eq(scroll.offset, min)
}
}
let mut scroll_x = false;
let mut scroll_y = false;
if x != 0.0 {
let new_scroll =
widget::scroll::State::update(self, idx, &kid_area,
maybe_x_scroll, x);
if let Some(prev_scroll) = maybe_x_scroll {
let (prev_is_at_bound, new_is_at_bound) =
(offset_is_at_bound(&prev_scroll, x),
offset_is_at_bound(&new_scroll, x));
scroll_x = !prev_is_at_bound || !new_is_at_bound;
}
}
if y != 0.0 {
let new_scroll =
widget::scroll::State::update(self, idx, &kid_area,
maybe_y_scroll, y);
if let Some(prev_scroll) = maybe_y_scroll {
let (prev_is_at_bound, new_is_at_bound) =
(offset_is_at_bound(&prev_scroll, y),
offset_is_at_bound(&new_scroll, y));
scroll_y = !prev_is_at_bound || !new_is_at_bound;
}
}
if scroll_x || scroll_y {
let event = event::Ui::Scroll(Some(idx), event::Scroll {
x: x,
y: y,
modifiers: self.global_input.current.modifiers,
}).into();
self.global_input.push_event(event);
break;
}
}
if x != 0.0 || y != 0.0 {
let widget = self.global_input.current.widget_capturing_mouse;
if let Some(idx) = widget {
if let Some(widget) = self.widget_graph.widget(idx) {
if widget.maybe_x_scroll_state.is_none()
&& widget.maybe_y_scroll_state.is_none() {
let scroll = event::Scroll {
x: x,
y: y,
modifiers: self.global_input.current.modifiers,
};
let event = event::Ui::Scroll(Some(idx), scroll);
self.global_input.push_event(event.into());
}
}
}
}
self.track_widget_under_mouse_and_update_capturing();
},
_ => (),
}
},
Input::Text(string) => {
let text = event::Text {
string: string,
modifiers: self.global_input.current.modifiers,
};
let widget = self.global_input.current.widget_capturing_keyboard;
let text_event = event::Ui::Text(widget, text).into();
self.global_input.push_event(text_event);
},
Input::Touch(touch) => match touch.phase {
input::touch::Phase::Start => {
let widget_under_touch =
graph::algo::pick_widgets(&self.depth_order.indices, touch.xy)
.next(&self.widget_graph, &self.depth_order.indices, &self.theme);
let start = input::state::touch::Start {
time: instant::Instant::now(),
xy: touch.xy,
widget: widget_under_touch,
};
let state = input::state::touch::Touch {
start: start,
xy: touch.xy,
widget: widget_under_touch,
};
self.global_input.current.touch.insert(touch.id, state);
let event = event::Ui::Touch(widget_under_touch, touch);
self.global_input.push_event(event.into());
if let Some(widget) = widget_under_touch {
let source = input::Source::Touch(touch.id);
let event = event::Ui::WidgetCapturesInputSource(widget, source);
self.global_input.push_event(event.into());
}
},
input::touch::Phase::Move => {
let widget = match self.global_input.current.touch.get_mut(&touch.id) {
Some(touch_state) => {
touch_state.widget =
graph::algo::pick_widgets(&self.depth_order.indices, touch.xy)
.next(&self.widget_graph,
&self.depth_order.indices,
&self.theme);
touch_state.xy = touch.xy;
touch_state.start.widget
},
None => None,
};
let event = event::Ui::Touch(widget, touch);
self.global_input.push_event(event.into());
},
input::touch::Phase::Cancel => {
let widget = self.global_input.current.touch.remove(&touch.id).and_then(|t| t.start.widget);
let event = event::Ui::Touch(widget, touch);
self.global_input.push_event(event.into());
if let Some(widget) = widget {
let source = input::Source::Touch(touch.id);
let event = event::Ui::WidgetUncapturesInputSource(widget, source);
self.global_input.push_event(event.into());
}
},
input::touch::Phase::End => {
let old_touch = self.global_input.current.touch.remove(&touch.id).map(|touch| touch);
let widget_capturing = old_touch.as_ref().and_then(|touch| touch.start.widget);
let event = event::Ui::Touch(widget_capturing, touch);
self.global_input.push_event(event.into());
let tapped_widget =
graph::algo::pick_widgets(&self.depth_order.indices, touch.xy)
.next(&self.widget_graph, &self.depth_order.indices, &self.theme)
.and_then(|widget| match Some(widget) == widget_capturing {
true => Some(widget),
false => None,
});
let tap = event::Tap { id: touch.id, xy: touch.xy };
let event = event::Ui::Tap(tapped_widget, tap);
self.global_input.push_event(event.into());
if let Some(widget) = widget_capturing {
let source = input::Source::Touch(touch.id);
let event = event::Ui::WidgetUncapturesInputSource(widget, source);
self.global_input.push_event(event.into());
}
},
},
Input::Focus(focused) if focused == true => self.needs_redraw(),
Input::Focus(_focused) => (),
Input::Redraw => self.needs_redraw(),
}
}
pub fn global_input(&self) -> &input::Global {
&self.global_input
}
pub fn keyboard_capture(&mut self, idx: widget::Id) {
let source = input::Source::Keyboard;
if self.global_input.current.widget_capturing_keyboard.is_some() {
let event = event::Ui::WidgetUncapturesInputSource(idx, source);
self.global_input.push_event(event.into());
self.global_input.current.widget_capturing_keyboard = None;
}
let event = event::Ui::WidgetCapturesInputSource(idx, source).into();
self.global_input.push_event(event);
self.global_input.current.widget_capturing_keyboard = Some(idx);
}
pub fn calc_xy(&self,
maybe_id: Option<widget::Id>,
maybe_parent_id: Option<widget::Id>,
x_position: Position,
y_position: Position,
dim: Dimensions,
place_on_kid_area: bool) -> Point
{
use utils::vec2_add;
fn abs_from_position<R, P>(ui: &Ui,
maybe_parent_id: Option<widget::Id>,
position: Position,
dim: Scalar,
place_on_kid_area: bool,
range_from_rect: R,
start_and_end_pad: P) -> Scalar
where R: FnOnce(Rect) -> Range,
P: FnOnce(Padding) -> Range,
{
let (relative, maybe_id) = match position {
Position::Absolute(abs) => return abs,
Position::Relative(relative, maybe_id) => (relative, maybe_id),
};
match relative {
position::Relative::Scalar(scalar) =>
maybe_id.or(ui.maybe_prev_widget_id).or(Some(ui.window.into()))
.and_then(|idx| ui.rect_of(idx).map(range_from_rect))
.map(|other_range| other_range.middle() + scalar)
.unwrap_or(scalar),
position::Relative::Direction(direction, amt) =>
maybe_id.or(ui.maybe_prev_widget_id)
.and_then(|idx| ui.rect_of(idx).map(range_from_rect))
.map(|other_range| {
let range = Range::from_pos_and_len(0.0, dim);
match direction {
Direction::Forwards => range.align_after(other_range).middle() + amt,
Direction::Backwards => range.align_before(other_range).middle() - amt,
}
})
.unwrap_or_else(|| match direction {
Direction::Forwards => amt,
Direction::Backwards => -amt,
}),
position::Relative::Align(align) =>
maybe_id.or(ui.maybe_prev_widget_id).or(Some(ui.window.into()))
.and_then(|idx| ui.rect_of(idx).map(range_from_rect))
.map(|other_range| {
let range = Range::from_pos_and_len(0.0, dim);
match align {
Align::Start => range.align_start_of(other_range).middle(),
Align::Middle => other_range.middle(),
Align::End => range.align_end_of(other_range).middle(),
}
})
.unwrap_or(0.0),
position::Relative::Place(place) => {
let parent_id = maybe_id
.or(maybe_parent_id)
.or(ui.maybe_current_parent_id)
.unwrap_or(ui.window.into());
let maybe_area = match place_on_kid_area {
true => ui.widget_graph.widget(parent_id)
.map(|w| w.kid_area)
.map(|k| (range_from_rect(k.rect), start_and_end_pad(k.pad))),
false => ui.rect_of(parent_id)
.map(|rect| (range_from_rect(rect), Range::new(0.0, 0.0))),
};
maybe_area
.map(|(parent_range, pad)| {
let range = Range::from_pos_and_len(0.0, dim);
let parent_range = parent_range.pad_start(pad.start).pad_end(pad.end);
match place {
position::Place::Start(maybe_mgn) =>
range.align_start_of(parent_range).middle() + maybe_mgn.unwrap_or(0.0),
position::Place::Middle =>
parent_range.middle(),
position::Place::End(maybe_mgn) =>
range.align_end_of(parent_range).middle() - maybe_mgn.unwrap_or(0.0),
}
})
.unwrap_or(0.0)
},
}
}
fn x_range(rect: Rect) -> Range { rect.x }
fn y_range(rect: Rect) -> Range { rect.y }
fn x_pad(pad: Padding) -> Range { pad.x }
fn y_pad(pad: Padding) -> Range { pad.y }
let x = abs_from_position(self, maybe_parent_id, x_position, dim[0], place_on_kid_area, x_range, x_pad);
let y = abs_from_position(self, maybe_parent_id, y_position, dim[1], place_on_kid_area, y_range, y_pad);
let xy = [x, y];
maybe_id
.map(|idx| vec2_add(xy, graph::algo::scroll_offset(&self.widget_graph, idx)))
.unwrap_or(xy)
}
pub fn set_widgets(&mut self) -> UiCell {
self.maybe_prev_widget_id = None;
self.maybe_current_parent_id = None;
{
let Ui { ref mut updated_widgets, ref mut prev_updated_widgets, .. } = *self;
std::mem::swap(updated_widgets, prev_updated_widgets);
updated_widgets.clear();
}
let mut ui_cell = UiCell { ui: self };
{
use {color, Colorable, Borderable, Positionable};
type Window = widget::BorderedRectangle;
Window::new([ui_cell.win_w, ui_cell.win_h])
.no_parent()
.x_y(0.0, 0.0)
.border(0.0)
.border_color(color::BLACK.alpha(0.0))
.color(ui_cell.maybe_background_color.unwrap_or(color::BLACK.alpha(0.0)))
.set(ui_cell.window, &mut ui_cell);
}
ui_cell.ui.maybe_current_parent_id = Some(ui_cell.window.into());
ui_cell.set_mouse_cursor(cursor::MouseCursor::Arrow);
ui_cell
}
pub fn set_num_redraw_frames(&mut self, num_frames: u8) {
self.num_redraw_frames = num_frames;
}
pub fn needs_redraw(&self) {
self.redraw_count.store(self.num_redraw_frames as usize, atomic::Ordering::Relaxed);
}
pub fn clear_with(&mut self, color: Color) {
self.maybe_background_color = Some(color);
}
pub fn draw(&self) -> render::Primitives {
let Ui {
ref redraw_count,
ref widget_graph,
ref depth_order,
ref theme,
ref fonts,
win_w, win_h,
..
} = *self;
let indices = &depth_order.indices;
let remaining_redraws = redraw_count.load(atomic::Ordering::Relaxed);
if remaining_redraws > 0 {
redraw_count.store(remaining_redraws - 1, atomic::Ordering::Relaxed);
}
render::Primitives::new(widget_graph, indices, theme, fonts, [win_w, win_h])
}
pub fn draw_if_changed(&self) -> Option<render::Primitives> {
if self.has_changed() {
return Some(self.draw())
}
None
}
pub fn has_changed(&self) -> bool {
self.redraw_count.load(atomic::Ordering::Relaxed) > 0
}
pub fn kids_bounding_box(&self, id: widget::Id) -> Option<Rect> {
graph::algo::kids_bounding_box(&self.widget_graph, &self.prev_updated_widgets, id)
}
pub fn visible_area(&self, id: widget::Id) -> Option<Rect> {
graph::algo::cropped_area_of_widget(&self.widget_graph, id)
}
pub fn mouse_cursor(&self) -> cursor::MouseCursor {
self.mouse_cursor
}
}
impl<'a> UiCell<'a> {
pub fn theme(&self) -> &Theme { &self.ui.theme }
pub fn font(&self, id: text::font::Id) -> Option<&text::Font> {
self.ui.fonts.get(id)
}
pub fn window_dim(&self) -> Dimensions {
[self.ui.win_w, self.ui.win_h]
}
pub fn global_input(&self) -> &input::Global {
&self.ui.global_input
}
pub fn widget_input(&self, id: widget::Id) -> input::Widget {
self.ui.widget_input(id)
}
pub fn widget_id_generator(&mut self) -> widget::id::Generator {
self.ui.widget_id_generator()
}
pub fn kids_bounding_box(&self, id: widget::Id) -> Option<Rect> {
self.ui.kids_bounding_box(id)
}
pub fn scroll_widget(&mut self, id: widget::Id, offset: [Scalar; 2]) {
let (x, y) = (offset[0], offset[1]);
if x != 0.0 || y != 0.0 {
let event = event::Ui::Scroll(Some(id), event::Scroll {
x: x,
y: y,
modifiers: self.ui.global_input.current.modifiers,
});
self.ui.pending_scroll_events.push(event);
}
}
pub fn set_mouse_cursor(&mut self, cursor: cursor::MouseCursor) {
self.ui.mouse_cursor = cursor;
}
}
impl<'a> Drop for UiCell<'a> {
fn drop(&mut self) {
let changed = self.ui.updated_widgets != self.ui.prev_updated_widgets;
if changed {
self.ui.needs_redraw();
}
{
let Ui {
ref widget_graph,
ref mut depth_order,
window,
ref updated_widgets,
..
} = *self.ui;
depth_order.update(widget_graph, window, updated_widgets);
}
self.ui.global_input.clear_events_and_update_start_state();
if changed {
self.ui.track_widget_under_mouse_and_update_capturing();
}
for scroll_event in self.ui.pending_scroll_events.drain(0..) {
self.ui.global_input.push_event(scroll_event.into());
}
}
}
impl<'a> ::std::ops::Deref for UiCell<'a> {
type Target = Ui;
fn deref(&self) -> &Ui {
self.ui
}
}
impl<'a> AsRef<Ui> for UiCell<'a> {
fn as_ref(&self) -> &Ui {
&self.ui
}
}
pub fn ref_mut_from_ui_cell<'a, 'b: 'a>(ui_cell: &'a mut UiCell<'b>) -> &'a mut Ui {
ui_cell.ui
}
pub fn widget_graph_mut(ui: &mut Ui) -> &mut Graph {
&mut ui.widget_graph
}
pub fn infer_parent_from_position(ui: &Ui, x: Position, y: Position) -> Option<widget::Id> {
use Position::Relative;
use position::Relative::{Align, Direction, Place, Scalar};
match (x, y) {
(Relative(Place(_), maybe_parent_id), _) | (_, Relative(Place(_), maybe_parent_id)) =>
maybe_parent_id,
(Relative(Direction(_, _), maybe_id), _) | (_, Relative(Direction(_, _), maybe_id)) |
(Relative(Align(_), maybe_id), _) | (_, Relative(Align(_), maybe_id)) |
(Relative(Scalar(_), maybe_id), _) | (_, Relative(Scalar(_), maybe_id)) =>
maybe_id.or(ui.maybe_prev_widget_id)
.and_then(|idx| ui.widget_graph.depth_parent(idx)),
_ => None,
}
}
pub fn infer_parent_unchecked(ui: &Ui, x_pos: Position, y_pos: Position) -> widget::Id {
infer_parent_from_position(ui, x_pos, y_pos)
.or(ui.maybe_current_parent_id)
.unwrap_or(ui.window.into())
}
pub fn pre_update_cache(ui: &mut Ui, widget: widget::PreUpdateCache) {
ui.maybe_prev_widget_id = Some(widget.id);
ui.maybe_current_parent_id = widget.maybe_parent_id;
let widget_id = widget.id;
ui.widget_graph.pre_update_cache(ui.window, widget, ui.updated_widgets.len());
ui.updated_widgets.insert(widget_id);
}
pub fn post_update_cache<W>(ui: &mut Ui, widget: widget::PostUpdateCache<W>)
where W: Widget,
W::State: 'static,
W::Style: 'static,
{
ui.maybe_prev_widget_id = Some(widget.id);
ui.maybe_current_parent_id = widget.maybe_parent_id;
ui.widget_graph.post_update_cache(widget);
}