aboutsummaryrefslogtreecommitdiffstats
path: root/lvc/widgets/osx/contextmenu.py
blob: 6fd932ccf799690485c9a82a762b535f7491388d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
from AppKit import *
from objc import nil

from .base import Widget


class ContextMenuHandler(NSObject):
    def initWithCallback_widget_i_(self, callback, widget, i):
        self = super(ContextMenuHandler, self).init()
        self.callback = callback
        self.widget = widget
        self.i = i
        return self

    def handleMenuItem_(self, sender):
        self.callback(self.widget, self.i)


class MiroContextMenu(NSMenu):
    # Works exactly like NSMenu, except it keeps a reference to the menu
    # handler objects.
    def init(self):
        self = super(MiroContextMenu, self).init()
        self.handlers = set()
        return self

    def addItem_(self, item):
        if isinstance(item.target(), ContextMenuHandler):
            self.handlers.add(item.target())
        return NSMenu.addItem_(self, item)


class ContextMenu(object):

    def __init__(self, options):
        super(ContextMenu, self).__init__()
        self.menu = MiroContextMenu.alloc().init()
        for i, item_info in enumerate(options):
            if item_info is None:
                nsitem = NSMenuItem.separatorItem()
            else:
                label, callback = item_info
                nsitem = NSMenuItem.alloc().init()
                font_size = NSFont.systemFontSize()
                font = NSFont.fontWithName_size_("Lucida Sans Italic",
                                                 font_size)
                if font is None:
                    font = NSFont.systemFontOfSize_(font_size)
                    attributes = {NSFontAttributeName: font}
                    attributed_label = \
                        NSAttributedString.alloc().initWithString_attributes_(
                            label, attributes)
                    nsitem.setAttributedTitle_(attributed_label)
                else:
                    nsitem.setTitle_(label)
                if isinstance(callback, list):
                    submenu = ContextMenu(callback)
                    self.menu.setSubmenu_forItem_(submenu.menu, nsitem)
                else:
                    handler = \
                        ContextMenuHandler.alloc().initWithCallback_widget_i_(
                            callback, self, i)
                    nsitem.setTarget_(handler)
                    nsitem.setAction_('handleMenuItem:')
            self.menu.addItem_(nsitem)

    def popup(self):
        # support for non-window based popups thanks to
        # http://stackoverflow.com/questions/9033534/how-can-i-pop-up-nsmenu-at-mouse-cursor-position
        location = NSEvent.mouseLocation()
        frame = NSMakeRect(location.x, location.y, 200, 200)
        window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(
            frame,
            NSBorderlessWindowMask,
            NSBackingStoreBuffered,
            NO)
        window.setAlphaValue_(0)
        window.makeKeyAndOrderFront_(NSApp)
        location_in_window = window.convertScreenToBase_(location)
        event = NSEvent.mouseEventWithType_location_modifierFlags_timestamp_windowNumber_context_eventNumber_clickCount_pressure_(
            NSLeftMouseDown,
            location_in_window,
            0,
            0,
            window.windowNumber(),
            nil,
            0,
            0,
            0)
        NSMenu.popUpContextMenu_withEvent_forView_(self.menu,
                                                   event,
                                                   window.contentView())