From 1ab228f6f5996ac8912e91b49a6bbab6ac6dba87 Mon Sep 17 00:00:00 2001 From: Johann DAVID Date: Sat, 30 Mar 2024 00:31:48 +0100 Subject: [PATCH] Allow to hook application window events --- examples/keyboard_handler/Cargo.toml | 1 + examples/keyboard_handler/src/main.rs | 55 +++++++++++++++++++++------ src/app.rs | 25 ++++++++++-- 3 files changed, 65 insertions(+), 16 deletions(-) diff --git a/examples/keyboard_handler/Cargo.toml b/examples/keyboard_handler/Cargo.toml index 17a7d963a..7ee1d88c4 100644 --- a/examples/keyboard_handler/Cargo.toml +++ b/examples/keyboard_handler/Cargo.toml @@ -8,3 +8,4 @@ version.workspace = true [dependencies] floem = { path = "../.." } +floem-winit = { version = "0.29.4", features = ["rwh_05"] } diff --git a/examples/keyboard_handler/src/main.rs b/examples/keyboard_handler/src/main.rs index bd4e4d862..97da9dcaa 100644 --- a/examples/keyboard_handler/src/main.rs +++ b/examples/keyboard_handler/src/main.rs @@ -1,31 +1,62 @@ use floem::{ + Application, event::{Event, EventListener}, keyboard::Key, + peniko::Color, + quit_app, + style::CursorStyle, unit::UnitExt, + views::{text, Decorators, container}, view::View, - views::{stack, text, Decorators}, }; +use floem_winit::event::WindowEvent; + +const MESSAGE: &str = " +Keyboard event handler example + +Focus-based keyboard handling: Click inside this frame to get keyboard focus, then hit 'q' to quit. +Global keyboard handling: Hit 'z' at anytime to quit as well (no focus needed). +"; fn app_view() -> impl View { - let view = - stack((text("Example: Keyboard event handler").style(|s| s.padding(10.0)),)).style(|s| { - s.size(100.pct(), 100.pct()) - .flex_col() - .items_center() - .justify_center() - }); - view.keyboard_navigatable() + let frame = text(MESSAGE) + .style(|s| s + .size(90.pct(), 90.pct()) + .cursor(CursorStyle::Pointer) + .border(3.0) + .focus(|s| s.border_color(Color::BLUE).background(Color::LIGHT_GRAY)) + .padding(10.0) + ) + .keyboard_navigatable() // A view must be navigatable in order to receive keyboard events .on_event_stop(EventListener::KeyDown, move |e| { if let Event::KeyDown(e) = e { if e.key.logical_key == Key::Character("q".into()) { println!("Goodbye :)"); - std::process::exit(0) + quit_app(); } println!("Key pressed in KeyCode: {:?}", e.key.physical_key); } - }) + }); + + container(frame) + .style(|s| s + .size_full() + .items_center() + .justify_center() + ) } fn main() { - floem::launch(app_view); + Application::new() + .window(|_| app_view(), None) + .on_window_event(|event| { + if let WindowEvent::KeyboardInput { device_id: _ , event, is_synthetic: _ } = event { + if event.logical_key == Key::Character("z".into()) { + quit_app(); + return true; // Event was handled + } + } + false // Event was not handled, let floem handle it + }) + .run(); } diff --git a/src/app.rs b/src/app.rs index 21c79bc55..dd2754a4c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,6 +2,7 @@ use std::{cell::RefCell, rc::Rc, sync::Arc}; use floem_reactive::WriteSignal; use floem_winit::{ + event::WindowEvent, event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy}, monitor::MonitorHandle, window::WindowId, @@ -80,12 +81,13 @@ pub(crate) fn add_app_update_event(event: AppUpdateEvent) { }); } -/// Floem top level application +/// Floem top level application. /// This is the entry point of the application. pub struct Application { handle: Option, event_listener: Option>, event_loop: EventLoop, + custom_window_event_listener: Option bool>> } impl Default for Application { @@ -109,6 +111,7 @@ impl Application { handle: Some(handle), event_listener: None, event_loop, + custom_window_event_listener: None, } } @@ -117,8 +120,16 @@ impl Application { self } - /// create a new window for the application, if you want multiple windows, - /// just chain more window method to the builder + /// Can be used to hook window events before they are handled by floem. Must return `true` if + /// the event was handled and the native floem event handling is not desired, or `false` to let + /// floem handle the event normally. + pub fn on_window_event(mut self, handle_event: impl Fn(&WindowEvent) -> bool + 'static) -> Self { + self.custom_window_event_listener = Some(Box::new(handle_event)); + self + } + + /// Create a new window for the application. If you want multiple windows, just chain more + /// `window()` methods to the builder. pub fn window( mut self, app_view: impl FnOnce(WindowId) -> V + 'static, @@ -142,7 +153,13 @@ impl Application { match event { floem_winit::event::Event::NewEvents(_) => {} floem_winit::event::Event::WindowEvent { window_id, event } => { - handle.handle_window_event(window_id, event, event_loop); + let custom_handled = self.custom_window_event_listener + .as_ref() + .map(|handle_event| handle_event(&event)) + .unwrap_or(false); + if !custom_handled { + handle.handle_window_event(window_id, event, event_loop); + } } floem_winit::event::Event::DeviceEvent { .. } => {} floem_winit::event::Event::UserEvent(event) => {