Как определить, закрыто ли NSMenu?

Я начал эту кроличью нору, потому что кнопка SwiftUI, предназначенная для всплывающего меню, со стилем .buttonStyle(BorderlessButtonStyle()) не вела себя правильно при переключении со светлого на темный режим.

Разочарованный, я решил просто сделать свою собственную кнопку. Я заметил, что кнопка SwiftUI как бы переключается при появлении всплывающего меню, и поэтому я хочу, чтобы это поведение было и для моей кнопки. Поэтому мне нужно узнать, когда закрывается NSMenu.

Я попробовал ответ, предложенный здесь ⤵︎

class NSMenuDelegateModified: NSObject, NSMenuDelegate, ObservableObject {

    var menu: NSMenu

    @Published var isVisible: Bool?

    internal init(menu: NSMenu) {
        self.menu = menu
        super.init()
        self.menu.delegate = self
    }

    func menuDidClose(_ menu: NSMenu) {
        isVisible = false
    }

    func menuWillOpen(_ menu: NSMenu) {
        isVisible = true
    }
}

Теперь это скажет мне, видимо ли меню внутри класса, но когда я пытаюсь распечатать .isVisible для экземпляра объекта класса, он возвращает только false.

Есть ли способ выяснить это?


person tyirvine    schedule 10.07.2021    source источник


Ответы (1)


Итак, я понял, почему я не могу получить true из .isVisible вне класса! Это потому, что я использую menu.popUp(...), чтобы открыть свое меню.

Оказывается, эта функция приостанавливает выполнение некоторых частей приложения или, возможно, всего основного потока (я действительно не уверен), пока не сможет вернуть состояние ⤵︎

true = user selected something from the menu

false = user hasn't selected anything and the menu closed

Подробнее см. в документации Apple.


Решение

Имея это в виду, это сделало все намного проще! Чтобы переключить кнопку, я мог просто изменить цвет после вызова menu.popUp(...).

Это означало бы, что когда всплывающее меню всплывает, цвет не будет меняться снова, пока из функции не будет возвращено состояние и выполнение не будет возобновлено!

Обновленный ответ

Похоже, я перепутал функциональность, и это оказалось сложнее, чем я ожидал. Вот что у меня получилось ниже ⤵︎

.opacity(pressed ? 1 : 0.6)
.inactiveWindowTap { pressed in

    if popped == nil {

        // Only register mouse ups
        if !pressed {
            popped = menu.popUp(...)
            // Execution pauses here and waits till a state is returned
        }

        self.pressed = pressed
    }

    else if !pressed {
        popped = nil
    }
}
.whenHovered { hovering in
    if hovering == false, popped != nil {
        popped = nil
    }
}

Это решение довольно специфично для моих нужд, поэтому я надеюсь, что сказанное здесь все еще кому-то поможет. Надеюсь, кто-то может придумать лучший ответ, чем этот.

Старый ответ (это неверно и не работает должным образом)

.opacity(pressed ? 1 : 0.6)
.inactiveWindowTap { pressed in

    // Only register mouse ups
    if !pressed {
        // You can use the bool returned from this function but you don't have to.
        menu.popUp(...) // Execution pauses here until a state is found.
    }

    self.pressed = pressed
}
person tyirvine    schedule 10.07.2021
comment
NSMenu при активном отображении меню блокирует RunLoop.Mode.default основного потока, также известного как NSDefaultRunLoopMode. Есть способы обойти использование, используя другой режим цикла выполнения. - person Kamil.S; 21.07.2021
comment
Спасибо за понимание! На данный момент это решение работает, но я могу представить много других случаев, когда это может быть проблемой. - person tyirvine; 21.07.2021