use super::{EdgeIndex, Graph};
use daggy::Walker;
use fnv;
use position::{Point, Rect};
use theme::Theme;
use widget;
#[derive(Clone)]
#[allow(missing_copy_implementations)]
pub struct PickWidgets {
xy: Point,
idx: usize,
}
#[derive(Clone)]
#[allow(missing_copy_implementations)]
pub struct PickScrollableWidgets {
pick_widgets: PickWidgets,
}
impl PickWidgets {
pub fn next_including_graphics_children(
&mut self,
graph: &Graph,
depth_order: &[widget::Id],
theme: &Theme,
) -> Option<widget::Id> {
while self.idx > 0 {
self.idx -= 1;
let idx = match depth_order.get(self.idx) {
None => break,
Some(&idx) => idx,
};
let visible_rect = match cropped_area_of_widget(graph, idx) {
None => continue,
Some(rect) => rect,
};
if !visible_rect.is_over(self.xy) {
continue;
}
let mut id = idx;
loop {
let container = match graph.widget(id) {
None => break,
Some(container) => container,
};
match (container.is_over.0)(&container, self.xy, theme) {
widget::IsOver::Bool(false) => break,
widget::IsOver::Bool(true) => return Some(id),
widget::IsOver::Widget(w_id) => {
assert!(
id != w_id,
"the specified IsOver::Widget \
would cause an infinite loop"
);
id = w_id;
}
}
}
}
None
}
pub fn next(
&mut self,
graph: &Graph,
depth_order: &[widget::Id],
theme: &Theme,
) -> Option<widget::Id> {
self.next_including_graphics_children(graph, depth_order, theme)
.map(|idx| {
graph
.graphic_parent_recursion(idx)
.last_node(graph)
.unwrap_or(idx)
})
}
}
impl PickScrollableWidgets {
pub fn next(
&mut self,
graph: &Graph,
depth_order: &[widget::Id],
theme: &Theme,
) -> Option<widget::Id> {
while let Some(idx) =
self.pick_widgets
.next_including_graphics_children(graph, depth_order, theme)
{
if let Some(ref container) = graph.widget(idx) {
if container.maybe_x_scroll_state.is_some()
|| container.maybe_y_scroll_state.is_some()
{
return Some(idx);
}
}
}
None
}
}
pub fn pick_widgets(depth_order: &[widget::Id], xy: Point) -> PickWidgets {
PickWidgets {
xy: xy,
idx: depth_order.len(),
}
}
pub fn pick_scrollable_widgets(depth_order: &[widget::Id], xy: Point) -> PickScrollableWidgets {
PickScrollableWidgets {
pick_widgets: pick_widgets(depth_order, xy),
}
}
pub fn cropped_area_of_widget(graph: &Graph, idx: widget::Id) -> Option<Rect> {
cropped_area_of_widget_maybe_within_depth(graph, idx, None)
}
pub fn cropped_area_of_widget_within_depth(
graph: &Graph,
idx: widget::Id,
deepest_parent_idx: widget::Id,
) -> Option<Rect> {
cropped_area_of_widget_maybe_within_depth(graph, idx, Some(deepest_parent_idx))
}
fn cropped_area_of_widget_maybe_within_depth(
graph: &Graph,
mut id: widget::Id,
deepest_id: Option<widget::Id>,
) -> Option<Rect> {
graph.widget(id).and_then(|widget| {
let mut overlapping_rect = widget.rect;
let mut depth_parents = graph.depth_parent_recursion(id);
while let Some(depth_parent) = depth_parents.next_node(graph) {
if Some(depth_parent) == deepest_id {
break;
}
if let Some(depth_parent_widget) = graph.widget(depth_parent) {
if depth_parent_widget.maybe_x_scroll_state.is_some()
|| depth_parent_widget.maybe_y_scroll_state.is_some()
{
if !graph.does_graphic_edge_exist(depth_parent, id) {
match overlapping_rect.overlap(depth_parent_widget.kid_area.rect) {
Some(overlap) => overlapping_rect = overlap,
None => return None,
}
}
}
}
id = depth_parent;
}
Some(overlapping_rect)
})
}
pub fn kids_bounding_box(
graph: &Graph,
prev_updated: &fnv::FnvHashSet<widget::Id>,
idx: widget::Id,
) -> Option<Rect> {
let kid_filter = &|g: &Graph, _e, n| -> bool {
let is_not_graphic_kid = !g.graphic_parent(n).is_some();
let is_set = prev_updated.contains(&n);
is_not_graphic_kid && is_set
};
fn kids_dfs<F>(
graph: &Graph,
idx: widget::Id,
deepest_parent_idx: widget::Id,
kid_filter: &F,
) -> Option<Rect>
where
F: Fn(&Graph, EdgeIndex, widget::Id) -> bool,
{
cropped_area_of_widget_within_depth(graph, idx, deepest_parent_idx).map(|rect| {
let kids_bounds = graph
.depth_children(idx)
.filter(kid_filter)
.iter(graph)
.nodes()
.filter_map(|n| kids_dfs(graph, n, deepest_parent_idx, kid_filter));
kids_bounds.fold(rect, |max, next| max.max(next))
})
}
graph.widget(idx).and_then(|_| {
let mut kids_bounds = graph
.depth_children(idx)
.filter(kid_filter)
.iter(graph)
.nodes()
.filter_map(|n| kids_dfs(graph, n, idx, kid_filter));
kids_bounds
.next()
.map(|first| kids_bounds.fold(first, |max, next| max.max(next)))
})
}
pub fn scroll_offset(graph: &Graph, idx: widget::Id) -> Point {
const NO_OFFSET: Point = [0.0, 0.0];
if let Some(depth_parent) = graph.depth_parent(idx) {
if let Some(depth_parent_widget) = graph.widget(depth_parent) {
if depth_parent_widget.maybe_x_scroll_state.is_some()
|| depth_parent_widget.maybe_y_scroll_state.is_some()
{
if graph
.graphic_parent_recursion(idx)
.any(graph, |_g, _e, n| n == depth_parent)
{
return NO_OFFSET;
}
let is_already_offset = |mut position_parents: super::RecursiveWalk<_>| {
while let Some(position_parent) = position_parents.next_node(graph) {
if graph
.depth_parent_recursion(position_parent)
.any(graph, |_g, _e, n| n == depth_parent)
{
return true;
}
}
false
};
let x_offset = depth_parent_widget
.maybe_x_scroll_state
.map(|scroll| {
let position_parents = graph.x_position_parent_recursion(idx);
if is_already_offset(position_parents) {
0.0
} else {
scroll.offset
}
})
.unwrap_or(0.0);
let y_offset = depth_parent_widget
.maybe_y_scroll_state
.map(|scroll| {
let position_parents = graph.y_position_parent_recursion(idx);
if is_already_offset(position_parents) {
0.0
} else {
scroll.offset
}
})
.unwrap_or(0.0);
return [x_offset, y_offset];
}
}
}
NO_OFFSET
}