use crate::geometry::{Edge, LogicalPoint, LogicalRect, LogicalSize};
pub fn place_popup(
anchor: LogicalRect,
popup: LogicalSize,
work_area: LogicalRect,
edge: Edge,
gap: f32,
) -> LogicalPoint {
let (mut x, mut y) = match edge {
Edge::Bottom => {
let below = anchor.max_y() + gap;
let above = anchor.origin.y - gap - popup.height;
let y = if below + popup.height <= work_area.max_y() || above < work_area.origin.y {
below
} else {
above
};
(anchor.origin.x, y)
}
Edge::Top => {
let above = anchor.origin.y - gap - popup.height;
let below = anchor.max_y() + gap;
let y = if above >= work_area.origin.y || below + popup.height > work_area.max_y() {
above
} else {
below
};
(anchor.origin.x, y)
}
Edge::Right => {
let right = anchor.max_x() + gap;
let left = anchor.origin.x - gap - popup.width;
let x = if right + popup.width <= work_area.max_x() || left < work_area.origin.x {
right
} else {
left
};
(x, anchor.origin.y)
}
Edge::Left => {
let left = anchor.origin.x - gap - popup.width;
let right = anchor.max_x() + gap;
let x = if left >= work_area.origin.x || right + popup.width > work_area.max_x() {
left
} else {
right
};
(x, anchor.origin.y)
}
};
let max_x = (work_area.max_x() - popup.width).max(work_area.origin.x);
let max_y = (work_area.max_y() - popup.height).max(work_area.origin.y);
x = x.clamp(work_area.origin.x, max_x);
y = y.clamp(work_area.origin.y, max_y);
LogicalPoint::new(x, y)
}
#[cfg(test)]
mod tests {
use super::*;
fn work() -> LogicalRect {
LogicalRect::new(LogicalPoint::new(0.0, 0.0), LogicalSize::new(1440.0, 900.0))
}
#[test]
fn bottom_edge_opens_below_and_left_aligned() {
let anchor = LogicalRect::new(LogicalPoint::new(100.0, 0.0), LogicalSize::new(24.0, 24.0));
let p = place_popup(
anchor,
LogicalSize::new(200.0, 300.0),
work(),
Edge::Bottom,
2.0,
);
assert_eq!(p.x, 100.0);
assert_eq!(p.y, 26.0); }
#[test]
fn bottom_edge_flips_above_when_it_would_spill_off_the_bottom() {
let anchor = LogicalRect::new(
LogicalPoint::new(100.0, 850.0),
LogicalSize::new(24.0, 24.0),
);
let p = place_popup(
anchor,
LogicalSize::new(200.0, 300.0),
work(),
Edge::Bottom,
2.0,
);
assert_eq!(p.y, 548.0);
}
#[test]
fn x_is_clamped_so_a_right_edge_icon_stays_on_screen() {
let anchor = LogicalRect::new(LogicalPoint::new(1430.0, 0.0), LogicalSize::new(24.0, 24.0));
let p = place_popup(
anchor,
LogicalSize::new(200.0, 300.0),
work(),
Edge::Bottom,
2.0,
);
assert_eq!(p.x, 1240.0); }
#[test]
fn top_edge_opens_above_the_anchor() {
let anchor = LogicalRect::new(
LogicalPoint::new(100.0, 860.0),
LogicalSize::new(24.0, 24.0),
);
let p = place_popup(
anchor,
LogicalSize::new(200.0, 300.0),
work(),
Edge::Top,
2.0,
);
assert_eq!(p.y, 558.0); }
#[test]
fn right_edge_opens_to_the_right_then_flips_left_on_spill() {
let near = LogicalRect::new(LogicalPoint::new(10.0, 100.0), LogicalSize::new(24.0, 24.0));
let p = place_popup(
near,
LogicalSize::new(200.0, 300.0),
work(),
Edge::Right,
2.0,
);
assert_eq!(p.x, 36.0);
let far = LogicalRect::new(
LogicalPoint::new(1400.0, 100.0),
LogicalSize::new(24.0, 24.0),
);
let p = place_popup(far, LogicalSize::new(200.0, 300.0), work(), Edge::Left, 2.0);
assert_eq!(p.x, 1198.0); }
#[test]
fn an_oversized_popup_is_pinned_to_the_work_area_origin() {
let anchor = LogicalRect::new(LogicalPoint::new(700.0, 0.0), LogicalSize::new(24.0, 24.0));
let p = place_popup(
anchor,
LogicalSize::new(2000.0, 2000.0),
work(),
Edge::Bottom,
2.0,
);
assert_eq!(p.x, 0.0);
assert_eq!(p.y, 0.0);
}
fn secondary_monitor_up_left() -> LogicalRect {
LogicalRect::new(
LogicalPoint::new(-1920.0, -1080.0),
LogicalSize::new(1920.0, 1080.0),
)
}
#[test]
fn bottom_edge_places_correctly_on_an_offset_monitor() {
let work = secondary_monitor_up_left();
let anchor = LogicalRect::new(
LogicalPoint::new(-1820.0, -1080.0),
LogicalSize::new(24.0, 24.0),
);
let p = place_popup(
anchor,
LogicalSize::new(200.0, 300.0),
work,
Edge::Bottom,
2.0,
);
assert_eq!(p.x, -1820.0);
assert_eq!(p.y, -1054.0); }
#[test]
fn bottom_edge_flips_above_when_spilling_off_an_offset_monitor() {
let work = secondary_monitor_up_left();
let anchor = LogicalRect::new(
LogicalPoint::new(-1820.0, -230.0),
LogicalSize::new(24.0, 24.0),
);
let p = place_popup(
anchor,
LogicalSize::new(200.0, 300.0),
work,
Edge::Bottom,
2.0,
);
assert_eq!(p.y, -532.0);
}
#[test]
fn x_is_clamped_against_an_offset_monitors_far_edge() {
let work = secondary_monitor_up_left();
let anchor = LogicalRect::new(
LogicalPoint::new(-20.0, -1080.0),
LogicalSize::new(24.0, 24.0),
);
let p = place_popup(
anchor,
LogicalSize::new(200.0, 300.0),
work,
Edge::Bottom,
2.0,
);
assert_eq!(p.x, -200.0);
}
}