Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/widget/menu/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,53 @@ macro_rules! itl_iter_slice_enum {
};
}
pub(super) use itl_iter_slice_enum;

#[derive(Debug, Clone, Copy)]
pub(super) struct SafeTriangle {
pub(super) p1: iced_core::Point,
pub(super) p2: iced_core::Point,
pub(super) p3: iced_core::Point,
}

impl SafeTriangle {
pub(super) fn new(
p1: iced_core::Point,
child_bounds: Rectangle,
direction: (Direction, Direction),
) -> Self {
let (child_corner1, child_corner2) = match direction.0 {
Direction::Positive => (
iced_core::Point::new(child_bounds.x, child_bounds.y),
iced_core::Point::new(child_bounds.x, child_bounds.y + child_bounds.height),
),
Direction::Negative => (
iced_core::Point::new(child_bounds.x + child_bounds.width, child_bounds.y),
iced_core::Point::new(
child_bounds.x + child_bounds.width,
child_bounds.y + child_bounds.height,
),
),
};

Self {
p1,
p2: child_corner1,
p3: child_corner2,
}
}

pub(super) fn contains(&self, point: iced_core::Point) -> bool {
let sign = |p1: iced_core::Point, p2: iced_core::Point, p3: iced_core::Point| -> f32 {
(p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y)
};

let d1 = sign(point, self.p1, self.p2);
let d2 = sign(point, self.p2, self.p3);
let d3 = sign(point, self.p3, self.p1);

let has_neg = (d1 < 0.0) || (d2 < 0.0) || (d3 < 0.0);
let has_pos = (d1 > 0.0) || (d2 > 0.0) || (d3 > 0.0);

!(has_neg && has_pos)
}
}
62 changes: 62 additions & 0 deletions src/widget/menu/menu_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
pub(super) scroll_offset: f32,
pub(super) active: Index,
pub(super) slice: MenuSlice,
pub(super) safe_triangle: Option<SafeTriangle>,
pub(super) last_cursor_on_parent: Option<Point>,
}
impl MenuState {
/// item_tree: Tree{item state, [Tree{widget state}, Tree{menu state, [...]}]}
Expand Down Expand Up @@ -117,6 +119,8 @@
lower_bound_rel: 0.0,
upper_bound_rel: f32::MAX,
},
safe_triangle: None,
last_cursor_on_parent: None,
}
}
}
Expand Down Expand Up @@ -418,6 +422,46 @@
Padding::new(global_parameters.safe_bounds_margin),
);

{
let menu_state = tree.state.downcast_mut::<MenuState>();
let parent_direction = {
let hcenter = viewport.width / 2.0;
let vcenter = viewport.height / 2.0;
let phcenter = parent_bounds.x + parent_bounds.width / 2.0;
let pvcenter = parent_bounds.y + parent_bounds.height / 2.0;
(
if phcenter < hcenter {
Direction::Positive
} else {
Direction::Negative
},
if pvcenter < vcenter {
Direction::Positive
} else {
Direction::Negative
},
)
};

if cursor.is_over(parent_bounds) {

Check warning on line 446 in src/widget/menu/menu_tree.rs

View workflow job for this annotation

GitHub Actions / all

this `if` statement can be collapsed
if let Some(pos) = cursor.position() {
menu_state.last_cursor_on_parent = Some(pos);
}
}

let p1 = menu_state
.last_cursor_on_parent
.unwrap_or_else(|| parent_bounds.center());

let triangle = SafeTriangle::new(p1, background_bounds, parent_direction);

#[cfg(feature = "debug_log")]
debug!(target:"menu::Menu::update", "SafeTriangle created: p1={:?}, p2={:?}, p3={:?}",
triangle.p1, triangle.p2, triangle.p3);

menu_state.safe_triangle = Some(triangle);
}

enum Op {
UpdateItems,
OpenEvent,
Expand Down Expand Up @@ -595,9 +639,27 @@
assert!(!shell.is_event_captured(), "Returning RecEvent::None");
RecEvent::None
} else {
let menu_state = tree.state.downcast_ref::<MenuState>();
let in_safe_triangle = if let (Some(cursor_pos), Some(triangle)) =
(cursor.position(), menu_state.safe_triangle)
{
let result = triangle.contains(cursor_pos);
#[cfg(feature = "debug_log")]
debug!(target:"menu::Menu::update", "Cursor at {:?}, in_safe_triangle: {}", cursor_pos, result);
result
} else {
#[cfg(feature = "debug_log")]
debug!(target:"menu::Menu::update", "No cursor position or no safe triangle");
false
};

let open = {
if global_state.pressed {
true
} else if in_safe_triangle {
#[cfg(feature = "debug_log")]
debug!(target:"menu::Menu::update", "Keeping menu open due to safe triangle");
true
} else if prev_bounds_list.iter().any(|r| cursor.is_over(*r)) {
false
} else {
Expand Down