Skip to content

Commit debe2d1

Browse files
alphastrataItsDoot
authored andcommitted
Expose set_cursor_hittest() from winit (bevyengine#6664)
# Objective - Bevy should be usable to create 'overlay' type apps, where the input is not captured by Bevy, but passed down/into a target app, or to allow passive displays/widgets etc. ## Solution - the `winit::window::Window` already has a `set_cursor_hittest()` which basically does this for mouse input events, so I've exposed it (trying to copy the style laid out in the existing wrappings, and added a simple demo. --- ## Changelog - Added `hittest` to `WindowAttributes` - Added the `hittest`'s setters/getters - Modified the `WindowBuilder` - Modifed the `WindowDescriptor`'s `Default` impl. - Added an example `cargo run --example fallthrough`
1 parent 3dd0202 commit debe2d1

File tree

5 files changed

+98
-1
lines changed

5 files changed

+98
-1
lines changed

Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,16 @@ description = "Illustrates creating and updating a button"
14421442
category = "UI (User Interface)"
14431443
wasm = true
14441444

1445+
[[example]]
1446+
name = "window_fallthrough"
1447+
path = "examples/ui/window_fallthrough.rs"
1448+
1449+
[package.metadata.example.window_fallthrough]
1450+
name = "Window Fallthrough"
1451+
description = "Illustrates how to access `winit::window::Window`'s `hittest` functionality."
1452+
category = "UI (User Interface)"
1453+
wasm = false
1454+
14451455
[[example]]
14461456
name = "font_atlas_debug"
14471457
path = "examples/ui/font_atlas_debug.rs"

crates/bevy_window/src/window.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ pub struct Window {
286286
cursor_icon: CursorIcon,
287287
cursor_visible: bool,
288288
cursor_grab_mode: CursorGrabMode,
289+
hittest: bool,
289290
physical_cursor_position: Option<DVec2>,
290291
raw_handle: Option<RawHandleWrapper>,
291292
focused: bool,
@@ -351,6 +352,10 @@ pub enum WindowCommand {
351352
SetCursorPosition {
352353
position: Vec2,
353354
},
355+
/// Set whether or not mouse events within *this* window are captured, or fall through to the Window below.
356+
SetCursorHitTest {
357+
hittest: bool,
358+
},
354359
/// Set whether or not the window is maximized.
355360
SetMaximized {
356361
maximized: bool,
@@ -435,6 +440,7 @@ impl Window {
435440
cursor_visible: window_descriptor.cursor_visible,
436441
cursor_grab_mode: window_descriptor.cursor_grab_mode,
437442
cursor_icon: CursorIcon::Default,
443+
hittest: true,
438444
physical_cursor_position: None,
439445
raw_handle,
440446
focused: false,
@@ -777,7 +783,20 @@ impl Window {
777783
self.command_queue
778784
.push(WindowCommand::SetCursorPosition { position });
779785
}
780-
786+
/// Modifies whether the window catches cursor events.
787+
///
788+
/// If true, the window will catch the cursor events.
789+
/// If false, events are passed through the window such that any other window behind it receives them. By default hittest is enabled.
790+
pub fn set_cursor_hittest(&mut self, hittest: bool) {
791+
self.hittest = hittest;
792+
self.command_queue
793+
.push(WindowCommand::SetCursorHitTest { hittest });
794+
}
795+
/// Get whether or not the hittest is active.
796+
#[inline]
797+
pub fn hittest(&self) -> bool {
798+
self.hittest
799+
}
781800
#[allow(missing_docs)]
782801
#[inline]
783802
pub fn update_focused_status_from_backend(&mut self, focused: bool) {
@@ -961,6 +980,8 @@ pub struct WindowDescriptor {
961980
pub cursor_visible: bool,
962981
/// Sets whether and how the window grabs the cursor.
963982
pub cursor_grab_mode: CursorGrabMode,
983+
/// Sets whether or not the window listens for 'hits' of mouse activity over _this_ window.
984+
pub hittest: bool,
964985
/// Sets the [`WindowMode`](crate::WindowMode).
965986
///
966987
/// The monitor to go fullscreen on can be selected with the `monitor` field.
@@ -1013,6 +1034,7 @@ impl Default for WindowDescriptor {
10131034
decorations: true,
10141035
cursor_grab_mode: CursorGrabMode::None,
10151036
cursor_visible: true,
1037+
hittest: true,
10161038
mode: WindowMode::Windowed,
10171039
transparent: false,
10181040
canvas: None,

crates/bevy_winit/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ fn change_window(
230230
let window = winit_windows.get_window(id).unwrap();
231231
window.set_always_on_top(always_on_top);
232232
}
233+
bevy_window::WindowCommand::SetCursorHitTest { hittest } => {
234+
let window = winit_windows.get_window(id).unwrap();
235+
window.set_cursor_hittest(hittest).unwrap();
236+
}
233237
bevy_window::WindowCommand::Close => {
234238
// Since we have borrowed `windows` to iterate through them, we can't remove the window from it.
235239
// Add the removal requests to a queue to solve this

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ Example | Description
319319
[UI](../examples/ui/ui.rs) | Illustrates various features of Bevy UI
320320
[UI Scaling](../examples/ui/ui_scaling.rs) | Illustrates how to scale the UI
321321
[UI Z-Index](../examples/ui/z_index.rs) | Demonstrates how to control the relative depth (z-position) of UI elements
322+
[Window Fallthrough](../examples/ui/window_fallthrough.rs) | Illustrates how to access `winit::window::Window`'s `hittest` functionality.
322323

323324
## Window
324325

examples/ui/window_fallthrough.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//! This example illustrates how have a mouse's clicks/wheel/movement etc fall through the spawned transparent window to a window below.
2+
//! If you build this, and hit 'P' it should toggle on/off the mouse's passthrough.
3+
4+
use bevy::prelude::*;
5+
6+
fn main() {
7+
// Set the window's parameters, note we're setting always_on_top to be true.
8+
let window_desc = WindowDescriptor {
9+
transparent: true,
10+
decorations: true,
11+
always_on_top: true,
12+
..default()
13+
};
14+
15+
App::new()
16+
.insert_resource(ClearColor(Color::NONE)) // Use a transparent window, to make effects obvious.
17+
.add_plugins(DefaultPlugins.set(WindowPlugin {
18+
window: window_desc,
19+
..default()
20+
}))
21+
.add_startup_system(setup)
22+
.add_system(toggle_mouse_passthrough) // This allows us to hit 'P' to toggle on/off the mouse's passthrough
23+
.run();
24+
}
25+
26+
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
27+
// UI camera
28+
commands.spawn(Camera2dBundle::default());
29+
// Text with one section
30+
commands.spawn((
31+
// Create a TextBundle that has a Text with a single section.
32+
TextBundle::from_section(
33+
// Accepts a `String` or any type that converts into a `String`, such as `&str`
34+
"Hit 'P' then scroll/click around!",
35+
TextStyle {
36+
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
37+
font_size: 100.0, // Nice and big so you can see it!
38+
color: Color::WHITE,
39+
},
40+
)
41+
// Set the style of the TextBundle itself.
42+
.with_style(Style {
43+
position_type: PositionType::Absolute,
44+
position: UiRect {
45+
bottom: Val::Px(5.),
46+
right: Val::Px(10.),
47+
..default()
48+
},
49+
..default()
50+
}),
51+
));
52+
}
53+
// A simple system to handle some keyboard input and toggle on/off the hittest.
54+
fn toggle_mouse_passthrough(keyboard_input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
55+
if keyboard_input.just_pressed(KeyCode::P) {
56+
let window = windows.primary_mut();
57+
let hittest: bool = window.hittest();
58+
window.set_cursor_hittest(!hittest);
59+
}
60+
}

0 commit comments

Comments
 (0)