#[cfg(all(test, target_os = "windows"))]
use super::text::DWrite;
use crate::event::Rect;
use crate::primitives::layout_metrics::{pixel_toast_stack_layout, TextMeasure};
use crate::primitives::toast::{ToastOverlay, ToastStackLayout};
#[cfg(all(test, target_os = "windows"))]
use crate::theme::Theme;
pub fn win_toast_stack_layout(
measure: &dyn TextMeasure,
rect: Rect,
stack: &ToastOverlay,
line_height: f32,
) -> ToastStackLayout {
pixel_toast_stack_layout(
stack,
measure,
rect.x,
rect.y,
rect.width,
rect.height,
line_height,
)
}
#[cfg(all(test, target_os = "windows"))]
mod tests {
use super::*;
use crate::primitives::toast::{Toast, ToastButton, ToastCorner, ToastHit, ToastSeverity};
use crate::types::{Color, WidgetId};
use crate::win::testing::HeadlessSurface;
const W: u32 = 400;
const H: u32 = 300;
const LINE_HEIGHT: f32 = 16.0;
const BOX_COLOR: Color = Color::rgb(10, 20, 30);
fn colored_toast(id: &str, title: &str) -> Toast {
Toast {
id: WidgetId::new(id),
title: title.into(),
body: "Details here".into(),
severity: ToastSeverity::Info,
actions: vec![ToastButton {
id: WidgetId::new(format!("{id}:act")),
label: "Undo".into(),
primary: false,
}],
accent: Some(BOX_COLOR),
}
}
fn stack_br(toasts: Vec<Toast>) -> ToastOverlay {
ToastOverlay {
id: WidgetId::new("toasts"),
corner: ToastCorner::BottomRight,
toasts,
focus: None,
}
}
#[test]
fn paint_and_hit_test_round_trip() {
let surface = HeadlessSurface::new(W, H).expect("create surface");
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let stack = stack_br(vec![colored_toast("t1", "Saved")]);
let rect = Rect::new(0.0, 0.0, W as f32, H as f32);
let theme = Theme::default();
let layout = surface
.paint(|target| {
let mut raw = super::super::surface::D2dSurface {
target,
dwrite: Some(&dwrite),
};
crate::primitives::toast::native_surface_paint::paint(
&stack,
&mut raw,
&theme,
&crate::style::Style::default(),
rect.x,
rect.y,
rect.width,
rect.height,
LINE_HEIGHT,
);
})
.map(|_| win_toast_stack_layout(&dwrite, rect, &stack, LINE_HEIGHT))
.expect("paint toast stack");
assert_eq!(layout.visible_toasts.len(), 1);
let vt = &layout.visible_toasts[0];
let probe = surface.pixel_at(
(vt.bounds.x + 2.0) as u32,
(vt.bounds.y + vt.bounds.height / 2.0) as u32,
);
assert_eq!(
(probe.r, probe.g, probe.b),
(BOX_COLOR.r, BOX_COLOR.g, BOX_COLOR.b)
);
let db = vt.dismiss_bounds.expect("dismiss bounds present");
let dismiss_hit = layout.hit_test(db.x + db.width / 2.0, db.y + db.height / 2.0);
assert_eq!(dismiss_hit, ToastHit::Dismiss(WidgetId::new("t1")));
let ab = *vt.action_rects.first().expect("action bounds present");
let action_hit = layout.hit_test(ab.x + ab.width / 2.0, ab.y + ab.height / 2.0);
assert_eq!(action_hit, ToastHit::Action(WidgetId::new("t1:act")));
let body_hit = layout.hit_test(vt.bounds.x + 2.0, vt.bounds.y + vt.bounds.height - 2.0);
assert_eq!(body_hit, ToastHit::Body(WidgetId::new("t1")));
}
#[test]
fn no_paint_layout_matches_paint_layout() {
let stack = stack_br(vec![colored_toast("t1", "Saved")]);
let rect = Rect::new(0.0, 0.0, W as f32, H as f32);
let theme = Theme::default();
let surface = HeadlessSurface::new(W, H).expect("create surface");
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let painted = surface
.paint(|target| {
let mut raw = super::super::surface::D2dSurface {
target,
dwrite: Some(&dwrite),
};
crate::primitives::toast::native_surface_paint::paint(
&stack,
&mut raw,
&theme,
&crate::style::Style::default(),
rect.x,
rect.y,
rect.width,
rect.height,
LINE_HEIGHT,
);
})
.map(|_| win_toast_stack_layout(&dwrite, rect, &stack, LINE_HEIGHT))
.expect("paint");
let no_paint = win_toast_stack_layout(&dwrite, rect, &stack, LINE_HEIGHT);
assert_eq!(painted, no_paint);
}
#[test]
fn paint_and_hit_test_round_trip_at_nonzero_origin() {
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let stack = stack_br(vec![colored_toast("t1", "Saved")]);
let rect = Rect::new(20.0, 30.0, W as f32, H as f32);
let theme = Theme::default();
let surface = HeadlessSurface::new(W + 20, H + 30).expect("create surface");
let layout = surface
.paint(|target| {
let mut raw = super::super::surface::D2dSurface {
target,
dwrite: Some(&dwrite),
};
crate::primitives::toast::native_surface_paint::paint(
&stack,
&mut raw,
&theme,
&crate::style::Style::default(),
rect.x,
rect.y,
rect.width,
rect.height,
LINE_HEIGHT,
);
})
.map(|_| win_toast_stack_layout(&dwrite, rect, &stack, LINE_HEIGHT))
.expect("paint toast stack");
let vt = &layout.visible_toasts[0];
assert!(
vt.bounds.x >= rect.x && vt.bounds.y >= rect.y,
"toast bounds should be shifted into the overlay's absolute frame"
);
let probe = surface.pixel_at(
(vt.bounds.x + 2.0) as u32,
(vt.bounds.y + vt.bounds.height / 2.0) as u32,
);
assert_eq!(
(probe.r, probe.g, probe.b),
(BOX_COLOR.r, BOX_COLOR.g, BOX_COLOR.b)
);
}
}