Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/window-lifecycle-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": minor
---

Moved Android and iOS lifecycle events to `WindowEvent`, including `Started`, `Stopped`, `Suspended`, and `Resumed`, so they are emitted for the specific window whose activity or scene changed lifecycle state.
66 changes: 42 additions & 24 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,6 @@ pub enum Event<'a, T: 'static> {
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
UserEvent(T),

/// Emitted when the application has been suspended.
///
/// ## Platform-specific
///
/// - **Android**: This is triggered by `onPause` method of the Activity.
/// - **iOS**: This is triggered by `applicationWillResignActive` method of the UIApplicationDelegate.
/// - **Linux / macOS / Windows**: Unsupported.
Suspended,

/// Emitted when the application has been resumed.
///
/// ## Platform-specific
///
/// - **Android**: This is triggered by `onResume` method of the Activity. The first onResume() is ignored to match the iOS implementation, since that is called on activity creation.
/// - **iOS**: This is triggered by `applicationWillEnterForeground` method of the UIApplicationDelegate.
/// - **Linux / macOS / Windows**: Unsupported.
Resumed,

/// Emitted when all of the event loop's input events have been processed and redraw processing
/// is about to begin.
///
Expand Down Expand Up @@ -189,8 +171,6 @@ impl<T: Clone> Clone for Event<'static, T> {
RedrawRequested(wid) => RedrawRequested(*wid),
RedrawEventsCleared => RedrawEventsCleared,
LoopDestroyed => LoopDestroyed,
Suspended => Suspended,
Resumed => Resumed,
Opened { urls } => Opened { urls: urls.clone() },
Reopen {
has_visible_windows,
Expand Down Expand Up @@ -218,8 +198,6 @@ impl<'a, T> Event<'a, T> {
RedrawRequested(wid) => Ok(RedrawRequested(wid)),
RedrawEventsCleared => Ok(RedrawEventsCleared),
LoopDestroyed => Ok(LoopDestroyed),
Suspended => Ok(Suspended),
Resumed => Ok(Resumed),
Opened { urls } => Ok(Opened { urls }),
Reopen {
has_visible_windows,
Expand All @@ -246,8 +224,6 @@ impl<'a, T> Event<'a, T> {
RedrawRequested(wid) => Some(RedrawRequested(wid)),
RedrawEventsCleared => Some(RedrawEventsCleared),
LoopDestroyed => Some(LoopDestroyed),
Suspended => Some(Suspended),
Resumed => Some(Resumed),
Opened { urls } => Some(Opened { urls }),
Reopen {
has_visible_windows,
Expand Down Expand Up @@ -314,6 +290,40 @@ pub enum WindowEvent<'a> {
/// - **macOS:** Fired if the [`crate::window::Window`] is dropped or the dock `Quit` item is clicked.
Destroyed,

/// The window has been started.
///
/// ## Platform-specific
///
/// - **Android**: This is triggered by `onStart` method of the Activity.
/// - **Linux / macOS / iOS / Windows**: Unsupported.
Started,

/// The window has been suspended.
///
/// ## Platform-specific
///
/// - **Android**: This is triggered by `onPause` method of the Activity.
/// - **iOS**: This is triggered by `applicationWillResignActive` method of the UIApplicationDelegate.
/// - **Linux / macOS / Windows**: Unsupported.
Suspended,

/// The window has been resumed.
///
/// ## Platform-specific
///
/// - **Android**: This is triggered by `onResume` method of the Activity. The first onResume() is ignored to match the iOS implementation, since that is called on activity creation.
/// - **iOS**: This is triggered by `applicationWillEnterForeground` method of the UIApplicationDelegate.
/// - **Linux / macOS / Windows**: Unsupported.
Resumed,

/// The window has been stopped.
///
/// ## Platform-specific
///
/// - **Android**: This is triggered by `onStop` method of the Activity.
/// - **Linux / macOS / iOS / Windows**: Unsupported.
Stopped,

/// A file has been dropped into the window.
///
/// When the user drops multiple files at once, this event will be emitted for each file
Expand Down Expand Up @@ -467,6 +477,10 @@ impl Clone for WindowEvent<'static> {
Moved(pos) => Moved(*pos),
CloseRequested => CloseRequested,
Destroyed => Destroyed,
Started => Started,
Suspended => Suspended,
Resumed => Resumed,
Stopped => Stopped,
DroppedFile(file) => DroppedFile(file.clone()),
HoveredFile(file) => HoveredFile(file.clone()),
HoveredFileCancelled => HoveredFileCancelled,
Expand Down Expand Up @@ -559,6 +573,10 @@ impl<'a> WindowEvent<'a> {
Moved(position) => Some(Moved(position)),
CloseRequested => Some(CloseRequested),
Destroyed => Some(Destroyed),
Started => Some(Started),
Suspended => Some(Suspended),
Resumed => Some(Resumed),
Stopped => Some(Stopped),
DroppedFile(file) => Some(DroppedFile(file)),
HoveredFile(file) => Some(HoveredFile(file)),
HoveredFileCancelled => Some(HoveredFileCancelled),
Expand Down
61 changes: 48 additions & 13 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use ndk::{
};
use once_cell::sync::Lazy;
use std::{
collections::VecDeque,
collections::{HashSet, VecDeque},
error::Error,
fmt,
sync::RwLock,
Expand Down Expand Up @@ -170,7 +170,7 @@ pub struct EventLoop<T: 'static> {
first_event: Option<EventSource>,
start_cause: event::StartCause,
looper: ThreadLooper,
running: bool,
running: HashSet<window::WindowId>,
}

#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
Expand All @@ -192,7 +192,7 @@ impl<T: 'static> EventLoop<T> {
first_event: None,
start_cause: event::StartCause::Init,
looper: ThreadLooper::for_thread().unwrap(),
running: false,
running: HashSet::new(),
}
}

Expand Down Expand Up @@ -224,15 +224,33 @@ impl<T: 'static> EventLoop<T> {

match self.first_event.take() {
Some(EventSource::Callback) => match ndk_glue::poll_events().unwrap() {
Event::Resume => {
self.call_event_handler(event_handler, control_flow, event::Event::Resumed);
Event::Resume { id: window_id } => {
self.call_event_handler(
event_handler,
control_flow,
event::Event::WindowEvent {
window_id,
event: event::WindowEvent::Resumed,
},
);
}
Event::WindowEvent {
id: window_id,
event,
} => match event {
WindowEvent::Resized => resized_window_id = Some(window_id),
WindowEvent::RedrawNeeded => redraw_window_id = Some(window_id),
WindowEvent::Started => {
self.running.insert(window_id);
self.call_event_handler(
event_handler,
control_flow,
event::Event::WindowEvent {
window_id,
event: event::WindowEvent::Started,
},
);
}
WindowEvent::Focused(focused) => {
self.call_event_handler(
event_handler,
Expand All @@ -243,7 +261,19 @@ impl<T: 'static> EventLoop<T> {
},
);
}
WindowEvent::Stopped => {
self.running.remove(&window_id);
self.call_event_handler(
event_handler,
control_flow,
event::Event::WindowEvent {
window_id,
event: event::WindowEvent::Stopped,
},
);
}
WindowEvent::Destroyed => {
self.running.remove(&window_id);
self.call_event_handler(
event_handler,
control_flow,
Expand All @@ -255,11 +285,16 @@ impl<T: 'static> EventLoop<T> {
}
_ => {}
},
Event::Pause => {
self.call_event_handler(event_handler, control_flow, event::Event::Suspended);
Event::Pause { id: window_id } => {
self.call_event_handler(
event_handler,
control_flow,
event::Event::WindowEvent {
window_id,
event: event::WindowEvent::Suspended,
},
);
}
Event::Stop => self.running = false,
Event::Start => self.running = true,
Event::Opened => {
let urls = ndk_glue::take_intent_urls();
if !urls.is_empty() {
Expand Down Expand Up @@ -400,7 +435,7 @@ impl<T: 'static> EventLoop<T> {
self.call_event_handler(event_handler, control_flow, event::Event::MainEventsCleared);

if let Some(window_id) = resized_window_id {
if self.running {
if self.running.contains(&window_id) {
let size = MonitorHandle.size();
let event = event::Event::WindowEvent {
window_id,
Expand All @@ -411,7 +446,7 @@ impl<T: 'static> EventLoop<T> {
}

if let Some(window_id) = redraw_window_id {
if self.running {
if self.running.contains(&window_id) {
let event = event::Event::RedrawRequested(window_id);
self.call_event_handler(event_handler, control_flow, event);
}
Expand Down Expand Up @@ -852,7 +887,7 @@ impl Window {
if let Some(w) = ndk_glue::activity_window_manager(self.activity_id).as_ref() {
handle.a_native_window = w.as_obj().as_raw() as *mut _;
} else {
panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
panic!("Cannot get the native window, it's null and will always be null before WindowEvent::Resumed and after WindowEvent::Suspended. Make sure you only call this function between those events.");
};
rwh_04::RawWindowHandle::AndroidNdk(handle)
}
Expand All @@ -864,7 +899,7 @@ impl Window {
if let Some(w) = ndk_glue::activity_window_manager(self.activity_id).as_ref() {
handle.a_native_window = w.as_obj().as_raw() as *mut _;
} else {
panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
panic!("Cannot get the native window, it's null and will always be null before WindowEvent::Resumed and after WindowEvent::Suspended. Make sure you only call this function between those events.");
};
rwh_05::RawWindowHandle::AndroidNdk(handle)
}
Expand Down
Loading
Loading