mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-24 06:11:30 +11:00
Fix declare_class!
indentation (#2461)
* Fix NSWindow delegate indentation * Fix NSView delegate indentation
This commit is contained in:
parent
d67c928120
commit
e517e468f8
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue