Fix declare_class! indentation (#2461)

* Fix NSWindow delegate indentation

* Fix NSView delegate indentation
This commit is contained in:
Mads Marquart 2022-09-02 19:38:32 +02:00 committed by GitHub
parent d67c928120
commit e517e468f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 1178 additions and 1176 deletions

File diff suppressed because it is too large Load diff

View file

@ -128,7 +128,7 @@ pub fn new_delegate(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Id
} }
} }
declare_class! { declare_class!(
#[derive(Debug)] #[derive(Debug)]
struct WinitWindowDelegate { struct WinitWindowDelegate {
state: *mut c_void, state: *mut c_void,
@ -139,363 +139,364 @@ declare_class! {
} }
unsafe impl WinitWindowDelegate { unsafe impl WinitWindowDelegate {
#[sel(dealloc)] #[sel(dealloc)]
fn dealloc(&mut self) { fn dealloc(&mut self) {
self.with_state(|state| unsafe { self.with_state(|state| unsafe {
drop(Box::from_raw(state as *mut WindowDelegateState)); drop(Box::from_raw(state as *mut WindowDelegateState));
}); });
} }
#[sel(initWithWinit:)] #[sel(initWithWinit:)]
fn init_with_winit(&mut self, state: *mut c_void) -> Option<&mut Self> { fn init_with_winit(&mut self, state: *mut c_void) -> Option<&mut Self> {
let this: Option<&mut Self> = unsafe { msg_send![self, init] }; let this: Option<&mut Self> = unsafe { msg_send![self, init] };
this.map(|this| { this.map(|this| {
*this.state = state; *this.state = state;
this.with_state(|state| { this.with_state(|state| {
let _: () = unsafe { msg_send![*state.ns_window, setDelegate: &*this] }; let _: () = unsafe { msg_send![*state.ns_window, setDelegate: &*this] };
}); });
this this
}) })
} }
} }
// NSWindowDelegate + NSDraggingDestination protocols // NSWindowDelegate + NSDraggingDestination protocols
unsafe impl WinitWindowDelegate { unsafe impl WinitWindowDelegate {
#[sel(windowShouldClose:)] #[sel(windowShouldClose:)]
fn window_should_close(&self, _: id) -> bool { fn window_should_close(&self, _: id) -> bool {
trace_scope!("windowShouldClose:"); trace_scope!("windowShouldClose:");
self.with_state(|state| state.emit_event(WindowEvent::CloseRequested)); self.with_state(|state| state.emit_event(WindowEvent::CloseRequested));
false false
}
#[sel(windowWillClose:)]
fn window_will_close(&self, _: id) {
trace_scope!("windowWillClose:");
self.with_state(|state| unsafe {
// `setDelegate:` retains the previous value and then autoreleases it
autoreleasepool(|_| {
// Since El Capitan, we need to be careful that delegate methods can't
// be called after the window closes.
let _: () = msg_send![*state.ns_window, setDelegate: nil];
});
state.emit_event(WindowEvent::Destroyed);
});
}
#[sel(windowDidResize:)]
fn window_did_resize(&self, _: id) {
trace_scope!("windowDidResize:");
self.with_state(|state| {
// NOTE: WindowEvent::Resized is reported in frameDidChange.
state.emit_move_event();
});
}
// This won't be triggered if the move was part of a resize.
#[sel(windowDidMove:)]
fn window_did_move(&self, _: id) {
trace_scope!("windowDidMove:");
self.with_state(|state| {
state.emit_move_event();
});
}
#[sel(windowDidChangeBackingProperties:)]
fn window_did_change_backing_properties(&self, _: id) {
trace_scope!("windowDidChangeBackingProperties:");
self.with_state(|state| {
state.emit_static_scale_factor_changed_event();
});
}
#[sel(windowDidBecomeKey:)]
fn window_did_become_key(&self, _: id) {
trace_scope!("windowDidBecomeKey:");
self.with_state(|state| {
// TODO: center the cursor if the window had mouse grab when it
// lost focus
state.emit_event(WindowEvent::Focused(true));
});
}
#[sel(windowDidResignKey:)]
fn window_did_resign_key(&self, _: id) {
trace_scope!("windowDidResignKey:");
self.with_state(|state| {
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
// NSWindowDelegate will receive a didResignKey event despite no event
// being received when the modifiers are released. This is because
// flagsChanged events are received by the NSView instead of the
// NSWindowDelegate, and as a result a tracked modifiers state can quite
// easily fall out of synchrony with reality. This requires us to emit
// a synthetic ModifiersChanged event when we lose focus.
//
// Here we (very unsafely) acquire the winitState (a ViewState) from the
// Object referenced by state.ns_view (an IdRef, which is dereferenced
// to an id)
let view_state: &mut ViewState = unsafe {
let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref");
let state_ptr: *mut c_void = *ns_view.ivar("winitState");
&mut *(state_ptr as *mut ViewState)
};
// Both update the state and emit a ModifiersChanged event.
if !view_state.modifiers.is_empty() {
view_state.modifiers = ModifiersState::empty();
state.emit_event(WindowEvent::ModifiersChanged(view_state.modifiers));
} }
state.emit_event(WindowEvent::Focused(false)); #[sel(windowWillClose:)]
}); fn window_will_close(&self, _: id) {
} trace_scope!("windowWillClose:");
self.with_state(|state| unsafe {
/// Invoked when the dragged image enters destination bounds or frame // `setDelegate:` retains the previous value and then autoreleases it
#[sel(draggingEntered:)] autoreleasepool(|_| {
fn dragging_entered(&self, sender: id) -> bool { // Since El Capitan, we need to be careful that delegate methods can't
trace_scope!("draggingEntered:"); // be called after the window closes.
let _: () = msg_send![*state.ns_window, setDelegate: nil];
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration}; });
use std::path::PathBuf; state.emit_event(WindowEvent::Destroyed);
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
for file in unsafe { filenames.iter() } {
use cocoa::foundation::NSString;
use std::ffi::CStr;
unsafe {
let f = NSString::UTF8String(file);
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
self.with_state(|state| {
state.emit_event(WindowEvent::HoveredFile(PathBuf::from(path)));
}); });
} }
}
true
}
/// Invoked when the image is released
#[sel(prepareForDragOperation:)]
fn prepare_for_drag_operation(&self, _: id) -> bool {
trace_scope!("prepareForDragOperation:");
true
}
/// Invoked after the released image has been removed from the screen
#[sel(performDragOperation:)]
fn perform_drag_operation(&self, sender: id) -> bool {
trace_scope!("performDragOperation:");
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
use std::path::PathBuf;
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
for file in unsafe { filenames.iter() } {
use cocoa::foundation::NSString;
use std::ffi::CStr;
unsafe {
let f = NSString::UTF8String(file);
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
#[sel(windowDidResize:)]
fn window_did_resize(&self, _: id) {
trace_scope!("windowDidResize:");
self.with_state(|state| { self.with_state(|state| {
state.emit_event(WindowEvent::DroppedFile(PathBuf::from(path))); // NOTE: WindowEvent::Resized is reported in frameDidChange.
state.emit_move_event();
}); });
} }
}
true // This won't be triggered if the move was part of a resize.
} #[sel(windowDidMove:)]
fn window_did_move(&self, _: id) {
trace_scope!("windowDidMove:");
self.with_state(|state| {
state.emit_move_event();
});
}
/// Invoked when the dragging operation is complete #[sel(windowDidChangeBackingProperties:)]
#[sel(concludeDragOperation:)] fn window_did_change_backing_properties(&self, _: id) {
fn conclude_drag_operation(&self, _: id) { trace_scope!("windowDidChangeBackingProperties:");
trace_scope!("concludeDragOperation:"); self.with_state(|state| {
} state.emit_static_scale_factor_changed_event();
});
}
/// Invoked when the dragging operation is cancelled #[sel(windowDidBecomeKey:)]
#[sel(draggingExited:)] fn window_did_become_key(&self, _: id) {
fn dragging_exited(&self, _: id) { trace_scope!("windowDidBecomeKey:");
trace_scope!("draggingExited:"); self.with_state(|state| {
self.with_state(|state| { // TODO: center the cursor if the window had mouse grab when it
state.emit_event(WindowEvent::HoveredFileCancelled) // lost focus
}); state.emit_event(WindowEvent::Focused(true));
} });
}
/// Invoked when before enter fullscreen #[sel(windowDidResignKey:)]
#[sel(windowWillEnterFullscreen:)] fn window_did_resign_key(&self, _: id) {
fn window_will_enter_fullscreen(&self, _: id) { trace_scope!("windowDidResignKey:");
trace_scope!("windowWillEnterFullscreen:"); self.with_state(|state| {
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
// NSWindowDelegate will receive a didResignKey event despite no event
// being received when the modifiers are released. This is because
// flagsChanged events are received by the NSView instead of the
// NSWindowDelegate, and as a result a tracked modifiers state can quite
// easily fall out of synchrony with reality. This requires us to emit
// a synthetic ModifiersChanged event when we lose focus.
//
// Here we (very unsafely) acquire the winitState (a ViewState) from the
// Object referenced by state.ns_view (an IdRef, which is dereferenced
// to an id)
let view_state: &mut ViewState = unsafe {
let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref");
let state_ptr: *mut c_void = *ns_view.ivar("winitState");
&mut *(state_ptr as *mut ViewState)
};
self.with_state(|state| { // Both update the state and emit a ModifiersChanged event.
state.with_window(|window| { if !view_state.modifiers.is_empty() {
let mut shared_state = window.lock_shared_state("window_will_enter_fullscreen"); view_state.modifiers = ModifiersState::empty();
shared_state.maximized = window.is_zoomed(); state.emit_event(WindowEvent::ModifiersChanged(view_state.modifiers));
let fullscreen = shared_state.fullscreen.as_ref(); }
match fullscreen {
// Exclusive mode sets the state in `set_fullscreen` as the user state.emit_event(WindowEvent::Focused(false));
// can't enter exclusive mode by other means (like the });
// fullscreen button on the window decorations) }
Some(Fullscreen::Exclusive(_)) => (),
// `window_will_enter_fullscreen` was triggered and we're already /// Invoked when the dragged image enters destination bounds or frame
// in fullscreen, so we must've reached here by `set_fullscreen` #[sel(draggingEntered:)]
// as it updates the state fn dragging_entered(&self, sender: id) -> bool {
Some(Fullscreen::Borderless(_)) => (), trace_scope!("draggingEntered:");
// Otherwise, we must've reached fullscreen by the user clicking
// on the green fullscreen button. Update state! use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
None => { use std::path::PathBuf;
let current_monitor = Some(window.current_monitor_inner());
shared_state.fullscreen = Some(Fullscreen::Borderless(current_monitor)) let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
let filenames =
unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
for file in unsafe { filenames.iter() } {
use cocoa::foundation::NSString;
use std::ffi::CStr;
unsafe {
let f = NSString::UTF8String(file);
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
self.with_state(|state| {
state.emit_event(WindowEvent::HoveredFile(PathBuf::from(path)));
});
} }
} }
shared_state.in_fullscreen_transition = true;
})
});
}
/// Invoked when before exit fullscreen true
#[sel(windowWillExitFullScreen:)]
fn window_will_exit_fullscreen(&self, _: id) {
trace_scope!("windowWillExitFullScreen:");
self.with_state(|state| {
state.with_window(|window| {
let mut shared_state = window.lock_shared_state("window_will_exit_fullscreen");
shared_state.in_fullscreen_transition = true;
});
});
}
#[sel(window:willUseFullScreenPresentationOptions:)]
fn window_will_use_fullscreen_presentation_options(
&self,
_: id,
proposed_options: NSUInteger,
) -> NSUInteger {
trace_scope!("window:willUseFullScreenPresentationOptions:");
// Generally, games will want to disable the menu bar and the dock. Ideally,
// this would be configurable by the user. Unfortunately because of our
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
// placed on top of the menu bar in exclusive fullscreen mode. This looks
// broken so we always disable the menu bar in exclusive fullscreen. We may
// still want to make this configurable for borderless fullscreen. Right now
// we don't, for consistency. If we do, it should be documented that the
// user-provided options are ignored in exclusive fullscreen.
let mut options: NSUInteger = proposed_options;
self.with_state(|state| {
state.with_window(|window| {
let shared_state =
window.lock_shared_state("window_will_use_fullscreen_presentation_options");
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
options = (NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar)
.bits() as NSUInteger;
}
})
});
options
}
/// Invoked when entered fullscreen
#[sel(windowDidEnterFullscreen:)]
fn window_did_enter_fullscreen(&self, _: id) {
trace_scope!("windowDidEnterFullscreen:");
self.with_state(|state| {
state.initial_fullscreen = false;
state.with_window(|window| {
let mut shared_state = window.lock_shared_state("window_did_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
window.set_fullscreen(target_fullscreen);
}
});
});
}
/// Invoked when exited fullscreen
#[sel(windowDidExitFullscreen:)]
fn window_did_exit_fullscreen(&self, _: id) {
trace_scope!("windowDidExitFullscreen:");
self.with_state(|state| {
state.with_window(|window| {
window.restore_state_from_fullscreen();
let mut shared_state = window.lock_shared_state("window_did_exit_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
window.set_fullscreen(target_fullscreen);
}
})
});
}
/// Invoked when fail to enter fullscreen
///
/// When this window launch from a fullscreen app (e.g. launch from VS Code
/// terminal), it creates a new virtual destkop and a transition animation.
/// This animation takes one second and cannot be disable without
/// elevated privileges. In this animation time, all toggleFullscreen events
/// will be failed. In this implementation, we will try again by using
/// performSelector:withObject:afterDelay: until window_did_enter_fullscreen.
/// It should be fine as we only do this at initialzation (i.e with_fullscreen
/// was set).
///
/// From Apple doc:
/// In some cases, the transition to enter full-screen mode can fail,
/// due to being in the midst of handling some other animation or user gesture.
/// This method indicates that there was an error, and you should clean up any
/// work you may have done to prepare to enter full-screen mode.
#[sel(windowDidFailToEnterFullscreen:)]
fn window_did_fail_to_enter_fullscreen(&self, _: id) {
trace_scope!("windowDidFailToEnterFullscreen:");
self.with_state(|state| {
state.with_window(|window| {
let mut shared_state = window.lock_shared_state("window_did_fail_to_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
shared_state.target_fullscreen = None;
});
if state.initial_fullscreen {
unsafe {
let _: () = msg_send![*state.ns_window,
performSelector:sel!(toggleFullScreen:)
withObject:nil
afterDelay: 0.5
];
};
} else {
state.with_window(|window| window.restore_state_from_fullscreen());
} }
});
}
// Invoked when the occlusion state of the window changes /// Invoked when the image is released
#[sel(windowDidChangeOcclusionState:)] #[sel(prepareForDragOperation:)]
fn window_did_change_occlusion_state(&self, _: id) { fn prepare_for_drag_operation(&self, _: id) -> bool {
trace_scope!("windowDidChangeOcclusionState:"); trace_scope!("prepareForDragOperation:");
unsafe { true
self.with_state(|state| { }
state.emit_event(WindowEvent::Occluded(
!state /// Invoked after the released image has been removed from the screen
.ns_window #[sel(performDragOperation:)]
.occlusionState() fn perform_drag_operation(&self, sender: id) -> bool {
.contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible), trace_scope!("performDragOperation:");
))
}); use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
use std::path::PathBuf;
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
let filenames =
unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
for file in unsafe { filenames.iter() } {
use cocoa::foundation::NSString;
use std::ffi::CStr;
unsafe {
let f = NSString::UTF8String(file);
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
self.with_state(|state| {
state.emit_event(WindowEvent::DroppedFile(PathBuf::from(path)));
});
}
}
true
}
/// Invoked when the dragging operation is complete
#[sel(concludeDragOperation:)]
fn conclude_drag_operation(&self, _: id) {
trace_scope!("concludeDragOperation:");
}
/// Invoked when the dragging operation is cancelled
#[sel(draggingExited:)]
fn dragging_exited(&self, _: id) {
trace_scope!("draggingExited:");
self.with_state(|state| state.emit_event(WindowEvent::HoveredFileCancelled));
}
/// Invoked when before enter fullscreen
#[sel(windowWillEnterFullscreen:)]
fn window_will_enter_fullscreen(&self, _: id) {
trace_scope!("windowWillEnterFullscreen:");
self.with_state(|state| {
state.with_window(|window| {
let mut shared_state = window.lock_shared_state("window_will_enter_fullscreen");
shared_state.maximized = window.is_zoomed();
let fullscreen = shared_state.fullscreen.as_ref();
match fullscreen {
// Exclusive mode sets the state in `set_fullscreen` as the user
// can't enter exclusive mode by other means (like the
// fullscreen button on the window decorations)
Some(Fullscreen::Exclusive(_)) => (),
// `window_will_enter_fullscreen` was triggered and we're already
// in fullscreen, so we must've reached here by `set_fullscreen`
// as it updates the state
Some(Fullscreen::Borderless(_)) => (),
// Otherwise, we must've reached fullscreen by the user clicking
// on the green fullscreen button. Update state!
None => {
let current_monitor = Some(window.current_monitor_inner());
shared_state.fullscreen = Some(Fullscreen::Borderless(current_monitor))
}
}
shared_state.in_fullscreen_transition = true;
})
});
}
/// Invoked when before exit fullscreen
#[sel(windowWillExitFullScreen:)]
fn window_will_exit_fullscreen(&self, _: id) {
trace_scope!("windowWillExitFullScreen:");
self.with_state(|state| {
state.with_window(|window| {
let mut shared_state = window.lock_shared_state("window_will_exit_fullscreen");
shared_state.in_fullscreen_transition = true;
});
});
}
#[sel(window:willUseFullScreenPresentationOptions:)]
fn window_will_use_fullscreen_presentation_options(
&self,
_: id,
proposed_options: NSUInteger,
) -> NSUInteger {
trace_scope!("window:willUseFullScreenPresentationOptions:");
// Generally, games will want to disable the menu bar and the dock. Ideally,
// this would be configurable by the user. Unfortunately because of our
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
// placed on top of the menu bar in exclusive fullscreen mode. This looks
// broken so we always disable the menu bar in exclusive fullscreen. We may
// still want to make this configurable for borderless fullscreen. Right now
// we don't, for consistency. If we do, it should be documented that the
// user-provided options are ignored in exclusive fullscreen.
let mut options: NSUInteger = proposed_options;
self.with_state(|state| {
state.with_window(|window| {
let shared_state =
window.lock_shared_state("window_will_use_fullscreen_presentation_options");
if let Some(Fullscreen::Exclusive(_)) = shared_state.fullscreen {
options = (NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar)
.bits() as NSUInteger;
}
})
});
options
}
/// Invoked when entered fullscreen
#[sel(windowDidEnterFullscreen:)]
fn window_did_enter_fullscreen(&self, _: id) {
trace_scope!("windowDidEnterFullscreen:");
self.with_state(|state| {
state.initial_fullscreen = false;
state.with_window(|window| {
let mut shared_state = window.lock_shared_state("window_did_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
window.set_fullscreen(target_fullscreen);
}
});
});
}
/// Invoked when exited fullscreen
#[sel(windowDidExitFullscreen:)]
fn window_did_exit_fullscreen(&self, _: id) {
trace_scope!("windowDidExitFullscreen:");
self.with_state(|state| {
state.with_window(|window| {
window.restore_state_from_fullscreen();
let mut shared_state = window.lock_shared_state("window_did_exit_fullscreen");
shared_state.in_fullscreen_transition = false;
let target_fullscreen = shared_state.target_fullscreen.take();
drop(shared_state);
if let Some(target_fullscreen) = target_fullscreen {
window.set_fullscreen(target_fullscreen);
}
})
});
}
/// Invoked when fail to enter fullscreen
///
/// When this window launch from a fullscreen app (e.g. launch from VS Code
/// terminal), it creates a new virtual destkop and a transition animation.
/// This animation takes one second and cannot be disable without
/// elevated privileges. In this animation time, all toggleFullscreen events
/// will be failed. In this implementation, we will try again by using
/// performSelector:withObject:afterDelay: until window_did_enter_fullscreen.
/// It should be fine as we only do this at initialzation (i.e with_fullscreen
/// was set).
///
/// From Apple doc:
/// In some cases, the transition to enter full-screen mode can fail,
/// due to being in the midst of handling some other animation or user gesture.
/// This method indicates that there was an error, and you should clean up any
/// work you may have done to prepare to enter full-screen mode.
#[sel(windowDidFailToEnterFullscreen:)]
fn window_did_fail_to_enter_fullscreen(&self, _: id) {
trace_scope!("windowDidFailToEnterFullscreen:");
self.with_state(|state| {
state.with_window(|window| {
let mut shared_state =
window.lock_shared_state("window_did_fail_to_enter_fullscreen");
shared_state.in_fullscreen_transition = false;
shared_state.target_fullscreen = None;
});
if state.initial_fullscreen {
unsafe {
let _: () = msg_send![*state.ns_window,
performSelector:sel!(toggleFullScreen:)
withObject:nil
afterDelay: 0.5
];
};
} else {
state.with_window(|window| window.restore_state_from_fullscreen());
}
});
}
// Invoked when the occlusion state of the window changes
#[sel(windowDidChangeOcclusionState:)]
fn window_did_change_occlusion_state(&self, _: id) {
trace_scope!("windowDidChangeOcclusionState:");
unsafe {
self.with_state(|state| {
state.emit_event(WindowEvent::Occluded(
!state
.ns_window
.occlusionState()
.contains(NSWindowOcclusionState::NSWindowOcclusionStateVisible),
))
});
}
}
} }
} );
}
}
impl WinitWindowDelegate { impl WinitWindowDelegate {
// This function is definitely unsafe (&self -> &mut state), but labeling that // This function is definitely unsafe (&self -> &mut state), but labeling that