#!/usr/bin/env python # --------------------------------------------------------------------------------- # # Pyfa's custom Notebook core python IMPLEMENTATION # # Darriele (homeworld using gmail point com) - 10/27/2010 # Updated: 11/11/2010 # # --------------------------------------------------------------------------------- # import wx import wx.lib.newevent import copy import time import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils from gui import bitmapLoader _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() _PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent() _PageClosing, EVT_NOTEBOOK_PAGE_CLOSING = wx.lib.newevent.NewEvent() PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent() PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent() class VetoAble(): def __init__(self): self.__vetoed = False def Veto(self): self.__vetoed = True def isVetoed(self): return self.__vetoed class NotebookTabChangeEvent(): def __init__(self, old, new): self.__old = old self.__new = new def GetOldSelection(self): return self.__old def GetSelection(self): return self.__new OldSelection = property(GetOldSelection) Selection = property(GetSelection) class PageChanging(_PageChanging, NotebookTabChangeEvent, VetoAble): def __init__(self, old, new): NotebookTabChangeEvent.__init__(self, old, new) _PageChanging.__init__(self) VetoAble.__init__(self) class PageChanged(_PageChanged, NotebookTabChangeEvent): def __init__(self, old, new): NotebookTabChangeEvent.__init__(self, old, new) _PageChanged.__init__(self) class PageClosing(_PageClosing, VetoAble): def __init__(self, i): self.__index = i _PageClosing.__init__(self) VetoAble.__init__(self) self.Selection = property(self.GetSelection) def GetSelection(self): return self.__index class PageAdding(_PageAdding, VetoAble): def __init__(self): _PageAdding.__init__(self) VetoAble.__init__(self) class PFNotebook(wx.Panel): def __init__(self, parent, canAdd = True): wx.Panel.__init__(self, parent, wx.ID_ANY,size = (-1,-1)) self.pages = [] self.activePage = None mainSizer = wx.BoxSizer( wx.VERTICAL ) tabsSizer = wx.BoxSizer( wx.VERTICAL ) self.tabsContainer = PFTabsContainer(self, canAdd = canAdd) tabsSizer.Add( self.tabsContainer, 0, wx.EXPAND ) mainSizer.Add( tabsSizer, 0, wx.EXPAND, 5 ) contentSizer = wx.BoxSizer( wx.VERTICAL ) if 'wxMSW' in wx.PlatformInfo: style = wx.DOUBLE_BORDER else: style = wx.SIMPLE_BORDER self.pageContainer = wx.Panel(self, style = style) self.pageContainer.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) contentSizer.Add( self.pageContainer, 1, wx.EXPAND, 5 ) mainSizer.Add( contentSizer, 1, wx.EXPAND | wx.BOTTOM, 2 ) self.SetSizer( mainSizer ) self.Bind(wx.EVT_SIZE, self.OnSize) self.Layout() def GetPage(self, i): return self.pages[i] def SetPage(self, i, page): if i >= len(self.pages) or i is None or page is None: return oldPage = self.pages[i] self.pages[i] = page if oldPage == self.activePage: oldPage.Destroy() self.activePage = page else: oldPage.Destroy() page.Reparent(self.pageContainer) if self.activePage == page: self.ShowActive() def GetBorders(self): bx = wx.SystemSettings_GetMetric(wx.SYS_BORDER_X) by = wx.SystemSettings_GetMetric(wx.SYS_BORDER_Y) if bx<0: bx = 1 if by<0: by = 1 return (bx,by) def ReplaceActivePage(self, page): self.SetPage(self.GetSelection(), page) def GetSelectedPage(self): return self.activePage def GetPageIndex(self, page): return self.pages.index(page) if page in self.pages else None def GetSelection(self): return self.GetPageIndex(self.activePage) def GetCurrentPage(self): return self.activePage def GetPageCount(self): return len(self.pages) def NextPage(self): cpage = self.GetSelection() if cpage is None: return if cpage < self.GetPageCount() - 1: self.SetSelection(cpage + 1) npage = cpage +1 else: self.SetSelection(0) npage = 0 wx.PostEvent(self, PageChanged(cpage, npage)) def PrevPage(self): cpage = self.GetSelection() if cpage is None: return if cpage > 0: self.SetSelection(cpage - 1) npage = cpage -1 else: self.SetSelection(self.GetPageCount() - 1) npage = self.GetPageCount() - 1 wx.PostEvent(self, PageChanged(cpage, npage)) def AddPage(self, tabWnd = None, tabTitle ="Empty Tab", tabImage = None, showClose = True): if self.activePage: self.activePage.Hide() if not tabWnd: tabWnd = wx.Panel(self) tabWnd.Reparent(self.pageContainer) self.pageContainer.Layout() self.pages.append(tabWnd) self.tabsContainer.AddTab(tabTitle, tabImage, showClose) self.activePage = tabWnd self.ShowActive(True) def SetSelection(self, page): oldsel = self.GetSelection() if oldsel != page: self.activePage.Hide() self.activePage = self.pages[page] self.tabsContainer.SetSelected(page) self.ShowActive() def DeletePage(self, n, internal = False): page = self.pages[n] self.pages.remove(page) page.Destroy() if not internal: self.tabsContainer.DeleteTab(n, True) sel = self.tabsContainer.GetSelected() if sel is not None: self.activePage = self.pages[sel] self.ShowActive() wx.PostEvent(self, PageChanged(-1, sel)) else: self.activePage = None def SwitchPages(self, src, dest, internal = False): self.pages[src], self.pages[dest] = self.pages[dest], self.pages[src] def ShowActive(self, resizeOnly = False): size = self.pageContainer.GetSize() bx, by = self.GetBorders() ww,wh = size ww -= bx * 4 wh -= by * 4 self.activePage.SetSize((ww,wh)) self.activePage.SetPosition((0,0)) if not resizeOnly: self.activePage.Show() self.Layout() def IsActive(self, page): return self.activePage == page def SetPageText(self, i, text, refresh=True): tab = self.tabsContainer.tabs[i] tab.text = text if refresh: self.tabsContainer.AdjustTabsSize() self.Refresh() def SetPageIcon(self, i, icon, refresh=True): tab = self.tabsContainer.tabs[i] tab.tabImg = icon if refresh: self.tabsContainer.AdjustTabsSize() self.Refresh() def SetPageTextIcon(self, i, text=wx.EmptyString, icon=None): self.SetPageText(i, text, False) self.SetPageIcon(i, icon, False) self.tabsContainer.AdjustTabsSize() self.Refresh() def Refresh(self): self.tabsContainer.Refresh() def OnSize(self, event): w,h= self.GetSize() self.tabsContainer.SetSize((w, -1)) self.tabsContainer.UpdateSize() self.tabsContainer.Refresh() self.Layout() if self.activePage: self.ShowActive() event.Skip() class PFTabRenderer: def __init__(self, size = (36,24), text = wx.EmptyString, img = None, inclination = 6 , closeButton = True, fontSize = 14): # tab left/right zones inclination self.ctabLeft = bitmapLoader.getImage("ctableft", "icons") self.ctabMiddle = bitmapLoader.getImage("ctabmiddle", "icons") self.ctabRight = bitmapLoader.getImage("ctabright", "icons") self.ctabClose = bitmapLoader.getImage("ctabclose", "icons") self.leftWidth = self.ctabLeft.GetWidth() self.rightWidth = self.ctabRight.GetWidth() self.middleWidth = self.ctabMiddle.GetWidth() self.closeBtnWidth = self.ctabClose.GetWidth() width, height = size if width < self.leftWidth + self.rightWidth + self.middleWidth: width = self.leftWidth + self.rightWidth + self.middleWidth if height < self.ctabMiddle.GetHeight(): height = self.ctabMiddle.GetHeight() self.inclination = inclination self.text = text self.tabSize = (width, height) self.closeButton = closeButton self.fontSize = fontSize self.selected = False self.closeBtnHovering = False self.tabBitmap = None self.tabBackBitmap = None self.cbSize = 5 self.padding = 4 self.font = wx.FontFromPixelSize((0, self.fontSize), wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.tabImg = img self.position = (0, 0) # Not used internaly for rendering - helper for tab container self.InitTab() def SetPosition(self, position): self.position = position def GetPosition(self): return self.position def GetSize(self): return self.tabSize def SetSize(self, size): otw,oth = self.tabSize w,h = size if w< self.leftWidth + self.rightWidth + self.middleWidth: w = self.leftWidth + self.rightWidth + self.middleWidth if h < self.ctabMiddle.GetHeight(): h = self.ctabMiddle.GetHeight() self.tabSize = (w,h) self.InitTab() def SetSelected(self, sel = True): self.selected = sel self.InitTab() def GetSelected(self): return self.selected def IsSelected(self): return self.selected def ShowCloseButtonHovering(self, hover = True): if self.closeBtnHovering != hover: self.closeBtnHovering = hover self._Render() def GetCloseButtonHoverStatus(self): return self.closeBtnHovering def GetTabRegion(self): nregion = self.CopyRegion(self.tabRegion) nregion.SubtractRegion(self.closeBtnRegion) if self.closeButton else self.tabRegion return nregion def GetCloseButtonRegion(self): return self.CopyRegion(self.closeBtnRegion) def GetMinSize(self): ebmp = wx.EmptyBitmap(1,1) mdc = wx.MemoryDC() mdc.SelectObject(ebmp) mdc.SetFont(self.font) textSizeX, textSizeY = mdc.GetTextExtent(self.text) totalSize = self.leftWidth + self.rightWidth + textSizeX + self.closeBtnWidth/2 + 16 + self.padding*2 mdc.SelectObject(wx.NullBitmap) return (totalSize, self.tabHeight) def SetTabImage(self, img): self.tabImg = img def CopyRegion(self, region): rect = region.GetBox() newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) newRegion.IntersectRegion(region) return newRegion def InitTab(self, skipLRzones = False): self.tabWidth, self.tabHeight = self.tabSize # content width is tabWidth - (left+right) zones self.contentWidth = self.tabWidth - self.leftWidth - self.rightWidth self.tabRegion = None self.closeBtnRegion = None self.InitColors() self.InitBitmaps() self.ComposeTabBack() self.InitTabRegions() self._Render() def InitBitmaps(self): if self.selected: tr,tg,tb = self.selectedColor else: tr,tg,tb = self.inactiveColor ctabLeft = self.ctabLeft.Copy() ctabRight = self.ctabRight.Copy() ctabMiddle = self.ctabMiddle.Copy() ctabLeft.Replace(0,0,0,tr, tg, tb) ctabRight.Replace(0,0,0,tr, tg, tb) ctabMiddle.Replace(0,0,0,tr, tg, tb) self.ctabLeftBmp = wx.BitmapFromImage(ctabLeft) self.ctabRightBmp = wx.BitmapFromImage(ctabRight) self.ctabMiddleBmp = wx.BitmapFromImage(ctabMiddle) self.ctabCloseBmp = wx.BitmapFromImage(self.ctabClose) def ComposeTabBack(self): bkbmp = wx.EmptyBitmap(self.tabWidth, self.tabHeight) mdc = wx.MemoryDC() mdc.SelectObject(bkbmp) mdc.SetBackground( wx.Brush((0x12,0x23,0x32))) mdc.Clear() mdc.DrawBitmap(self.ctabLeftBmp, 0, 0) cm = self.ctabMiddleBmp.ConvertToImage() mimg = cm.Scale(self.contentWidth, self.ctabMiddle.GetHeight(), wx.IMAGE_QUALITY_NORMAL) mbmp = wx.BitmapFromImage(mimg) mdc.DrawBitmap(mbmp, self.leftWidth, 0 ) mdc.DrawBitmap(self.ctabRightBmp, self.contentWidth + self.leftWidth, 0 ) mdc.SelectObject(wx.NullBitmap) bkbmp.SetMaskColour( (0x12, 0x23, 0x32) ) if self.tabBackBitmap: del self.tabBackBitmap self.tabBackBitmap = bkbmp def InitTabRegions(self): self.tabRegion = wx.RegionFromBitmap(self.tabBackBitmap) self.closeBtnRegion = wx.RegionFromBitmap(self.ctabCloseBmp) self.closeBtnRegion.Offset(self.contentWidth+self.leftWidth - self.ctabCloseBmp.GetWidth()/2, (self.tabHeight - self.ctabCloseBmp.GetHeight())/2) def InitColors(self): self.tabColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) self.inactiveColor = colorUtils.GetSuitableColor(self.tabColor, 0.25) self.selectedColor = colorUtils.GetSuitableColor(self.tabColor, 0.10) def Render(self): return self.tabBitmap def _Render(self): if self.tabBitmap: del self.tabBitmap inc = 6 height = self.tabHeight width = self.tabWidth contentWidth = self.contentWidth rect = wx.Rect(0,0,self.tabWidth, self.tabHeight) canvas = wx.EmptyBitmap(rect.width, rect.height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) mdc.SetBackground(wx.Brush ((0x12,0x23,0x32))) mdc.Clear() r = copy.copy(rect) r.top = r.left = 0 r.height = height mdc.DrawBitmap(self.tabBackBitmap, 0, 0, True) if self.tabImg: bmp = wx.BitmapFromImage(self.tabImg) if self.contentWidth > 16: mdc.DrawBitmap(bmp, self.leftWidth + self.padding - bmp.GetWidth()/2, (height - bmp.GetHeight())/2) textStart = self.leftWidth + self.padding + bmp.GetWidth()/2 else: textStart = self.leftWidth mdc.SetFont(self.font) maxsize = self.tabWidth - textStart - self.rightWidth - self.padding*4 if self.selected: color = self.selectedColor else: color = self.inactiveColor mdc.SetTextForeground(colorUtils.GetSuitableColor(color, 1)) text = drawUtils.GetPartialText(mdc, self.text, maxsize, "") tx,ty = mdc.GetTextExtent(text) mdc.DrawText(text, textStart + self.padding , height / 2 - ty / 2) if self.closeButton: if self.closeBtnHovering: mdc.DrawBitmap(self.ctabCloseBmp,self.contentWidth+self.leftWidth - self.ctabCloseBmp.GetWidth()/2,(height - self.ctabCloseBmp.GetHeight())/2) else: cimg = self.ctabCloseBmp.ConvertToImage() cimg = cimg.AdjustChannels(0.7,0.7,0.7,0.3) cbmp = wx.BitmapFromImage(cimg) mdc.DrawBitmap(cbmp,self.contentWidth+self.leftWidth - self.ctabCloseBmp.GetWidth()/2,(height - self.ctabCloseBmp.GetHeight())/2) mdc.SelectObject(wx.NullBitmap) canvas.SetMaskColour((0x12,0x23,0x32)) img = canvas.ConvertToImage() if not img.HasAlpha(): img.InitAlpha() bmp = wx.BitmapFromImage(img) self.tabBitmap = bmp class PFAddRenderer: def __init__(self): self.addImg = bitmapLoader.getImage("ctabadd", "icons") self.width = self.addImg.GetWidth() self.height = self.addImg.GetHeight() self.region = None self.tbmp = wx.BitmapFromImage(self.addImg) self.addBitmap = None self.position = (0,0) self.highlighted = False self.InitRenderer() def GetPosition(self): return self.position def SetPosition(self,pos): self.position = pos def GetSize(self): return (self.width, self.height) def GetHeight(self): return self.height def GetWidth(self): return self.width def InitRenderer(self): self.region = self.CreateRegion() self._Render() def CreateRegion(self): region = wx.RegionFromBitmap(self.tbmp) return region def CopyRegion(self, region): rect = region.GetBox() newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) newRegion.IntersectRegion(region) return newRegion def GetRegion(self): return self.CopyRegion(self.region) def Highlight(self, highlight = False): self.highlighted = highlight self._Render() def IsHighlighted(self): return self.highlighted def Render(self): return self.addBitmap def _Render(self): rect = wx.Rect(0 ,0 ,self.width, self.height) if self.addBitmap: del self.addBitmap if self.highlighted: alpha = 1 else: alpha = 0.3 img = self.addImg.AdjustChannels(1, 1, 1, alpha) bbmp = wx.BitmapFromImage(img) self.addBitmap = bbmp class PFTabsContainer(wx.Panel): def __init__(self, parent, pos = (0,0), size = (100,22), id = wx.ID_ANY, canAdd = True): wx.Panel.__init__(self, parent, id , pos, size) self.tabs = [] width, height = size self.width = width self.height = height self.containerHeight = height self.startDrag = False self.dragging = False self.inclination = 7 if canAdd: self.reserved = 48 else: self.reserved = self.inclination * 4 self.dragTrail = 3 self.dragx = 0 self.dragy = 0 self.draggedTab = None self.dragTrigger = self.dragTrail self.showAddButton = canAdd self.tabContainerWidth = width - self.reserved self.tabMinWidth = width self.tabShadow = None self.addButton = PFAddRenderer() self.addBitmap = self.addButton.Render() self.previewTimer = None self.previewTimerID = wx.ID_ANY self.previewWnd = None self.previewBmp = None self.previewPos = None self.previewTab = None self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_MOTION, self.OnMotion) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged) self.tabShadow = PFTabRenderer((self.tabMinWidth, self.height + 1), inclination = self.inclination) def OnSysColourChanged(self, event): for tab in self.tabs: tab.InitTab() self.Refresh() def OnSize(self, event): self.UpdateSize() event.Skip() def UpdateSize(self): width, dummy = self.GetSize() if width != self.width: self.width = width self.tabContainerWidth = self.width - self.reserved self.AdjustTabsSize() def OnLeftDown(self, event): mposx,mposy = event.GetPosition() if not self.startDrag: tab = self.FindTabAtPos(mposx, mposy) if tab: self.CheckTabSelected(tab, mposx, mposy) if self.showAddButton: self.startDrag = True tx,ty = tab.GetPosition() self.dragx = mposx - tx self.dragy = self.containerHeight - self.height self.Refresh() self.draggedTab = tab def OnLeftUp(self, event): mposx,mposy = event.GetPosition() if self.startDrag and self.dragging: self.dragging = False self.startDrag = False self.draggedTab = None self.dragTrigger = self.dragTrail self.UpdateTabsPosition() self.Refresh() if self.HasCapture(): self.ReleaseMouse() return if self.startDrag: self.startDrag = False self.dragTrigger = self.dragTrail if self.CheckAddButton(mposx, mposy): return if self.GetTabsCount() == 0: return selTab = self.GetSelectedTab() if self.CheckTabClose(selTab, mposx, mposy): return for tab in self.tabs: if self.CheckTabClose(tab, mposx, mposy): return def GetSelectedTab(self): for tab in self.tabs: if tab.GetSelected(): return tab return None def GetSelected(self): for tab in self.tabs: if tab.GetSelected(): return self.tabs.index(tab) return None def SetSelected(self, tabIndex): oldSelTab = self.GetSelectedTab() oldSelTab.SetSelected(False) self.tabs[tabIndex].SetSelected(True) self.Refresh() def CheckTabSelected(self,tab, mposx, mposy): oldSelTab = self.GetSelectedTab() if oldSelTab == tab: return True if self.TabHitTest(tab, mposx, mposy): tab.SetSelected(True) oldSelTab.SetSelected(False) ev = PageChanging(self.tabs.index(oldSelTab), self.tabs.index(tab)) wx.PostEvent(self.Parent, ev) if ev.isVetoed(): return False self.Refresh() selTab = self.tabs.index(tab) self.Parent.SetSelection(selTab) wx.PostEvent(self.Parent, PageChanged(self.tabs.index(oldSelTab), self.tabs.index(tab))) return True return False def CheckTabClose(self, tab, mposx, mposy): if not tab.closeButton: return False closeBtnReg = tab.GetCloseButtonRegion() tabPosX, tabPosY = tab.GetPosition() closeBtnReg.Offset(tabPosX,tabPosY) if closeBtnReg.Contains(mposx, mposy): index = self.tabs.index(tab) ev = PageClosing(index) wx.PostEvent(self.Parent, ev) if ev.isVetoed(): return False index = self.GetTabIndex(tab) self.DeleteTab(index) wx.PostEvent(self.Parent, PageClosed(index=index)) sel = self.GetSelected() if sel is not None: wx.PostEvent(self.Parent, PageChanged(-1, sel)) return True return False def CheckAddButton(self, mposx,mposy): if not self.showAddButton: return reg = self.addButton.GetRegion() ax,ay = self.addButton.GetPosition() reg.Offset(ax,ay) if reg.Contains(mposx, mposy): ev = PageAdding() wx.PostEvent(self.Parent, ev) if ev.isVetoed(): return False self.Parent.AddPage() wx.PostEvent(self.Parent, PageAdded()) return True def CheckCloseButtons(self, mposx, mposy): dirty = False for tab in self.tabs: closeBtnReg = tab.GetCloseButtonRegion() tabPos = tab.GetPosition() tabPosX, tabPosY = tabPos closeBtnReg.Offset(tabPosX,tabPosY) if closeBtnReg.Contains(mposx,mposy): if not tab.GetCloseButtonHoverStatus(): tab.ShowCloseButtonHovering(True) dirty = True else: if tab.GetCloseButtonHoverStatus(): tab.ShowCloseButtonHovering(False) dirty = True if dirty: self.Refresh() def FindTabAtPos(self, x, y): if self.GetTabsCount() == 0: return None selTab = self.GetSelectedTab() if self.TabHitTest(selTab, x, y): return selTab for tab in self.tabs: if self.TabHitTest(tab, x, y): return tab return None def TabHitTest(self, tab, x, y): tabRegion = tab.GetTabRegion() tabPos = tab.GetPosition() tabPosX, tabPosY = tabPos tabRegion.Offset(tabPosX, tabPosY) if tabRegion.Contains(x, y): return True return False def GetTabAtLeft(self, tabIndex): if tabIndex>0: return self.tabs[tabIndex - 1] else: return None def GetTabAtRight(self, tabIndex): if tabIndex < self.GetTabsCount() - 1: return self.tabs[tabIndex + 1] else: return None def SwitchTabs(self, src, dest, draggedTab = None): self.tabs[src], self.tabs[dest] = self.tabs[dest], self.tabs[src] self.UpdateTabsPosition(draggedTab) self.Parent.SwitchPages(src,dest, True) self.Refresh() def GetTabIndex(self, tab): return self.tabs.index(tab) def OnMotion(self, event): mposx,mposy = event.GetPosition() if self.startDrag: if not self.dragging: if self.dragTrigger < 0: self.dragging = True self.dragTrigger = self.dragTrail self.CaptureMouse() else: self.dragTrigger -= 1 if self.dragging: dtx = mposx - self.dragx w,h = self.draggedTab.GetSize() if dtx < 0: dtx = 0 if dtx + w > self.tabContainerWidth + self.inclination * 2: dtx = self.tabContainerWidth - w + self.inclination * 2 self.draggedTab.SetPosition( (dtx, self.dragy)) index = self.GetTabIndex(self.draggedTab) leftTab = self.GetTabAtLeft(index) rightTab = self.GetTabAtRight(index) if leftTab: lw,lh = leftTab.GetSize() lx,ly = leftTab.GetPosition() if lx + lw / 2 - self.inclination * 2 > dtx: self.SwitchTabs(index - 1 , index, self.draggedTab) return if rightTab: rw,rh = rightTab.GetSize() rx,ry = rightTab.GetPosition() if rx + rw / 2 + self.inclination * 2 < dtx + w: self.SwitchTabs(index + 1 , index, self.draggedTab) return self.UpdateTabsPosition(self.draggedTab) self.Refresh() return return self.CheckCloseButtons(mposx, mposy) self.CheckAddHighlighted(mposx,mposy) self.CheckTabPreview(mposx, mposy) event.Skip() def CheckTabPreview(self, mposx, mposy): if self.previewTimer: if self.previewTimer.IsRunning(): if self.previewWnd: self.previewTimer.Stop() return if self.previewWnd: self.previewWnd.Show(False) del self.previewWnd self.previewWnd = None for tab in self.tabs: if not tab.GetSelected(): if self.TabHitTest(tab, mposx, mposy): try: page = self.Parent.GetPage(self.GetTabIndex(tab)) if hasattr(page, "Snapshot"): if not self.previewTimer: self.previewTimer = wx.Timer(self, self.previewTimerID) self.previewTab = tab self.previewTimer.Start(1500, True) break except: pass def CheckAddHighlighted(self, mposx, mposy): if not self.showAddButton: return reg = self.addButton.GetRegion() ax,ay = self.addButton.GetPosition() reg.Offset(ax,ay) if reg.Contains(mposx, mposy): if not self.addButton.IsHighlighted(): self.addButton.Highlight(True) self.Refresh() else: if self.addButton.IsHighlighted(): self.addButton.Highlight(False) self.Refresh() def OnPaint(self, event): if "wxGTK" in wx.PlatformInfo: mdc = wx.AutoBufferedPaintDC(self) else: rect = self.GetRect() mdc = wx.BufferedPaintDC(self) selected = 0 if 'wxMac' in wx.PlatformInfo: color = wx.Colour(0, 0, 0) brush = wx.Brush(color) from Carbon.Appearance import kThemeBrushDialogBackgroundActive brush.MacSetTheme(kThemeBrushDialogBackgroundActive) else: color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) brush = wx.Brush(color) if "wxGTK" not in wx.PlatformInfo: mdc.SetBackground (brush) mdc.Clear() selected = None tabsWidth = 0 for tab in self.tabs: tabsWidth += tab.tabWidth - self.inclination*2 pos = tabsWidth if self.showAddButton: ax,ay = self.addButton.GetPosition() mdc.DrawBitmap(self.addButton.Render(), ax, ay, True) for i in xrange(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] width = tab.tabWidth - 6 posx, posy = tab.GetPosition() if not tab.IsSelected(): mdc.DrawBitmap(self.efxBmp, posx, posy, True ) bmp = tab.Render() img = bmp.ConvertToImage() img = img.AdjustChannels(1, 1, 1, 0.85) bmp = wx.BitmapFromImage(img) mdc.DrawBitmap(bmp, posx, posy, True) else: selected = tab if selected: posx, posy = selected.GetPosition() mdc.DrawBitmap(self.efxBmp, posx, posy, True) bmp = selected.Render() if self.dragging: img = bmp.ConvertToImage() img = img.AdjustChannels(1.2, 1.2, 1.2, 0.7) bmp = wx.BitmapFromImage(img) mdc.DrawBitmap(bmp, posx, posy, True) def OnErase(self, event): pass def UpdateTabFX(self): w,h = self.tabShadow.GetSize() if w != self.tabMinWidth: self.tabShadow.SetSize((self.tabMinWidth, self.height + 1)) fxBmp = self.tabShadow.Render() simg = fxBmp.ConvertToImage() if not simg.HasAlpha(): simg.InitAlpha() simg = simg.Blur(2) simg = simg.AdjustChannels(0.3,0.3,0.3,0.35) self.efxBmp = wx.BitmapFromImage(simg) def AddTab(self, title = wx.EmptyString, img = None, showClose = False): self.ClearTabsSelected() tabRenderer = PFTabRenderer( (120,self.height), title, img, self.inclination, closeButton = showClose) tabRenderer.SetSelected(True) self.tabs.append( tabRenderer ) self.AdjustTabsSize() self.Refresh() def ClearTabsSelected(self): for tab in self.tabs: tab.SetSelected(False) def DeleteTab(self, tab, external = False): tabRenderer = self.tabs[tab] wasSelected = tabRenderer.GetSelected() self.tabs.remove(tabRenderer) if tabRenderer: del tabRenderer if wasSelected and self.GetTabsCount() > 0: if tab > self.GetTabsCount() -1: self.tabs[self.GetTabsCount() - 1].SetSelected(True) else: self.tabs[tab].SetSelected(True) if not external: self.Parent.DeletePage(tab, True) self.AdjustTabsSize() self.Refresh() def GetTabsCount(self): return len(self.tabs) def AdjustTabsSize(self): tabMinWidth = 9000000 # Really, it should be over 9000 tabMaxWidth = 0 for tab in self.tabs: mw,mh = tab.GetMinSize() if tabMinWidth > mw: tabMinWidth = mw if tabMaxWidth < mw: tabMaxWidth = mw if tabMaxWidth < 100: tabMaxWidth = 100 if self.GetTabsCount() >0: if (self.GetTabsCount()) * (tabMaxWidth - self.inclination * 2) > self.tabContainerWidth: self.tabMinWidth = float(self.tabContainerWidth) / float(self.GetTabsCount()) + self.inclination * 2 else: self.tabMinWidth = tabMaxWidth if self.tabMinWidth <1: self.tabMinWidth = 1 for tab in self.tabs: w,h = tab.GetSize() tab.SetSize( (self.tabMinWidth, self.height) ) if self.GetTabsCount() > 0: self.UpdateTabFX() self.UpdateTabsPosition() def UpdateTabsPosition(self, skipTab = None): tabsWidth = 0 for tab in self.tabs: tabsWidth += tab.tabWidth - self.inclination*2 pos = tabsWidth selected = None for i in xrange(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] width = tab.tabWidth - self.inclination*2 pos -= width if not tab.IsSelected(): tab.SetPosition((pos, self.containerHeight - self.height)) else: selected = tab selpos = pos if selected is not skipTab: selected.SetPosition((selpos, self.containerHeight - self.height)) self.addButton.SetPosition((round(tabsWidth) + self.inclination*2, self.containerHeight - self.height/2 - self.addButton.GetHeight()/3)) def OnLeaveWindow(self, event): if self.startDrag and not self.dragging: self.dragging = False self.startDrag = False self.draggedTab = None self.dragTrigger = self.dragTrail if self.HasCapture(): self.ReleaseMouse() if self.previewWnd: self.previewWnd.Show(False) del self.previewWnd self.previewWnd = None event.Skip() def OnTimer(self, event): mposx, mposy = wx.GetMousePosition() cposx, cposy = self.ScreenToClient((mposx, mposy)) if self.FindTabAtPos(cposx, cposy) == self.previewTab: if not self.previewTab.GetSelected(): page = self.Parent.GetPage(self.GetTabIndex(self.previewTab)) if page.Snapshot(): self.previewWnd = PFNotebookPagePreview(self,(mposx+3,mposy+3), page.Snapshot(), self.previewTab.text) self.previewWnd.Show() event.Skip() class PFNotebookPagePreview(wx.Frame): def __init__ (self,parent, pos, bitmap, title): wx.Frame.__init__(self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = pos, size = wx.DefaultSize, style = wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) self.title = title self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) self.Bind(wx.EVT_PAINT,self.OnWindowPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnWindowEraseBk) self.Bind(wx.EVT_TIMER, self.OnTimer) self.timer = wx.Timer(self,wx.ID_ANY) self.timerSleep = None self.timerSleepId = wx.NewId() self.direction = 1 self.padding = 15 self.transp = 0 hfont = wx.FontFromPixelSize((0,14), wx.SWISS, wx.NORMAL,wx.NORMAL, False) self.SetFont(hfont) tx, ty = self.GetTextExtent(self.title) tx += self.padding * 2 if bitmap.GetWidth() < tx: width = tx else: width = bitmap.GetWidth() self.SetSize((width, bitmap.GetHeight()+16)) self.SetTransparent(0) self.Refresh() def OnTimer(self, event): self.transp += 20*self.direction if self.transp > 220: self.transp = 220 self.timer.Stop() if self.transp < 0: self.transp = 0 self.timer.Stop() wx.Frame.Show(self,False) self.Destroy() return self.SetTransparent(self.transp) def RaiseParent(self): wnd = self lastwnd = None while wnd is not None: lastwnd = wnd wnd = wnd.Parent if lastwnd: lastwnd.Raise() def Show(self, showWnd = True): if showWnd: wx.Frame.Show(self, showWnd) self.RaiseParent() self.direction = 1 self.timer.Start(10) else: self.direction = -1 self.timer.Start(10) def OnWindowEraseBk(self,event): pass def OnWindowPaint(self,event): rect = self.GetRect() canvas = wx.EmptyBitmap(rect.width, rect.height) mdc = wx.BufferedPaintDC(self) mdc.SelectObject(canvas) color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) mdc.SetBackground(wx.Brush(color)) mdc.Clear() font = wx.FontFromPixelSize((0,14), wx.SWISS, wx.NORMAL,wx.NORMAL, False) mdc.SetFont(font) x,y = mdc.GetTextExtent(self.title) mdc.SetBrush(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT))) mdc.DrawRectangle(0,0,rect.width,16) mdc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) mdc.DrawText(self.title, (rect.width - x)/2, (16 -y)/2) mdc.DrawBitmap(self.bitmap, 0, 16) mdc.SetPen( wx.Pen("#000000", width = 1 ) ) mdc.SetBrush( wx.TRANSPARENT_BRUSH ) mdc.DrawRectangle( 0, 16, rect.width,rect.height - 16)