1064 lines
33 KiB
Python
Executable File
1064 lines
33 KiB
Python
Executable File
#!/usr/bin/env python
|
|
import wx
|
|
import copy
|
|
import time
|
|
|
|
class PFTabRenderer:
|
|
def __init__(self, size = (36,24), text = wx.EmptyString, img = None, inclination = 6 , closeButton = True, fontSize = 8):
|
|
|
|
# tab left/right zones inclination
|
|
self.inclination = inclination
|
|
self.text = text
|
|
self.img = img
|
|
self.tabSize = size
|
|
self.closeButton = closeButton
|
|
self.fontSize = fontSize
|
|
self.selected = False
|
|
self.closeBtnHovering = False
|
|
self.tabBitmap = None
|
|
self.cbSize = 5
|
|
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
|
|
self.tabSize = size
|
|
w,h = self.tabSize
|
|
if h != oth:
|
|
self.InitTab(True)
|
|
else:
|
|
self.InitTab()
|
|
|
|
def SetSelected(self, sel = True):
|
|
self.selected = sel
|
|
self.InitColors()
|
|
self._Render()
|
|
|
|
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):
|
|
mdc = wx.MemoryDC()
|
|
mdc.SetFont(self.font)
|
|
textSizeX, textSizeY = mdc.GetTextExtent(self.text)
|
|
totalSize = self.lrZoneWidth * 2 + textSizeX + self.cbSize*2 if self.closeButton else 0
|
|
return (totalSize, self.tabHeight)
|
|
|
|
|
|
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.inclination * 6 - self.cbSize if self.closeButton else 0
|
|
|
|
self.leftZoneSpline = []
|
|
self.rightZoneSpline = []
|
|
|
|
self.lrZoneWidth = self.inclination * 3
|
|
if not skipLRzones:
|
|
self.CreateLRZoneSplines()
|
|
|
|
self.leftRegion = self.CreateLeftRegion()
|
|
self.rightRegion = self.CreateRightRegion()
|
|
|
|
self.contentRegion = wx.Region(0, 0, self.contentWidth, self.tabHeight)
|
|
self.tabRegion = None
|
|
self.closeBtnRegion = None
|
|
self.font = wx.Font(self.fontSize, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
|
|
|
|
self.InitTabRegions()
|
|
self.InitColors()
|
|
self._Render()
|
|
|
|
def InitColors(self):
|
|
self.tabColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
|
|
self.leftColor = self.CalculateColor(self.tabColor, 0x2F)
|
|
self.rightColor = self.CalculateColor(self.tabColor, 0x44)
|
|
self.gradientStartColor = self.CalculateColor(self.tabColor, 0x17 if self.selected else 0x20)
|
|
|
|
def CalculateColor(self, color, delta):
|
|
bkR ,bkG , bkB = color
|
|
if bkR + bkG + bkB > 127*3:
|
|
scale = - delta
|
|
else:
|
|
scale = delta*2
|
|
|
|
r = bkR + scale
|
|
g = bkG + scale
|
|
b = bkB + scale
|
|
|
|
r = min(max(r,0),255)
|
|
b = min(max(b,0),255)
|
|
g = min(max(g,0),255)
|
|
|
|
return wx.Colour(r,g,b)
|
|
|
|
def InitTabRegions(self):
|
|
self.tabRegion = wx.Region(0, 0, self.tabWidth, self.tabHeight)
|
|
self.tabRegion.IntersectRegion(self.leftRegion)
|
|
|
|
self.contentRegion.Offset(self.lrZoneWidth, 0)
|
|
self.tabRegion.UnionRegion(self.contentRegion)
|
|
|
|
self.rightRegion.Offset(self.tabWidth - self.lrZoneWidth, 0)
|
|
self.tabRegion.UnionRegion(self.rightRegion)
|
|
self.closeBtnRegion = wx.Region(self.tabWidth - self.lrZoneWidth - self.cbSize -2 , (self.tabHeight - self.cbSize) / 2 - 2, self.cbSize + 4, self.cbSize + 4)
|
|
cbtRegion = wx.Region(self.tabWidth - self.lrZoneWidth - self.cbSize ,0, self.cbSize, self.tabHeight)
|
|
self.tabRegion.UnionRegion(cbtRegion)
|
|
|
|
def CreateLRZoneSplines(self):
|
|
height = self.tabHeight
|
|
inc = self.inclination
|
|
|
|
self.leftZoneSpline = [wx.Point(0, height), wx.Point(inc * 2/3, height - inc/2), wx.Point(inc+inc/2, 2),
|
|
wx.Point(inc * 3, 0)]
|
|
self.rightZoneSpline = [wx.Point(0, 0), wx.Point(inc+inc/2,2),wx.Point(inc*2 +inc*2/3,height-inc/2), wx.Point(inc*3,height) ]
|
|
|
|
def CreateLeftRegion(self):
|
|
|
|
width = self.lrZoneWidth + 1
|
|
height = self.tabHeight + 1
|
|
inc = self.inclination
|
|
|
|
mdc = wx.MemoryDC()
|
|
|
|
mbmp = wx.EmptyBitmap(width,height)
|
|
mdc.SelectObject(mbmp)
|
|
|
|
mdc.SetBackground( wx.Brush((255,255,255)))
|
|
mdc.Clear()
|
|
|
|
mdc.SetPen( wx.Pen("#000000", width = 1 ) )
|
|
mdc.DrawSpline(self.leftZoneSpline)
|
|
|
|
mdc.SetBrush(wx.Brush((255,255,0)))
|
|
mdc.FloodFill(inc*2,height-2, wx.Color(0,0,0), wx.FLOOD_BORDER)
|
|
|
|
mdc.SelectObject(wx.NullBitmap)
|
|
|
|
mbmp.SetMaskColour( (255, 255, 255) )
|
|
|
|
region = wx.RegionFromBitmap(mbmp)
|
|
region.Offset(-1,0)
|
|
|
|
return region
|
|
|
|
def CreateRightRegion(self):
|
|
|
|
width = self.lrZoneWidth + 1
|
|
height = self.tabHeight
|
|
inc = self.inclination
|
|
|
|
mdc = wx.MemoryDC()
|
|
|
|
mbmp = wx.EmptyBitmap(width,height)
|
|
mdc.SelectObject(mbmp)
|
|
|
|
mdc.SetBackground( wx.Brush((255,255,255)))
|
|
mdc.Clear()
|
|
|
|
mdc.SetPen( wx.Pen("#000000", width = 1 ) )
|
|
mdc.DrawSpline(self.rightZoneSpline)
|
|
|
|
mdc.SetBrush(wx.Brush((255,255,0)))
|
|
mdc.FloodFill(inc,height-inc, wx.Color(0,0,0), wx.FLOOD_BORDER)
|
|
|
|
mdc.SelectObject(wx.NullBitmap)
|
|
|
|
mbmp.SetMaskColour( (255, 255, 255) )
|
|
|
|
region = wx.RegionFromBitmap(mbmp)
|
|
|
|
return region
|
|
|
|
def OffsetPointList(self, list , x, y):
|
|
tlist = []
|
|
for i in list:
|
|
tlist.append(wx.Point(i.x + x, i.y + y))
|
|
|
|
return tlist
|
|
|
|
def Render(self):
|
|
return self.tabBitmap
|
|
|
|
def _Render(self):
|
|
if self.tabBitmap:
|
|
del self.tabBitmap
|
|
|
|
inc = self.lrZoneWidth
|
|
height = self.tabHeight
|
|
width = self.tabWidth
|
|
contentWidth = self.contentWidth + self.cbSize if self.closeButton else 0
|
|
|
|
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 ((13,22,31)))
|
|
mdc.Clear()
|
|
mdc.DestroyClippingRegion()
|
|
mdc.SetClippingRegionAsRegion(self.tabRegion)
|
|
|
|
r = copy.copy(rect)
|
|
r.top = r.left = 0
|
|
r.height = height
|
|
|
|
mdc.GradientFillLinear(r,self.gradientStartColor,self.tabColor,wx.SOUTH)
|
|
mdc.SetPen( wx.Pen(self.leftColor, width = 1 ) )
|
|
|
|
dpleft = self.OffsetPointList(self.leftZoneSpline, -1, 0)
|
|
dpright = self.OffsetPointList(self.rightZoneSpline, inc + contentWidth, 0)
|
|
|
|
mdc.DrawSpline(dpleft)
|
|
mdc.SetPen( wx.Pen(self.rightColor, width = 1 ) )
|
|
mdc.DrawSpline(dpright)
|
|
|
|
lrect = wx.Rect()
|
|
lrect.left=inc - 1
|
|
lrect.top=0
|
|
lrect.width = contentWidth+1
|
|
lrect.height = 1
|
|
mdc.GradientFillLinear(lrect,self.leftColor,self.rightColor, wx.EAST)
|
|
# if not self.selected:
|
|
# mdc.DrawLine(0,height - 1,width,height - 1)
|
|
mdc.SetPen( wx.Pen(self.rightColor, width = 1 ) )
|
|
if self.closeButton:
|
|
cbsize = self.cbSize
|
|
|
|
cbx = width - self.lrZoneWidth-cbsize
|
|
cby = (height - cbsize)/2
|
|
if self.closeBtnHovering:
|
|
mdc.SetPen( wx.Pen( wx.Colour(255,22,22), 0))
|
|
mdc.SetBrush(wx.Brush(wx.Colour(255,22,22)))
|
|
mdc.DrawCircle(cbx + cbsize / 2 +1, cby + cbsize / 2 + 1, cbsize)
|
|
selColor = self.CalculateColor(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT), 255)
|
|
mdc.SetPen( wx.Pen( selColor, 1))
|
|
|
|
mdc.DrawLine(cbx, cby, cbx + cbsize + 1 , cby + cbsize + 1 )
|
|
mdc.DrawLine(cbx, cby + cbsize, cbx + cbsize + 1, cby - 1 )
|
|
|
|
mdc.SetClippingRegionAsRegion(self.contentRegion)
|
|
mdc.SetFont(self.font)
|
|
text = self.text
|
|
fnwidths = mdc.GetPartialTextExtents(text)
|
|
count = 0
|
|
maxsize = self.contentWidth - self.cbSize if self.closeButton else 0
|
|
for i in fnwidths:
|
|
if i <= maxsize:
|
|
count +=1
|
|
else:
|
|
break
|
|
|
|
# text = "%s%s" % (text[:count],"." if len(text)>count else "")
|
|
text = "%s" % text[:count]
|
|
|
|
|
|
tx,ty = mdc.GetTextExtent(text)
|
|
if self.selected:
|
|
mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
|
|
else:
|
|
color = self.CalculateColor(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT), 0x44)
|
|
mdc.SetTextForeground(color)
|
|
mdc.DrawText(text, inc, height / 2 - ty / 2)
|
|
|
|
mdc.DestroyClippingRegion()
|
|
|
|
mdc.SelectObject(wx.NullBitmap)
|
|
canvas.SetMaskColour((13,22,31))
|
|
if not self.selected:
|
|
img = canvas.ConvertToImage()
|
|
img = img.AdjustChannels(1, 1, 1, 0.8)
|
|
canvas = wx.BitmapFromImage(img)
|
|
self.tabBitmap = canvas
|
|
|
|
class PFAddRenderer:
|
|
def __init__(self, size = (24,12)):
|
|
self.width, self.height = size
|
|
self.addBitmap = None
|
|
self.spline = []
|
|
self.inclination = 3
|
|
self.region = None
|
|
self.InitRenderer()
|
|
|
|
def GetSize(self):
|
|
return (self.width, self.height)
|
|
|
|
def InitRenderer(self):
|
|
self.CreateSpline()
|
|
self.region = self.CreateRegion()
|
|
self._Render()
|
|
|
|
def CreateSpline(self):
|
|
width = self.width
|
|
height = self.height - 1
|
|
inc = self.inclination
|
|
|
|
self.spline = [wx.Point(0, 0), wx.Point(inc*3/2, height),wx.Point(inc*2 + inc*2/3, height), wx.Point(width, height), wx.Point(width, height),
|
|
wx.Point(width - inc, inc), wx.Point(width - inc*2, 0), wx.Point(0, 0), wx.Point(0, 0)]
|
|
def CreateRegion(self):
|
|
width = self.width
|
|
height = self.height
|
|
inc = self.inclination
|
|
|
|
mdc = wx.MemoryDC()
|
|
|
|
mbmp = wx.EmptyBitmap(width,height)
|
|
mdc.SelectObject(mbmp)
|
|
|
|
mdc.SetBackground( wx.Brush((255,255,255)))
|
|
mdc.Clear()
|
|
|
|
mdc.SetPen( wx.Pen("#000000", width = 1 ) )
|
|
mdc.DrawSpline(self.spline)
|
|
|
|
mdc.SetBrush(wx.Brush((255,255,0)))
|
|
mdc.FloodFill(width/2,height/2, wx.Color(0,0,0), wx.FLOOD_BORDER)
|
|
|
|
mdc.SelectObject(wx.NullBitmap)
|
|
|
|
mbmp.SetMaskColour( (255, 255, 255) )
|
|
|
|
region = wx.RegionFromBitmap(mbmp)
|
|
# region.Offset(-1,0)
|
|
|
|
return region
|
|
|
|
def CalculateColor(self, color, delta):
|
|
bkR ,bkG , bkB = color
|
|
if bkR + bkG + bkB > 127*3:
|
|
scale = - delta
|
|
else:
|
|
scale = delta*2
|
|
|
|
r = bkR + scale
|
|
g = bkG + scale
|
|
b = bkB + scale
|
|
|
|
r = min(max(r,0),255)
|
|
b = min(max(b,0),255)
|
|
g = min(max(g,0),255)
|
|
|
|
return wx.Colour(r,b,g)
|
|
|
|
def Render(self):
|
|
return self.addBitmap
|
|
|
|
def _Render(self):
|
|
inc = self.inclination
|
|
rect = wx.Rect(0 ,0 ,self.width, self.height)
|
|
if self.addBitmap:
|
|
del self.addBitmap
|
|
|
|
canvas = wx.EmptyBitmap(self.width, self.height)
|
|
|
|
mdc = wx.MemoryDC()
|
|
mdc.SelectObject(canvas)
|
|
|
|
mdc.SetBackground(wx.Brush ((13,22,31)))
|
|
mdc.Clear()
|
|
|
|
mdc.DestroyClippingRegion()
|
|
mdc.SetClippingRegionAsRegion(self.region)
|
|
# mdc.GradientFillLinear(rect, (0x30,0x30,0x30), (0x6f,0x6f,0x6f), wx.SOUTH)
|
|
mdc.FloodFill(self.width/2,self.height/2, wx.Color(13,22,31), wx.FLOOD_BORDER)
|
|
mdc.DestroyClippingRegion()
|
|
mdc.SetPen( wx.Pen( wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT), 1))
|
|
mdc.DrawSpline(self.spline)
|
|
mdc.SelectObject(wx.NullBitmap)
|
|
|
|
canvas.SetMaskColour((13,22,31))
|
|
|
|
img = canvas.ConvertToImage()
|
|
img.InitAlpha()
|
|
img = img.AdjustChannels(1, 1, 1, 0.6)
|
|
img = img.Blur(1)
|
|
bbmp = wx.BitmapFromImage(img)
|
|
|
|
del mdc
|
|
del canvas
|
|
canvas = wx.EmptyBitmap(self.width, self.height)
|
|
|
|
mdc = wx.MemoryDC()
|
|
mdc.SelectObject(canvas)
|
|
|
|
mdc.SetBackground(wx.Brush ((255,255,255 , 0)))
|
|
mdc.Clear()
|
|
|
|
mdc.DrawBitmap(bbmp,0,0,True)
|
|
|
|
cx = self.width / 2 - 1
|
|
cy = self.height / 2
|
|
|
|
mdc.SetPen( wx.Pen( wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT), 1))
|
|
mdc.DrawLine(cx - inc + 1, cy, cx + inc + 1, cy)
|
|
mdc.DrawLine(cx - inc + 1, cy-1, cx + inc + 1, cy-1)
|
|
mdc.DrawLine(cx, cy - inc, cx, cy + inc )
|
|
mdc.DrawLine(cx+1, cy - inc, cx+1, cy + inc )
|
|
|
|
self.wColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
|
|
color = self.CalculateColor(self.wColor, 0x99)
|
|
mdc.SetPen( wx.Pen( color, 1))
|
|
|
|
mdc.DrawSpline(self.spline)
|
|
|
|
mdc.SelectObject(wx.NullBitmap)
|
|
canvas.SetMaskColour((255,255,255))
|
|
|
|
img = canvas.ConvertToImage()
|
|
|
|
img.InitAlpha()
|
|
img = img.AdjustChannels(1, 1, 1, 0.3)
|
|
|
|
bbmp = wx.BitmapFromImage(img)
|
|
self.addBitmap = bbmp
|
|
|
|
|
|
class PFTabsContainer(wx.Window):
|
|
def __init__(self, parent, pos = (0,0), size = (100,27), id = wx.ID_ANY):
|
|
wx.Window.__init__(self, parent, id , pos, size , style = 0)
|
|
self.tabs = []
|
|
width, height = size
|
|
self.width = width
|
|
self.height = height - 3
|
|
self.containerHeight = height
|
|
self.startDrag = False
|
|
self.dragging = False
|
|
self.reserved = 48
|
|
self.inclination = 6
|
|
self.dragTrail = 3
|
|
self.dragx = 0
|
|
self.dragy = 0
|
|
self.draggedTab = None
|
|
self.dragTrigger = self.dragTrail
|
|
|
|
self.tabContainerWidth = width - self.reserved
|
|
self.tabMinWidth = width
|
|
self.tabShadow = None
|
|
|
|
self.addButton = PFAddRenderer()
|
|
self.addBitmap = self.addButton.Render()
|
|
|
|
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.tabShadow = PFTabRenderer((self.tabMinWidth, self.height + 1), inclination = self.inclination)
|
|
|
|
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)
|
|
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.GetTabsCount() == 0:
|
|
return
|
|
selTab = self.GetSelectedTab()
|
|
|
|
if self.CheckTabClose(selTab, mposx, mposy):
|
|
return
|
|
|
|
# if self.CheckTabSelected(selTab, mposx, mposy):
|
|
# return
|
|
|
|
for tab in self.tabs:
|
|
|
|
if self.CheckTabClose(tab, mposx, mposy):
|
|
return
|
|
|
|
# if self.CheckTabSelected(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 CheckTabSelected(self,tab, mposx, mposy):
|
|
|
|
oldSelTab = self.GetSelectedTab()
|
|
if oldSelTab == tab:
|
|
return True
|
|
|
|
if self.TabHitTest(tab, mposx, mposy):
|
|
tab.SetSelected(True)
|
|
if tab != oldSelTab:
|
|
oldSelTab.SetSelected(False)
|
|
self.Refresh()
|
|
print "Selected: %s" %tab.text
|
|
|
|
selTab = self.tabs.index(tab)
|
|
self.Parent.SetSelected(selTab)
|
|
|
|
return True
|
|
return False
|
|
|
|
def CheckTabClose(self, tab, mposx, mposy):
|
|
closeBtnReg = tab.GetCloseButtonRegion()
|
|
tabPosX, tabPosY = tab.GetPosition()
|
|
|
|
closeBtnReg.Offset(tabPosX,tabPosY)
|
|
|
|
if closeBtnReg.Contains(mposx, mposy):
|
|
print "Close tab: %s" % tab.text
|
|
index = self.GetTabIndex(tab)
|
|
self.DeleteTab(index)
|
|
return True
|
|
return False
|
|
|
|
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)
|
|
|
|
event.Skip()
|
|
|
|
def OnPaint(self, event):
|
|
rect = self.GetRect()
|
|
canvas = wx.EmptyBitmap(rect.width, rect.height)
|
|
mdc = wx.BufferedPaintDC(self)
|
|
mdc.SelectObject(canvas)
|
|
|
|
selected = 0
|
|
|
|
mdc.SetBackground (wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)))
|
|
# mdc.SetBackground (wx.Brush((66,113,202)))
|
|
mdc.Clear()
|
|
|
|
selected = None
|
|
selpos = 0
|
|
selWidth = selHeight = 0
|
|
selColor = self.CalculateColor(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW), 0x34)
|
|
startColor = self.leftColor = self.CalculateColor(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW), 0x2f)
|
|
tabsWidth = 0
|
|
|
|
|
|
for tab in self.tabs:
|
|
tabsWidth += tab.tabWidth - self.inclination*2
|
|
|
|
pos = tabsWidth
|
|
|
|
mdc.DrawBitmap(self.addBitmap, round(tabsWidth) + self.inclination*2, self.containerHeight - self.height/2 - self.addBitmap.GetHeight()/2, True)
|
|
|
|
for i in xrange(len(self.tabs) - 1, -1, -1):
|
|
tab = self.tabs[i]
|
|
width = tab.tabWidth - self.inclination*2
|
|
posx, posy = tab.GetPosition()
|
|
if not tab.IsSelected():
|
|
mdc.DrawBitmap(self.efxBmp, posx, posy - 1, True )
|
|
# img = tab.Render().ConvertToImage()
|
|
# img = img.AdjustChannels(1, 1, 1, 0.8)
|
|
# bmp = wx.BitmapFromImage(img)
|
|
mdc.DrawBitmap(tab.Render(), posx, posy, True)
|
|
else:
|
|
selected = tab
|
|
if selected:
|
|
posx, posy = selected.GetPosition()
|
|
mdc.DrawBitmap(self.efxBmp, posx, posy - 1, 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)
|
|
selpos = posx
|
|
selWidth,selHeight = selected.GetSize()
|
|
|
|
if selWidth%2:
|
|
offset = 1
|
|
else:
|
|
offset = 0
|
|
r1 = wx.Rect(0,self.containerHeight-1,selpos,1)
|
|
r2 = wx.Rect(selpos + selWidth - offset, self.containerHeight -1, self.width - selpos - selWidth,1)
|
|
mdc.GradientFillLinear(r1, startColor, selColor, wx.EAST)
|
|
mdc.GradientFillLinear(r2, selColor, startColor, wx.EAST)
|
|
|
|
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()
|
|
# simg.InitAlpha()
|
|
simg = simg.Blur(2)
|
|
simg = simg.AdjustChannels(0.2,0.2,0.2,0.3)
|
|
|
|
self.efxBmp = wx.BitmapFromImage(simg)
|
|
|
|
def AddTab(self, title = wx.EmptyString, img = None):
|
|
self.ClearTabsSelected()
|
|
|
|
tabRenderer = PFTabRenderer( (120,self.height), title, img, self.inclination)
|
|
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):
|
|
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)
|
|
|
|
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
|
|
|
|
for tab in self.tabs:
|
|
mw,mh = tab.GetMinSize()
|
|
if tabMinWidth > mw:
|
|
tabMinWidth = mw
|
|
|
|
if self.GetTabsCount() >0:
|
|
if (self.GetTabsCount()) * (tabMinWidth - self.inclination * 2) > self.tabContainerWidth:
|
|
self.tabMinWidth = float(self.tabContainerWidth) / float(self.GetTabsCount()) + self.inclination * 2
|
|
else:
|
|
self.tabMinWidth = tabMinWidth
|
|
if self.tabMinWidth <1:
|
|
self.tabMinWidth = 1
|
|
for tab in self.tabs:
|
|
w,h = tab.GetSize()
|
|
if w != self.tabMinWidth:
|
|
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))
|
|
|
|
|
|
def CalculateColor(self, color, delta):
|
|
bkR ,bkG , bkB = color
|
|
if bkR + bkG + bkB > 127*3:
|
|
scale = - delta
|
|
else:
|
|
scale = delta*2
|
|
|
|
r = bkR + scale
|
|
g = bkG + scale
|
|
b = bkB + scale
|
|
|
|
r = min(max(r,0),255)
|
|
g = min(max(g,0),255)
|
|
b = min(max(b,0),255)
|
|
|
|
return wx.Colour(r,g,b)
|
|
|
|
class PFNotebook(wx.Panel):
|
|
def __init__(self, parent):
|
|
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)
|
|
tabsSizer.Add( self.tabsContainer, 0, wx.EXPAND )
|
|
|
|
mainSizer.Add( tabsSizer, 0, wx.EXPAND, 5 )
|
|
|
|
contentSizer = wx.BoxSizer( wx.VERTICAL )
|
|
self.pageContainer = wx.Panel(self)
|
|
contentSizer.Add( self.pageContainer, 1, wx.EXPAND, 5 )
|
|
|
|
mainSizer.Add( contentSizer, 1, wx.EXPAND, 5 )
|
|
|
|
self.SetSizer( mainSizer )
|
|
self.Bind(wx.EVT_SIZE, self.OnSize)
|
|
self.Layout()
|
|
# for i in xrange(10):
|
|
# self.tabsContainer.AddTab("Pyfa TAB #%d Aw" % i)
|
|
|
|
def AddPage(self, tabWnd, tabTitle = wx.EmptyString, tabImage = None):
|
|
if self.activePage:
|
|
self.activePage.Hide()
|
|
|
|
tabWnd.Reparent(self.pageContainer)
|
|
self.pageContainer.Layout()
|
|
|
|
self.pages.append(tabWnd)
|
|
self.tabsContainer.AddTab(tabTitle, tabImage)
|
|
|
|
self.activePage = tabWnd
|
|
|
|
def SetSelected(self, page):
|
|
oldsel = self.pages.index(self.activePage)
|
|
if oldsel != page:
|
|
self.activePage.Hide()
|
|
self.activePage = self.pages[page]
|
|
self.ShowActive()
|
|
|
|
def DeletePage(self, n, internal = False):
|
|
page = self.pages[n]
|
|
|
|
self.pages.remove(page)
|
|
page.Hide()
|
|
page.Destroy()
|
|
|
|
if not internal:
|
|
self.tabsContainer.DeleteTab(n)
|
|
sel = self.tabsContainer.GetSelected()
|
|
if sel is not None:
|
|
self.activePage = self.pages[sel]
|
|
self.ShowActive()
|
|
|
|
def SwitchPages(self, src, dest, internal = False):
|
|
self.pages[src], self.pages[dest] = self.pages[dest], self.pages[src]
|
|
|
|
def ShowActive(self):
|
|
self.activePage.SetSize(self.pageContainer.GetSize())
|
|
self.activePage.Show()
|
|
self.Layout()
|
|
def OnSize(self, event):
|
|
w,h= self.GetSize()
|
|
self.tabsContainer.SetSize((w, -1))
|
|
self.tabsContainer.UpdateSize()
|
|
self.tabsContainer.Refresh()
|
|
self.Layout()
|
|
size = self.pageContainer.GetSize()
|
|
if self.activePage:
|
|
self.activePage.SetSize(size)
|
|
event.Skip()
|
|
|
|
class MiniFrame(wx.Frame):
|
|
def __init__(self):
|
|
wx.Frame.__init__(self, None, -1, 'MEGA Frame',
|
|
size=(1000, 200))
|
|
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
|
|
# self.Bind(wx.EVT_SIZE, self.OnSize)
|
|
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
|
self.nb = PFNotebook(self)
|
|
|
|
self.nb.AddPage(TestPanel(self),"TEST 1")
|
|
self.nb.AddPage(TestPanel(self),"TEST 2")
|
|
self.nb.AddPage(TestPanel(self),"TEST 3")
|
|
self.nb.AddPage(TestPanel(self),"TEST 4")
|
|
self.nb.AddPage(TestPanel(self),"TEST 5")
|
|
self.nb.AddPage(TestPanel(self),"TEST 6")
|
|
|
|
mainSizer.Add(self.nb,1,wx.EXPAND)
|
|
self.SetSizer(mainSizer)
|
|
self.Layout()
|
|
|
|
# def OnSize(self, event):
|
|
# size = self.GetRect()
|
|
# self.tabContainer.SetSize((size.width, -1))
|
|
# self.tabContainer.UpdateSize()
|
|
# self.tabContainer.Refresh()
|
|
# event.Skip()
|
|
# def OnLeftDown(self, event):
|
|
# event.Skip()
|
|
#
|
|
# def OnErase(self, event):
|
|
# pass
|
|
def OnCloseWindow(self, event):
|
|
self.Destroy()
|
|
|
|
|
|
# def OnPaint(self, event):
|
|
# rect = self.GetRect()
|
|
# canvas = wx.EmptyBitmap(rect.width, rect.height)
|
|
# mdc = wx.BufferedPaintDC(self)
|
|
# mdc.SelectObject(canvas)
|
|
#
|
|
# mdc.SetBackground (wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)))
|
|
# mdc.Clear()
|
|
#
|
|
# selected = None
|
|
# selpos = 0
|
|
# tabsWidth = 0
|
|
# offset = 10
|
|
#
|
|
# for tab in self.tabs:
|
|
# tabsWidth += tab.tabWidth - tab.lrZoneWidth/2
|
|
#
|
|
# pos = tabsWidth
|
|
#
|
|
# for i in xrange(len(self.tabs) - 1, -1, -1):
|
|
# tab = self.tabs[i]
|
|
# width = tab.tabWidth - tab.lrZoneWidth/2
|
|
# pos -= width
|
|
# if not tab.IsSelected():
|
|
# mdc.DrawBitmap(tab.Render(),pos+offset,10, True)
|
|
# tab.SetPosition((pos + offset, 10))
|
|
# else:
|
|
# selected = tab
|
|
# selpos = pos + offset
|
|
# if selected:
|
|
# mdc.DrawBitmap(selected.Render(), selpos,10,True)
|
|
# selected.SetPosition((selpos, 10))
|
|
#
|
|
# mdc.SetPen( wx.Pen("#D0D0D0", width = 1 ) )
|
|
# mdc.DrawLine(10,34,10,100)
|
|
# mdc.DrawLine(10,100,tabsWidth + 18,100)
|
|
# mdc.DrawLine(tabsWidth+18,100,tabsWidth+18,33)
|
|
|
|
class TestPanel ( wx.Panel ):
|
|
|
|
def __init__( self, parent ):
|
|
wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.TAB_TRAVERSAL )
|
|
|
|
bSizer4 = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self.m_staticText3 = wx.StaticText( self, wx.ID_ANY, u"TESSSSSSSSSST %s" % time.time(), wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE )
|
|
self.m_staticText3.Wrap( -1 )
|
|
bSizer4.Add( self.m_staticText3, 1, wx.ALL|wx.EXPAND, 5 )
|
|
|
|
self.m_button1 = wx.Button( self, wx.ID_ANY, u"MyButton", wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
bSizer4.Add( self.m_button1, 0, wx.ALL, 5 )
|
|
|
|
self.m_textCtrl1 = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
|
|
bSizer4.Add( self.m_textCtrl1, 0, wx.ALL, 5 )
|
|
|
|
self.m_staticline1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL )
|
|
bSizer4.Add( self.m_staticline1, 0, wx.EXPAND |wx.ALL, 5 )
|
|
|
|
self.m_gauge1 = wx.Gauge( self, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL )
|
|
bSizer4.Add( self.m_gauge1, 0, wx.ALL, 5 )
|
|
self.SetSizer( bSizer4 )
|
|
self.Layout()
|
|
|
|
def __del__( self ):
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
app = wx.PySimpleApp()
|
|
MiniFrame().Show()
|
|
app.MainLoop()
|