Created
April 13, 2021 12:49
-
-
Save typesupply/b028fdb502b4a6d1c3d30433f38114bc to your computer and use it in GitHub Desktop.
vanilla.py version 0.0000000000000000000000000000000001, March 25, 2005 2:35 AM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
To do: | |
- auto alignment bugs? | |
- get rid of the frigging background override in List. UGH! | |
- implement all Window methods from W | |
- add all (or at least all feasable) init methods form W | |
- lots of other stuff. | |
- zzzzzzzzzzzz. | |
""" | |
from AppKit import * | |
from Foundation import * | |
def _frameMagic(parentFrame, objFrame): | |
# | |
# NOTE! | |
# | |
# this was adapted from Wbase.Widget._calcbounds. | |
# the doc there states that this algorithm was written by | |
# Peter Kreins and Petr van Blokland and that parts of the | |
# algo are applied for patents by Ericsson, Sweden. | |
# | |
(pL, pB), (pW, pH) = parentFrame | |
(oL, oT), (oW, oH) = objFrame | |
if oW < 0: | |
# right is relative to the parent width | |
oW = pW + oW - oL | |
if oL < 0: | |
# left is relative to the parent width | |
oL = pW + oL | |
if oH < 0: | |
# bottom is relative to parent height | |
oH = pH + oH - oT | |
if oT < 0: | |
# top is relative to the parent height | |
oT = pH + oT | |
return (oL, oT), (oW, oH) | |
def _calcFrame(parentFrame, objFrame): | |
(pL, pB), (pW, pH) = parentFrame | |
(oL, oT), (oW, oH) = _frameMagic(parentFrame, objFrame) | |
oB = pH - oT - oH | |
return (oL, oB), (oW, oH) | |
class Window(object): | |
def __init__(self, posSize, title="", minSize=None, maxSize=None, textured=False): | |
mask = NSTitledWindowMask | |
mask = mask + NSClosableWindowMask | |
mask = mask + NSMiniaturizableWindowMask | |
if minSize or maxSize: | |
mask = mask + NSResizableWindowMask | |
if textured: | |
mask = mask + NSTexturedBackgroundWindowMask | |
# start the window | |
## too magical? | |
if len(posSize) == 2: | |
l = t = 100 | |
w, h = posSize | |
else: | |
l, t, w, h = posSize | |
frame = _calcFrame(NSScreen.mainScreen().visibleFrame(), ((l, t), (w, h))) | |
self._window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_( | |
frame, mask, NSBackingStoreBuffered, False) | |
if minSize is not None: | |
self._window.setMinSize_(minSize) | |
if maxSize is not None: | |
self._window.setMaxSize_(maxSize) | |
# set the basic attributes | |
self._window.setTitle_(title) | |
# make it a normal floating window | |
self._window.setLevel_(NSNormalWindowLevel) | |
self._windowController = NSWindowController.alloc().init() | |
self._windowController.setWindow_(self._window) | |
self._contentView = self._window.contentView() | |
def __setattr__(self, attr, value): | |
if isinstance(value, VanillaBaseObject): | |
# position the object relative to the top, left of the window | |
winFrame = self._contentView.frame() | |
value._setFrame(winFrame) | |
# add the object to the window | |
self._contentView.addSubview_(value._nsObject) | |
super(Window, self).__setattr__(attr, value) | |
def __delattr__(self, attr): | |
raise NotImplementedError, "need to ask Just about this" | |
def open(self, parentWindow=None): | |
if parentWindow is not None: | |
NSApp().beginSheet_modalForWindow_modalDelegate_didEndSelector_contextInfo_( | |
self._window, parentWindow._window, None, None, 0) | |
else: | |
self._windowController.showWindow_(self._window) | |
def close(self): | |
if self._window.isSheet(): | |
NSApp().endSheet_(self._window) | |
self._window.orderOut_(self._windowController) | |
else: | |
self._windowController.close() | |
class VanillaCallbackWrapper(NSObject): | |
def initWithCallback_(self, callback): | |
self = self.init() | |
self.callback = callback | |
return self | |
def action_(self, arg): | |
self.callback() | |
class VanillaBaseObject(object): | |
def __init__(self, nsCls, posSize, callback=None): | |
#self._nsObject = nsCls.alloc().initWithFrame_(((l, t), (w, h))) | |
self._nsObject = nsCls.alloc().init() | |
self._posSize = posSize | |
self._setCallback(callback) | |
self._setAutosizingFromPosSize() | |
def _setCallback(self, callback): | |
if callback is not None: | |
self._target = VanillaCallbackWrapper.alloc().initWithCallback_(callback) | |
self._nsObject.setTarget_(self._target) | |
self._nsObject.setAction_("action:") | |
def _setAutosizingFromPosSize(self): | |
l, t, w, h = self._posSize | |
widthFixed = w > 0 | |
leftFixed = l > 0 | |
rightFixed = w < 0 | |
heightFixed = h > 0 | |
topFixed = t > 0 | |
bottomFixed = h < 0 | |
self._setAutosizing(widthFixed=widthFixed, heightFixed=heightFixed, | |
leftFixed=leftFixed, rightFixed=rightFixed, | |
topFixed=topFixed, bottomFixed=bottomFixed, | |
) | |
def _setAutosizing(self, widthFixed=True, heightFixed=True, | |
leftFixed=True, rightFixed=True, | |
topFixed=True, bottomFixed=True): | |
mask = None | |
if not widthFixed: | |
mask = NSViewWidthSizable | |
if not heightFixed: | |
if mask is None: | |
mask = NSViewHeightSizable | |
else: | |
mask = mask + NSViewHeightSizable | |
if not leftFixed: | |
if mask is None: | |
mask = NSViewMinXMargin | |
else: | |
mask = mask + NSViewMinXMargin | |
if not rightFixed: | |
if mask is None: | |
mask = NSViewMaxXMargin | |
else: | |
mask = mask + NSViewMaxXMargin | |
if not bottomFixed: | |
if mask is None: | |
mask = NSViewMinYMargin | |
else: | |
mask = mask + NSViewMinYMargin | |
if not topFixed: | |
if mask is None: | |
mask = NSViewMaxYMargin | |
else: | |
mask = mask + NSViewMaxYMargin | |
if mask is None: | |
mask = NSViewNotSizable | |
self._nsObject.setAutoresizingMask_(mask) | |
def _setFrame(self, parentFrame): | |
l, t, w, h = self._posSize | |
frame = _calcFrame(parentFrame, ((l, t), (w, h))) | |
self._nsObject.setFrame_(frame) | |
def enable(self, onOff): | |
self._nsObject.setEnabled_(onOff) | |
def show(self, onOff): | |
hidden = onOff == False | |
self._nsObject.setHidden_(hidden) | |
## | |
## Text | |
## | |
class TextBox(VanillaBaseObject): | |
def __init__(self, posSize, text=""): | |
super(TextBox, self).__init__(NSTextField, posSize) | |
self._nsObject.setStringValue_(text) | |
self._nsObject.setDrawsBackground_(False) | |
self._nsObject.setBezeled_(False) | |
self._nsObject.setSelectable_(False) | |
def get(self): | |
return self._nsObject.stringValue() | |
def set(self, value): | |
self._nsObject.setStringValue_(value) | |
class EditText(VanillaBaseObject): | |
def __init__(self, posSize, text="", callback=None, readOnly=False): | |
super(EditText, self).__init__(NSTextField, posSize, callback=callback) | |
self._nsObject.setStringValue_(text) | |
self._nsObject.setDrawsBackground_(True) | |
self._nsObject.setBezeled_(True) | |
selectable = readOnly == False | |
self._nsObject.setSelectable_(selectable) | |
def get(self): | |
return self._nsObject.stringValue() | |
def set(self, value): | |
self._nsObject.setStringValue_(value) | |
class TextEditor(VanillaBaseObject): | |
def __init__(self, posSize, text="", callback=None, readOnly=False): | |
super(TextEditor, self).__init__(NSTextView, posSize, callback=callback) | |
self._nsObject.setString_(text) | |
def get(self): | |
return self._nsObject.stringValue() | |
def set(self, value): | |
self._nsObject.setString_(value) | |
## | |
## Controls | |
## | |
class VanillaBaseButton(VanillaBaseObject): | |
def setTitle(self, title): | |
self._nsObject.setTitle_(title) | |
def getTitle(self): | |
return self._nsObject.getTitle() | |
def set(self, value): | |
raise NotImplementedError | |
def get(self): | |
raise NotImplementedError | |
class Button(VanillaBaseButton): | |
def __init__(self, posSize, title="Button", callback=None): | |
super(Button, self).__init__(NSButton, posSize, callback=callback) | |
self._nsObject.setTitle_(title) | |
self._nsObject.setBezelStyle_(NSRoundedBezelStyle) | |
class CheckBox(VanillaBaseButton): | |
def __init__(self, posSize, title="Checkbox", callback=None, value=False): | |
super(CheckBox, self).__init__(NSButton, posSize, callback=callback) | |
self._nsObject.setTitle_(title) | |
self._nsObject.setButtonType_(NSSwitchButton) | |
def set(self, value): | |
self._nsObject.setState_(value) | |
def get(self): | |
return self._nsObject.state() | |
## | |
## Lists | |
## | |
class VanillaListDataSourceAndDelegate(NSObject): | |
def initWithItems_(self, items): | |
self = self.init() | |
self.setItems_(items) | |
self.callback = None | |
return self | |
def setItems_(self, items): | |
self.items = list(items) | |
def numberOfRowsInTableView_(self, aTableView): | |
return len(self.items) | |
def tableView_objectValueForTableColumn_row_(self, aTableView, aTableColumn, rowIndex): | |
return self.items[rowIndex] | |
def tableView_shouldEditTableColumn_row_(self, aTableView, aTableColumn, rowIndex): | |
return False | |
def tableViewSelectionDidChange_(self, notification): | |
if self.callback is not None: | |
self.callback() | |
class List(VanillaBaseObject): | |
# XXX! | |
""" | |
api idea: | |
if the user wants more than one column, then the items list needs | |
to contain dicts with keys that match the column titles. the deleate can | |
then easily get the correct info. | |
""" | |
def __init__(self, posSize, items=[], columnTitles=[], showColumnTitles=True, callback=None): | |
# don't use super to establish the nsObject | |
# because we will need several of them here | |
self._posSize = posSize | |
self._nsObject = NSScrollView.alloc().init() | |
self._nsObject.setAutohidesScrollers_(True) | |
self._nsObject.setHasHorizontalScroller_(True) | |
self._nsObject.setHasVerticalScroller_(True) | |
self._nsObject.setBorderType_(NSBezelBorder) | |
self._nsObject.setDrawsBackground_(False) | |
# | |
self._tableView = NSTableView.alloc().init() | |
self._nsObject.setDocumentView_(self._tableView) | |
# handle the table columns | |
if columnTitles: | |
for title in columnTitles: | |
column = NSTableColumn.alloc().initWithIdentifier_(title) | |
header = column.headerCell().setTitle_(title) | |
self._tableView.addTableColumn_(column) | |
else: | |
column = NSTableColumn.alloc().initWithIdentifier_("VanillaColumn") | |
self._tableView.addTableColumn_(column) | |
# hide the header | |
if not showColumnTitles or not columnTitles: | |
self._tableView.setHeaderView_(None) | |
self._tableView.setCornerView_(None) | |
# set the table attributes | |
self._tableView.setUsesAlternatingRowBackgroundColors_(True) | |
self._tableView.setRowHeight_(17.0) | |
self._tableView.setAllowsEmptySelection_(True) | |
self._tableView.setAllowsMultipleSelection_(True) | |
self._tableView.setAutoresizesAllColumnsToFit_(True) | |
# | |
self._dataSource = VanillaListDataSourceAndDelegate.alloc().initWithItems_(items) | |
self._tableView.setDataSource_(self._dataSource) | |
self._tableView.setDelegate_(self._dataSource) | |
# do the base object init methods | |
self._setCallback(callback) | |
self._setAutosizingFromPosSize() | |
def _setFrame(self, parentFrame): | |
l, t, w, h = self._posSize | |
frame = _calcFrame(parentFrame, ((l, t), (w, h))) | |
self._tableView.setFrame_(((0, 0), frame[1])) | |
self._nsObject.setFrame_(frame) | |
def _setCallback(self, callback): | |
if callback is not None: | |
self._dataSource.callback = callback | |
### | |
### horribly ineffecient list methods | |
### | |
def __len__(self): | |
return len(self._dataSource.items) | |
def __getitem__(self, index): | |
return self._dataSource.items[index] | |
def __setitem__(self, index, value): | |
self._dataSource.items[index] = item | |
self._tableView.reloadData() | |
def __delitem__(self, index): | |
del self._dataSource.items[index] | |
self._tableView.reloadData() | |
def __getslice__(self, a, b): | |
return self._dataSource.items[a:b] | |
def __delslice__(self, a, b): | |
del self._dataSource.items[a:b] | |
self._tableView.reloadData() | |
def __setslice__(self, a, b, items): | |
self._dataSource.items[a:b] = items | |
self._tableView.reloadData() | |
def append(self, item): | |
self._dataSource.items.append(item) | |
self._tableView.reloadData() | |
def remove(self, item): | |
index = self._dataSource.items.index(item) | |
del self._dataSource.items[index] | |
self._tableView.reloadData() | |
def index(self, item): | |
return self._dataSource.items.index(item) | |
def insert(self, index, item): | |
self._dataSource.items.insert(index, item) | |
self._tableView.reloadData() | |
### | |
def set(self, value): | |
self._dataSource.setItems_(value) | |
self._tableView.reloadData() | |
def get(self): | |
return list(self._dataSource.items) | |
def getSelection(self): | |
return [i for i in self._tableView.selectedRowEnumerator()] | |
def setSelection(self, selection): | |
self._tableView.selectRowIndexes_byExtendingSelection_(NSIndexSet.alloc().init(), False) | |
# ugh. is it possible to make an index set *without* a continuous range? | |
for index in selection: | |
self._tableView.selectRowIndexes_byExtendingSelection_(NSIndexSet.alloc().initWithIndex_(index), True) | |
self._tableView.scrollRowToVisible_(min(selection)) | |
## | |
## Tabs | |
## | |
class _TabItem(VanillaBaseObject): | |
"""This should never be instantiated directly.""" | |
def __init__(self, title): | |
self._nsObject = NSTabViewItem.alloc().initWithIdentifier_(title) | |
self._nsObject.setLabel_(title) | |
def __setattr__(self, attr, value): | |
if isinstance(value, VanillaBaseObject): | |
view = self._nsObject.view() | |
frame = view.frame() | |
value._setFrame(frame) | |
view.addSubview_(value._nsObject) | |
super(_TabItem, self).__setattr__(attr, value) | |
def __delattr__(self, attr): | |
raise NotImplementedError, "need to ask Just about this" | |
class Tabs(VanillaBaseObject): | |
def __init__(self, posSize, titles=["Tab"]): | |
super(Tabs, self).__init__(NSTabView, posSize) | |
self._tabItems = [] | |
for title in titles: | |
tab = _TabItem(title) | |
self._tabItems.append(tab) | |
self._nsObject.addTabViewItem_(tab._nsObject) | |
# hm. best API for this? | |
# maybe it should be more dict like and use the titles as keys? | |
def __getitem__(self, index): | |
return self._tabItems[index] | |
## | |
## Test | |
## | |
import sys | |
_testList = list(sys.path) | |
class Test(object): | |
def __init__(self): | |
self.w = Window((200, 300), "This is a window!", minSize=(100, 100), textured=False) | |
self.w.et = EditText((10, 10, -10, 22), "This is a text entry field", callback=self.editTextCallback) | |
self.w.t = TextBox((10, 42, -10, 20), "This is some text") | |
self.w.b = Button((4, 62, -4, 32), "This is a Button", callback=self.buttonCallback) | |
self.w.cb = CheckBox((10, 95, -10, 20), "This is a checkbox", callback=self.checkboxCallback, value=True) | |
self.w.l = List((10, 125, -10, -10), _testList, callback=self.listCallback) | |
self.w2 = Window((320, 320)) | |
self.w2.tab = Tabs((10, 10, -10, -50), titles=["Tab One", "Tab Two"]) | |
self.w2.tab[0].t = EditText((10, 10, -10, -10), "Check it out! This is a sheet! Oh. And look! It's a tab view!") | |
self.w2.tab[1].t = TextBox((10, 10, -10, -10), "Nothing to see here.") | |
self.w2.b = Button((10, -40, -10, 32), "Close this sheet because it is freaking me out", callback=self.closeSheet) | |
#self.w.te = TextEditor((10, 150, -10, -10), "This is a real text editor. And it is (currently) really broken.") | |
self.w.open() | |
def buttonCallback(self): | |
self.w.t.set("You hit the button!") | |
_testList.reverse() | |
self.w.l.set(_testList) | |
self.w2.open(self.w) | |
def checkboxCallback(self): | |
self.w.t.set("Checkbox value %d!"%self.w.cb.get()) | |
def editTextCallback(self): | |
print self.w.et.get() | |
def listCallback(self): | |
print "selected rows:", self.w.l.getSelection() | |
def closeSheet(self): | |
self.w2.close() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment