use bevy_ecs::prelude::*;
use tracing::warn;
use super::hit_region::HitRegionMap;
use super::{Arrangement, D2DRectExt, GlobalArrangement};
use crate::ecs::WindowPos;
use crate::ecs::common::DepthFirstReversePostOrder;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PhysicalPoint {
pub x: f32,
pub y: f32,
}
impl PhysicalPoint {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HitTestMode {
None,
#[default]
Bounds,
AlphaMask,
NamedRegions,
}
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct HitTest {
pub mode: HitTestMode,
}
impl HitTest {
pub fn none() -> Self {
Self {
mode: HitTestMode::None,
}
}
pub fn bounds() -> Self {
Self {
mode: HitTestMode::Bounds,
}
}
pub fn alpha_mask() -> Self {
Self {
mode: HitTestMode::AlphaMask,
}
}
pub fn named_regions() -> Self {
Self {
mode: HitTestMode::NamedRegions,
}
}
}
pub fn hit_test_entity(world: &World, entity: Entity, point: PhysicalPoint) -> bool {
use crate::ecs::widget::bitmap_source::BitmapSourceResource;
let mode = world
.get::<HitTest>(entity)
.map(|h| h.mode)
.unwrap_or(HitTestMode::Bounds);
if mode == HitTestMode::None {
return false;
}
let Some(global) = world.get::<GlobalArrangement>(entity) else {
return false;
};
if !global.bounds.contains(point.x, point.y) {
return false;
}
if mode == HitTestMode::Bounds {
const ALPHA_THRESHOLD: f32 = 128.0 / 255.0;
let opacity = world
.get::<super::Opacity>(entity)
.map(|o| o.clamped())
.unwrap_or(1.0);
let foreground_a = world
.get::<crate::ecs::widget::brushes::Brushes>(entity)
.and_then(|b| b.foreground.as_color())
.map(|c| c.a)
.unwrap_or_else(|| {
crate::ecs::widget::brushes::DEFAULT_FOREGROUND
.as_color()
.map(|c| c.a)
.unwrap_or(1.0)
});
let composite_alpha = opacity * foreground_a;
if composite_alpha < ALPHA_THRESHOLD {
return false;
}
return true;
}
let Some(resource) = world.get::<BitmapSourceResource>(entity) else {
return true;
};
let Some(alpha_mask) = resource.alpha_mask() else {
return true;
};
let bounds = &global.bounds;
let bounds_width = bounds.right - bounds.left;
let bounds_height = bounds.bottom - bounds.top;
if bounds_width <= 0.0 || bounds_height <= 0.0 {
return true; }
let rel_x = (point.x - bounds.left) / bounds_width;
let rel_y = (point.y - bounds.top) / bounds_height;
let mask_x = (rel_x * alpha_mask.width() as f32) as u32;
let mask_y = (rel_y * alpha_mask.height() as f32) as u32;
alpha_mask.is_hit(mask_x, mask_y)
}
#[derive(Debug, Clone)]
pub struct HitTestResult {
pub entity: Entity,
pub region: Option<String>,
}
pub(crate) enum RegionHit {
Miss,
Hit(Option<String>),
}
pub(crate) fn hit_test_entity_ex(world: &World, entity: Entity, point: PhysicalPoint) -> RegionHit {
use crate::ecs::widget::bitmap_source::BitmapSourceResource;
let mode = world
.get::<HitTest>(entity)
.map(|h| h.mode)
.unwrap_or(HitTestMode::Bounds);
if mode == HitTestMode::None {
return RegionHit::Miss;
}
let Some(global) = world.get::<GlobalArrangement>(entity) else {
return RegionHit::Miss;
};
if !global.bounds.contains(point.x, point.y) {
return RegionHit::Miss;
}
match mode {
HitTestMode::None => unreachable!(),
HitTestMode::Bounds => {
const ALPHA_THRESHOLD: f32 = 128.0 / 255.0;
let opacity = world
.get::<super::Opacity>(entity)
.map(|o| o.clamped())
.unwrap_or(1.0);
let foreground_a = world
.get::<crate::ecs::widget::brushes::Brushes>(entity)
.and_then(|b| b.foreground.as_color())
.map(|c| c.a)
.unwrap_or_else(|| {
crate::ecs::widget::brushes::DEFAULT_FOREGROUND
.as_color()
.map(|c| c.a)
.unwrap_or(1.0)
});
let composite_alpha = opacity * foreground_a;
if composite_alpha < ALPHA_THRESHOLD {
RegionHit::Miss
} else {
RegionHit::Hit(None)
}
}
HitTestMode::AlphaMask => {
let Some(resource) = world.get::<BitmapSourceResource>(entity) else {
return RegionHit::Hit(None); };
let Some(alpha_mask) = resource.alpha_mask() else {
return RegionHit::Hit(None); };
let bounds = &global.bounds;
let bounds_width = bounds.right - bounds.left;
let bounds_height = bounds.bottom - bounds.top;
if bounds_width <= 0.0 || bounds_height <= 0.0 {
return RegionHit::Hit(None);
}
let rel_x = (point.x - bounds.left) / bounds_width;
let rel_y = (point.y - bounds.top) / bounds_height;
let mask_x = (rel_x * alpha_mask.width() as f32) as u32;
let mask_y = (rel_y * alpha_mask.height() as f32) as u32;
if alpha_mask.is_hit(mask_x, mask_y) {
RegionHit::Hit(None)
} else {
RegionHit::Miss
}
}
HitTestMode::NamedRegions => {
let Some(region_map) = world.get::<HitRegionMap>(entity) else {
warn!(
entity = ?entity,
"NamedRegions モードですが HitRegionMap がありません。Bounds にフォールバックします"
);
return RegionHit::Hit(None);
};
let bounds = &global.bounds;
let bounds_width = bounds.right - bounds.left;
let bounds_height = bounds.bottom - bounds.top;
if bounds_width <= 0.0 || bounds_height <= 0.0 {
return RegionHit::Hit(None);
}
let rel_x = (point.x - bounds.left) / bounds_width;
let rel_y = (point.y - bounds.top) / bounds_height;
let entity_size =
world
.get::<Arrangement>(entity)
.map(|a| a.size)
.unwrap_or(super::Size {
width: bounds_width,
height: bounds_height,
});
let region_name = region_map.hit_test_region(rel_x, rel_y, &entity_size);
RegionHit::Hit(region_name.map(|s| s.to_string()))
}
}
}
pub fn hit_test(world: &World, root: Entity, screen_point: PhysicalPoint) -> Option<Entity> {
let mut traversal = DepthFirstReversePostOrder::new(root);
while let Some(entity) = traversal.next(world) {
let hit = hit_test_entity(world, entity, screen_point);
if hit {
return Some(entity);
}
}
None
}
pub fn hit_test_in_window(
world: &World,
window: Entity,
client_point: PhysicalPoint,
) -> Option<Entity> {
let window_pos = world.get::<WindowPos>(window)?;
let position = window_pos.position?;
let screen_point = PhysicalPoint::new(
client_point.x + position.x as f32,
client_point.y + position.y as f32,
);
hit_test(world, window, screen_point)
}
pub fn hit_test_ex(
world: &World,
root: Entity,
screen_point: PhysicalPoint,
) -> Option<HitTestResult> {
let mut traversal = DepthFirstReversePostOrder::new(root);
while let Some(entity) = traversal.next(world) {
match hit_test_entity_ex(world, entity, screen_point) {
RegionHit::Hit(region) => {
return Some(HitTestResult { entity, region });
}
RegionHit::Miss => continue,
}
}
None
}
pub fn hit_test_in_window_ex(
world: &World,
window: Entity,
client_point: PhysicalPoint,
) -> Option<HitTestResult> {
let window_pos = world.get::<WindowPos>(window)?;
let position = window_pos.position?;
let screen_point = PhysicalPoint::new(
client_point.x + position.x as f32,
client_point.y + position.y as f32,
);
hit_test_ex(world, window, screen_point)
}
#[cfg(test)]
mod tests {
use super::*;
use bevy_ecs::world::World;
use windows::Win32::Foundation::{POINT, SIZE};
use windows::Win32::Graphics::Direct2D::Common::{D2D_RECT_F, D2D1_COLOR_F};
use windows_numerics::Matrix3x2;
fn make_global_arrangement(left: f32, top: f32, right: f32, bottom: f32) -> GlobalArrangement {
GlobalArrangement {
transform: Matrix3x2::translation(left, top),
bounds: D2D_RECT_F {
left,
top,
right,
bottom,
},
}
}
#[test]
fn test_hit_test_mode_default() {
assert_eq!(HitTestMode::default(), HitTestMode::Bounds);
}
#[test]
fn test_hit_test_none_constructor() {
let hit_test = HitTest::none();
assert_eq!(hit_test.mode, HitTestMode::None);
}
#[test]
fn test_hit_test_bounds_constructor() {
let hit_test = HitTest::bounds();
assert_eq!(hit_test.mode, HitTestMode::Bounds);
}
#[test]
fn test_hit_test_default() {
let hit_test = HitTest::default();
assert_eq!(hit_test.mode, HitTestMode::Bounds);
}
#[test]
fn test_hit_test_entity_hit_inside() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(10.0, 20.0, 110.0, 70.0), HitTest::bounds(),
))
.id();
let point = PhysicalPoint::new(50.0, 40.0);
assert!(hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_entity_miss_outside() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(10.0, 20.0, 110.0, 70.0),
HitTest::bounds(),
))
.id();
let point = PhysicalPoint::new(200.0, 200.0);
assert!(!hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_entity_skip_none_mode() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(10.0, 20.0, 110.0, 70.0),
HitTest::none(), ))
.id();
let point = PhysicalPoint::new(50.0, 40.0);
assert!(!hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_entity_no_component_defaults_to_bounds() {
let mut world = World::new();
let entity = world
.spawn(make_global_arrangement(10.0, 20.0, 110.0, 70.0))
.id();
let point = PhysicalPoint::new(50.0, 40.0);
assert!(hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_entity_no_global_arrangement() {
let mut world = World::new();
let entity = world.spawn(HitTest::bounds()).id();
let point = PhysicalPoint::new(50.0, 40.0);
assert!(!hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_front_priority() {
let mut world = World::new();
let back = world
.spawn(make_global_arrangement(0.0, 0.0, 100.0, 100.0))
.id();
let front = world
.spawn(make_global_arrangement(20.0, 20.0, 80.0, 80.0))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[back, front]);
let point = PhysicalPoint::new(50.0, 50.0);
let result = hit_test(&world, root, point);
assert_eq!(result, Some(front));
}
#[test]
fn test_hit_test_back_only() {
let mut world = World::new();
let back = world
.spawn(make_global_arrangement(0.0, 0.0, 100.0, 100.0))
.id();
let front = world
.spawn(make_global_arrangement(50.0, 50.0, 100.0, 100.0))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[back, front]);
let point = PhysicalPoint::new(10.0, 10.0);
let result = hit_test(&world, root, point);
assert_eq!(result, Some(back));
}
#[test]
fn test_hit_test_none() {
let mut world = World::new();
let entity = world
.spawn(make_global_arrangement(10.0, 10.0, 60.0, 60.0))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[entity]);
let point = PhysicalPoint::new(200.0, 200.0);
let result = hit_test(&world, root, point);
assert_eq!(result, None);
}
#[test]
fn test_hit_test_skip_none_mode() {
let mut world = World::new();
let back = world
.spawn(make_global_arrangement(0.0, 0.0, 100.0, 100.0))
.id();
let overlay = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::none(),
))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[back, overlay]);
let point = PhysicalPoint::new(50.0, 50.0);
let result = hit_test(&world, root, point);
assert_eq!(result, Some(back));
}
#[test]
fn test_hit_test_child_priority_over_parent() {
let mut world = World::new();
let parent = world
.spawn(make_global_arrangement(0.0, 0.0, 100.0, 100.0))
.id();
let child = world
.spawn(make_global_arrangement(10.0, 10.0, 90.0, 90.0))
.id();
world.entity_mut(parent).add_children(&[child]);
let point = PhysicalPoint::new(50.0, 50.0);
let result = hit_test(&world, parent, point);
assert_eq!(result, Some(child));
}
#[test]
fn test_hit_test_in_window_coordinate_conversion() {
let mut world = World::new();
let window = world
.spawn((
make_global_arrangement(100.0, 200.0, 500.0, 500.0),
WindowPos {
position: Some(POINT { x: 100, y: 200 }),
size: Some(SIZE { cx: 400, cy: 300 }),
..Default::default()
},
))
.id();
let widget = world
.spawn(make_global_arrangement(150.0, 250.0, 250.0, 300.0)) .id();
world.entity_mut(window).add_children(&[widget]);
let client_point = PhysicalPoint::new(50.0, 50.0);
let result = hit_test_in_window(&world, window, client_point);
assert_eq!(result, Some(widget));
}
#[test]
fn test_hit_test_in_window_no_window_pos() {
let mut world = World::new();
let window = world
.spawn(make_global_arrangement(0.0, 0.0, 100.0, 100.0))
.id();
let client_point = PhysicalPoint::new(50.0, 50.0);
let result = hit_test_in_window(&world, window, client_point);
assert_eq!(result, None);
}
#[test]
fn test_hit_test_in_window_position_none() {
let mut world = World::new();
let window = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
WindowPos {
position: None, ..Default::default()
},
))
.id();
let client_point = PhysicalPoint::new(50.0, 50.0);
let result = hit_test_in_window(&world, window, client_point);
assert_eq!(result, None);
}
#[test]
fn test_hit_test_alpha_mask_constructor() {
let hit_test = HitTest::alpha_mask();
assert_eq!(hit_test.mode, HitTestMode::AlphaMask);
}
#[test]
fn test_hit_test_alpha_mask_fallback_no_bitmap_source() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::alpha_mask(),
))
.id();
let point = PhysicalPoint::new(50.0, 50.0);
assert!(hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_alpha_mask_fallback_no_mask() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::alpha_mask(),
))
.id();
let point = PhysicalPoint::new(50.0, 50.0);
assert!(hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_alpha_mask_outside_bounds() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::alpha_mask(),
))
.id();
let point = PhysicalPoint::new(200.0, 200.0);
assert!(!hit_test_entity(&world, entity, point));
}
#[test]
fn test_hit_test_named_regions_constructor() {
let hit_test = HitTest::named_regions();
assert_eq!(hit_test.mode, HitTestMode::NamedRegions);
}
#[test]
fn test_hit_test_entity_ex_named_regions_rect_hit() {
let mut world = World::new();
let region_map = HitRegionMap::builder()
.rect("head", 20.0, 0.0, 60.0, 40.0) .build()
.unwrap();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::named_regions(),
region_map,
))
.id();
let point = PhysicalPoint::new(50.0, 20.0);
match hit_test_entity_ex(&world, entity, point) {
RegionHit::Hit(Some(name)) => assert_eq!(name, "head"),
other => panic!(
"Expected Hit(Some('head')), got {:?}",
match other {
RegionHit::Miss => "Miss",
RegionHit::Hit(None) => "Hit(None)",
RegionHit::Hit(Some(_)) => "Hit(Some(...))",
}
),
}
}
#[test]
fn test_hit_test_entity_ex_named_regions_no_region() {
let mut world = World::new();
let region_map = HitRegionMap::builder()
.rect("head", 20.0, 0.0, 60.0, 40.0)
.build()
.unwrap();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::named_regions(),
region_map,
))
.id();
let point = PhysicalPoint::new(5.0, 80.0);
match hit_test_entity_ex(&world, entity, point) {
RegionHit::Hit(None) => {} other => panic!(
"Expected Hit(None), got {:?}",
match other {
RegionHit::Miss => "Miss",
RegionHit::Hit(None) => "Hit(None)",
RegionHit::Hit(Some(ref s)) => s.as_str(),
}
),
}
}
#[test]
fn test_hit_test_entity_ex_named_regions_no_region_map_fallback() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::named_regions(),
))
.id();
let point = PhysicalPoint::new(50.0, 50.0);
match hit_test_entity_ex(&world, entity, point) {
RegionHit::Hit(None) => {} other => panic!(
"Expected Hit(None) (fallback), got {:?}",
match other {
RegionHit::Miss => "Miss",
RegionHit::Hit(None) => "Hit(None)",
RegionHit::Hit(Some(ref s)) => s.as_str(),
}
),
}
}
#[test]
fn test_hit_test_entity_ex_bounds_mode_region_none() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
))
.id();
let point = PhysicalPoint::new(50.0, 50.0);
match hit_test_entity_ex(&world, entity, point) {
RegionHit::Hit(None) => {}
_ => panic!("Expected Hit(None) for Bounds mode"),
}
}
#[test]
fn test_hit_test_entity_ex_outside_bounds() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::named_regions(),
))
.id();
let point = PhysicalPoint::new(200.0, 200.0);
match hit_test_entity_ex(&world, entity, point) {
RegionHit::Miss => {}
_ => panic!("Expected Miss for outside bounds"),
}
}
#[test]
fn test_hit_test_ex_returns_region_name() {
let mut world = World::new();
let region_map = HitRegionMap::builder()
.rect("head", 0.0, 0.0, 100.0, 50.0)
.rect("body", 0.0, 50.0, 100.0, 50.0)
.build()
.unwrap();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::named_regions(),
region_map,
))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[entity]);
let result = hit_test_ex(&world, root, PhysicalPoint::new(50.0, 25.0));
assert!(result.is_some());
let result = result.unwrap();
assert_eq!(result.entity, entity);
assert_eq!(result.region.as_deref(), Some("head"));
let result = hit_test_ex(&world, root, PhysicalPoint::new(50.0, 75.0));
assert!(result.is_some());
let result = result.unwrap();
assert_eq!(result.entity, entity);
assert_eq!(result.region.as_deref(), Some("body"));
}
#[test]
fn test_hit_test_ex_bounds_mode() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[entity]);
let result = hit_test_ex(&world, root, PhysicalPoint::new(50.0, 50.0));
assert!(result.is_some());
let result = result.unwrap();
assert_eq!(result.entity, entity);
assert_eq!(result.region, None);
}
#[test]
fn test_hit_test_ex_backward_compat_same_entity() {
let mut world = World::new();
let back = world
.spawn(make_global_arrangement(0.0, 0.0, 100.0, 100.0))
.id();
let front = world
.spawn(make_global_arrangement(20.0, 20.0, 80.0, 80.0))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[back, front]);
let point = PhysicalPoint::new(50.0, 50.0);
let old_result = hit_test(&world, root, point);
let new_result = hit_test_ex(&world, root, point);
assert_eq!(old_result.unwrap(), new_result.unwrap().entity);
}
#[test]
fn test_hit_test_in_window_ex_with_region() {
let mut world = World::new();
let region_map = HitRegionMap::builder()
.rect("button", 0.0, 0.0, 100.0, 50.0)
.build()
.unwrap();
let window = world
.spawn((
make_global_arrangement(100.0, 200.0, 500.0, 500.0),
WindowPos {
position: Some(POINT { x: 100, y: 200 }),
size: Some(SIZE { cx: 400, cy: 300 }),
..Default::default()
},
))
.id();
let widget = world
.spawn((
make_global_arrangement(150.0, 250.0, 250.0, 300.0), HitTest::named_regions(),
region_map,
))
.id();
world.entity_mut(window).add_children(&[widget]);
let result = hit_test_in_window_ex(&world, window, PhysicalPoint::new(50.0, 50.0));
assert!(result.is_some());
let result = result.unwrap();
assert_eq!(result.entity, widget);
assert_eq!(result.region.as_deref(), Some("button"));
}
#[test]
fn test_hit_test_in_window_ex_no_window_pos() {
let mut world = World::new();
let window = world
.spawn(make_global_arrangement(0.0, 0.0, 100.0, 100.0))
.id();
let result = hit_test_in_window_ex(&world, window, PhysicalPoint::new(50.0, 50.0));
assert!(result.is_none());
}
#[test]
fn test_existing_api_backward_compat() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
))
.id();
let root = world.spawn_empty().id();
world.entity_mut(root).add_children(&[entity]);
let result = hit_test(&world, root, PhysicalPoint::new(50.0, 50.0));
assert_eq!(result, Some(entity));
let result = hit_test(&world, root, PhysicalPoint::new(200.0, 200.0));
assert_eq!(result, None);
}
#[test]
fn test_hit_test_entity_bounds_alpha_boundary_above() {
use super::super::Opacity;
use crate::ecs::widget::brushes::Brushes;
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
Opacity(0.502),
Brushes::with_foreground(D2D1_COLOR_F {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
}),
))
.id();
assert!(hit_test_entity(
&world,
entity,
PhysicalPoint::new(50.0, 50.0)
));
}
#[test]
fn test_hit_test_entity_bounds_alpha_boundary_below() {
use super::super::Opacity;
use crate::ecs::widget::brushes::Brushes;
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
Opacity(0.501),
Brushes::with_foreground(D2D1_COLOR_F {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
}),
))
.id();
assert!(!hit_test_entity(
&world,
entity,
PhysicalPoint::new(50.0, 50.0)
));
}
#[test]
fn test_hit_test_entity_bounds_low_opacity() {
use super::super::Opacity;
use crate::ecs::widget::brushes::Brushes;
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
Opacity(0.4),
Brushes::with_foreground(D2D1_COLOR_F {
r: 0.0,
g: 1.0,
b: 0.0,
a: 1.0,
}),
))
.id();
assert!(!hit_test_entity(
&world,
entity,
PhysicalPoint::new(50.0, 50.0)
));
}
#[test]
fn test_hit_test_entity_bounds_low_foreground_alpha() {
use super::super::Opacity;
use crate::ecs::widget::brushes::Brushes;
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
Opacity(1.0),
Brushes::with_foreground(D2D1_COLOR_F {
r: 0.0,
g: 0.0,
b: 1.0,
a: 0.4,
}),
))
.id();
assert!(!hit_test_entity(
&world,
entity,
PhysicalPoint::new(50.0, 50.0)
));
}
#[test]
fn test_hit_test_entity_bounds_no_opacity_no_brushes() {
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
))
.id();
assert!(hit_test_entity(
&world,
entity,
PhysicalPoint::new(50.0, 50.0)
));
}
#[test]
fn test_hit_test_entity_bounds_inherit_foreground() {
use super::super::Opacity;
use crate::ecs::widget::brushes::Brushes;
let mut world = World::new();
let entity = world
.spawn((
make_global_arrangement(0.0, 0.0, 100.0, 100.0),
HitTest::bounds(),
Opacity(0.502),
Brushes::default(), ))
.id();
assert!(hit_test_entity(
&world,
entity,
PhysicalPoint::new(50.0, 50.0)
));
}
}