#![allow(dead_code)]
pub const WINDOW_WIDTH: f64 = 320.0;
pub const WINDOW_HEIGHT: f64 = 420.0;
pub const MIN_POPOVER_HEIGHT: f64 = 360.0;
pub const WORK_AREA_MARGIN: f64 = 16.0;
pub const POPOVER_TOP_GAP: f64 = 0.0;
pub const POPOVER_BOTTOM_GAP: f64 = 24.0;
pub const POPOVER_SIDE_MARGIN: f64 = 8.0;
pub const POPOVER_MAX_HEIGHT_FRACTION: f64 = 0.85;
pub const MENU_BAR_MIN_HEIGHT: f64 = 24.0;
pub const FALLBACK_WORK_AREA_HEIGHT: f64 = 800.0;
pub const CLICK_LOCK_MS: u64 = 400;
pub const LIGHT_BACKGROUND: (u8, u8, u8, u8) = (255, 255, 255, 255);
pub const DARK_BACKGROUND: (u8, u8, u8, u8) = (30, 30, 30, 255);
pub const CORNER_RADIUS: f64 = 13.0;
pub fn clamp_popover_height(requested: f64, work_area_height: f64) -> f64 {
let max = (work_area_height - WORK_AREA_MARGIN).max(MIN_POPOVER_HEIGHT);
requested.round().clamp(MIN_POPOVER_HEIGHT, max)
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CocoaRect {
pub x: f64,
pub y: f64,
pub w: f64,
pub h: f64,
}
impl CocoaRect {
pub fn contains(self, px: f64, py: f64) -> bool {
px >= self.x && px < self.x + self.w && py >= self.y && py < self.y + self.h
}
pub fn max_x(self) -> f64 {
self.x + self.w
}
pub fn max_y(self) -> f64 {
self.y + self.h
}
}
#[derive(Debug, Clone, Copy)]
pub struct PopoverPlacement {
pub visible: CocoaRect,
pub below_y: f64,
pub icon_x: f64,
pub popover_w: f64,
pub popover_h: f64,
}
pub fn menu_bar_bottom_y(screen: CocoaRect, visible: CocoaRect) -> f64 {
let inset = (screen.max_y() - visible.max_y()).max(0.0);
screen.max_y() - inset.max(MENU_BAR_MIN_HEIGHT)
}
pub fn cocoa_popover_frame(p: PopoverPlacement) -> CocoaRect {
let top = p.below_y - POPOVER_TOP_GAP;
let bottom = p.visible.y + POPOVER_BOTTOM_GAP;
let available = (top - bottom).max(1.0);
let max_h = (available * POPOVER_MAX_HEIGHT_FRACTION).min(available);
let h = p.popover_h.min(max_h).max(1.0);
let max_w = (p.visible.w - 2.0 * POPOVER_SIDE_MARGIN).max(1.0);
let w = p.popover_w.min(max_w);
let min_x = p.visible.x + POPOVER_SIDE_MARGIN;
let max_x = p.visible.max_x() - POPOVER_SIDE_MARGIN - w;
let x = (p.icon_x - w / 2.0).clamp(min_x, max_x.max(min_x));
let y = (top - h).max(bottom);
CocoaRect { x, y, w, h }
}
#[cfg(test)]
mod tests {
use super::{
CocoaRect, MENU_BAR_MIN_HEIGHT, MIN_POPOVER_HEIGHT, POPOVER_BOTTOM_GAP,
POPOVER_SIDE_MARGIN, POPOVER_TOP_GAP, PopoverPlacement, WORK_AREA_MARGIN,
clamp_popover_height, cocoa_popover_frame, menu_bar_bottom_y,
};
fn place(visible: CocoaRect, icon_x: f64, h: f64) -> CocoaRect {
cocoa_popover_frame(PopoverPlacement {
visible,
below_y: menu_bar_bottom_y(visible, visible),
icon_x,
popover_w: 320.0,
popover_h: h,
})
}
#[test]
fn clamp_raises_requests_below_the_minimum() {
assert_eq!(clamp_popover_height(40.0, 1080.0), MIN_POPOVER_HEIGHT);
}
#[test]
fn clamp_keeps_requests_within_range_rounded() {
assert_eq!(clamp_popover_height(512.4, 1080.0), 512.0);
assert_eq!(clamp_popover_height(512.6, 1080.0), 513.0);
}
#[test]
fn clamp_caps_requests_at_work_area_minus_margin() {
assert_eq!(
clamp_popover_height(5000.0, 1080.0),
1080.0 - WORK_AREA_MARGIN
);
}
#[test]
fn clamp_never_shrinks_below_minimum_on_tiny_work_area() {
assert_eq!(clamp_popover_height(300.0, 100.0), MIN_POPOVER_HEIGHT);
assert_eq!(clamp_popover_height(50.0, 10.0), MIN_POPOVER_HEIGHT);
}
fn primary() -> CocoaRect {
CocoaRect {
x: 0.0,
y: 0.0,
w: 1440.0,
h: 900.0,
}
}
fn right_of_primary() -> CocoaRect {
CocoaRect {
x: 1440.0,
y: 0.0,
w: 1920.0,
h: 1080.0,
}
}
#[test]
fn popover_on_a_right_hand_screen_does_not_jump_to_the_primary() {
let visible = right_of_primary();
let frame = place(visible, 2400.0, 420.0);
assert!(
frame.x >= visible.x,
"left edge {} must stay on the secondary (origin {})",
frame.x,
visible.x
);
assert!(frame.max_x() <= visible.max_x() + 0.01);
let expected_top = menu_bar_bottom_y(visible, visible) - POPOVER_TOP_GAP;
assert!((frame.max_y() - expected_top).abs() < 0.01);
}
#[test]
fn popover_near_the_right_edge_is_clamped_onto_that_screen() {
let visible = right_of_primary();
let frame = place(visible, 1440.0 + 1910.0, 420.0);
assert!((frame.max_x() - (visible.max_x() - POPOVER_SIDE_MARGIN)).abs() < 0.01);
assert!(frame.x >= visible.x + POPOVER_SIDE_MARGIN);
}
#[test]
fn popover_on_a_left_hand_screen_keeps_a_negative_origin() {
let left = CocoaRect {
x: -1920.0,
y: 0.0,
w: 1920.0,
h: 1080.0,
};
let frame = place(left, -200.0, 420.0);
assert!(frame.x >= left.x);
assert!(frame.max_x() <= left.max_x() + 0.01);
assert!(frame.x < 0.0, "must not be shifted onto the primary");
}
#[test]
fn a_tall_popover_keeps_a_gap_above_the_bottom_of_the_screen() {
let visible = primary();
let frame = place(visible, 200.0, 5000.0);
assert!(frame.y >= visible.y + POPOVER_BOTTOM_GAP - 0.01);
assert!(frame.max_y() <= visible.max_y() - MENU_BAR_MIN_HEIGHT + 0.01);
assert!(frame.h < visible.h);
}
#[test]
fn accessory_visible_frame_still_clears_the_menu_bar() {
let screen = primary();
let below = menu_bar_bottom_y(screen, screen);
assert_eq!(below, screen.max_y() - MENU_BAR_MIN_HEIGHT);
let frame = cocoa_popover_frame(PopoverPlacement {
visible: screen,
below_y: below,
icon_x: 200.0,
popover_w: 320.0,
popover_h: 420.0,
});
assert!(
(frame.max_y() - (below - POPOVER_TOP_GAP)).abs() < 0.01,
"popover top {} must sit flush under the menu bar at {}",
frame.max_y(),
below
);
}
#[test]
fn popover_top_is_flush_with_the_menu_bar() {
let visible = primary();
let below = menu_bar_bottom_y(visible, visible);
let frame = place(visible, 200.0, 420.0);
assert_eq!(frame.max_y(), below);
assert!(frame.max_y() < visible.max_y());
}
}