Changeset 264

Show
Ignore:
Timestamp:
09/11/08 09:44:25 (4 months ago)
Author:
tal
Message:
Big changes to the cell view.
- The old Vanilla wrapper for the cell view is gone. Use the new GlyphCollectionView.
- The way the detail pop up is handled has been completely reworked. It no longer uses a glyph representation. It now uses a special Vanilla window. Notes on this are in the documentation.
- The needed pop up windows have been added.
- Fixed lots of small bugs in the cell view.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • packages/defconAppKit/trunk/Lib/defconAppKit/views/glyphCellView.py

    r254 r264  
    77from defconAppKit.notificationObserver import NSObjectNotificationObserver 
    88from defconAppKit.tools.iconCountBadge import addCountBadgeToIcon 
     9from defconAppKit.windows.popUpWindow import InformationPopUpWindow, HUDTextBox, HUDHorizontalLine 
    910 
    1011 
     
    1213selectionColor = NSColor.colorWithCalibratedRed_green_blue_alpha_(.82, .82, .9, 1.0) 
    1314selectionColor = NSColor.colorWithCalibratedRed_green_blue_alpha_(.62, .62, .7, .5) 
    14  
    15  
    1615 
    1716def _makeGlyphCellDragIcon(glyphs): 
     
    8483class DefconAppKitGlyphCellNSView(NSView): 
    8584 
    86     def initWithFrame_cellRepresentationName_detailRepresentationName_(self, 
    87         frame, cellRepresentationName, detailRepresentationName): 
     85    def initWithFrame_cellRepresentationName_detailWindowClass_(self, 
     86        frame, cellRepresentationName, detailWindowClass): 
    8887        self = super(DefconAppKitGlyphCellNSView, self).initWithFrame_(frame) 
    8988        self._cellWidth = 50 
     
    103102 
    104103        self._cellRepresentationName = cellRepresentationName 
    105         self._detailRepresentationName = detailRepresentationName 
    106104        self._cellRepresentationArguments = {} 
    107105 
    108106        self._allowDrag = False 
    109107 
    110         self._glyphDetailMenu = None 
     108        if detailWindowClass is not None: 
     109            self._glyphDetailWindow = detailWindowClass() 
     110        else: 
     111            self._glyphDetailWindow = None 
     112        self._glyphDetailRequiredModifiers = [NSControlKeyMask] 
     113        self._glyphDetailOnMouseDown = True 
     114        self._glyphDetailOnMouseUp = False 
     115        self._glyphDetailOnMouseDragged = True 
     116        self._glyphDetailOnMouseMoved = False 
    111117 
    112118        return self 
     
    173179        self.setNeedsDisplay_(True) 
    174180 
     181    def setGlyphDetailConditions_(self, modifiers=[], mouseDown=False, mouseUp=False, mouseDragged=False, mouseMoved=False): 
     182        """ 
     183        Set the conditions that will be used to determine the visibility of the detail window. 
     184        This is subject to change, so use it at your own risk. 
     185        """ 
     186        self._glyphDetailRequiredModifiers = modifiers 
     187        self._glyphDetailOnMouseDown = mouseDown 
     188        self._glyphDetailOnMouseUp = mouseUp 
     189        self._glyphDetailOnMouseDragged = mouseDragged 
     190        self._glyphDetailOnMouseMoved = mouseMoved 
     191 
     192    def glyphDetailWindow(self): 
     193        return self._glyphDetailWindow 
     194 
    175195    # selection 
    176196 
     
    227247    def dealloc(self): 
    228248        self._unsubscribeFromGlyphs() 
     249        if self._glyphDetailWindow is not None: 
     250            self._glyphDetailWindow = None 
    229251        super(DefconAppKitGlyphCellNSView, self).dealloc() 
    230252 
     
    291313        path.stroke() 
    292314 
    293         if self._glyphDetailMenu is not None: 
    294             shadow = NSShadow.alloc().init() 
    295             shadow.setShadowOffset_((0, -3)) 
    296             shadow.setShadowColor_(NSColor.blackColor()) 
    297             shadow.setShadowBlurRadius_(10.0) 
    298             shadow.set() 
    299  
    300             point, image = self._getPositionForGlyphDetailMenu() 
    301             image.drawAtPoint_fromRect_operation_fraction_( 
    302                 point, ((0, 0), image.size()), NSCompositeSourceOver, 1.0) 
    303  
    304     def _getPositionForGlyphDetailMenu(self): 
    305         (left, top), image = self._glyphDetailMenu 
    306         width, height = image.size() 
    307         right = left + width 
    308         bottom = top + height 
    309  
    310         (visibleLeft, visibleTop), (visibleWidth, visibleHeight) = self.superview().documentVisibleRect() 
    311         visibleRight = visibleLeft + visibleWidth 
    312         visibleBottom = visibleTop + visibleHeight 
    313  
    314         if visibleBottom < bottom: 
    315             bottom = visibleBottom 
    316             top = bottom - height 
    317         if visibleTop > top: 
    318             top = visibleTop 
    319  
    320         if visibleRight < right: 
    321             right = visibleRight 
    322             left = right - width 
    323         if visibleLeft > left: 
    324             left = visibleLeft 
    325  
    326         return (left, top), image 
    327  
    328315    # --------- 
    329316    # Selection 
     
    357344 
    358345    def mouseDown_(self, event): 
    359         self._mouseSelection(event, mouseDown=True) 
     346        found = self._findGlyphForEvent(event) 
     347        self._mouseSelection(event, found, mouseDown=True) 
     348        self._handleDetailWindow(event, found, mouseDown=True) 
    360349        if event.clickCount() > 1: 
    361350            vanillaWrapper = self.vanillaWrapper() 
     
    365354 
    366355    def mouseDragged_(self, event): 
    367         self._mouseSelection(event, mouseDragged=True) 
     356        found = self._findGlyphForEvent(event) 
     357        self._mouseSelection(event, found, mouseDragged=True) 
     358        self._handleDetailWindow(event, found, mouseDragged=True) 
    368359        self.autoscroll_(event) 
    369360 
     361    def mouseMoved_(self, event): 
     362        found = self._findGlyphForEvent(event) 
     363        self._handleDetailWindow(event, found, mouseMoved=True) 
     364 
    370365    def mouseUp_(self, event): 
    371         self._mouseSelection(event, mouseUp=True) 
     366        found = self._findGlyphForEvent(event) 
     367        self._mouseSelection(event, found, mouseUp=True) 
     368        self._handleDetailWindow(event, found, mouseUp=True) 
    372369        if self._selection != self._oldSelection: 
    373370            vanillaWrapper = self.vanillaWrapper() 
     
    375372                vanillaWrapper._selectionCallback(vanillaWrapper) 
    376373        del self._oldSelection 
    377         if self._glyphDetailMenu is not None: 
    378             self._glyphDetailMenu = None 
    379             self.setNeedsDisplay_(True) 
    380  
    381     def _mouseSelection(self, event, mouseDown=False, mouseDragged=False, mouseUp=False): 
    382         if mouseDown: 
    383             self._oldSelection = set(self._selection) 
    384  
     374 
     375    def _findGlyphForEvent(self, event): 
    385376        eventLocation = event.locationInWindow() 
    386377        mouseLocation = self.convertPoint_fromView_(eventLocation, None) 
    387  
    388378        found = None 
    389  
    390379        for rect, index in self._clickRectsToIndex.items(): 
    391380            if NSPointInRect(mouseLocation, rect): 
    392381                found = index 
    393382                break 
     383        return found 
     384 
     385    def _handleDetailWindow(self, event, found, mouseDown=False, mouseMoved=False, mouseDragged=False, mouseUp=False, inDragAndDrop=False): 
     386        # no window 
     387        if self._glyphDetailWindow is None: 
     388            return 
     389        # determine show/hide 
     390        shouldBeVisible = True 
     391        eventLocation = event.locationInWindow() 
     392        mouseLocation = self.convertPoint_fromView_(eventLocation, None) 
     393        ## drag and drop 
     394        if inDragAndDrop: 
     395            shouldBeVisible = False 
     396        ## modifiers 
     397        modifiers = event.modifierFlags() 
     398        for modifier in self._glyphDetailRequiredModifiers: 
     399            if not modifiers & modifier: 
     400                shouldBeVisible = False 
     401                break 
     402        ## mouse conditions 
     403        haveMouseCondition = False 
     404        requireMouseCondition = True in (self._glyphDetailOnMouseDown, self._glyphDetailOnMouseUp, self._glyphDetailOnMouseMoved, self._glyphDetailOnMouseDragged) 
     405        if not requireMouseCondition: 
     406            haveMouseCondition = True 
     407        else: 
     408            if self._glyphDetailOnMouseDown and mouseDown: 
     409                haveMouseCondition = True 
     410            elif self._glyphDetailOnMouseUp and mouseUp: 
     411                haveMouseCondition = True 
     412            elif self._glyphDetailOnMouseMoved and mouseMoved: 
     413                haveMouseCondition = True 
     414            elif self._glyphDetailOnMouseDragged and mouseDragged: 
     415                haveMouseCondition = True 
     416        if not haveMouseCondition: 
     417            shouldBeVisible = False 
     418        ## glyph hit 
     419        if not found: 
     420            shouldBeVisible = False 
     421        ## mouse position is visible 
     422        if not NSPointInRect(mouseLocation, self.visibleRect()): 
     423            shouldBeVisible = False 
     424        # set the position 
     425        if shouldBeVisible: 
     426            x, y = eventLocation 
     427            windowX, windowY = event.window().frame().origin 
     428            detailX = windowX + x 
     429            detailY = windowY + y 
     430            glyph = self._glyphs[found] 
     431            self._glyphDetailWindow.setPositionNearCursor((detailX, detailY)) 
     432            self._glyphDetailWindow.set(glyph) 
     433            if not self._glyphDetailWindow.isVisible(): 
     434                self._glyphDetailWindow.show() 
     435        else: 
     436            self._glyphDetailWindow.hide() 
     437 
     438    def _mouseSelection(self, event, found, mouseDown=False, mouseDragged=False, mouseUp=False, mouseMoved=False): 
     439        if mouseDown: 
     440            self._oldSelection = set(self._selection) 
     441        if found is None: 
     442            return 
    394443 
    395444        modifiers = event.modifierFlags() 
     
    398447        optionDown = modifiers & NSAlternateKeyMask 
    399448        controlDown = modifiers & NSControlKeyMask 
    400  
    401         # turn off glyph detail menu if necessary 
    402         if (not controlDown or found is None) and self._glyphDetailMenu is not None: 
    403             self._glyphDetailMenu = None 
    404             self.setNeedsDisplay_(True) 
    405  
    406         if found is None: 
    407             return 
    408449 
    409450        # dragging 
     
    414455                self._beginDrag(event) 
    415456                return 
    416  
     457        # selecting 
    417458        newSelection = None 
    418  
    419         # detail menu 
    420         if controlDown and found is not None and self._detailRepresentationName is not None: 
    421             newSelection = set([found]) 
    422             x, y = mouseLocation 
    423             x += 10 
    424             y -= 10 
    425             glyph = self._glyphs[found] 
    426             self._glyphDetailMenu = ((int(x), int(y)), glyph.getRepresentation(self._detailRepresentationName)) 
    427             self.setNeedsDisplay_(True) 
    428         # selecting 
    429         elif commandDown: 
     459        if commandDown: 
    430460            if found is None: 
    431461                return 
     
    455485            else: 
    456486                newSelection = set([found]) 
    457  
    458487        if newSelection is not None: 
    459488            self._selection = newSelection 
     
    640669 
    641670    def _beginDrag(self, event): 
     671        # hide the detail window 
     672        self._handleDetailWindow(event=event, found=None, inDragAndDrop=True) 
     673        # prep 
    642674        indexes = [i for i in sorted(self._selection)] 
    643675        image = _makeGlyphCellDragIcon([self._glyphs[i] for i in self._selection]) 
     
    747779 
    748780 
    749 class GlyphCellView(vanilla.ScrollView): 
    750  
    751     def __init__(self, posSize, 
    752         selectionCallback=None, doubleClickCallback=None, deleteCallback=None, dropCallback=None, 
    753         cellRepresentationName="defconAppKitGlyphCell", detailRepresentationName="defconAppKitGlyphCellDetail", 
    754         autohidesScrollers=True, selfWindowDropSettings=None, selfDocumentDropSettings=None, 
    755         selfApplicationDropSettings=None, otherApplicationDropSettings=None, allowDrag=False, 
    756         dragAndDropType="DefconAppKitSelectedGlyphIndexesPboardType"): 
    757         self._glyphCellView = DefconAppKitGlyphCellNSView.alloc().initWithFrame_cellRepresentationName_detailRepresentationName_( 
    758             ((0, 0), (400, 400)), cellRepresentationName, detailRepresentationName) 
    759         self._glyphCellView.vanillaWrapper = weakref.ref(self) 
    760         super(GlyphCellView, self).__init__(posSize, self._glyphCellView, hasHorizontalScroller=False, autohidesScrollers=autohidesScrollers, backgroundColor=backgroundColor) 
    761         self._glyphCellView.subscribeToScrollViewFrameChange_(self._nsObject) 
    762  
    763         if dropCallback is not None: 
    764             from warnings import warn 
    765             warn(DeprecationWarning("dropCallback is deprecated. Use the new drop attributes.")) 
    766             selfWindowDropSettings = dict(operation=NSDragOperationCopy, callback=self._deprecatedDropCallback) 
    767             selfDocumentDropSettings = dict(operation=NSDragOperationCopy, callback=self._deprecatedDropCallback) 
    768             selfApplicationDropSettings = dict(operation=NSDragOperationCopy, callback=self._deprecatedDropCallback) 
    769             otherApplicationDropSettings = dict(operation=NSDragOperationCopy, callback=self._deprecatedDropCallback) 
    770         for i in (selfWindowDropSettings, selfDocumentDropSettings, selfApplicationDropSettings, otherApplicationDropSettings): 
    771             if i is not None: 
    772                 i["type"] = dragAndDropType 
    773         for i in (selfWindowDropSettings, selfDocumentDropSettings, selfApplicationDropSettings, otherApplicationDropSettings): 
    774             if i is not None: 
    775                 self._glyphCellView.registerForDraggedTypes_([dragAndDropType]) 
    776                 break 
    777         self._selfWindowDropSettings = selfWindowDropSettings 
    778         self._selfDocumentDropSettings = selfDocumentDropSettings 
    779         self._otherApplicationDropSettings = selfApplicationDropSettings 
    780         self._otherApplicationDropSettings = otherApplicationDropSettings 
    781         self._glyphCellView.setAllowsDrag_(allowDrag) 
    782         self._dragAndDropType = dragAndDropType 
    783         # callbacks 
    784         self._dropCallback = dropCallback 
    785         self._selectionCallback = selectionCallback 
    786         self._doubleClickCallback = doubleClickCallback 
    787         self._deleteCallback = deleteCallback 
    788         # storage 
    789         self._glyphs = [] 
    790  
    791     def _breakCycles(self): 
    792         if hasattr(self, "_glyphCellView"): 
    793             self._glyphCellView.unsubscribeToScrollViewFrameChange_(self._nsObject) 
    794             del self._glyphCellView.vanillaWrapper 
    795             del self._glyphCellView 
    796         self._selectionCallback = None 
    797         self._doubleClickCallback = None 
    798         self._deleteCallback = None 
    799         super(GlyphCellView, self)._breakCycles() 
    800  
    801     def _removeSelection(self): 
    802         if self._deleteCallback is not None: 
    803             self._deleteCallback(self) 
    804  
    805     def _deprecatedDropCallback(self, sender, dropInfo): 
    806         source = dropInfo["source"] 
    807         indexes = [int(i) for i in dropInfo["data"]] 
    808         if isinstance(source, vanilla.VanillaBaseObject): 
    809             glyphs = [source[i] for i in indexes] 
    810         else: 
    811             glyphs = source.getGlyphsAtIndexes_(indexes) 
    812         return self._dropCallback(self, glyphs, not dropInfo["isProposal"]) 
    813  
    814     def getGlyphCellView(self): 
    815         return self._glyphCellView 
    816  
    817     def __getitem__(self, index): 
    818         return self._glyphs[index] 
    819  
    820     def get(self): 
    821         return list(self._glyphs) 
    822  
    823     def set(self, glyphs): 
    824         self._glyphCellView.setGlyphs_(glyphs) 
    825         self._glyphs = glyphs 
    826  
    827     def setCellSize(self, (width, height)): 
    828         self._glyphCellView.setCellSize_((width, height)) 
    829  
    830     def getSelection(self): 
    831         return self._glyphCellView.getSelection() 
    832  
    833     def setSelection(self, selection): 
    834         self._glyphCellView.setSelection_(selection) 
    835  
    836     def setCellRepresentationArguments(self, **kwargs): 
    837         self._glyphCellView.setCellRepresentationArguments_(**kwargs) 
    838  
    839     def getCellRepresentationArguments(self): 
    840         return self._glyphCellView.getCellRepresentationArguments() 
    841  
     781# ------------------------- 
     782# Information Pop Up Window 
     783# ------------------------- 
     784 
     785 
     786class GlyphInformationPopUpWindow(InformationPopUpWindow): 
     787 
     788    def __init__(self): 
     789        posSize = (200, 280) 
     790        super(GlyphInformationPopUpWindow, self).__init__(posSize) 
     791        self.glyphView = GlyphInformationGlyphView((5, 5, -5, 145)) 
     792 
     793        self.line = HUDHorizontalLine((0, 160, -0, 1)) 
     794 
     795        titleWidth = 100 
     796        entryLeft = 105 
     797        self.nameTitle = HUDTextBox((0, 170, titleWidth, 17), "Name:", alignment="right") 
     798        self.name = HUDTextBox((entryLeft, 170, -5, 17), "") 
     799        self.unicodeTitle = HUDTextBox((0, 190, titleWidth, 17), "Unicode:", alignment="right") 
     800        self.unicode = HUDTextBox((entryLeft, 190, -5, 17), "") 
     801        self.widthTitle = HUDTextBox((0, 210, titleWidth, 17), "Width:", alignment="right") 
     802        self.width = HUDTextBox((entryLeft, 210, -5, 17), "") 
     803        self.leftMarginTitle = HUDTextBox((0, 230, titleWidth, 17), "Left Margin:", alignment="right") 
     804        self.leftMargin = HUDTextBox((entryLeft, 230, -5, 17), "") 
     805        self.rightMarginTitle = HUDTextBox((0, 250, titleWidth, 17), "Right Margin:", alignment="right") 
     806        self.rightMargin = HUDTextBox((entryLeft, 250, -5, 17), "") 
     807 
     808    def set(self, glyph): 
     809        # name 
     810        name = glyph.name 
     811        # unicode 
     812        uni = glyph.unicode 
     813        if uni is None: 
     814            uni = "" 
     815        else: 
     816            uni = hex(uni)[2:].upper() 
     817            if len(uni) < 4: 
     818                uni = uni.zfill(4) 
     819        # width 
     820        width = glyph.width 
     821        if width is None: 
     822            width = 0 
     823        width = round(width, 3) 
     824        if width == int(width): 
     825            width = int(width) 
     826        # left margin 
     827        leftMargin = glyph.leftMargin 
     828        if leftMargin is None: 
     829            leftMargin = 0 
     830        leftMargin = round(leftMargin, 3) 
     831        if leftMargin == int(leftMargin): 
     832            leftMargin = int(leftMargin) 
     833        # right margin 
     834        rightMargin = glyph.rightMargin 
     835        if rightMargin is None: 
     836            rightMargin = 0 
     837        rightMargin = round(rightMargin, 3) 
     838        if rightMargin == int(rightMargin): 
     839            rightMargin = int(rightMargin) 
     840        # set 
     841        self.name.set(name) 
     842        self.unicode.set(uni) 
     843        self.width.set(width) 
     844        self.leftMargin.set(leftMargin) 
     845        self.rightMargin.set(rightMargin) 
     846        self.glyphView.set(glyph) 
     847        self._window.invalidateShadow() 
     848 
     849 
     850class DefconAppKitGlyphInformationNSView(NSView): 
     851 
     852    def setGlyph_(self, glyph): 
     853        self._glyph = glyph 
     854        self.setNeedsDisplay_(True) 
     855 
     856    def drawRect_(self, rect): 
     857        if not hasattr(self, "_glyph"): 
     858            return 
     859        inset = 10 
     860        bounds = self.bounds() 
     861        bounds = NSInsetRect(bounds, inset, inset) 
     862        vWidth, vHeight = bounds.size 
     863        glyph = self._glyph 
     864        font = glyph.getParent() 
     865        if font is None: 
     866            upm = 1000 
     867            descender = -250 
     868        else: 
     869            upm = font.info.unitsPerEm 
     870            descender = font.info.descender 
     871        scale = vHeight / upm 
     872        centerOffset = (vWidth - (glyph.width * scale)) / 2 
     873        transform = NSAffineTransform.transform() 
     874        transform.translateXBy_yBy_(centerOffset+inset, inset) 
     875        transform.scaleBy_(scale) 
     876        transform.translateXBy_yBy_(0, -descender) 
     877        transform.concat() 
     878        NSColor.whiteColor().set() 
     879        path = glyph.getRepresentation("NSBezierPath") 
     880        path.fill() 
     881 
     882 
     883class GlyphInformationGlyphView(vanilla.VanillaBaseObject): 
     884 
     885    def __init__(self, posSize): 
     886        self._setupView(DefconAppKitGlyphInformationNSView, posSize) 
     887 
     888    def set(self, glyph): 
     889        self._nsObject.setGlyph_(glyph) 
     890 
  • packages/defconAppKit/trunk/Lib/defconAppKit/views/glyphCollectionView.py

    r256 r264  
    33import vanilla 
    44from defconAppKit.views.placardScrollView import DefconAppKitPlacardNSScrollView, PlacardSegmentedButton 
    5 from defconAppKit.views.glyphCellView import DefconAppKitGlyphCellNSView, gridColor 
     5from defconAppKit.views.glyphCellView import DefconAppKitGlyphCellNSView, gridColor, GlyphInformationPopUpWindow 
    66 
    77 
     
    4343    The representation name used to fetch the cell representations. 
    4444 
    45     cellDetailRepresentationName 
    46     The representation name used for showing the detail popup in cell mode. 
     45    glyphDetailWindowClass 
     46    A window class to use when the user control-clicks a cell. This must be a 
     47    subclass of vanilla.Window and it must have the following methods: 
     48        window.set(glyph) 
     49        window.setPosition((x, y)) 
    4750 
    4851    selectionCallback, doubleClickCallback, deleteCallback, editCallback 
     
    7174 
    7275    def __init__(self, posSize, initialMode="cell", listColumnDescriptions=None, listShowColumnTitles=False, 
    73         cellRepresentationName="defconAppKitGlyphCell", cellDetailRepresentationName="defconAppKitGlyphCellDetail"
     76        cellRepresentationName="defconAppKitGlyphCell", glyphDetailWindowClass=GlyphInformationPopUpWindow
    7477        selectionCallback=None, doubleClickCallback=None, deleteCallback=None, editCallback=None, 
    7578        enableDelete=False, 
     
    131134            self._orderedListKeys.append(key) 
    132135        ## set up the cell view 
    133         self._glyphCellView = DefconAppKitGlyphCellNSView.alloc().initWithFrame_cellRepresentationName_detailRepresentationName_( 
    134             ((0, 0), (400, 400)), cellRepresentationName, cellDetailRepresentationName
     136        self._glyphCellView = DefconAppKitGlyphCellNSView.alloc().initWithFrame_cellRepresentationName_detailWindowClass_( 
     137            ((0, 0), (400, 400)), cellRepresentationName, glyphDetailWindowClass
    135138        self._glyphCellView.vanillaWrapper = weakref.ref(self) 
    136139        self._glyphCellView.subscribeToScrollViewFrameChange_(self._nsObject)