9c5c87674a
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@39158 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
782 lines
25 KiB
Python
782 lines
25 KiB
Python
#----------------------------------------------------------------------
|
|
# Name: wx.lib.splitter
|
|
# Purpose: A class similar to wx.SplitterWindow but that allows more
|
|
# than a single split
|
|
#
|
|
# Author: Robin Dunn
|
|
#
|
|
# Created: 9-June-2005
|
|
# RCS-ID: $Id$
|
|
# Copyright: (c) 2005 by Total Control Software
|
|
# Licence: wxWindows license
|
|
#----------------------------------------------------------------------
|
|
"""
|
|
This module provides the `MultiSplitterWindow` class, which is very
|
|
similar to the standard `wx.SplitterWindow` except it can be split
|
|
more than once.
|
|
"""
|
|
|
|
import wx
|
|
|
|
_RENDER_VER = (2,6,1,1)
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
class MultiSplitterWindow(wx.PyPanel):
|
|
"""
|
|
This class is very similar to `wx.SplitterWindow` except that it
|
|
allows for more than two windows and more than one sash. Many of
|
|
the same styles, constants, and methods behave the same as in
|
|
wx.SplitterWindow. The key differences are seen in the methods
|
|
that deal with the child windows managed by the splitter, and also
|
|
those that deal with the sash positions. In most cases you will
|
|
need to pass an index value to tell the class which window or sash
|
|
you are refering to.
|
|
|
|
The concept of the sash position is also different than in
|
|
wx.SplitterWindow. Since the wx.Splitterwindow has only one sash
|
|
you can think of it's position as either relative to the whole
|
|
splitter window, or as relative to the first window pane managed
|
|
by the splitter. Once there is more than one sash then the
|
|
distinciton between the two concepts needs to be clairified. I've
|
|
chosen to use the second definition, and sash positions are the
|
|
distance (either horizontally or vertically) from the origin of
|
|
the window just before the sash in the splitter stack.
|
|
|
|
NOTE: These things are not yet supported:
|
|
|
|
* Using negative sash positions to indicate a position offset
|
|
from the end.
|
|
|
|
* User controlled unsplitting (with double clicks on the sash
|
|
or dragging a sash until the pane size is zero.)
|
|
|
|
* Sash gravity
|
|
|
|
"""
|
|
def __init__(self, parent, id=-1,
|
|
pos = wx.DefaultPosition, size = wx.DefaultSize,
|
|
style = 0, name="multiSplitter"):
|
|
|
|
# always turn on tab traversal
|
|
style |= wx.TAB_TRAVERSAL
|
|
|
|
# and turn off any border styles
|
|
style &= ~wx.BORDER_MASK
|
|
style |= wx.BORDER_NONE
|
|
|
|
# initialize the base class
|
|
wx.PyPanel.__init__(self, parent, id, pos, size, style, name)
|
|
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
|
|
|
|
# initialize data members
|
|
self._windows = []
|
|
self._sashes = []
|
|
self._pending = {}
|
|
self._permitUnsplitAlways = self.HasFlag(wx.SP_PERMIT_UNSPLIT)
|
|
self._orient = wx.HORIZONTAL
|
|
self._dragMode = wx.SPLIT_DRAG_NONE
|
|
self._activeSash = -1
|
|
self._oldX = 0
|
|
self._oldY = 0
|
|
self._checkRequestedSashPosition = False
|
|
self._minimumPaneSize = 0
|
|
self._sashCursorWE = wx.StockCursor(wx.CURSOR_SIZEWE)
|
|
self._sashCursorNS = wx.StockCursor(wx.CURSOR_SIZENS)
|
|
self._sashTrackerPen = wx.Pen(wx.BLACK, 2, wx.SOLID)
|
|
self._needUpdating = False
|
|
self._isHot = False
|
|
|
|
# Bind event handlers
|
|
self.Bind(wx.EVT_PAINT, self._OnPaint)
|
|
self.Bind(wx.EVT_IDLE, self._OnIdle)
|
|
self.Bind(wx.EVT_SIZE, self._OnSize)
|
|
self.Bind(wx.EVT_MOUSE_EVENTS, self._OnMouse)
|
|
|
|
|
|
|
|
def SetOrientation(self, orient):
|
|
"""
|
|
Set whether the windows managed by the splitter will be
|
|
stacked vertically or horizontally. The default is
|
|
horizontal.
|
|
"""
|
|
assert orient in [ wx.VERTICAL, wx.HORIZONTAL ]
|
|
self._orient = orient
|
|
|
|
def GetOrientation(self):
|
|
"""
|
|
Returns the current orientation of the splitter, either
|
|
wx.VERTICAL or wx.HORIZONTAL.
|
|
"""
|
|
return self._orient
|
|
|
|
|
|
def SetMinimumPaneSize(self, minSize):
|
|
"""
|
|
Set the smallest size that any pane will be allowed to be
|
|
resized to.
|
|
"""
|
|
self._minimumPaneSize = minSize
|
|
|
|
def GetMinimumPaneSize(self):
|
|
"""
|
|
Returns the smallest allowed size for a window pane.
|
|
"""
|
|
return self._minimumPaneSize
|
|
|
|
|
|
|
|
def AppendWindow(self, window, sashPos=-1):
|
|
"""
|
|
Add a new window to the splitter at the right side or bottom
|
|
of the window stack. If sashPos is given then it is used to
|
|
size the new window.
|
|
"""
|
|
self.InsertWindow(len(self._windows), window, sashPos)
|
|
|
|
|
|
def InsertWindow(self, idx, window, sashPos=-1):
|
|
"""
|
|
Insert a new window into the splitter at the position given in
|
|
``idx``.
|
|
"""
|
|
assert window not in self._windows, "A window can only be in the splitter once!"
|
|
self._windows.insert(idx, window)
|
|
self._sashes.insert(idx, -1)
|
|
if not window.IsShown():
|
|
window.Show()
|
|
if sashPos != -1:
|
|
self._pending[window] = sashPos
|
|
self._checkRequestedSashPosition = False
|
|
self._SizeWindows()
|
|
|
|
|
|
def DetachWindow(self, window):
|
|
"""
|
|
Removes the window from the stack of windows managed by the
|
|
splitter. The window will still exist so you should `Hide` or
|
|
`Destroy` it as needed.
|
|
"""
|
|
assert window in self._windows, "Unknown window!"
|
|
idx = self._windows.index(window)
|
|
del self._windows[idx]
|
|
del self._sashes[idx]
|
|
self._SizeWindows()
|
|
|
|
|
|
def ReplaceWindow(self, oldWindow, newWindow):
|
|
"""
|
|
Replaces oldWindow (which is currently being managed by the
|
|
splitter) with newWindow. The oldWindow window will still
|
|
exist so you should `Hide` or `Destroy` it as needed.
|
|
"""
|
|
assert oldWindow in self._windows, "Unknown window!"
|
|
idx = self._windows.index(oldWindow)
|
|
self._windows[idx] = newWindow
|
|
if not newWindow.IsShown():
|
|
newWindow.Show()
|
|
self._SizeWindows()
|
|
|
|
|
|
def ExchangeWindows(self, window1, window2):
|
|
"""
|
|
Trade the positions in the splitter of the two windows.
|
|
"""
|
|
assert window1 in self._windows, "Unknown window!"
|
|
assert window2 in self._windows, "Unknown window!"
|
|
idx1 = self._windows.index(window1)
|
|
idx2 = self._windows.index(window2)
|
|
self._windows[idx1] = window2
|
|
self._windows[idx2] = window1
|
|
self._SizeWindows()
|
|
|
|
|
|
def GetWindow(self, idx):
|
|
"""
|
|
Returns the idx'th window being managed by the splitter.
|
|
"""
|
|
assert idx < len(self._windows)
|
|
return self._windows[idx]
|
|
|
|
|
|
def GetSashPosition(self, idx):
|
|
"""
|
|
Returns the position of the idx'th sash, measured from the
|
|
left/top of the window preceding the sash.
|
|
"""
|
|
assert idx < len(self._sashes)
|
|
return self._sashes[idx]
|
|
|
|
|
|
def SetSashPosition(self, idx, pos):
|
|
"""
|
|
Set the psition of the idx'th sash, measured from the left/top
|
|
of the window preceding the sash.
|
|
"""
|
|
assert idx < len(self._sashes)
|
|
self._sashes[idx] = pos
|
|
self._SizeWindows()
|
|
|
|
|
|
def SizeWindows(self):
|
|
"""
|
|
Reposition and size the windows managed by the splitter.
|
|
Useful when windows have been added/removed or when styles
|
|
have been changed.
|
|
"""
|
|
self._SizeWindows()
|
|
|
|
|
|
def DoGetBestSize(self):
|
|
"""
|
|
Overridden base class virtual. Determines the best size of
|
|
the control based on the best sizes of the child windows.
|
|
"""
|
|
best = wx.Size(0,0)
|
|
if not self._windows:
|
|
best = wx.Size(10,10)
|
|
|
|
sashsize = self._GetSashSize()
|
|
if self._orient == wx.HORIZONTAL:
|
|
for win in self._windows:
|
|
winbest = win.GetAdjustedBestSize()
|
|
best.width += max(self._minimumPaneSize, winbest.width)
|
|
best.height = max(best.height, winbest.height)
|
|
best.width += sashsize * (len(self._windows)-1)
|
|
|
|
else:
|
|
for win in self._windows:
|
|
winbest = win.GetAdjustedBestSize()
|
|
best.height += max(self._minimumPaneSize, winbest.height)
|
|
best.width = max(best.width, winbest.width)
|
|
best.height += sashsize * (len(self._windows)-1)
|
|
|
|
border = 2 * self._GetBorderSize()
|
|
best.width += border
|
|
best.height += border
|
|
return best
|
|
|
|
# -------------------------------------
|
|
# Event handlers
|
|
|
|
def _OnPaint(self, evt):
|
|
dc = wx.PaintDC(self)
|
|
self._DrawSash(dc)
|
|
|
|
|
|
def _OnSize(self, evt):
|
|
parent = wx.GetTopLevelParent(self)
|
|
if parent.IsIconized():
|
|
evt.Skip()
|
|
return
|
|
self._SizeWindows()
|
|
|
|
|
|
def _OnIdle(self, evt):
|
|
evt.Skip()
|
|
# if this is the first idle time after a sash position has
|
|
# potentially been set, allow _SizeWindows to check for a
|
|
# requested size.
|
|
if not self._checkRequestedSashPosition:
|
|
self._checkRequestedSashPosition = True
|
|
self._SizeWindows()
|
|
|
|
if self._needUpdating:
|
|
self._SizeWindows()
|
|
|
|
|
|
|
|
def _OnMouse(self, evt):
|
|
if self.HasFlag(wx.SP_NOSASH):
|
|
return
|
|
|
|
x, y = evt.GetPosition()
|
|
isLive = self.HasFlag(wx.SP_LIVE_UPDATE)
|
|
adjustNeighbor = evt.ShiftDown()
|
|
|
|
# LeftDown: set things up for dragging the sash
|
|
if evt.LeftDown() and self._SashHitTest(x, y) != -1:
|
|
self._activeSash = self._SashHitTest(x, y)
|
|
self._dragMode = wx.SPLIT_DRAG_DRAGGING
|
|
|
|
self.CaptureMouse()
|
|
self._SetResizeCursor()
|
|
|
|
if not isLive:
|
|
self._pendingPos = (self._sashes[self._activeSash],
|
|
self._sashes[self._activeSash+1])
|
|
self._DrawSashTracker(x, y)
|
|
|
|
self._oldX = x
|
|
self._oldY = y
|
|
return
|
|
|
|
# LeftUp: Finsish the drag
|
|
elif evt.LeftUp() and self._dragMode == wx.SPLIT_DRAG_DRAGGING:
|
|
self._dragMode = wx.SPLIT_DRAG_NONE
|
|
self.ReleaseMouse()
|
|
self.SetCursor(wx.STANDARD_CURSOR)
|
|
|
|
if not isLive:
|
|
# erase the old tracker
|
|
self._DrawSashTracker(self._oldX, self._oldY)
|
|
|
|
diff = self._GetMotionDiff(x, y)
|
|
|
|
# determine if we can change the position
|
|
if isLive:
|
|
oldPos1, oldPos2 = (self._sashes[self._activeSash],
|
|
self._sashes[self._activeSash+1])
|
|
else:
|
|
oldPos1, oldPos2 = self._pendingPos
|
|
newPos1, newPos2 = self._OnSashPositionChanging(self._activeSash,
|
|
oldPos1 + diff,
|
|
oldPos2 - diff,
|
|
adjustNeighbor)
|
|
if newPos1 == -1:
|
|
# the change was not allowed
|
|
return
|
|
|
|
# TODO: check for unsplit?
|
|
|
|
self._SetSashPositionAndNotify(self._activeSash, newPos1, newPos2, adjustNeighbor)
|
|
self._activeSash = -1
|
|
self._pendingPos = (-1, -1)
|
|
self._SizeWindows()
|
|
|
|
# Entering or Leaving a sash: Change the cursor
|
|
elif (evt.Moving() or evt.Leaving() or evt.Entering()) and self._dragMode == wx.SPLIT_DRAG_NONE:
|
|
if evt.Leaving() or self._SashHitTest(x, y) == -1:
|
|
self._OnLeaveSash()
|
|
else:
|
|
self._OnEnterSash()
|
|
|
|
# Dragging the sash
|
|
elif evt.Dragging() and self._dragMode == wx.SPLIT_DRAG_DRAGGING:
|
|
diff = self._GetMotionDiff(x, y)
|
|
if not diff:
|
|
return # mouse didn't move far enough
|
|
|
|
# determine if we can change the position
|
|
if isLive:
|
|
oldPos1, oldPos2 = (self._sashes[self._activeSash],
|
|
self._sashes[self._activeSash+1])
|
|
else:
|
|
oldPos1, oldPos2 = self._pendingPos
|
|
newPos1, newPos2 = self._OnSashPositionChanging(self._activeSash,
|
|
oldPos1 + diff,
|
|
oldPos2 - diff,
|
|
adjustNeighbor)
|
|
if newPos1 == -1:
|
|
# the change was not allowed
|
|
return
|
|
|
|
if newPos1 == self._sashes[self._activeSash]:
|
|
return # nothing was changed
|
|
|
|
if not isLive:
|
|
# erase the old tracker
|
|
self._DrawSashTracker(self._oldX, self._oldY)
|
|
|
|
if self._orient == wx.HORIZONTAL:
|
|
x = self._SashToCoord(self._activeSash, newPos1)
|
|
else:
|
|
y = self._SashToCoord(self._activeSash, newPos1)
|
|
|
|
# Remember old positions
|
|
self._oldX = x
|
|
self._oldY = y
|
|
|
|
if not isLive:
|
|
# draw a new tracker
|
|
self._pendingPos = (newPos1, newPos2)
|
|
self._DrawSashTracker(self._oldX, self._oldY)
|
|
else:
|
|
self._DoSetSashPosition(self._activeSash, newPos1, newPos2, adjustNeighbor)
|
|
self._needUpdating = True
|
|
|
|
|
|
# -------------------------------------
|
|
# Internal helpers
|
|
|
|
def _RedrawIfHotSensitive(self, isHot):
|
|
if not wx.VERSION >= _RENDER_VER:
|
|
return
|
|
if wx.RendererNative.Get().GetSplitterParams(self).isHotSensitive:
|
|
self._isHot = isHot
|
|
dc = wx.ClientDC(self)
|
|
self._DrawSash(dc)
|
|
|
|
|
|
def _OnEnterSash(self):
|
|
self._SetResizeCursor()
|
|
self._RedrawIfHotSensitive(True)
|
|
|
|
|
|
def _OnLeaveSash(self):
|
|
self.SetCursor(wx.STANDARD_CURSOR)
|
|
self._RedrawIfHotSensitive(False)
|
|
|
|
|
|
def _SetResizeCursor(self):
|
|
if self._orient == wx.HORIZONTAL:
|
|
self.SetCursor(self._sashCursorWE)
|
|
else:
|
|
self.SetCursor(self._sashCursorNS)
|
|
|
|
|
|
def _OnSashPositionChanging(self, idx, newPos1, newPos2, adjustNeighbor):
|
|
# TODO: check for possibility of unsplit (pane size becomes zero)
|
|
|
|
# make sure that minsizes are honored
|
|
newPos1, newPos2 = self._AdjustSashPosition(idx, newPos1, newPos2, adjustNeighbor)
|
|
|
|
# sanity check
|
|
if newPos1 <= 0:
|
|
newPos2 += newPos1
|
|
newPos1 = 0
|
|
|
|
# send the events
|
|
evt = MultiSplitterEvent(
|
|
wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, self)
|
|
evt.SetSashIdx(idx)
|
|
evt.SetSashPosition(newPos1)
|
|
if not self._DoSendEvent(evt):
|
|
# the event handler vetoed the change
|
|
newPos1 = -1
|
|
else:
|
|
# or it might have changed the value
|
|
newPos1 = evt.GetSashPosition()
|
|
|
|
if adjustNeighbor and newPos1 != -1:
|
|
evt.SetSashIdx(idx+1)
|
|
evt.SetSashPosition(newPos2)
|
|
if not self._DoSendEvent(evt):
|
|
# the event handler vetoed the change
|
|
newPos2 = -1
|
|
else:
|
|
# or it might have changed the value
|
|
newPos2 = evt.GetSashPosition()
|
|
if newPos2 == -1:
|
|
newPos1 = -1
|
|
|
|
return (newPos1, newPos2)
|
|
|
|
|
|
def _AdjustSashPosition(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
|
|
total = newPos1 + newPos2
|
|
|
|
# these are the windows on either side of the sash
|
|
win1 = self._windows[idx]
|
|
win2 = self._windows[idx+1]
|
|
|
|
# make adjustments for window min sizes
|
|
minSize = self._GetWindowMin(win1)
|
|
if minSize == -1 or self._minimumPaneSize > minSize:
|
|
minSize = self._minimumPaneSize
|
|
minSize += self._GetBorderSize()
|
|
if newPos1 < minSize:
|
|
newPos1 = minSize
|
|
newPos2 = total - newPos1
|
|
|
|
if adjustNeighbor:
|
|
minSize = self._GetWindowMin(win2)
|
|
if minSize == -1 or self._minimumPaneSize > minSize:
|
|
minSize = self._minimumPaneSize
|
|
minSize += self._GetBorderSize()
|
|
if newPos2 < minSize:
|
|
newPos2 = minSize
|
|
newPos1 = total - newPos2
|
|
|
|
return (newPos1, newPos2)
|
|
|
|
|
|
def _DoSetSashPosition(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
|
|
newPos1, newPos2 = self._AdjustSashPosition(idx, newPos1, newPos2, adjustNeighbor)
|
|
if newPos1 == self._sashes[idx]:
|
|
return False
|
|
self._sashes[idx] = newPos1
|
|
if adjustNeighbor:
|
|
self._sashes[idx+1] = newPos2
|
|
return True
|
|
|
|
|
|
def _SetSashPositionAndNotify(self, idx, newPos1, newPos2=-1, adjustNeighbor=False):
|
|
# TODO: what is the thing about _requestedSashPosition for?
|
|
|
|
self._DoSetSashPosition(idx, newPos1, newPos2, adjustNeighbor)
|
|
|
|
evt = MultiSplitterEvent(
|
|
wx.wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, self)
|
|
evt.SetSashIdx(idx)
|
|
evt.SetSashPosition(newPos1)
|
|
self._DoSendEvent(evt)
|
|
|
|
if adjustNeighbor:
|
|
evt.SetSashIdx(idx+1)
|
|
evt.SetSashPosition(newPos2)
|
|
self._DoSendEvent(evt)
|
|
|
|
|
|
def _GetMotionDiff(self, x, y):
|
|
# find the diff from the old pos
|
|
if self._orient == wx.HORIZONTAL:
|
|
diff = x - self._oldX
|
|
else:
|
|
diff = y - self._oldY
|
|
return diff
|
|
|
|
|
|
def _SashToCoord(self, idx, sashPos):
|
|
coord = 0
|
|
for i in range(idx):
|
|
coord += self._sashes[i]
|
|
coord += self._GetSashSize()
|
|
coord += sashPos
|
|
return coord
|
|
|
|
|
|
def _GetWindowMin(self, window):
|
|
if self._orient == wx.HORIZONTAL:
|
|
return window.GetMinWidth()
|
|
else:
|
|
return window.GetMinHeight()
|
|
|
|
|
|
def _GetSashSize(self):
|
|
if self.HasFlag(wx.SP_NOSASH):
|
|
return 0
|
|
if wx.VERSION >= _RENDER_VER:
|
|
return wx.RendererNative.Get().GetSplitterParams(self).widthSash
|
|
else:
|
|
return 5
|
|
|
|
|
|
def _GetBorderSize(self):
|
|
if wx.VERSION >= _RENDER_VER:
|
|
return wx.RendererNative.Get().GetSplitterParams(self).border
|
|
else:
|
|
return 0
|
|
|
|
|
|
def _DrawSash(self, dc):
|
|
if wx.VERSION >= _RENDER_VER:
|
|
if self.HasFlag(wx.SP_3DBORDER):
|
|
wx.RendererNative.Get().DrawSplitterBorder(
|
|
self, dc, self.GetClientRect())
|
|
|
|
# if there are no splits then we're done.
|
|
if len(self._windows) < 2:
|
|
return
|
|
|
|
# if we are not supposed to use a sash then we're done.
|
|
if self.HasFlag(wx.SP_NOSASH):
|
|
return
|
|
|
|
# Reverse the sense of the orientation, in this case it refers
|
|
# to the direction to draw the sash not the direction that
|
|
# windows are stacked.
|
|
orient = { wx.HORIZONTAL : wx.VERTICAL,
|
|
wx.VERTICAL : wx.HORIZONTAL }[self._orient]
|
|
|
|
flag = 0
|
|
if self._isHot:
|
|
flag = wx.CONTROL_CURRENT
|
|
|
|
pos = 0
|
|
for sash in self._sashes[:-1]:
|
|
pos += sash
|
|
if wx.VERSION >= _RENDER_VER:
|
|
wx.RendererNative.Get().DrawSplitterSash(self, dc,
|
|
self.GetClientSize(),
|
|
pos, orient, flag)
|
|
else:
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
dc.SetBrush(wx.Brush(self.GetBackgroundColour()))
|
|
sashsize = self._GetSashSize()
|
|
if orient == wx.VERTICAL:
|
|
x = pos
|
|
y = 0
|
|
w = sashsize
|
|
h = self.GetClientSize().height
|
|
else:
|
|
x = 0
|
|
y = pos
|
|
w = self.GetClientSize().width
|
|
h = sashsize
|
|
dc.DrawRectangle(x, y, w, h)
|
|
|
|
pos += self._GetSashSize()
|
|
|
|
|
|
def _DrawSashTracker(self, x, y):
|
|
# Draw a line to represent the dragging sash, for when not
|
|
# doing live updates
|
|
w, h = self.GetClientSize()
|
|
dc = wx.ScreenDC()
|
|
|
|
if self._orient == wx.HORIZONTAL:
|
|
x1 = x
|
|
y1 = 2
|
|
x2 = x
|
|
y2 = h-2
|
|
if x1 > w:
|
|
x1 = w
|
|
x2 = w
|
|
elif x1 < 0:
|
|
x1 = 0
|
|
x2 = 0
|
|
else:
|
|
x1 = 2
|
|
y1 = y
|
|
x2 = w-2
|
|
y2 = y
|
|
if y1 > h:
|
|
y1 = h
|
|
y2 = h
|
|
elif y1 < 0:
|
|
y1 = 0
|
|
y2 = 0
|
|
|
|
x1, y1 = self.ClientToScreenXY(x1, y1)
|
|
x2, y2 = self.ClientToScreenXY(x2, y2)
|
|
|
|
dc.SetLogicalFunction(wx.INVERT)
|
|
dc.SetPen(self._sashTrackerPen)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.DrawLine(x1, y1, x2, y2)
|
|
dc.SetLogicalFunction(wx.COPY)
|
|
|
|
|
|
def _SashHitTest(self, x, y, tolerance=5):
|
|
# if there are no splits then we're done.
|
|
if len(self._windows) < 2:
|
|
return -1
|
|
|
|
if self._orient == wx.HORIZONTAL:
|
|
z = x
|
|
else:
|
|
z = y
|
|
|
|
pos = 0
|
|
for idx, sash in enumerate(self._sashes[:-1]):
|
|
pos += sash
|
|
hitMin = pos - tolerance
|
|
hitMax = pos + self._GetSashSize() + tolerance
|
|
|
|
if z >= hitMin and z <= hitMax:
|
|
return idx
|
|
|
|
pos += self._GetSashSize()
|
|
|
|
return -1
|
|
|
|
|
|
def _SizeWindows(self):
|
|
# no windows yet?
|
|
if not self._windows:
|
|
return
|
|
|
|
# are there any pending size settings?
|
|
for window, spos in self._pending.items():
|
|
idx = self._windows.index(window)
|
|
# TODO: this may need adjusted to make sure they all fit
|
|
# in the current client size
|
|
self._sashes[idx] = spos
|
|
del self._pending[window]
|
|
|
|
# are there any that still have a -1?
|
|
for idx, spos in enumerate(self._sashes[:-1]):
|
|
if spos == -1:
|
|
# TODO: this should also be adjusted
|
|
self._sashes[idx] = 100
|
|
|
|
cw, ch = self.GetClientSize()
|
|
border = self._GetBorderSize()
|
|
sash = self._GetSashSize()
|
|
|
|
if len(self._windows) == 1:
|
|
# there's only one, it's an easy layout
|
|
self._windows[0].SetDimensions(border, border,
|
|
cw - 2*border, ch - 2*border)
|
|
else:
|
|
if 'wxMSW' in wx.PlatformInfo:
|
|
self.Freeze()
|
|
if self._orient == wx.HORIZONTAL:
|
|
x = y = border
|
|
h = ch - 2*border
|
|
for idx, spos in enumerate(self._sashes[:-1]):
|
|
self._windows[idx].SetDimensions(x, y, spos, h)
|
|
x += spos + sash
|
|
# last one takes the rest of the space. TODO make this configurable
|
|
last = cw - 2*border - x
|
|
self._windows[idx+1].SetDimensions(x, y, last, h)
|
|
if last > 0:
|
|
self._sashes[idx+1] = last
|
|
else:
|
|
x = y = border
|
|
w = cw - 2*border
|
|
for idx, spos in enumerate(self._sashes[:-1]):
|
|
self._windows[idx].SetDimensions(x, y, w, spos)
|
|
y += spos + sash
|
|
# last one takes the rest of the space. TODO make this configurable
|
|
last = ch - 2*border - y
|
|
self._windows[idx+1].SetDimensions(x, y, w, last)
|
|
if last > 0:
|
|
self._sashes[idx+1] = last
|
|
if 'wxMSW' in wx.PlatformInfo:
|
|
self.Thaw()
|
|
|
|
self._DrawSash(wx.ClientDC(self))
|
|
self._needUpdating = False
|
|
|
|
|
|
def _DoSendEvent(self, evt):
|
|
return not self.GetEventHandler().ProcessEvent(evt) or evt.IsAllowed()
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
class MultiSplitterEvent(wx.PyCommandEvent):
|
|
"""
|
|
This event class is almost the same as `wx.SplitterEvent` except
|
|
it adds an accessor for the sash index that is being changed. The
|
|
same event type IDs and event binders are used as with
|
|
`wx.SplitterEvent`.
|
|
"""
|
|
def __init__(self, type=wx.wxEVT_NULL, splitter=None):
|
|
wx.PyCommandEvent.__init__(self, type)
|
|
if splitter:
|
|
self.SetEventObject(splitter)
|
|
self.SetId(splitter.GetId())
|
|
self.sashIdx = -1
|
|
self.sashPos = -1
|
|
self.isAllowed = True
|
|
|
|
def SetSashIdx(self, idx):
|
|
self.sashIdx = idx
|
|
|
|
def SetSashPosition(self, pos):
|
|
self.sashPos = pos
|
|
|
|
def GetSashIdx(self):
|
|
return self.sashIdx
|
|
|
|
def GetSashPosition(self):
|
|
return self.sashPos
|
|
|
|
# methods from wx.NotifyEvent
|
|
def Veto(self):
|
|
self.isAllowed = False
|
|
def Allow(self):
|
|
self.isAllowed = True
|
|
def IsAllowed(self):
|
|
return self.isAllowed
|
|
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
|