Add EventLoopBuilder

This commit adds an `EventLoopBuilder` struct to simplify event loop
customization and providing options to it upon creation. It also
deprecates the use of `EventLoop::with_user_event` in favor of the same
method on new builder, and replaces old platforms specific extension
traits with the new ones on the `EventLoopBuilder`.
This commit is contained in:
Mads Marquart 2022-02-16 22:09:03 +01:00 committed by GitHub
parent 0e52672f4a
commit f3f6f1008a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 324 additions and 271 deletions

View file

@ -13,12 +13,8 @@ use std::{
static AUX_DELEGATE_STATE_NAME: &str = "auxState";
pub struct AuxDelegateState {
/// We store this value in order to be able to defer setting the activation policy until
/// after the app has finished launching. If the activation policy is set earlier, the
/// menubar is initially unresponsive on macOS 10.15 for example.
pub activation_policy: ActivationPolicy,
pub create_default_menu: bool,
pub default_menu: bool,
}
pub struct AppDelegateClass(pub *const Class);
@ -54,11 +50,12 @@ extern "C" fn new(class: &Class, _: Sel) -> id {
unsafe {
let this: id = msg_send![class, alloc];
let this: id = msg_send![this, init];
// TODO: Remove the need for this initialization here
(*this).set_ivar(
AUX_DELEGATE_STATE_NAME,
Box::into_raw(Box::new(RefCell::new(AuxDelegateState {
activation_policy: ActivationPolicy::Regular,
create_default_menu: true,
default_menu: true,
}))) as *mut c_void,
);
this

View file

@ -296,7 +296,7 @@ impl AppState {
};
HANDLER.set_ready();
HANDLER.waker().start();
let create_default_menu = unsafe { get_aux_state_mut(app_delegate).create_default_menu };
let create_default_menu = unsafe { get_aux_state_mut(app_delegate).default_menu };
if create_default_menu {
// The menubar initialization should be before the `NewEvents` event, to allow
// overriding of the default menu even if it's created
@ -486,7 +486,7 @@ fn apply_activation_policy(app_delegate: &Object) {
let ns_app = NSApp();
// We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar won't be interactable.
// menu bar is initially unresponsive on macOS 10.15.
let act_pol = get_aux_state_mut(app_delegate).activation_policy;
ns_app.setActivationPolicy_(match act_pol {
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,

View file

@ -22,13 +22,17 @@ use crate::{
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::platform::{
app::APP_CLASS,
app_delegate::APP_DELEGATE_CLASS,
app_state::{AppState, Callback},
monitor::{self, MonitorHandle},
observer::*,
util::IdRef,
platform::macos::ActivationPolicy,
platform_impl::{
get_aux_state_mut,
platform::{
app::APP_CLASS,
app_delegate::APP_DELEGATE_CLASS,
app_state::{AppState, Callback},
monitor::{self, MonitorHandle},
observer::*,
util::IdRef,
},
},
};
@ -100,7 +104,9 @@ impl<T> EventLoopWindowTarget<T> {
}
pub struct EventLoop<T: 'static> {
pub(crate) delegate: IdRef,
/// The delegate is only weakly referenced by NSApplication, so we keep
/// it around here as well.
_delegate: IdRef,
window_target: Rc<RootWindowTarget<T>>,
panic_info: Rc<PanicInfo>,
@ -114,8 +120,23 @@ pub struct EventLoop<T: 'static> {
_callback: Option<Rc<Callback<T>>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
pub(crate) struct PlatformSpecificEventLoopAttributes {
pub(crate) activation_policy: ActivationPolicy,
pub(crate) default_menu: bool,
}
impl Default for PlatformSpecificEventLoopAttributes {
fn default() -> Self {
Self {
activation_policy: Default::default(), // Regular
default_menu: true,
}
}
}
impl<T> EventLoop<T> {
pub fn new() -> Self {
pub(crate) fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Self {
let delegate = unsafe {
let is_main_thread: BOOL = msg_send!(class!(NSThread), isMainThread);
if is_main_thread == NO {
@ -129,15 +150,21 @@ impl<T> EventLoop<T> {
let app: id = msg_send![APP_CLASS.0, sharedApplication];
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
let mut aux_state = get_aux_state_mut(&**delegate);
aux_state.activation_policy = attributes.activation_policy;
aux_state.default_menu = attributes.default_menu;
autoreleasepool(|| {
let _: () = msg_send![app, setDelegate:*delegate];
});
delegate
};
let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(Rc::downgrade(&panic_info));
EventLoop {
delegate,
_delegate: delegate,
window_target: Rc::new(RootWindowTarget {
p: Default::default(),
_marker: PhantomData,

View file

@ -18,9 +18,12 @@ mod window_delegate;
use std::{fmt, ops::Deref, sync::Arc};
pub use self::{
app_delegate::{get_aux_state_mut, AuxDelegateState},
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
pub(crate) use self::{
app_delegate::get_aux_state_mut,
event_loop::{
EventLoop, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
Proxy as EventLoopProxy,
},
monitor::{MonitorHandle, VideoMode},
window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow},
};