From f06843be442c4ad06e66166d1a7924347739c204 Mon Sep 17 00:00:00 2001 From: Golden Water <2439577268@qq.com> Date: Thu, 2 May 2024 21:05:25 +0800 Subject: [PATCH] feat(macos): add Reopen event, closes #218 (#517) * feat(macOS): add Reopen event * fix: unable to compile on macos when arch isn't aarch64 * fix(examples::reopen_event): only create new window when no window exists --- .changes/macos_reopen_event.md | 5 +++ examples/README.md | 1 + examples/reopen_event.rs | 45 +++++++++++++++++++++++++ src/event.rs | 23 ++++++++++++- src/platform_impl/macos/app_delegate.rs | 25 +++++++++++--- src/platform_impl/macos/app_state.rs | 6 ++++ 6 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 .changes/macos_reopen_event.md create mode 100644 examples/reopen_event.rs diff --git a/.changes/macos_reopen_event.md b/.changes/macos_reopen_event.md new file mode 100644 index 000000000..22ae74c1e --- /dev/null +++ b/.changes/macos_reopen_event.md @@ -0,0 +1,5 @@ +--- +"tao": minor +--- + +Add `event::Reopen` for handle click on dock icon on macOS. diff --git a/examples/README.md b/examples/README.md index 2955e8237..7e11878d1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -24,6 +24,7 @@ Run the `cargo run --example ` to see how each example works. - `multithreaded`: same as multiwindow but multithreaded. - `multiwindow`: create multiple windows - `parentwindow`: a window inside another window. +- `reopen_event`: handle click on dock icon on macOS - `resizable`: allow resizing window or not. - `set_ime_position`: set IME (input method editor) position when click. - `transparent`: make a transparent window. diff --git a/examples/reopen_event.rs b/examples/reopen_event.rs new file mode 100644 index 000000000..c236024fb --- /dev/null +++ b/examples/reopen_event.rs @@ -0,0 +1,45 @@ +// Copyright 2014-2021 The winit contributors +// Copyright 2021-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 + +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::Window, +}; + +#[allow(clippy::single_match)] +fn main() { + let event_loop = EventLoop::new(); + + let mut window = Some(Window::new(&event_loop).unwrap()); + + event_loop.run(move |event, event_loop, control_flow| { + *control_flow = ControlFlow::Wait; + + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + // drop the window + window = None; + } + Event::Reopen { + has_visible_windows, + .. + } => { + println!("on reopen, has visible windows: {has_visible_windows}"); + if !has_visible_windows { + window = Some(Window::new(&event_loop).unwrap()) + } + } + Event::MainEventsCleared => { + if let Some(w) = &window { + w.request_redraw(); + } + } + _ => (), + } + }); +} diff --git a/src/event.rs b/src/event.rs index fb3b1727b..b0d916059 100644 --- a/src/event.rs +++ b/src/event.rs @@ -136,6 +136,13 @@ pub enum Event<'a, T: 'static> { /// Emitted when the app is open by external resources, like opening a file or deeplink. Opened { urls: Vec }, + + /// ## Platform-specific + /// + /// - **macOS**: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen with return value same as hasVisibleWindows + /// - **Other**: Unsupported. + #[non_exhaustive] + Reopen { has_visible_windows: bool }, } impl Clone for Event<'static, T> { @@ -159,6 +166,11 @@ impl Clone for Event<'static, T> { Suspended => Suspended, Resumed => Resumed, Opened { urls } => Opened { urls: urls.clone() }, + Reopen { + has_visible_windows, + } => Reopen { + has_visible_windows: *has_visible_windows, + }, } } } @@ -178,6 +190,11 @@ impl<'a, T> Event<'a, T> { Suspended => Ok(Suspended), Resumed => Ok(Resumed), Opened { urls } => Ok(Opened { urls }), + Reopen { + has_visible_windows, + } => Ok(Reopen { + has_visible_windows, + }), } } @@ -199,6 +216,11 @@ impl<'a, T> Event<'a, T> { Suspended => Some(Suspended), Resumed => Some(Resumed), Opened { urls } => Some(Opened { urls }), + Reopen { + has_visible_windows, + } => Some(Reopen { + has_visible_windows, + }), } } } @@ -692,7 +714,6 @@ pub struct KeyEvent { /// ## Platform-specific /// - **Web:** Dead keys might be reported as the real key instead /// of `Dead` depending on the browser/OS. - /// pub logical_key: keyboard::Key<'static>, /// Contains the text produced by this keypress. diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index 6beea942f..2bcf7b3b3 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -4,8 +4,10 @@ use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState}; -use cocoa::base::id; -use cocoa::foundation::NSString; +use cocoa::{ + base::{id, NO}, + foundation::NSString, +}; use objc::{ declare::ClassDecl, runtime::{Class, Object, Sel, BOOL}, @@ -15,8 +17,7 @@ use std::{ os::raw::c_void, }; -use cocoa::foundation::NSArray; -use cocoa::foundation::NSURL; +use cocoa::foundation::{NSArray, NSURL}; use std::ffi::CStr; static AUX_DELEGATE_STATE_NAME: &str = "auxState"; @@ -54,6 +55,10 @@ lazy_static! { sel!(application:openURLs:), application_open_urls as extern "C" fn(&Object, Sel, id, id), ); + decl.add_method( + sel!(applicationShouldHandleReopen:hasVisibleWindows:), + application_should_handle_reopen as extern "C" fn(&Object, Sel, id, BOOL) -> BOOL, + ); decl.add_method( sel!(applicationSupportsSecureRestorableState:), application_supports_secure_restorable_state as extern "C" fn(&Object, Sel, id) -> BOOL, @@ -125,6 +130,18 @@ extern "C" fn application_open_urls(_: &Object, _: Sel, _: id, urls: id) -> () { trace!("Completed `application:openURLs:`"); } +extern "C" fn application_should_handle_reopen( + _: &Object, + _: Sel, + _: id, + has_visible_windows: BOOL, +) -> BOOL { + trace!("Triggered `applicationShouldHandleReopen`"); + AppState::reopen(has_visible_windows != NO); + trace!("Completed `applicationShouldHandleReopen`"); + has_visible_windows +} + extern "C" fn application_supports_secure_restorable_state(_: &Object, _: Sel, _: id) -> BOOL { trace!("Triggered `applicationSupportsSecureRestorableState`"); trace!("Completed `applicationSupportsSecureRestorableState`"); diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 2a6b775c0..02f500e4d 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -306,6 +306,12 @@ impl AppState { HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::Opened { urls })); } + pub fn reopen(has_visible_windows: bool) { + HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::Reopen { + has_visible_windows, + })); + } + pub fn wakeup(panic_info: Weak) { let panic_info = panic_info .upgrade()