Skip to content

Instantly share code, notes, and snippets.

@typemytype
Last active February 17, 2016 07:27
Show Gist options
  • Save typemytype/6429212 to your computer and use it in GitHub Desktop.
Save typemytype/6429212 to your computer and use it in GitHub Desktop.
A vanilla Popover proposal
from AppKit import *
from vanilla.vanillaBase import _setAttr, _delAttr, VanillaBaseObject
_edgeMap = {
"left" : NSMinXEdge,
"right" : NSMaxXEdge,
"top" : NSMinYEdge,
"bottom" : NSMaxYEdge
}
_popoverBehaviorMap = {
"normal" : 0,
"asMenu" : 1,
"semiAsMenu" : 2
}
class PopoverContentView(NSView):
def _getContentView(self):
return self
class Popover(NSObject):
nsContentViewClass = PopoverContentView
def __new__(cls, *args, **kwargs):
return cls.alloc().init()
def __init__(self, posSize, parentView, edge, relativeRect=None, HUD=False, behavior="normal"):
# transform the parentView to a NSView
if isinstance(parentView, VanillaBaseObject):
parentView = parentView._getContentView()
self._parentView = parentView
# get the edge from the edge map
self._edge = _edgeMap[edge]
# if no relative Rect is given use a NSZeroRect
# so the bounds of the relative view is used
if relativeRect is None:
relativeRect = NSZeroRect
self._relativeRect = relativeRect
self._bindings = {}
w, h = posSize
# setup the content view
self._contentView = self.nsContentViewClass.alloc().initWithFrame_(NSMakeRect(0, 0, w, h))
# create a view controller
self._contentViewController = NSViewController.alloc().init()
# add the view
self._contentViewController.setView_(self._contentView)
# create a NSPopover
self._nsObject = NSPopover.alloc().init()
# set the delegate
self._nsObject.setDelegate_(self)
# set HUD if necessary (black UI)
if HUD:
self._nsObject.setAppearance_(1)
# set the behavior of the popover
self._nsObject.setBehavior_(_popoverBehaviorMap[behavior])
# set the content view controller
self._nsObject.setContentViewController_(self._contentViewController)
def __setattr__(self, attr, value):
if isinstance(value, VanillaBaseObject):
_setAttr(self.nsContentViewClass, self._contentView, attr, value)
else:
super(Popover, self).__setattr__(attr, value)
def __delattr__(self, attr):
if hasattr(self._contentView, attr):
_delAttr(self.nsContentViewClass, self._contentView, attr)
else:
super(Popover, self).__delattr__(attr)
def open(self, view=None, relativeRect=None, edge=None):
if view is not None:
# transform the parentView to a NSView
if isinstance(view, VanillaBaseObject):
view = view._getContentView()
self._parentView = view
if relativeRect is not None:
self._relativeRect = relativeRect
if edge is not None:
self._edge = edge
# open the pop over
self._nsObject.showRelativeToRect_ofView_preferredEdge_(self._relativeRect, self._parentView, self._edge)
def close(self):
# close the pop over
self._nsObject.close()
def resize(self, width, height, animate=True):
self._nsObject.setAnimates_(animate)
self._nsObject.setContentSize_((width, height))
## notifications
def bind(self, event, callback):
# same as in a Window object
if event not in self._bindings:
self._bindings[event] = []
self._bindings[event].append(callback)
def unbind(self, event, callback):
self._bindings[event].remove(callback)
def _alertBindings(self, event):
if hasattr(self, "_bindings"):
if event in self._bindings:
for callback in self._bindings[event]:
return callback(self)
def popoverWillShow_(self, notification):
self._alertBindings("will show")
def popoverDidShow_(self, notification):
self._alertBindings("did show")
def popoverShouldClose_(self, notification):
shouldClose = self._alertBindings("should close")
if shouldClose is None:
shouldClose = True
return shouldClose
def popoverWillClose_(self, notification):
self._alertBindings("will close")
if __name__ == "__main__":
from vanilla import *
from defconAppKit.windows.baseWindow import BaseWindowController
from random import randint
class TestingPopOver(BaseWindowController):
def __init__(self):
self.w = Window((400, 400), "PopOver")
self.w.button = Button((10, 10, 150, 22), "Pop Me Up, Scotty", callback=self.buttonCallback)
self.popover = Popover((200, 200), self.w.button, "top", behavior="semiAsMenu")
self.popover.text = TextBox((10, 10, 100, 22), "Hi")
self.popover.resize = Button((10, -70, 100, 22), "reisze", callback=self.resizeCallback)
self.popover.closeButton = Button((10, -30, 100, 22), "Close", callback=self.closePopoverCallback)
self.w.open()
def buttonCallback(self, sender):
self.popover.open()
def resizeCallback(self, sender):
self.popover.resize(200, randint(200, 400))
def closePopoverCallback(self, sender):
self.popover.close()
from vanilla.test.testTools import executeVanillaTest
executeVanillaTest(TestingPopOver)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment