use super::Size;
pub(super) use super::look::Rect;
use a7n_proto::v1::Edge;
const PROGRESS_FULL: u16 = 1000;
const GLOW_MIN_THICKNESS: f64 = 3.0;
const GLOW_MAX_THICKNESS: f64 = 28.0;
pub(super) const LANDING_DIAMETER: f64 = 64.0;
impl Rect {
fn contains(&self, x: f64, y: f64) -> bool {
(self.x..=self.x + self.width).contains(&x) && (self.y..=self.y + self.height).contains(&y)
}
}
pub(super) fn screen_containing(screens: &[Rect], x: f64, y: f64) -> Option<Rect> {
screens
.iter()
.find(|screen| screen.contains(x, y))
.or_else(|| screens.first())
.copied()
}
pub(super) fn glow_rect(screen: Rect, edge: Edge, progress: u16, size: Size) -> Option<Rect> {
let fraction = f64::from(progress.min(PROGRESS_FULL)) / f64::from(PROGRESS_FULL);
let thickness = (GLOW_MIN_THICKNESS + (GLOW_MAX_THICKNESS - GLOW_MIN_THICKNESS) * fraction)
* size.multiplier();
let Rect {
x,
y,
width,
height,
} = screen;
match edge {
Edge::Left => Some(Rect {
x,
y,
width: thickness,
height,
}),
Edge::Right => Some(Rect {
x: x + width - thickness,
y,
width: thickness,
height,
}),
Edge::Top => Some(Rect {
x,
y: y + height - thickness,
width,
height: thickness,
}),
Edge::Bottom => Some(Rect {
x,
y,
width,
height: thickness,
}),
Edge::Unspecified => None,
}
}
pub(super) fn landing_rect(desktop_x: i32, desktop_y: i32, primary_height: f64) -> Rect {
let centre_x = f64::from(desktop_x);
let centre_y = primary_height - f64::from(desktop_y);
Rect {
x: centre_x - LANDING_DIAMETER / 2.0,
y: centre_y - LANDING_DIAMETER / 2.0,
width: LANDING_DIAMETER,
height: LANDING_DIAMETER,
}
}
#[cfg(test)]
mod tests {
use super::*;
const SCREEN: Rect = Rect {
x: 0.0,
y: 0.0,
width: 1000.0,
height: 600.0,
};
#[test]
fn glow_rect_edges_hug_their_side_and_grow_with_progress() {
let cases = [
(
Edge::Left,
0,
Rect {
x: 0.0,
y: 0.0,
width: GLOW_MIN_THICKNESS,
height: 600.0,
},
),
(
Edge::Right,
PROGRESS_FULL,
Rect {
x: 1000.0 - GLOW_MAX_THICKNESS,
y: 0.0,
width: GLOW_MAX_THICKNESS,
height: 600.0,
},
),
(
Edge::Top,
PROGRESS_FULL * 2,
Rect {
x: 0.0,
y: 600.0 - GLOW_MAX_THICKNESS,
width: 1000.0,
height: GLOW_MAX_THICKNESS,
},
),
(
Edge::Bottom,
0,
Rect {
x: 0.0,
y: 0.0,
width: 1000.0,
height: GLOW_MIN_THICKNESS,
},
),
];
for (edge, progress, expected) in cases {
let rect = glow_rect(SCREEN, edge, progress, Size::Medium);
assert_eq!(rect, Some(expected), "{edge:?} at {progress}");
}
}
#[test]
fn screen_containing_point_off_every_screen_falls_back_to_primary() {
let right = Rect {
x: 1000.0,
..SCREEN
};
let screens = [SCREEN, right];
let inside = screen_containing(&screens, 1500.0, 10.0);
let outside = screen_containing(&screens, -50.0, 10.0);
assert_eq!((inside, outside), (Some(right), Some(SCREEN)));
}
#[test]
fn landing_rect_desktop_point_is_flipped_into_appkit_space() {
let primary_height = 900.0;
let rect = landing_rect(100, 200, primary_height);
let half = LANDING_DIAMETER / 2.0;
assert_eq!((rect.x, rect.y), (100.0 - half, 700.0 - half));
}
}