microproduct/atmosphericDelay/ISCEApp/site-packages/wx/lib/gizmos/dynamicsash.py

1203 lines
42 KiB
Python

#----------------------------------------------------------------------
# Name: wx.lib.gizmos.dynamicsash
# Purpose: A Python port of the C++ wxDynamicSashWindow from the
# old wxCode library.
#
# Author: Robin Dunn
#
# Created: 30-Oct-2017
# Copyright: (c) 2018 by Total Control Software
# Licence: wxWindows license
# Tags:
#----------------------------------------------------------------------
"""
A window which can be dynamically split to an arbitrary depth and later
reunified through the user interface.
"""
import wx
import wx.siplib
#----------------------------------------------------------------------------
# Styles
# DS_MANAGE_SCROLLBARS is a default style of DynamicSashWindow which
# will cause it to respond to scrollbar events for your application by
# automatically scrolling the child view.
DS_MANAGE_SCROLLBARS = 0x0010
# DS_DRAG_CORNER style indicates that the views can also be resized by
# dragging the corner piece between the scrollbars, and which is reflected up
# to the frame if necessary.
DS_DRAG_CORNER = 0x0020
# Default style
DS_DEFAULT = DS_MANAGE_SCROLLBARS | DS_DRAG_CORNER
#----------------------------------------------------------------------------
# Events
wxEVT_DYNAMIC_SASH_SPLIT = wx.NewEventType()
wxEVT_DYNAMIC_SASH_UNIFY = wx.NewEventType()
EVT_DYNAMIC_SASH_SPLIT = wx.PyEventBinder(wxEVT_DYNAMIC_SASH_SPLIT, 1)
EVT_DYNAMIC_SASH_UNIFY = wx.PyEventBinder(wxEVT_DYNAMIC_SASH_UNIFY, 1)
class DynamicSashSplitEvent(wx.PyCommandEvent):
"""
DynamicSashSplitEvents are sent to your view by DynamicSashWindow whenever
your view is being split by the user. It is your responsibility to handle
this event by creating a new view window as a child of the
DynamicSashWindow. DynamicSashWindow will automatically reparent it to
the proper place in its window hierarchy.
"""
def __init__(self, arg=None):
super(DynamicSashSplitEvent, self).__init__()
if isinstance(arg, DynamicSashSplitEvent):
obj = arg.GetEventObject()
else:
obj = arg
self.SetEventObject(obj)
self.SetEventType(wxEVT_DYNAMIC_SASH_SPLIT)
class DynamicSashUnifyEvent(wx.PyCommandEvent):
"""
DynamicSashUnifyEvents are sent to your view by DynamicSashWindow whenever
the sash which splits your view and its sibling is being reunified such
that your view is expanding to replace its sibling. You needn't do
anything with this event if you are allowing DynamicSashWindow to manage
your view's scrollbars, but it is useful if you are managing the
scrollbars yourself so that you can keep the scrollbars' event handlers
connected to your view's event handler class.
"""
def __init__(self, arg=None):
super(DynamicSashUnifyEvent, self).__init__()
if isinstance(arg, DynamicSashUnifyEvent):
obj = arg.GetEventObject()
else:
obj = arg
self.SetEventObject(obj)
self.SetEventType(wxEVT_DYNAMIC_SASH_UNIFY)
#----------------------------------------------------------------------------
# The public window class
class DynamicSashWindow(wx.Window):
"""
A DynamicSashWindow widget manages the way other widgets are viewed. When
a DynamicSashWindow is first shown, it will contain one child view, a
viewport for that child, and a pair of scrollbars to allow the user to
navigate the child view area. Next to each scrollbar is a small tab. By
clicking on either tab and dragging to the appropriate spot, a user can
split the view area into two smaller views separated by a draggable sash.
Later, when the user wishes to reunify the two subviews, the user simply
drags the sash to the side of the window. DynamicSashWindow will
automatically reparent the appropriate child view back up the window
hierarchy, and the DynamicSashWindow will have only one child view once
again.
As an application developer, you will simply create a DynamicSashWindow
using either the Create() function or the more complex constructor
provided below, and then create a view window whose parent is the
DynamicSashWindow. The child should respond to DynamicSashSplitEvents --
perhaps with an OnSplit() event handler -- by constructing a new view
window whose parent is also the DynamicSashWindow. That's it! Now your
users can dynamically split and reunify the view you provided.
If you wish to handle the scrollbar events for your view, rather than
allowing DynamicSashWindow to do it for you, things are a bit more
complex. (You might want to handle scrollbar events yourself, if, for
instance, you wish to scroll a subwindow of the view you add to your
DynamicSashWindow object, rather than scrolling the whole view.) In this
case, you will need to construct your DynamicSashWindow without the
wxDS_MANAGE_SCROLLBARS style and you will need to use the GetHScrollBar()
and GetVScrollBar() methods to retrieve the scrollbar controls and call
SetEventHandler() on them to redirect the scrolling events whenever your
window is reparented by wxDyanmicSashWindow. You will need to set the
scrollbars' event handler at three times:
* When your view is created When your view receives a
* DynamicSashSplitEvent When your view receives a
* DynamicSashUnifyEvent
See the dynsash_switch sample application for an example which does this.
"""
def __init__(self, *args, **kw):
"""
Create a new DynamicSashWindow.
Both the normal constructor style with all parameters, or wxWidgets
2-phase style default constructor is supported. If the default
constructor is used then the Create method will need to be called
later before the widget can actually be used.
"""
if not args and not kw:
self._init_default()
else:
self._init_full(*args, **kw)
def _init_default(self):
super(DynamicSashWindow, self).__init__()
self._init()
def _init_full(self, parent, id=wx.ID_ANY,
pos=wx.DefaultPosition, size=wx.DefaultSize,
style=DS_DEFAULT, name='dynamicSashWindow'):
super(DynamicSashWindow, self).__init__(parent, id, pos, size, style, name=name)
self._init()
self._post_create()
def Create(self, parent, id=wx.ID_ANY,
pos=wx.DefaultPosition, size=wx.DefaultSize,
style=DS_DEFAULT, name='dynamicSashWindow'):
super(DynamicSashWindow, self).Create(parent, id, pos, size, style, name=name)
self._post_create()
def _init(self):
# set default attributes
self.m_impl = None
def _post_create(self):
self.m_impl = _DynamicSashWindowImpl(self)
if not self.m_impl.Create():
self.m_impl.Destroy()
self.m_impl = None
return False
return True
def __dtor__(self):
# break possible cycles and etc.
self.SetEventHandler(self)
self.m_impl.Destroy()
self.m_impl = None
def GetHScrollBar(self, child):
return self.m_impl.FindScrollBar(child, 0)
def GetVScrollBar(self, child):
return self.m_impl.FindScrollBar(child, 1)
def AddChild(self, child):
super(DynamicSashWindow, self).AddChild(child)
self.m_impl.AddChild(child)
#==========================================================================
# Just internal "implementation details" from here down
# DynamicSashWindow works by internally storing a tree of Implementation
# objects (_DynamicSsahWindowImpl) and Leaf objects (_DynamicSashWindowLeaf).
# The DynamicSashWindow has a pointer to one implementation, and each
# implementation either has a pointer to a one leaf (_leaf) or a pointer to
# two children implementation objects (_child[]). The leaves each are
# responsible for drawing the frame and decorations around one user-provided
# views and for responding to mouse and scrollbar events.
#
# A resulting tree might look something like this:
#
# DynamicSashWindow
# |
# +- _DynamicSashWindowImpl
# |
# +- _DynamicSashWindowLeaf
# | |
# | +- user view window
# |
# +- _DynamicSashWindowImpl
# |
# +- _DynamicSashWindowLeaf
# | |
# | +- user view window
# |
# +- _DynamicSashWindowLeaf
# |
# +- user view window
#
# Each time a split occurs, one of the implementation objects removes its
# leaf, generates two new implementation object children, each with a new
# leaf, and reparents the user view which was connected to its old leaf to be
# one of the new leaf's user view, and sends a Split event to the user view in
# the hopes that it will generate a new user view for the other new leaf.
#
# When a unification occurs, an implementation object is replaced by one of
# its children, and the tree of its other child is pruned.
#
# One quirk is that the top-level implementation object (m_top) always keeps a
# pointer to the implementation object where a new child is needed.
# (_add_child_target). This is so that when a new user view is added to the
# hierarchy, AddChild() is able to reparent the new user view to the correct
# implementation object's leaf.
_wxEVT_DYNAMIC_SASH_REPARENT = wx.NewEventType()
_EVT_DYNAMIC_SASH_REPARENT = wx.PyEventBinder(_wxEVT_DYNAMIC_SASH_REPARENT, 1)
class _DynamicSashReparentEvent(wx.PyEvent):
def __init__(self, arg=None):
super(_DynamicSashReparentEvent, self).__init__()
if isinstance(arg, _DynamicSashReparentEvent):
obj = arg.GetEventObject()
else:
obj = arg
self.SetEventObject(obj)
self.SetEventType(_wxEVT_DYNAMIC_SASH_REPARENT)
# enum DynamicSashRegion
_DSR_NONE = 0
_DSR_VERTICAL_TAB = 1
_DSR_HORIZONTAL_TAB = 2
_DSR_CORNER = 3
_DSR_LEFT_EDGE = 4
_DSR_TOP_EDGE = 5
_DSR_RIGHT_EDGE = 6
_DSR_BOTTOM_EDGE = 7
_isMac = 'wxMac' in wx.PlatformInfo
#----------------------------------------------------------------------------
class _DynamicSashWindowImpl(wx.EvtHandler):
def __init__(self, window):
super(_DynamicSashWindowImpl, self).__init__()
self.m_window = window
self.m_add_child_target = self
self.m_container = None
self.m_parent = None
self.m_top = self
self.m_child = [None, None]
self.m_leaf = None
self.m_dragging = _DSR_NONE
self.m_split = _DSR_NONE
self.m_drag_x = -1
self.m_drag_y = -1
if _isMac:
self.m_overlay = wx.Overlay()
self.DrawSash = self.DrawSash_overlay
def __dtor__(self):
# break possible cycles and other cleanup
if self.m_leaf:
self.m_leaf.Destroy()
self.m_leaf = None
if self.m_child[0]:
self.m_child[0].Destroy()
self.m_child[0] = None
if self.m_child[1]:
self.m_child[1].Destroy()
self.m_child[1] = None
if self.m_container != self.m_window and self.m_container:
self.m_container.SetEventHandler(self.m_container)
self.m_container.Destroy()
self.m_add_child_target = None
self.m_top = None
self.m_window = None
def Create(self):
if not self.m_container:
self.m_container = self.m_window
self.m_container.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
self.m_leaf = _DynamicSashWindowLeaf(self)
if not self.m_leaf.Create():
self.m_leaf.Destroy()
self.m_leaf = None
return False
self.m_container.SetEventHandler(self)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseMove)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
self.Bind(wx.EVT_LEFT_DOWN, self.OnPress)
self.Bind(wx.EVT_LEFT_UP, self.OnRelease)
return True
def AddChild(self, window):
if self.m_add_child_target and self.m_add_child_target.m_leaf:
self.m_add_child_target.m_leaf.AddChild(window)
def DrawSash_overlay(self, x, y, mode):
if mode in ['press', 'move--']:
# these are not needed for this implementation
return
if mode == 'release':
dc = wx.ClientDC(self.m_container)
odc = wx.DCOverlay(self.m_overlay, dc)
odc.Clear()
del odc
self.m_overlay.Reset()
if mode == 'move':
dc = wx.ClientDC(self.m_container)
odc = wx.DCOverlay(self.m_overlay, dc)
odc.Clear()
penclr = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)
dc.SetPen(wx.Pen(penclr, 1))
bshclr = penclr.Get(False) + (0x80,)
bshclr = wx.Colour(*bshclr)
dc.SetBrush(wx.Brush(bshclr))
self._doDrawSash(dc, x, y, False)
def DrawSash(self, x, y, mode):
dc = wx.ScreenDC()
dc.StartDrawingOnTop(self.m_container)
bmp = wx.Bitmap(8, 8)
bdc = wx.MemoryDC(bmp)
bdc.DrawRectangle(-1, -1, 10, 10)
for i in range(8):
for j in range(8):
if (i + j) & 1:
bdc.DrawPoint(i, j)
bdc.SelectObject(wx.NullBitmap)
brush = wx.Brush(bmp)
dc.SetBrush(brush)
dc.SetLogicalFunction(wx.XOR)
self._doDrawSash(dc, x, y, True)
dc.EndDrawingOnTop()
def _doDrawSash(self, dc, x, y, useScreenCoords):
if (self.m_dragging == _DSR_CORNER
and (self.m_window.GetWindowStyle() & DS_DRAG_CORNER) != 0):
cx = cy = 0
if useScreenCoords:
cx, cy = self.m_container.ClientToScreen((cx, cy))
x, y = self.m_container.ClientToScreen((x, y))
if cx < x and cy < y:
dc.DrawRectangle(cx - 2, cy - 2, x - cx + 4, 4)
dc.DrawRectangle(x - 2, cy + 2, 4, y - cy)
dc.DrawRectangle(cx - 2, cy + 2, 4, y - cy)
dc.DrawRectangle(cx + 2, y - 2, x - cx - 4, 4)
else:
body_w, body_h = self.m_container.GetClientSize()
if y < 0:
y = 0
if y > body_h:
y = body_h
if x < 0:
x = 0
if x > body_w:
x = body_w
if self.m_dragging == _DSR_HORIZONTAL_TAB:
x = 0
else:
y = 0
if useScreenCoords:
x, y = self.m_container.ClientToScreen(x, y)
w = body_w
h = body_h
if self.m_dragging == _DSR_HORIZONTAL_TAB:
dc.DrawRectangle(x, y - 2, w, 4)
else:
dc.DrawRectangle(x - 2, y, 4, h)
def ConstrainChildren(self, px, py):
layout = wx.LayoutConstraints()
layout.left.SameAs(self.m_container, wx.Left)
layout.top.SameAs(self.m_container, wx.Top)
if self.m_split == _DSR_HORIZONTAL_TAB:
layout.right.SameAs(self.m_container, wx.Right)
layout.height.PercentOf(self.m_container, wx.Height, py)
else:
layout.bottom.SameAs(self.m_container, wx.Bottom)
layout.width.PercentOf(self.m_container, wx.Width, px)
self.m_child[0].m_container.SetConstraints(layout)
layout = wx.LayoutConstraints()
layout.right.SameAs(self.m_container, wx.Right)
layout.bottom.SameAs(self.m_container, wx.Bottom)
if self.m_split == _DSR_HORIZONTAL_TAB:
layout.top.Below(self.m_child[0].m_container, 1)
layout.left.SameAs(self.m_container, wx.Left)
else:
layout.left.RightOf(self.m_child[0].m_container, 1)
layout.top.SameAs(self.m_container, wx.Top)
self.m_child[1].m_container.SetConstraints(layout)
def Split(self, px, py):
self.m_add_child_target = None
self.m_child[0] = _DynamicSashWindowImpl(self.m_window)
self.m_child[0].m_container = wx.Window(self.m_container)
self.m_child[0].m_parent = self
self.m_child[0].m_top = self.m_top
self.m_child[0].Create()
if self.m_leaf.m_child:
self.m_leaf.m_child.Reparent(self.m_container)
self.m_child[0].AddChild(self.m_leaf.m_child)
self.m_child[1] = _DynamicSashWindowImpl(self.m_window)
self.m_child[1].m_container = wx.Window(self.m_container)
self.m_child[1].m_parent = self
self.m_child[1].m_top = self.m_top
self.m_child[1].Create()
self.m_split = self.m_dragging
self.ConstrainChildren(px, py)
self.m_top.m_add_child_target = self.m_child[1]
split = DynamicSashSplitEvent(self.m_child[0].m_leaf.m_child)
self.m_child[0].m_leaf._checkPendingChild()
self.m_child[0].m_leaf.m_child.GetEventHandler().ProcessEvent(split)
self.m_child[0].m_leaf.m_vscroll.SetScrollbar(self.m_leaf.m_vscroll.GetThumbPosition(),
self.m_leaf.m_vscroll.GetThumbSize(),
self.m_leaf.m_vscroll.GetRange(),
self.m_leaf.m_vscroll.GetPageSize())
self.m_child[0].m_leaf.m_hscroll.SetScrollbar(self.m_leaf.m_hscroll.GetThumbPosition(),
self.m_leaf.m_hscroll.GetThumbSize(),
self.m_leaf.m_hscroll.GetRange(),
self.m_leaf.m_hscroll.GetPageSize())
self.m_child[1].m_leaf.m_vscroll.SetScrollbar(self.m_leaf.m_vscroll.GetThumbPosition(),
self.m_leaf.m_vscroll.GetThumbSize(),
self.m_leaf.m_vscroll.GetRange(),
self.m_leaf.m_vscroll.GetPageSize())
self.m_child[1].m_leaf.m_hscroll.SetScrollbar(self.m_leaf.m_hscroll.GetThumbPosition(),
self.m_leaf.m_hscroll.GetThumbSize(),
self.m_leaf.m_hscroll.GetRange(),
self.m_leaf.m_hscroll.GetPageSize())
self.m_leaf.Destroy()
self.m_leaf = None
self.m_container.Layout()
def Unify(self, panel):
other = 1 if panel == 0 else 0
if self.m_child[panel].m_leaf:
child = self.m_child[:]
self.m_child[0] = self.m_child[1] = None
self.m_leaf = _DynamicSashWindowLeaf(self)
self.m_leaf.Create()
self.m_leaf.m_child = child[panel].m_leaf.m_child
self.m_leaf.m_vscroll.SetScrollbar(child[panel].m_leaf.m_vscroll.GetThumbPosition(),
child[panel].m_leaf.m_vscroll.GetThumbSize(),
child[panel].m_leaf.m_vscroll.GetRange(),
child[panel].m_leaf.m_vscroll.GetPageSize())
self.m_leaf.m_hscroll.SetScrollbar(child[panel].m_leaf.m_hscroll.GetThumbPosition(),
child[panel].m_leaf.m_hscroll.GetThumbSize(),
child[panel].m_leaf.m_hscroll.GetRange(),
child[panel].m_leaf.m_hscroll.GetPageSize())
self.m_add_child_target = None
event = _DynamicSashReparentEvent(self.m_leaf)
self.m_leaf.ProcessEvent(event)
for i in range(2):
child[i].Destroy()
self.m_split = _DSR_NONE
unify = DynamicSashUnifyEvent(self.m_leaf.m_child)
self.m_leaf.m_child.GetEventHandler().ProcessEvent(unify)
else:
self.m_split = self.m_child[panel].m_split
self.m_child[other].Destroy()
child_panel = self.m_child[panel]
self.m_child[0] = child_panel.m_child[0]
self.m_child[1] = child_panel.m_child[1]
self.m_child[0].m_parent = self
self.m_child[1].m_parent = self
self.m_add_child_target = None
self.m_child[0].m_container.Reparent(self.m_container)
self.m_child[1].m_container.Reparent(self.m_container)
child_panel.m_child[0] = child_panel.m_child[1] = None
child_panel.Destroy()
size = self.m_container.GetSize()
child_size = self.m_child[0].m_container.GetSize()
self.ConstrainChildren(child_size.GetWidth() * 100.0 / size.GetWidth(),
child_size.GetHeight() * 100.0 / size.GetHeight())
self.m_container.Layout()
def Resize(self, x, y):
h_parent = self.FindParent(_DSR_BOTTOM_EDGE)
v_parent = self.FindParent(_DSR_RIGHT_EDGE)
h_unify = -1
v_unify = -1
frame = self.FindFrame()
if x < 0:
x = 0
if y < 0:
y = 0
if h_parent:
_, y = self.m_container.ClientToScreen(0, y)
_, y = h_parent.m_container.ScreenToClient(0, y)
py = int((y * 100.0) / h_parent.m_container.GetSize().GetHeight() + 0.5)
if py < 10:
ho_parent = self.FindParent(_DSR_TOP_EDGE)
if ho_parent:
if self.FindUpperParent(h_parent, ho_parent) == ho_parent:
h_unify = 1
else:
py = int((ho_parent.m_child[0].m_container.GetSize().GetHeight() * 100.0)
/ h_parent.m_container.GetSize().GetHeight() + 0.5)
h_parent.m_child[0].m_container.GetConstraints().height.PercentOf(
h_parent.m_container, wx.Height, py)
h_parent = ho_parent
h_unify = 0
else:
h_unify = 1
elif py > 90:
h_unify = 0
else:
h_parent.m_child[0].m_container.GetConstraints().height.PercentOf(
h_parent.m_container, wx.Height, py)
h_parent.m_container.Layout()
else:
do_resize = 1
h_parent = self.FindParent(_DSR_TOP_EDGE)
if h_parent:
py = int((y * 100.0) /
(h_parent.m_container.GetSize().GetHeight() +
y - self.m_container.GetSize().GetHeight()) + 0.5)
if py < 10:
h_unify = 0
elif y < 64:
do_resize = 0
if do_resize:
size = frame.GetSize()
frame.SetSize(size.GetWidth(), size.GetHeight() + y - self.m_container.GetSize().GetHeight())
if v_parent:
x, _ = self.m_container.ClientToScreen(x, 0)
x, _ = v_parent.m_container.ScreenToClient(x, 0)
px = int((x * 100.0) / v_parent.m_container.GetSize().GetWidth() + 0.5)
if px < 10:
vo_parent = self.FindParent(_DSR_LEFT_EDGE)
if vo_parent:
if self.FindUpperParent(v_parent, vo_parent) == vo_parent:
v_unify = 1
else:
px = int((vo_parent.m_child[0].m_container.GetSize().GetWidth() * 100.0)
/ v_parent.m_container.GetSize().GetWidth() + 0.5)
v_parent.m_child[0].m_container.GetConstraints().width.PercentOf(
v_parent.m_container, wx.Width, px)
v_parent = vo_parent
v_unify = 0
else:
v_unify = 1
elif px > 90:
v_unify = 0
else:
v_parent.m_child[0].m_container.GetConstraints().width.PercentOf(
v_parent.m_container, wx.Width, px)
v_parent.m_container.Layout()
else:
do_resize = 1
v_parent = self.FindParent(_DSR_LEFT_EDGE)
if v_parent:
px = int((x * 100.0) /
(v_parent.m_container.GetSize().GetWidth() +
x - self.m_container.GetSize().GetWidth()) + 0.5)
if px < 10:
v_unify = 0
elif x < 64:
do_resize = 0
if do_resize:
size = frame.GetSize()
frame.SetSize(size.GetWidth() + x - self.m_container.GetSize().GetWidth(), size.GetHeight())
if h_unify != -1 and v_unify != -1:
parent = self.FindUpperParent(h_parent, v_parent)
if parent == h_parent:
h_parent.Unify(h_unify)
else:
v_parent.Unify(v_unify)
elif h_unify != -1:
h_parent.Unify(h_unify)
elif v_unify != -1:
v_parent.Unify(v_unify)
def FindParent(self, side):
if self.m_parent is None:
return None
if self.m_parent.m_split == _DSR_HORIZONTAL_TAB:
if side == _DSR_TOP_EDGE and self.m_parent.m_child[1] == self:
return self.m_parent
if side == _DSR_BOTTOM_EDGE and self.m_parent.m_child[0] == self:
return self.m_parent
elif self.m_parent.m_split == _DSR_VERTICAL_TAB:
if side == _DSR_LEFT_EDGE and self.m_parent.m_child[1] == self:
return self.m_parent
if side == _DSR_RIGHT_EDGE and self.m_parent.m_child[0] == self:
return self.m_parent
return self.m_parent.FindParent(side)
def FindUpperParent(self, sash_a, sash_b):
win = sash_a.m_container.GetParent()
while win and not win.IsTopLevel():
if win == sash_b.m_container:
return sash_b
win = win.GetParent()
return sash_a
def FindFrame(self):
win = self.m_window.GetParent()
while win and not win.IsTopLevel():
win = win.GetParent()
return win
def FindScrollBar(self, child, vert):
if self.m_child[0] is None and self.m_leaf is None:
return None
if not self.m_child[0]:
return self.m_leaf.FindScrollBar(child, vert)
ret = self.m_child[0].FindScrollBar(child, vert)
if not ret:
ret = self.m_child[1].FindScrollBar(child, vert)
return ret
def OnSize(self, event):
self.m_container.Layout()
if self.m_leaf:
self.m_leaf.OnSize(event)
def OnPaint(self, event):
if self.m_leaf:
self.m_leaf.OnPaint(event)
else:
dc = wx.PaintDC(self.m_container)
dc.SetBackground(wx.Brush(self.m_container.GetBackgroundColour(), wx.SOLID))
dc.Clear()
def OnMouseMove(self, event):
if self.m_dragging:
self.DrawSash(self.m_drag_x, self.m_drag_y, "move--")
self.m_drag_x = event.x
self.m_drag_y = event.y
self.DrawSash(self.m_drag_x, self.m_drag_y, "move")
elif self.m_leaf:
self.m_leaf.OnMouseMove(event)
def OnLeave(self, event):
if self.m_leaf:
self.m_leaf.OnLeave(event)
def OnPress(self, event):
if self.m_leaf:
self.m_leaf.OnPress(event)
else:
self.m_dragging = self.m_split
self.m_drag_x = event.x
self.m_drag_y = event.y
self.DrawSash(self.m_drag_x, self.m_drag_y, "press")
self.m_container.CaptureMouse()
def OnRelease(self, event):
if ((self.m_dragging == _DSR_CORNER) and
(self.m_window.GetWindowStyle() & DS_DRAG_CORNER) != 0):
self.DrawSash(self.m_drag_x, self.m_drag_y, "release")
self.m_container.ReleaseMouse()
self.Resize(event.x, event.y)
self.m_dragging = _DSR_NONE
elif self.m_dragging:
self.DrawSash(self.m_drag_x, self.m_drag_y, "release")
self.m_container.ReleaseMouse()
size = self.m_container.GetSize()
px = int((event.x * 100.0) / size.GetWidth() + 0.5)
py = int((event.y * 100.0) / size.GetHeight() + 0.5)
if ((self.m_dragging == _DSR_HORIZONTAL_TAB and py >= 10 and py <= 90)
or (self.m_dragging == _DSR_VERTICAL_TAB and px >= 10 and px <= 90)):
if self.m_child[0] == None:
self.Split(px, py)
else:
# It would be nice if moving *this* sash didn't implicitly move
# the sashes of our children (if any). But this will do.
layout = self.m_child[0].m_container.GetConstraints()
if self.m_split == _DSR_HORIZONTAL_TAB:
layout.height.PercentOf(self.m_container, wx.Height, py)
else:
layout.width.PercentOf(self.m_container, wx.Width, px)
self.m_container.Layout()
else:
if self.m_child[0] != None:
if ((self.m_dragging == _DSR_HORIZONTAL_TAB and py <= 10)
or (self.m_dragging == _DSR_VERTICAL_TAB and px <= 10)):
self.Unify(1)
else:
self.Unify(0)
if self.m_split == _DSR_HORIZONTAL_TAB:
cursor = wx.Cursor(wx.CURSOR_SIZENS)
elif self.m_split == _DSR_VERTICAL_TAB:
cursor = wx.Cursor(wx.CURSOR_SIZEWE)
else:
cursor = wx.Cursor(wx.CURSOR_ARROW)
self.m_container.SetCursor(cursor)
self.m_dragging = _DSR_NONE
elif self.m_leaf:
self.m_leaf.OnRelease(event)
#----------------------------------------------------------------------------
class _DynamicSashWindowLeaf(wx.EvtHandler):
def __init__(self, impl):
super(_DynamicSashWindowLeaf, self).__init__()
self.m_impl = impl
self.m_hscroll = None
self.m_vscroll = None
self.m_child = None
self.m_viewport = None
def __dtor__(self):
if self.m_hscroll:
self.m_hscroll.SetEventHandler(self.m_hscroll)
self.m_hscroll.Destroy()
if self.m_vscroll:
self.m_vscroll.SetEventHandler(self.m_vscroll)
self.m_vscroll.Destroy()
if self.m_viewport:
self.m_viewport.Destroy()
def Create(self):
self.m_hscroll = wx.ScrollBar()
self.m_vscroll = wx.ScrollBar()
self.m_viewport = wx.Window()
add_child_target = self.m_impl.m_add_child_target
self.m_impl.m_add_child_target = None
success = self.m_hscroll.Create(self.m_impl.m_container, style=wx.SB_HORIZONTAL)
if success:
success = self.m_vscroll.Create(self.m_impl.m_container, style=wx.SB_VERTICAL)
if success:
success = self.m_viewport.Create(self.m_impl.m_container)
if not success:
return False
self.m_impl.m_add_child_target = add_child_target
cursor = wx.Cursor(wx.CURSOR_ARROW)
self.m_hscroll.SetCursor(cursor)
self.m_vscroll.SetCursor(cursor)
self.m_viewport.SetCursor(cursor)
self.m_viewport.Bind(wx.EVT_SIZE, self.OnViewSize)
self.Bind(_EVT_DYNAMIC_SASH_REPARENT, self.OnReparent)
if self.m_impl.m_window.GetWindowStyle() & DS_MANAGE_SCROLLBARS:
for sbar in [self.m_hscroll, self.m_vscroll]:
sbar.Bind(wx.EVT_SET_FOCUS, self.OnFocus)
sbar.Bind(wx.EVT_SCROLL_TOP, self.OnScroll)
sbar.Bind(wx.EVT_SCROLL_BOTTOM, self.OnScroll)
sbar.Bind(wx.EVT_SCROLL_LINEUP, self.OnScroll)
sbar.Bind(wx.EVT_SCROLL_LINEDOWN, self.OnScroll)
sbar.Bind(wx.EVT_SCROLL_PAGEUP, self.OnScroll)
sbar.Bind(wx.EVT_SCROLL_PAGEDOWN, self.OnScroll)
sbar.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScroll)
sbar.Bind(wx.EVT_SCROLL_THUMBRELEASE, self.OnScroll)
layout = wx.LayoutConstraints()
size = self.m_hscroll.GetBestSize()
layout.left.SameAs(self.m_impl.m_container, wx.Left, 10)
layout.right.LeftOf(self.m_vscroll)
layout.bottom.SameAs(self.m_impl.m_container, wx.Bottom, 3)
layout.height.Absolute(size.GetHeight())
self.m_hscroll.SetConstraints(layout)
layout = wx.LayoutConstraints()
size = self.m_vscroll.GetBestSize()
layout.top.SameAs(self.m_impl.m_container, wx.Top, 10)
layout.bottom.Above(self.m_hscroll)
layout.right.SameAs(self.m_impl.m_container, wx.Right, 3)
layout.width.Absolute(size.GetWidth())
self.m_vscroll.SetConstraints(layout)
layout = wx.LayoutConstraints()
layout.left.SameAs(self.m_impl.m_container, wx.Left, 3)
layout.right.LeftOf(self.m_vscroll)
layout.top.SameAs(self.m_impl.m_container, wx.Top, 3)
layout.bottom.Above(self.m_hscroll)
self.m_viewport.SetConstraints(layout)
self.m_impl.m_container.Layout()
return True
def AddChild(self, window):
if self.m_child:
self.m_child.Destroy()
# Since the parent's AddWindow is called during the construction of
# the C++ part of the child, there isn't a proxy object created for
# the child window yet. When we later try to use the widget object
# given here then it will come up as being already deleted (because
# the real proxy exists now.) So instead of saving the `window` here
# we'll just save the object's address instead, and then use that to
# fetch the real proxy object when it's needed later.
self.m_child = None
self.m_child_ptr = wx.siplib.unwrapinstance(window)
# Delay the reparenting until after the AddChild has finished.
event = _DynamicSashReparentEvent(self)
self.AddPendingEvent(event)
def _checkPendingChild(self):
if hasattr(self, 'm_child_ptr'):
self.m_child = wx.siplib.wrapinstance(self.m_child_ptr, wx.Object)
del self.m_child_ptr
def OnReparent(self, event):
self._checkPendingChild()
if self.m_child:
self.m_child.Reparent(self.m_viewport)
self.ResizeChild(self.m_viewport.GetSize())
def GetRegion(self, x, y):
size = self.m_impl.m_container.GetSize()
w = size.GetWidth()
h = size.GetHeight()
size = self.m_hscroll.GetSize()
sh = size.GetHeight()
size = self.m_vscroll.GetSize()
sw = size.GetWidth()
if x >= w - sw - 3 and x < w and y >= h - sh - 3 and y < h:
return _DSR_CORNER
if x >= 3 and x < 10 and y >= h - sh - 3 and y < h - 2:
return _DSR_VERTICAL_TAB
if x >= w - sw - 3 and x < w - 2 and y >= 3 and y < 10:
return _DSR_HORIZONTAL_TAB
if x < 3:
return _DSR_LEFT_EDGE
if y < 3:
return _DSR_TOP_EDGE
if x >= w - 2:
return _DSR_RIGHT_EDGE
if y >= h - 2:
return _DSR_BOTTOM_EDGE
return _DSR_NONE
def ResizeChild(self, size):
self._checkPendingChild()
if self.m_child:
if self.m_impl.m_window.HasFlag(DS_MANAGE_SCROLLBARS):
best_size = self.m_child.GetBestSize()
if best_size.GetWidth() < size.GetWidth():
best_size.SetWidth(size.GetWidth())
if best_size.GetHeight() < size.GetHeight():
best_size.SetHeight(size.GetHeight())
self.m_child.SetSize(best_size)
hpos = self.m_hscroll.GetThumbPosition()
vpos = self.m_vscroll.GetThumbPosition()
if hpos < 0:
hpos = 0
if vpos < 0:
vpos = 0
if hpos > best_size.GetWidth() - size.GetWidth():
hpos = best_size.GetWidth() - size.GetWidth()
if vpos > best_size.GetHeight() - size.GetHeight():
vpos = best_size.GetHeight() - size.GetHeight()
self.m_hscroll.SetScrollbar(hpos, size.GetWidth(),
best_size.GetWidth(), size.GetWidth())
self.m_vscroll.SetScrollbar(vpos, size.GetHeight(),
best_size.GetHeight(), size.GetHeight())
# Umm, the scrollbars are doing something insane under GTK+ and subtracting
# one from the position I pass in. This works around that.
self.m_hscroll.SetThumbPosition(hpos + hpos - self.m_hscroll.GetThumbPosition())
self.m_vscroll.SetThumbPosition(vpos + vpos - self.m_vscroll.GetThumbPosition())
pos = self.m_child.GetPosition()
self.m_viewport.ScrollWindow(-hpos - pos.x, -vpos - pos.y)
else: # not DS_MANAGE_SCROLLBARS
self.m_child.SetSize(size)
def FindScrollBar(self, child, vert):
self._checkPendingChild()
if self.m_child == child:
return self.m_vscroll if vert else self.m_hscroll
return None
def OnSize(self, event):
self.m_impl.m_container.Refresh()
def OnViewSize(self, event):
if self.m_viewport is not None:
self.ResizeChild(self.m_viewport.GetSize())
def OnPaint(self, event):
dc = wx.PaintDC(self.m_impl.m_container)
dc.SetBackground(wx.Brush(self.m_impl.m_container.GetBackgroundColour()))
dc.Clear()
highlight = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT), 1)
shadow = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW), 1)
black = wx.Pen(wx.BLACK, 1)
size = self.m_impl.m_container.GetSize()
w = size.GetWidth()
h = size.GetHeight()
size = self.m_hscroll.GetSize()
sh = size.GetHeight()
size = self.m_vscroll.GetSize()
sw = size.GetWidth()
dc.SetPen(shadow)
dc.DrawLine(1, 1, 1, h - 2)
dc.DrawLine(1, 1, w - 2, 1)
dc.SetPen(black)
dc.DrawLine(2, 2, 2, h - 3)
dc.DrawLine(2, 2, w - 3, 2)
dc.SetPen(highlight)
dc.DrawLine(w - 2, 2, w - 2, h - sh - 2)
dc.DrawLine(w - 2, h - sh - 2, w - sw - 2, h - sh - 2)
dc.DrawLine(w - sw - 2, h - sh - 2, w - sw - 2, h - 2)
dc.DrawLine(w - sw - 2, h - 2, 2, h - 2)
dc.SetPen(highlight)
dc.DrawLine(w - sw - 2, 8, w - sw - 2, 4)
dc.DrawLine(w - sw - 2, 4, w - 5, 4)
dc.SetPen(shadow)
dc.DrawLine(w - 5, 4, w - 5, 8)
dc.DrawLine(w - 5, 8, w - sw - 2, 8)
dc.SetPen(black)
dc.DrawLine(w - 4, 3, w - 4, 9)
dc.DrawLine(w - 4, 9, w - sw - 3, 9)
dc.SetPen(highlight)
dc.DrawLine(4, h - 5, 4, h - sh - 2)
dc.DrawLine(4, h - sh - 2, 8, h - sh - 2)
dc.SetPen(shadow)
dc.DrawLine(8, h - sh - 2, 8, h - 5)
dc.DrawLine(8, h - 5, 4, h - 5)
dc.SetPen(black)
dc.DrawLine(9, h - sh - 3, 9, h - 4)
dc.DrawLine(9, h - 4, 3, h - 4)
cy = (h - sh + h - 6) / 2.0 + 1
cx = (w - sw + w - 6) / 2.0 + 1
sy = cy
while sy > h - sh:
sy -= 4
sx = cx
while sx > w - sw:
sx -= 4
for y in range(int(sy), h-2, 4): #(y = sy; y < h - 2; y += 4)
for x in range(int(sx), w-2, 4): #(x = sx; x < w - 2; x += 4)
if x - cx >= -(y - cy):
dc.SetPen(highlight)
dc.DrawPoint(x, y)
dc.SetPen(shadow)
dc.DrawPoint(x + 1, y + 1)
def OnScroll(self, event):
self._checkPendingChild()
nx = -self.m_hscroll.GetThumbPosition()
ny = -self.m_vscroll.GetThumbPosition()
if self.m_child:
pos = self.m_child.GetPosition()
self.m_viewport.ScrollWindow(nx - pos.x, ny - pos.y)
def OnFocus(self, event):
self._checkPendingChild()
if (event.GetEventObject() == self.m_hscroll or
event.GetEventObject() == self.m_vscroll):
self.m_child.SetFocus()
def OnMouseMove(self, event):
if self.m_impl.m_dragging:
return
region = self.GetRegion(event.x, event.y)
cursor = wx.Cursor(wx.CURSOR_ARROW)
if region == _DSR_HORIZONTAL_TAB:
cursor = wx.Cursor(wx.CURSOR_SIZENS)
elif region == _DSR_VERTICAL_TAB:
cursor = wx.Cursor(wx.CURSOR_SIZEWE)
elif (region == _DSR_CORNER and
self.m_impl.m_window.GetWindowStyle() & DS_DRAG_CORNER != 0):
cursor = wx.Cursor(wx.CURSOR_SIZENWSE)
elif (region == _DSR_LEFT_EDGE or region == _DSR_TOP_EDGE
or region == _DSR_RIGHT_EDGE or region == _DSR_BOTTOM_EDGE):
if self.m_impl.FindParent(region):
if region == _DSR_LEFT_EDGE or region == _DSR_RIGHT_EDGE:
cursor = wx.Cursor(wx.CURSOR_SIZEWE)
else:
cursor = wx.Cursor(wx.CURSOR_SIZENS)
self.m_impl.m_container.SetCursor(cursor)
def OnLeave(self, event):
cursor = wx.Cursor(wx.CURSOR_ARROW)
self.m_impl.m_container.SetCursor(cursor)
def OnPress(self, event):
region = self.GetRegion(event.x, event.y)
if region == _DSR_CORNER and (self.m_impl.m_window.GetWindowStyle() & DS_DRAG_CORNER) == 0:
return
if region == _DSR_HORIZONTAL_TAB or region == _DSR_VERTICAL_TAB or region == _DSR_CORNER:
self.m_impl.m_dragging = region
self.m_impl.m_drag_x = event.x
self.m_impl.m_drag_y = event.y
self.m_impl.DrawSash(event.x, event.y, "press")
self.m_impl.m_container.CaptureMouse()
elif (region == _DSR_LEFT_EDGE or region == _DSR_TOP_EDGE
or region == _DSR_RIGHT_EDGE or region == _DSR_BOTTOM_EDGE):
parent = self.m_impl.FindParent(region)
if parent:
x = event.x
y = event.y
x, y = self.m_impl.m_container.ClientToScreen(x, y)
x, y = parent.m_container.ScreenToClient(x, y)
parent.m_dragging = parent.m_split
parent.m_drag_x = x
parent.m_drag_y = y
parent.DrawSash(x, y, "press")
parent.m_container.CaptureMouse()
def OnRelease(self, event):
pass