-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bugfix: Fail to dismiss the Message Dialog merely in MacOS (#223)
* 1. Bug phenomenon: Only in MacOS, the Message Dialog originating from the command-line context fails to dismissing itself posterior to click the button "OK". 2. Solution: If the parent-window handle is absent during the initialization of the MessageDialog instance, the ABI `CFUserNotificationDisplayAlert` will be invoked to pop up a message dialog, instead of instantiate a NSAlert instance. 3. Ramification: Only message dialog for MacOS 4. Unit Test: cover both sync and async message dialogs and run the unit test by `cargo run --example msg` * 删除无用的 pub 可见性标记 * @PolyMeilex is right. Accordingly, I made the following enhancements: 1. Remove the conditional dependency on the `futures` crate. 2. Relinquish the heavy thread-pool instrument. 3. Spawn a background thread by means of the std lib. * delete the temporary variable `win` by moving `self.parent.as_ref()` into `ModalFuture::new(..)` * Add a new conditional branch for awaiting timeout.
- Loading branch information
1 parent
5cb7002
commit c7c9537
Showing
6 changed files
with
202 additions
and
17 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
use ::objc2_foundation::MainThreadMarker; | ||
use ::deferred_future::ThreadDeferredFuture; | ||
use ::std::{ mem::MaybeUninit, ptr, sync::PoisonError, thread }; | ||
use ::core_foundation::{ base::TCFType, string::CFString }; | ||
use ::core_foundation_sys::{ base::CFOptionFlags, date::CFTimeInterval, url::CFURLRef, user_notification::{ CFUserNotificationDisplayAlert, kCFUserNotificationStopAlertLevel, kCFUserNotificationCautionAlertLevel, kCFUserNotificationNoteAlertLevel, kCFUserNotificationDefaultResponse, kCFUserNotificationAlternateResponse, kCFUserNotificationOtherResponse, kCFUserNotificationCancelResponse } }; | ||
use crate::{ | ||
message_dialog::{ MessageButtons, MessageDialog, MessageDialogResult, MessageLevel }, | ||
backend::{ DialogFutureType, macos::utils::{ FocusManager, PolicyManager } } | ||
}; | ||
struct UserAlert { | ||
timeout: CFTimeInterval, | ||
flags: CFOptionFlags, | ||
icon_url: CFURLRef, | ||
sound_url: CFURLRef, | ||
localization_url: CFURLRef, | ||
alert_header: String, | ||
alert_message: String, | ||
default_button_title: Option<String>, | ||
alternate_button_title: Option<String>, | ||
other_button_title: Option<String>, | ||
buttons: MessageButtons, | ||
_focus_manager: Option<FocusManager>, | ||
_policy_manager: Option<PolicyManager>, | ||
} | ||
impl UserAlert { | ||
fn new(opt: MessageDialog, mtm: Option<MainThreadMarker>) -> Self { | ||
let mut buttons: [Option<String>; 3] = match &opt.buttons { | ||
MessageButtons::Ok => [None, None, None], | ||
MessageButtons::OkCancel => [None, Some("Cancel".to_string()), None], | ||
MessageButtons::YesNo => [Some("Yes".to_string()), Some("No".to_string()), None], | ||
MessageButtons::YesNoCancel => [Some("Yes".to_string()), Some("No".to_string()), Some("Cancel".to_string())], | ||
MessageButtons::OkCustom(ok_text) => [Some(ok_text.to_string()), None, None], | ||
MessageButtons::OkCancelCustom(ok_text, cancel_text) => [Some(ok_text.to_string()), Some(cancel_text.to_string()), None], | ||
MessageButtons::YesNoCancelCustom(yes_text, no_text, cancel_text) => [Some(yes_text.to_string()), Some(no_text.to_string()), Some(cancel_text.to_string())] | ||
}; | ||
UserAlert { | ||
timeout: 0_f64, | ||
icon_url: ptr::null(), | ||
sound_url: ptr::null(), | ||
localization_url: ptr::null(), | ||
flags: match opt.level { | ||
MessageLevel::Info => kCFUserNotificationNoteAlertLevel, | ||
MessageLevel::Warning => kCFUserNotificationCautionAlertLevel, | ||
MessageLevel::Error => kCFUserNotificationStopAlertLevel | ||
}, | ||
alert_header: opt.title, | ||
alert_message: opt.description, | ||
default_button_title: buttons[0].take(), | ||
alternate_button_title: buttons[1].take(), | ||
other_button_title: buttons[2].take(), | ||
buttons: opt.buttons, | ||
_policy_manager: mtm.map(|mtm| PolicyManager::new(mtm)), | ||
_focus_manager: mtm.map(|mtm| FocusManager::new(mtm)) | ||
} | ||
} | ||
fn run(self) -> MessageDialogResult { | ||
let alert_header = CFString::new(&self.alert_header[..]); | ||
let alert_message = CFString::new(&self.alert_message[..]); | ||
let default_button_title = self.default_button_title.map(|string| CFString::new(&string[..])); | ||
let alternate_button_title = self.alternate_button_title.map(|value| CFString::new(&value[..])); | ||
let other_button_title = self.other_button_title.map(|value| CFString::new(&value[..])); | ||
let mut response_flags = MaybeUninit::<CFOptionFlags>::uninit(); | ||
let is_cancel = unsafe { CFUserNotificationDisplayAlert( | ||
self.timeout, | ||
self.flags, | ||
self.icon_url, | ||
self.sound_url, | ||
self.localization_url, | ||
alert_header.as_concrete_TypeRef(), | ||
alert_message.as_concrete_TypeRef(), | ||
default_button_title.map_or(ptr::null(), |value| value.as_concrete_TypeRef()), | ||
alternate_button_title.map_or(ptr::null(), |value| value.as_concrete_TypeRef()), | ||
other_button_title.map_or(ptr::null(), |value| value.as_concrete_TypeRef()), | ||
response_flags.as_mut_ptr() | ||
) }; | ||
if is_cancel != 0 { | ||
return MessageDialogResult::Cancel; | ||
} | ||
let response = unsafe { response_flags.assume_init() }; | ||
if response == kCFUserNotificationCancelResponse { | ||
return MessageDialogResult::Cancel; | ||
} | ||
match self.buttons { | ||
MessageButtons::Ok if response == kCFUserNotificationDefaultResponse => MessageDialogResult::Ok, | ||
MessageButtons::OkCancel if response == kCFUserNotificationDefaultResponse => MessageDialogResult::Ok, | ||
MessageButtons::OkCancel if response == kCFUserNotificationAlternateResponse => MessageDialogResult::Cancel, | ||
MessageButtons::YesNo if response == kCFUserNotificationDefaultResponse => MessageDialogResult::Yes, | ||
MessageButtons::YesNo if response == kCFUserNotificationAlternateResponse => MessageDialogResult::No, | ||
MessageButtons::YesNoCancel if response == kCFUserNotificationDefaultResponse => MessageDialogResult::Yes, | ||
MessageButtons::YesNoCancel if response == kCFUserNotificationAlternateResponse => MessageDialogResult::No, | ||
MessageButtons::YesNoCancel if response == kCFUserNotificationOtherResponse => MessageDialogResult::Cancel, | ||
MessageButtons::OkCustom(custom) if response == kCFUserNotificationDefaultResponse => MessageDialogResult::Custom(custom.to_owned()), | ||
MessageButtons::OkCancelCustom(custom, _) if response == kCFUserNotificationDefaultResponse => MessageDialogResult::Custom(custom.to_owned()), | ||
MessageButtons::OkCancelCustom(_, custom) if response == kCFUserNotificationAlternateResponse => MessageDialogResult::Custom(custom.to_owned()), | ||
MessageButtons::YesNoCancelCustom(custom, _, _) if response == kCFUserNotificationDefaultResponse => MessageDialogResult::Custom(custom.to_owned()), | ||
MessageButtons::YesNoCancelCustom(_, custom, _) if response == kCFUserNotificationAlternateResponse => MessageDialogResult::Custom(custom.to_owned()), | ||
MessageButtons::YesNoCancelCustom(_, _, custom) if response == kCFUserNotificationOtherResponse => MessageDialogResult::Custom(custom.to_owned()), | ||
_ => MessageDialogResult::Cancel, | ||
} | ||
} | ||
} | ||
pub fn sync_pop_dialog(opt: MessageDialog, mtm: MainThreadMarker) -> MessageDialogResult { | ||
UserAlert::new(opt, Some(mtm)).run() | ||
} | ||
pub fn async_pop_dialog(opt: MessageDialog) -> DialogFutureType<MessageDialogResult> { | ||
let deferred_future = ThreadDeferredFuture::default(); | ||
let defer = deferred_future.defer(); | ||
thread::spawn(move || { | ||
let mut defer = defer.lock().unwrap_or_else(PoisonError::into_inner); | ||
let message_dialog_result = UserAlert::new(opt.clone(), None).run(); | ||
defer.complete(message_dialog_result); | ||
}); | ||
Box::pin(deferred_future) | ||
} |