2017-06-13 11 views
8

Je travaille sur une application de barre de menu et je définis une vue personnalisée à l'aide de la propriété view de NSMenuItem.NSMenuItem avec vue personnalisée ne reçoit pas les événements de souris

La vue s'affiche correctement, mais je ne parviens pas à recevoir des événements de type clic de souris pour les éléments de menu ayant des sous-menus ouverts.

Dans cette capture d'écran, j'ai ajouté un bouton à chaque élément. Les 3 boutons les plus à droite fonctionnent correctement, mais ceux des menus parents ne reçoivent aucun clic.

Screenshot

J'ai essayé un tas de choses, y compris:

  • Essayer de capturer des événements de souris à l'aide des mouseUp et mouseDown méthodes
  • Faire la NSWindow pour la clé de vue personnalisée lorsque la la souris entre dans cette vue
  • Ajout de moniteurs globaux et locaux pour NSEvents

... mais en vain

Même sans l'approche d'ajouter un bouton, je ne peux pas reproduire le comportement par défaut d'une norme NSMenuItem, comme le rappel target-action pour la NSMenuItem ne soit pas appelé si il a une vue personnalisée. (et je ne peux recevoir aucun événement click pour l'appeler moi-même)

En théorie, cela devrait être possible, car je suis capable de sélectionner des menus qui ont des sous-menus ouverts en utilisant le NSMenuItem par défaut (pas de vue personnalisée).

Quelqu'un peut-il aider?

Merci

+2

Pourriez-vous poster le code pour quand vous ajoutez les éléments de menu? De votre image, il semble que vous ajoutiez une vue (en haut) à votre élément de menu, au lieu de faire de l'élément de menu une vue personnalisée. Cela semble être une question intéressante. – Farini

Répondre

4

Je mis en place un projet de test comme le vôtre, avec NSButton s comme le view pour les éléments de menu, et vu le même comportement que vous voyez. C'est intrigant en effet. Si vous sous-classez NSApplication et remplacez sa méthode -sendEvent:, en ajoutant un journal pour voir quels événements passent par le mécanisme, vous constaterez que -sendEvent: n'est jamais réellement appelé lorsque vous cliquez sur l'un des éléments de menu, même ceux fonctionnent. N'est-ce pas bizarre? Donc, la prochaine chose à essayer est de sous-classer NSButton, ajouter un remplacement pour -mouseDown:, et y mettre un point d'arrêt. Effectivement, le point d'arrêt n'est jamais atteint pour l'élément avec le sous-menu ouvert, mais il est frappé pour les autres. Et quand nous le faisons, l'est backtrace:

(lldb) bt 
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 
    * frame #0: 0x0000000100002fa0 menutest`MyButton.mouseDown(event=0x0000608000121900, self=0x0000600000140a50) at AppDelegate.swift:33 
    frame #1: 0x000000010000303c menutest`@objc MyButton.mouseDown(with:) at AppDelegate.swift:0 
    frame #2: 0x00007fffa9f6724f AppKit`-[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 6341 
    frame #3: 0x00007fffa9f63a6c AppKit`-[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 1942 
    frame #4: 0x00007fffa9f62f0a AppKit`-[NSWindow(NSEventRouting) sendEvent:] + 541 
    frame #5: 0x00007fffa9a2328d AppKit`-[NSCarbonWindow sendEvent:] + 118 
    frame #6: 0x00007fffa9a20261 AppKit`NSMenuItemCarbonEventHandler + 10597 
    frame #7: 0x00007fffab0acd85 HIToolbox`DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) + 1708 
    frame #8: 0x00007fffab0abff6 HIToolbox`SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) + 428 
    frame #9: 0x00007fffab0c1d14 HIToolbox`SendEventToEventTarget + 40 
    frame #10: 0x00007fffab0ea7df HIToolbox`ToolboxEventDispatcherHandler(OpaqueEventHandlerCallRef*, OpaqueEventRef*, void*) + 2503 
    frame #11: 0x00007fffab0ad17a HIToolbox`DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) + 2721 
    frame #12: 0x00007fffab0abff6 HIToolbox`SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) + 428 
    frame #13: 0x00007fffab0c1d14 HIToolbox`SendEventToEventTarget + 40 
    frame #14: 0x00007fffab12e928 HIToolbox`IsUserStillTracking(MenuSelectData*, unsigned char*) + 1658 
    frame #15: 0x00007fffab255dc4 HIToolbox`TrackMenuCommon(MenuSelectData&, unsigned char*, SelectionData*, MenuResult*, MenuResult*) + 1664 
    frame #16: 0x00007fffab13a223 HIToolbox`MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*) + 554 
    frame #17: 0x00007fffab139f66 HIToolbox`_HandleMenuSelection2 + 460 
    frame #18: 0x00007fffa97ee368 AppKit`_NSHandleCarbonMenuEvent + 239 
    frame #19: 0x00007fffa9a68702 AppKit`_DPSEventHandledByCarbon + 54 
    frame #20: 0x00007fffa9de90c5 AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 963 
    frame #21: 0x00007fffa96623db AppKit`-[NSApplication run] + 926 
    frame #22: 0x00007fffa962ce0e AppKit`NSApplicationMain + 1237 
    frame #23: 0x00000001000035fd menutest`main at AppDelegate.swift:13 
    frame #24: 0x00007fffc12fc235 libdyld.dylib`start + 1 

Comme vous pouvez le voir, les événements ne sont pas Distribuée par le biais du mécanisme de distribution d'événements Cocoa car les menus sont en fait de carbone. C'est vrai, beaucoup de ces API et sous-systèmes Carbon censés avoir été supprimés lors de la transition vers le 64 bits sont en réalité encore assez vivants; ils sont juste API privée maintenant. Nous ne peut pas les utiliser en mode 64 bits, mais Apple est sûr, et l'ensemble du système de menu est toujours mis en œuvre sur le modèle d'événement Carbon. Parce que les développeurs tiers peuvent avoir à réécrire, disons, Photoshop à partir de zéro, mais le code de gestion de menu que quelqu'un a écrit en 1997 est trop précieux pour abandonner, je suis sûr que vous êtes d'accord.Quoi qu'il en soit, j'ai fait un petit test en changeant -[NSCarbonWindow sendEvent:], la première méthode Objective-C dans ce backtrace (autre que le très haut niveau), pour voir si elle était appelée quand on cliquait sur l'item du sous-menu et ce n'est pas le cas. Donc, si je devais deviner, je dirais que le problème réside dans le gestionnaire d'événements Carbon. Eh bien, cela peut être un peu pénible à l'arrière, mais bon, pas de problème! Nous pouvons contourner ce problème en passant au niveau Carbone et en installant notre propre gestionnaire d'événements Carbon. D'accord, retroussez vos manches, faisons 0-

Oh, c'est vrai.

Nous ne pouvons pas utiliser ces API en mode 64 bits.

Picard Facepalm

Quoi qu'il en soit, je malheureusement ne pense pas qu'il va y avoir un moyen d'obtenir ce travail à court d'utiliser hacks désagréables à utiliser ce sont des API maintenant privées comme this guy did et risquer la rupture future (sans parler être installé à partir de l'App Store). Ou faire quelque chose vraiment fou comme monkeypatching une de ces fonctions C dans le backtrace, ce qui va probablement être encore pire. Cependant, toute cette question semble digne d'un rapport radar. S'il vous plaît file one with Apple et faites-leur savoir à propos de ce problème, et peut-être qu'ils vont le corriger dans une version future.

EDIT: Il existe en fait une solution, en quelque sorte. Comme une vue attachée à un élément de menu qui n'a pas de sous-menu reçoit les événements de souris que vous attendez, vous pouvez renoncer au réglage submenu et avoir votre vue attraper les événements mouseEntered: et mouseExited: et afficher le menu vous-même, simulant ainsi le sous-menu. Ce n'est pas la solution la plus idéale au monde, mais c'est au moins quelque chose.