robotools / vanilla

A Pythonic wrapper around Cocoa.
MIT License
78 stars 28 forks source link

adding ModalWindow #126

Closed typemytype closed 11 months ago

typemytype commented 4 years ago

dialogKit had a ModalWindow, which is nice to have in some occasions

this can be added to vanillaWindow.py

from AppKit import NSModalPanelWindowLevel
from vanilla import Window

class ModalWindow(Window):

    nsWindowLevel = NSModalPanelWindowLevel

    def __init__(self, *args, **kwargs):
        kwargs["closable"] = False
        kwargs["miniaturizable"] = False
        self._doCenter = True
        if "center" in kwargs:
            self._doCenter = kwargs["center"]
            del kwargs["center"]

        super(ModalWindow, self).__init__(*args, **kwargs)

    def open(self):
        super(ModalWindow, self).open()
        if self._doCenter:
            self.center()
        NSApp().runModalForWindow_(self._window)

    def windowWillClose_(self, notification):
        super(ModalWindow, self).windowWillClose_(notification)
        NSApp().stopModal()
colinmford commented 4 years ago

Yeah, I think this will be pretty useful!

justvanrossum commented 4 years ago

Note that in general, modal windows are considered very poor UI design.

typemytype commented 4 years ago

True... its to have alternative for the archived dialogKit.ModalDialog.

sheet are also modal and there are cases where a modal window is a nice option.

justvanrossum commented 4 years ago

A sheet is only modal relative to its parent window.

A modal window is only "a nice option" if the code really can't be changed to support a non-modal UI.

A ModalDialog has no place in a modern application — ~that's probably why it was archived~.

typemytype commented 4 years ago

dialogKit is archived in favour of vanilla, to old to much FL code in there

justvanrossum commented 4 years ago

Ah of course, I forgot.

gferreira commented 4 years ago

Pop Up Tools is very nice, and uses a modal window…

justvanrossum commented 4 years ago

Pop Up Tools is very nice, and uses a modal window…

That's not a modal window at all, it's like a contextual menu. If you click somewhere else it disappears. It doesn't block you from doing something else.

gferreira commented 4 years ago

@justvanrossum I think the idea is that it doesn’t disappear… see Make the window better

typemytype commented 4 years ago

An frequent used example of a modal dialog is an Open panel. see https://developer.apple.com/design/human-interface-guidelines/macos/windows-and-views/dialogs/

I indicates a higher level of user attention: "the app cannot do anything until you give it a file to open..."

Its a convenient window class, and annoying to make it each time you needs it.

justvanrossum commented 4 years ago

I read that as "don't disappear if a click occurs inside the window, but outside of a control".

justvanrossum commented 4 years ago

On macOS 10.15 not even the actual Open panel is modal... (On 10.10 it is, I don't know when it changed.)

Modal windows/dialogs are a remnant of '80s & '90s UI design and should be avoided at all cost.

justvanrossum commented 4 years ago

"App-modal" I should say. There's obviously use for document-modal or window-modal windows, but those are "sheets" and we already have them.

gferreira commented 4 years ago

there are valid use cases for modal dialogs, see Modal & Nonmodal Dialogs: When (& When Not) to Use Them

Modal dialogs interrupt users and demand an action. They are appropriate when user’s attention needs to be directed toward important information.

the most reasonable course of action here seems to be:

typesupply commented 3 years ago

I was reading the latest release notes and noticed that sheets look like modal windows in macOS 11.

typesupply commented 11 months ago

I added the version that I created for ezui that was, in turn, based on the one in RoboFont.

import vanilla

class Test:

    def __init__(self):
        self.w = vanilla.ModalWindow((200, 200))
        self.w.b = vanilla.Button((10, 10, -10, 20), "Close", callback=self.closeCallback)
        self.w.open()

    def closeCallback(self, sender):
        self.w.close()

Test()