Skip to content

Commit

Permalink
Add wrappers for starting and stopping modal windows (#129)
Browse files Browse the repository at this point in the history
* Add App::run_modal_for_window.

This is a wrapper around https://developer.apple.com/documentation/appkit/nsapplication/1428436-runmodalforwindow.

* Add wrapper for stopModalWithCode, and conversions for ModalResponse.

* Make run_modal_for_window borrow a reference, rather than take the value.

The reference is needed because the caller may still need to use the
window after the dialog is finished.

* Add a note about using the sheets API for modal views.

* Move the ModalResponse enum from the filesystem module to the appkit::app module.

It is now being used outside of filesystem, and this more closely mirrors
the location of this enum with AppKit.

* Make run_modal_for_window have the same semantics as begin_sheet.

Specifically, allow windows with delegates to be used.
  • Loading branch information
hwright authored Dec 28, 2024
1 parent eb2c2d6 commit 5345c2f
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 54 deletions.
67 changes: 66 additions & 1 deletion src/appkit/app/enums.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Various types used at the AppController level.
use crate::foundation::NSUInteger;
use crate::foundation::{NSInteger, NSUInteger};

/// Used for determining how an application should handle quitting/terminating.
/// You return this in your `AppController` `should_terminate` method.
Expand Down Expand Up @@ -156,3 +156,68 @@ impl From<&PresentationOption> for NSUInteger {
}
}
}

/// Represents a modal response for macOS modal dialogs.
#[derive(Copy, Clone, Debug)]
pub enum ModalResponse {
/// The user hit the "Ok" button.
Ok,

/// Continue.
Continue,

/// Canceled.
Canceled,

/// Stopped.
Stopped,

/// Aborted.
Aborted,

/// The first button in the dialog was clicked.
FirstButtonReturned,

/// The second button in the dialog was clicked.
SecondButtonReturned,

/// The third button in the dialog was clicked.
ThirdButtonReturned
}

impl From<NSInteger> for ModalResponse {
fn from(i: NSInteger) -> Self {
match i {
1 => ModalResponse::Ok,
0 => ModalResponse::Canceled,
1000 => ModalResponse::FirstButtonReturned,
1001 => ModalResponse::SecondButtonReturned,
1002 => ModalResponse::ThirdButtonReturned,
-1000 => ModalResponse::Stopped,
-1001 => ModalResponse::Aborted,
-1002 => ModalResponse::Continue,

// @TODO: Definitely don't panic here, wtf was I thinking?
// Probably make this a ModalResponse::Unknown or something so a user can
// gracefully handle.
e => {
panic!("Unknown NSModalResponse sent back! {}", e);
}
}
}
}

impl Into<NSInteger> for ModalResponse {
fn into(self) -> NSInteger {
match self {
ModalResponse::Ok => 1,
ModalResponse::Canceled => 0,
ModalResponse::FirstButtonReturned => 1000,
ModalResponse::SecondButtonReturned => 1001,
ModalResponse::ThirdButtonReturned => 1002,
ModalResponse::Stopped => -1000,
ModalResponse::Aborted => -1001,
ModalResponse::Continue => -1002
}
}
}
31 changes: 29 additions & 2 deletions src/appkit/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use objc::runtime::Object;
use objc::{class, msg_send, msg_send_id, sel};

use crate::appkit::menu::Menu;
use crate::foundation::{id, nil, AutoReleasePool, NSUInteger, NO, YES};
use crate::foundation::{id, nil, AutoReleasePool, NSInteger, NSUInteger, NO, YES};
use crate::invoker::TargetActionHandler;
use crate::notification_center::Dispatcher;
use crate::utils::activate_cocoa_multithreading;
Expand All @@ -63,7 +63,7 @@ pub use enums::*;
mod traits;
pub use traits::AppDelegate;

use super::window::Window;
use super::window::{Window, WindowDelegate};

pub(crate) static APP_PTR: &str = "rstAppPtr";

Expand Down Expand Up @@ -319,4 +319,31 @@ impl App {
let _: () = msg_send![app, terminate: nil];
});
}

/// Starts a modal event loop for the given window.
///
/// This method runs a modal event loop for the specified window synchronously.
/// It displays the specified window, makes it key, starts the run loop, and
/// processes events for that window.
///
/// Modern MacOS modal views should really be using the sheets API, but this
/// is still part of AppKit, so provided for completeness. As with
/// begin_sheet, a WindowDelegate is required.
pub fn run_modal_for_window<W>(window: &Window<W>) -> ModalResponse
where
W: WindowDelegate + 'static
{
shared_application(|app| unsafe {
let modal_response: NSInteger = msg_send![app, runModalForWindow: &*window.objc];
modal_response.into()
})
}

/// Stop the current modal window, returning the given code.
pub fn stop_modal_with_code(modal_response: ModalResponse) {
shared_application(|app| unsafe {
let response: NSInteger = modal_response.into();
let _: () = msg_send![app, stopModalWithCode: response];
});
}
}
50 changes: 0 additions & 50 deletions src/filesystem/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,6 @@
use crate::foundation::{NSInteger, NSUInteger};

/// Represents a modal response for macOS modal dialogs.
#[derive(Copy, Clone, Debug)]
pub enum ModalResponse {
/// The user hit the "Ok" button.
Ok,

/// Continue.
Continue,

/// Canceled.
Canceled,

/// Stopped.
Stopped,

/// Aborted.
Aborted,

/// The first button in the dialog was clicked.
FirstButtonReturned,

/// The second button in the dialog was clicked.
SecondButtonReturned,

/// The third button in the dialog was clicked.
ThirdButtonReturned
}

impl From<NSInteger> for ModalResponse {
fn from(i: NSInteger) -> Self {
match i {
1 => ModalResponse::Ok,
0 => ModalResponse::Canceled,
1000 => ModalResponse::FirstButtonReturned,
1001 => ModalResponse::SecondButtonReturned,
1002 => ModalResponse::ThirdButtonReturned,
-1000 => ModalResponse::Stopped,
-1001 => ModalResponse::Aborted,
-1002 => ModalResponse::Continue,

// @TODO: Definitely don't panic here, wtf was I thinking?
// Probably make this a ModalResponse::Unknown or something so a user can
// gracefully handle.
e => {
panic!("Unknown NSModalResponse sent back! {}", e);
}
}
}
}

/// Represents a type of search path used in file manager calls.
#[derive(Copy, Clone, Debug)]
pub enum SearchPathDomainMask {
Expand Down
2 changes: 1 addition & 1 deletion src/filesystem/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use objc::rc::{Id, Shared};
use objc::runtime::Object;
use objc::{class, msg_send, msg_send_id, sel};

use crate::filesystem::enums::ModalResponse;
use crate::appkit::ModalResponse;
use crate::foundation::{id, nil, NSInteger, NSString, NO, NSURL, YES};

#[cfg(feature = "appkit")]
Expand Down

0 comments on commit 5345c2f

Please sign in to comment.