Blocking window modal dialogs on OS X

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
User avatar
SHC
In need of some credit
In need of some credit
Posts: 3
Joined: Fri May 11, 2018 10:57 am

Blocking window modal dialogs on OS X

Post by SHC »

I'm in the process of porting an old application of mine (made in wxWidgets 3.0.2 and targeting Windows) to macOS now. My application is a code editor, and when closing the application, I wanted to ask if the user wanted to save.

Code: Select all

switch (dialog->ShowModal())
{
    case wxID_YES:
        OnMenuSave(e);

        // Only break if the file is not saved
        // The user must have clicked on cancel button in save as box
        if (!editorPage->CodeChanged())
            break;

    case wxID_CANCEL:
        e.Veto();
        dialog->Destroy();
        return;
}
This used to work fine, but however I wanted to show the dialogs the mac way, like they used to be attached to the window they are created from.

So when I wrote the helper function to use the ShowWindowModalThenDo function on the dialog.

Code: Select all

template <typename Functor>
inline void ShowWindowModalThenDo(wxDialog *dlg, const Functor& onEndModal)
{
#ifdef __WXMAC__
    dlg->ShowWindowModalThenDo(onEndModal);
#else
    onEndModal(dlg->ShowModal());
#endif
}
The problem with this is that it doesn't pause the event, while the dialog is being shown. This is especially problematic as I handle this in the editor closing event. So even when the dialog is being shown, and allows for the user to click on cancel, the editor page is getting closed.

How can I temporarily pause the events from processing on the parent window?
User avatar
doublemax
Moderator
Moderator
Posts: 19115
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Blocking window modal dialogs on OS X

Post by doublemax »

What happens if you just veto the close event?
Use the source, Luke!
User avatar
SHC
In need of some credit
In need of some credit
Posts: 3
Joined: Fri May 11, 2018 10:57 am

Re: Blocking window modal dialogs on OS X

Post by SHC »

I can veto the close event, but then, since the callback happens after an event is sent, the scope is lost. So If the user clicked on either yes / no, I had to close it. And calling this->close() isn't working because the event gets vetoed and it just sends out another event. This keeps on going.
User avatar
doublemax
Moderator
Moderator
Posts: 19115
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Blocking window modal dialogs on OS X

Post by doublemax »

I don't work under OSX, so i can't test this, but it's really strange that ShowModal() should be non-blocking there.

Can you reproduce this by modifying the "minimal" sample?
Use the source, Luke!
User avatar
SHC
In need of some credit
In need of some credit
Posts: 3
Joined: Fri May 11, 2018 10:57 am

Re: Blocking window modal dialogs on OS X

Post by SHC »

I'm sorry if my words didn't convey my exact meaning, I'm trying my best to convey my thoughts as English is not my first language. I didn't say that ShowModal() was non-blocking. It was ShowWindowModal() that doesn't block the entire application and I can see why. Let me quote some documentation.
Unlike ShowModal(), dialogs shown with this function only prevent the user from interacting with their parent frame only but not with the rest of the application. They also don't block the program execution but instead return immediately, as Show(), and generate a wxEVT_WINDOW_MODAL_DIALOG_CLOSED event (wxWindowModalDialogEvent) later when the dialog is closed.
I got to know that this was the case, and because we have to wait for an event to generate, it is not made a blocking call. The design for sheets seems to be this way, as they are never a blocking calls in Cocoa on OSX and now on macOS. And then, my application is a code editor with tabs support (I use wxAuiNotebook to provide tabbed editing), and hence I had this high level psuedocode to handle the case of close clicked on a tab.
  1. User clicked on close button on tab
  2. If the current tab is not modified, delete the tab page.
  3. Or else if the current tab is modified:
    1. Ask user to save the file. Options YES / NO / CANCEL are given.
    2. If the user selected YES:
      1. Show the save file dialog (assuming the case of new file)
      2. If the user proceeds to save the file, then delete the tab page.
      3. If the user clicks on cancel in the save file dialog, veto the event.
    3. If the user selected NO, then delete the tab page.
    4. If the user selected CANCEL, then veto the event.
Now it looks fine, but I want to refactor out. I want to finish stuff in as much less lines as possible. (I know the line if it ain't broke, don't fix it, but am doing it for the sake of learning. Another cause for my love of tidy code might be that I'm a Haskell programmer at work.). Now looking at this, I already handle the 3(b) case in my OnMenuSave event. I want to reuse it.

If the code is synchronous (and it is when I was using ShowModal()) then I could have simply called the OnMenuSave handler that I defined. I also avoid code duplication that way. You might ask me why not use CPS (continuation passing style) and pass callbacks to create the flow, and I'm doing it already.

I'm just asking if there is any alternative that both shows the dialog as sheet and blocks the execution till the dialog result is recorded and returned. If that is the case, the code looks much more tidy in my opinion.

Thanks for your time, and sorry if I didn't make it clear in the first post itself.
User avatar
doublemax
Moderator
Moderator
Posts: 19115
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Blocking window modal dialogs on OS X

Post by doublemax »

Sorry, my mistake, too. I wasn't aware of ShowWindowModal() and ShowWindowModalThenDo(), i thought these were your methods.

But i still see only the solution to veto the event. Then, if you really want to close the window, you call Destroy(), not Close(). If you wanted to close the whole application, call wxTheApp->ExitMainLoop().
I can veto the close event, but then, since the callback happens after an event is sent, the scope is lost.
You just have to pass one more pointer around, e.g. to the particular tab.
Use the source, Luke!
Post Reply