Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Expose set_cursor_hittest() from winit #6664

Closed
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
alphastrata marked this conversation as resolved.
Show resolved Hide resolved
wasm = false

[[example]]
name = "font_atlas_debug"
path = "examples/ui/font_atlas_debug.rs"
Expand Down
24 changes: 23 additions & 1 deletion crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
60 changes: 60 additions & 0 deletions examples/ui/window_fallthrough.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}