#[cfg(test)]
use super::text::DWrite;
use crate::event::Rect;
use crate::primitives::layout_metrics::pixel_pipeline_view_layout;
use crate::primitives::pipeline_view::{PipelineView, PipelineViewLayout};
#[cfg(test)]
use crate::theme::Theme;
pub fn win_pipeline_view_layout(view: &PipelineView, rect: Rect) -> PipelineViewLayout {
pixel_pipeline_view_layout(view, rect.x, rect.y, rect.width, rect.height)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::primitives::layout_metrics::pixel;
use crate::primitives::pipeline_view::{PipelineHit, PipelineStage, StageStatus};
use crate::types::{Color, WidgetId};
use crate::win::testing::HeadlessSurface;
const W: f32 = 300.0;
const H: f32 = 80.0;
fn make_view() -> PipelineView {
PipelineView {
id: WidgetId::new("pipe"),
stages: vec![
PipelineStage {
label: "Build".into(),
status: StageStatus::Done,
action: None,
},
PipelineStage {
label: "Test".into(),
status: StageStatus::Active,
action: Some("Retry".into()),
},
],
focused_stage: None,
}
}
fn paint(
surface: &HeadlessSurface,
dwrite: &DWrite,
rect: Rect,
view: &PipelineView,
theme: &Theme,
) -> PipelineViewLayout {
surface
.paint(|target| {
let mut raw = super::super::surface::D2dSurface {
target,
dwrite: Some(dwrite),
};
crate::primitives::pipeline_view::native_surface_paint::paint(
view, &mut raw, theme, rect,
);
})
.map(|_| win_pipeline_view_layout(view, rect))
.expect("paint pipeline view")
}
#[test]
fn draw_pipeline_view_paints_text_and_returns_layout() {
let surface = HeadlessSurface::new(W as u32, H as u32).expect("create surface");
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let theme = Theme {
background: Color::rgb(255, 255, 255),
surface_bg: Color::rgb(255, 255, 255),
foreground: Color::rgb(0, 0, 0),
..Theme::default()
};
let view = make_view();
let rect = Rect::new(0.0, 0.0, W, H);
let layout = paint(&surface, &dwrite, rect, &view, &theme);
assert_eq!(layout.stages.len(), 2);
let bb = layout.stages[0].box_bounds;
let mut painted_any = false;
for x in (bb.x as u32)..(bb.x + bb.width) as u32 {
for y in (bb.y as u32)..(bb.y + bb.height) as u32 {
let px = surface.pixel_at(x, y);
if (px.r, px.g, px.b) != (255, 255, 255) {
painted_any = true;
}
}
}
assert!(
painted_any,
"expected pipeline_view to paint visible glyphs"
);
}
#[test]
fn paint_and_click_round_trip_action_button_at_nonzero_origin() {
let origin_x = 12.0_f32;
let origin_y = 5.0_f32;
let surface = HeadlessSurface::new(W as u32, H as u32).expect("create surface");
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let theme = Theme::default();
let view = make_view();
let rect = Rect::new(origin_x, origin_y, W - origin_x, H - origin_y);
let layout = paint(&surface, &dwrite, rect, &view, &theme);
let bb0 = layout.stages[0].box_bounds;
assert!(
(bb0.y - (origin_y + pixel::PIPELINE_FOCUS_INDICATOR_H)).abs() < 0.001,
"stage box top should be origin_y + PIPELINE_FOCUS_INDICATOR_H, got {}",
bb0.y,
);
let ab = layout.stages[1]
.action_bounds
.expect("action bounds for stage 1");
let hit = layout.hit_test(ab.x + ab.width / 2.0, ab.y + ab.height / 2.0);
assert_eq!(hit, PipelineHit::Action(1));
let hit0 = layout.hit_test(bb0.x + 2.0, bb0.y + 2.0);
assert_eq!(hit0, PipelineHit::Body(0));
}
#[test]
fn no_paint_layout_matches_paint_layout() {
let view = make_view();
let rect = Rect::new(0.0, 0.0, W, H);
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let surface = HeadlessSurface::new(W as u32, H as u32).expect("create surface");
let painted = paint(&surface, &dwrite, rect, &view, &Theme::default());
let no_paint = win_pipeline_view_layout(&view, rect);
assert_eq!(painted, no_paint);
}
#[test]
fn zero_size_rect_is_a_no_op() {
let surface = HeadlessSurface::new(W as u32, H as u32).expect("create surface");
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let theme = Theme::default();
let view = make_view();
let rect = Rect::new(0.0, 0.0, 0.0, H);
surface
.fill_rect(Rect::new(0.0, 0.0, W, H), Color::rgb(255, 255, 255))
.expect("fill background");
paint(&surface, &dwrite, rect, &view, &theme);
let px = surface.pixel_at(1, 1);
assert_eq!(
(px.r, px.g, px.b),
(255, 255, 255),
"a zero-width pipeline view should paint nothing at all",
);
}
#[test]
fn action_tint_is_a_real_alpha_composite_not_a_cpu_blend() {
let surface = HeadlessSurface::new(W as u32, H as u32).expect("create surface");
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let theme = Theme::default();
let view = make_view();
let rect = Rect::new(0.0, 0.0, W, H);
let layout = paint(&surface, &dwrite, rect, &view, &theme);
let ab = layout.stages[1]
.action_bounds
.expect("stage 1 has an action button");
assert!(ab.height > 6.0, "action strip too short to probe: {ab:?}");
let px = surface.pixel_at((ab.x + 3.0) as u32, (ab.y + 2.0) as u32);
assert_ne!(
(px.r, px.g, px.b),
(theme.surface_bg.r, theme.surface_bg.g, theme.surface_bg.b),
"action button area must show the accent tint, not the plain box fill",
);
assert_ne!(
(px.r, px.g, px.b),
(theme.accent_bg.r, theme.accent_bg.g, theme.accent_bg.b),
"a 0.15-alpha tint must not paint fully opaque accent_bg",
);
}
}