#[cfg(target_os = "windows")]
use windows::Win32::Graphics::Direct2D::ID2D1RenderTarget;
#[cfg(target_os = "windows")]
use super::text::DWrite;
use crate::event::Rect;
use crate::primitives::layout_metrics::TextMeasure;
#[cfg(target_os = "windows")]
use crate::primitives::menu_bar::native_surface_paint;
#[cfg(target_os = "windows")]
use crate::theme::Theme;
use crate::{MenuBar, MenuBarItemMeasure, MenuBarLayout};
const ITEM_H_PADDING_DIP: f32 = 16.0;
fn display_text(label: &str) -> String {
label.chars().filter(|&c| c != '&').collect()
}
pub fn win_menu_bar_layout(measure: &dyn TextMeasure, rect: Rect, bar: &MenuBar) -> MenuBarLayout {
bar.layout(rect, |i| {
let text = display_text(&bar.items[i].label);
MenuBarItemMeasure::new(measure.width_of(&text) + ITEM_H_PADDING_DIP)
})
}
#[cfg(target_os = "windows")]
pub fn draw_menu_bar(
target: &ID2D1RenderTarget,
dwrite: &DWrite,
rect: Rect,
bar: &MenuBar,
theme: &Theme,
) -> MenuBarLayout {
let layout = win_menu_bar_layout(dwrite, rect, bar);
let mut surface = super::surface::D2dSurface {
target,
dwrite: Some(dwrite),
};
native_surface_paint::paint(bar, &layout, &mut surface, theme);
layout
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(target_os = "windows")]
mod windows_only {
use super::*;
use crate::primitives::menu_bar::{MenuBarHit, MenuBarItem};
use crate::types::{Color, WidgetId};
use crate::win::testing::HeadlessSurface;
const W: f32 = 300.0;
const H: f32 = 24.0;
fn bar() -> MenuBar {
MenuBar {
id: WidgetId::new("bar"),
items: vec![
MenuBarItem {
id: WidgetId::new("bar:file"),
label: "&File".into(),
disabled: false,
submenu: None,
},
MenuBarItem {
id: WidgetId::new("bar:edit"),
label: "&Edit".into(),
disabled: false,
submenu: None,
},
],
open_item: Some(0),
focused_item: None,
}
}
fn is_painted(surface: &HeadlessSurface, x: u32, y: u32, bg: Color) -> bool {
let px = surface.pixel_at(x, y);
(px.r, px.g, px.b) != (bg.r, bg.g, bg.b)
}
#[test]
fn paint_and_hit_test_round_trip() {
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 bar = bar();
let rect = Rect::new(0.0, 0.0, W, H);
let theme = Theme::default();
surface
.paint(|target| {
draw_menu_bar(target, &dwrite, rect, &bar, &theme);
})
.expect("paint menu bar");
let layout = win_menu_bar_layout(&dwrite, rect, &bar);
assert_eq!(layout.visible_items.len(), 2, "both items should fit");
for vi in &layout.visible_items {
let cx = vi.bounds.x + vi.bounds.width / 2.0;
let cy = vi.bounds.y + vi.bounds.height / 2.0;
assert_eq!(
layout.hit_test(cx, cy),
MenuBarHit::Item(vi.item_idx),
"item {} centre should hit-test back to itself",
vi.item_idx,
);
let row_y = cy as u32;
let found = (vi.bounds.x as u32..(vi.bounds.x + vi.bounds.width) as u32)
.any(|x| is_painted(&surface, x, row_y, theme.tab_bar_bg));
assert!(
found,
"item {} should paint something distinguishable from the bar background",
vi.item_idx,
);
}
}
#[test]
fn no_paint_layout_matches_paint_layout() {
let (dwrite, _, _) = DWrite::new("Segoe UI", 10.0, None).expect("create DWrite");
let bar = bar();
let rect = Rect::new(3.0, 0.0, W, H);
let surface = HeadlessSurface::new((W + 3.0) as u32, H as u32).expect("create surface");
let mut painted = None;
surface
.paint(|target| {
painted = Some(draw_menu_bar(
target,
&dwrite,
rect,
&bar,
&Theme::default(),
));
})
.expect("paint");
let painted = painted.expect("draw_menu_bar ran");
let no_paint = win_menu_bar_layout(&dwrite, rect, &bar);
assert_eq!(painted, no_paint);
}
}
#[test]
fn display_text_strips_ampersand() {
assert_eq!(display_text("&File"), "File");
assert_eq!(display_text("File"), "File");
}
}