Expose set_cursor_hittest() from winit (#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` 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`
This commit is contained in:
Jer 2022-11-21 12:59:10 +00:00
parent 55ca7fc88e
commit b3e45b75d6
5 changed files with 98 additions and 1 deletions

View file

@ -1442,6 +1442,16 @@ description = "Illustrates creating and updating a button"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "window_fallthrough"
path = "examples/ui/window_fallthrough.rs"
[package.metadata.example.window_fallthrough]
name = "Window Fallthrough"
description = "Illustrates how to access `winit::window::Window`'s `hittest` functionality."
category = "UI (User Interface)"
wasm = false
[[example]]
name = "font_atlas_debug"
path = "examples/ui/font_atlas_debug.rs"

View file

@ -286,6 +286,7 @@ pub struct Window {
cursor_icon: CursorIcon,
cursor_visible: bool,
cursor_grab_mode: CursorGrabMode,
hittest: bool,
physical_cursor_position: Option<DVec2>,
raw_handle: Option<RawHandleWrapper>,
focused: bool,
@ -351,6 +352,10 @@ pub enum WindowCommand {
SetCursorPosition {
position: Vec2,
},
/// Set whether or not mouse events within *this* window are captured, or fall through to the Window below.
SetCursorHitTest {
hittest: bool,
},
/// Set whether or not the window is maximized.
SetMaximized {
maximized: bool,
@ -435,6 +440,7 @@ impl Window {
cursor_visible: window_descriptor.cursor_visible,
cursor_grab_mode: window_descriptor.cursor_grab_mode,
cursor_icon: CursorIcon::Default,
hittest: true,
physical_cursor_position: None,
raw_handle,
focused: false,
@ -777,7 +783,20 @@ impl Window {
self.command_queue
.push(WindowCommand::SetCursorPosition { position });
}
/// Modifies whether the window catches cursor events.
///
/// If true, the window will catch the cursor events.
/// If false, events are passed through the window such that any other window behind it receives them. By default hittest is enabled.
pub fn set_cursor_hittest(&mut self, hittest: bool) {
self.hittest = hittest;
self.command_queue
.push(WindowCommand::SetCursorHitTest { hittest });
}
/// Get whether or not the hittest is active.
#[inline]
pub fn hittest(&self) -> bool {
self.hittest
}
#[allow(missing_docs)]
#[inline]
pub fn update_focused_status_from_backend(&mut self, focused: bool) {
@ -961,6 +980,8 @@ pub struct WindowDescriptor {
pub cursor_visible: bool,
/// Sets whether and how the window grabs the cursor.
pub cursor_grab_mode: CursorGrabMode,
/// Sets whether or not the window listens for 'hits' of mouse activity over _this_ window.
pub hittest: bool,
/// Sets the [`WindowMode`](crate::WindowMode).
///
/// The monitor to go fullscreen on can be selected with the `monitor` field.
@ -1013,6 +1034,7 @@ impl Default for WindowDescriptor {
decorations: true,
cursor_grab_mode: CursorGrabMode::None,
cursor_visible: true,
hittest: true,
mode: WindowMode::Windowed,
transparent: false,
canvas: None,

View file

@ -230,6 +230,10 @@ fn change_window(
let window = winit_windows.get_window(id).unwrap();
window.set_always_on_top(always_on_top);
}
bevy_window::WindowCommand::SetCursorHitTest { hittest } => {
let window = winit_windows.get_window(id).unwrap();
window.set_cursor_hittest(hittest).unwrap();
}
bevy_window::WindowCommand::Close => {
// Since we have borrowed `windows` to iterate through them, we can't remove the window from it.
// Add the removal requests to a queue to solve this

View file

@ -319,6 +319,7 @@ Example | Description
[UI](../examples/ui/ui.rs) | Illustrates various features of Bevy UI
[UI Scaling](../examples/ui/ui_scaling.rs) | Illustrates how to scale the UI
[UI Z-Index](../examples/ui/z_index.rs) | Demonstrates how to control the relative depth (z-position) of UI elements
[Window Fallthrough](../examples/ui/window_fallthrough.rs) | Illustrates how to access `winit::window::Window`'s `hittest` functionality.
## Window

View file

@ -0,0 +1,60 @@
//! This example illustrates how have a mouse's clicks/wheel/movement etc fall through the spawned transparent window to a window below.
//! If you build this, and hit 'P' it should toggle on/off the mouse's passthrough.
use bevy::prelude::*;
fn main() {
// Set the window's parameters, note we're setting always_on_top to be true.
let window_desc = WindowDescriptor {
transparent: true,
decorations: true,
always_on_top: true,
..default()
};
App::new()
.insert_resource(ClearColor(Color::NONE)) // Use a transparent window, to make effects obvious.
.add_plugins(DefaultPlugins.set(WindowPlugin {
window: window_desc,
..default()
}))
.add_startup_system(setup)
.add_system(toggle_mouse_passthrough) // This allows us to hit 'P' to toggle on/off the mouse's passthrough
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// UI camera
commands.spawn(Camera2dBundle::default());
// Text with one section
commands.spawn((
// Create a TextBundle that has a Text with a single section.
TextBundle::from_section(
// Accepts a `String` or any type that converts into a `String`, such as `&str`
"Hit 'P' then scroll/click around!",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 100.0, // Nice and big so you can see it!
color: Color::WHITE,
},
)
// Set the style of the TextBundle itself.
.with_style(Style {
position_type: PositionType::Absolute,
position: UiRect {
bottom: Val::Px(5.),
right: Val::Px(10.),
..default()
},
..default()
}),
));
}
// A simple system to handle some keyboard input and toggle on/off the hittest.
fn toggle_mouse_passthrough(keyboard_input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
if keyboard_input.just_pressed(KeyCode::P) {
let window = windows.primary_mut();
let hittest: bool = window.hittest();
window.set_cursor_hittest(!hittest);
}
}