680 lines
23 KiB
Python
680 lines
23 KiB
Python
# DockingBar.py
|
|
|
|
# Ported directly (comments and all) from the samples at www.codeguru.com
|
|
|
|
# WARNING: Use at your own risk, as this interface is highly likely to change.
|
|
# Currently we support only one child per DockingBar. Later we need to add
|
|
# support for multiple children.
|
|
|
|
import struct
|
|
|
|
import win32api
|
|
import win32con
|
|
import win32ui
|
|
from pywin.mfc import afxres, window
|
|
|
|
clrBtnHilight = win32api.GetSysColor(win32con.COLOR_BTNHILIGHT)
|
|
clrBtnShadow = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
|
|
|
|
|
|
def CenterPoint(rect):
|
|
width = rect[2] - rect[0]
|
|
height = rect[3] - rect[1]
|
|
return rect[0] + width // 2, rect[1] + height // 2
|
|
|
|
|
|
def OffsetRect(rect, point):
|
|
(x, y) = point
|
|
return rect[0] + x, rect[1] + y, rect[2] + x, rect[3] + y
|
|
|
|
|
|
def DeflateRect(rect, point):
|
|
(x, y) = point
|
|
return rect[0] + x, rect[1] + y, rect[2] - x, rect[3] - y
|
|
|
|
|
|
def PtInRect(rect, pt):
|
|
return rect[0] <= pt[0] < rect[2] and rect[1] <= pt[1] < rect[3]
|
|
|
|
|
|
class DockingBar(window.Wnd):
|
|
def __init__(self, obj=None):
|
|
if obj is None:
|
|
obj = win32ui.CreateControlBar()
|
|
window.Wnd.__init__(self, obj)
|
|
self.dialog = None
|
|
self.nDockBarID = 0
|
|
self.sizeMin = 32, 32
|
|
self.sizeHorz = 200, 200
|
|
self.sizeVert = 200, 200
|
|
self.sizeFloat = 200, 200
|
|
self.bTracking = 0
|
|
self.bInRecalcNC = 0
|
|
self.cxEdge = 6
|
|
self.cxBorder = 3
|
|
self.cxGripper = 20
|
|
self.brushBkgd = win32ui.CreateBrush()
|
|
self.brushBkgd.CreateSolidBrush(win32api.GetSysColor(win32con.COLOR_BTNFACE))
|
|
|
|
# Support for diagonal resizing
|
|
self.cyBorder = 3
|
|
self.cCaptionSize = win32api.GetSystemMetrics(win32con.SM_CYSMCAPTION)
|
|
self.cMinWidth = win32api.GetSystemMetrics(win32con.SM_CXMIN)
|
|
self.cMinHeight = win32api.GetSystemMetrics(win32con.SM_CYMIN)
|
|
self.rectUndock = (0, 0, 0, 0)
|
|
|
|
def OnUpdateCmdUI(self, target, bDisableIfNoHndler):
|
|
return self.UpdateDialogControls(target, bDisableIfNoHndler)
|
|
|
|
def CreateWindow(
|
|
self,
|
|
parent,
|
|
childCreator,
|
|
title,
|
|
id,
|
|
style=win32con.WS_CHILD | win32con.WS_VISIBLE | afxres.CBRS_LEFT,
|
|
childCreatorArgs=(),
|
|
):
|
|
assert not (
|
|
(style & afxres.CBRS_SIZE_FIXED) and (style & afxres.CBRS_SIZE_DYNAMIC)
|
|
), "Invalid style"
|
|
self.rectClose = self.rectBorder = self.rectGripper = self.rectTracker = (
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
)
|
|
|
|
# save the style
|
|
self._obj_.dwStyle = style & afxres.CBRS_ALL
|
|
|
|
cursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
|
|
wndClass = win32ui.RegisterWndClass(
|
|
win32con.CS_DBLCLKS, cursor, self.brushBkgd.GetSafeHandle(), 0
|
|
)
|
|
|
|
self._obj_.CreateWindow(wndClass, title, style, (0, 0, 0, 0), parent, id)
|
|
|
|
# Create the child dialog
|
|
self.dialog = childCreator(*(self,) + childCreatorArgs)
|
|
|
|
# use the dialog dimensions as default base dimensions
|
|
assert self.dialog.IsWindow(), (
|
|
"The childCreator function %s did not create a window!" % childCreator
|
|
)
|
|
rect = self.dialog.GetWindowRect()
|
|
self.sizeHorz = self.sizeVert = self.sizeFloat = (
|
|
rect[2] - rect[0],
|
|
rect[3] - rect[1],
|
|
)
|
|
|
|
self.sizeHorz = self.sizeHorz[0], self.sizeHorz[1] + self.cxEdge + self.cxBorder
|
|
self.sizeVert = self.sizeVert[0] + self.cxEdge + self.cxBorder, self.sizeVert[1]
|
|
self.HookMessages()
|
|
|
|
def CalcFixedLayout(self, bStretch, bHorz):
|
|
rectTop = self.dockSite.GetControlBar(
|
|
afxres.AFX_IDW_DOCKBAR_TOP
|
|
).GetWindowRect()
|
|
rectLeft = self.dockSite.GetControlBar(
|
|
afxres.AFX_IDW_DOCKBAR_LEFT
|
|
).GetWindowRect()
|
|
if bStretch:
|
|
nHorzDockBarWidth = 32767
|
|
nVertDockBarHeight = 32767
|
|
else:
|
|
nHorzDockBarWidth = rectTop[2] - rectTop[0] + 4
|
|
nVertDockBarHeight = rectLeft[3] - rectLeft[1] + 4
|
|
|
|
if self.IsFloating():
|
|
return self.sizeFloat
|
|
if bHorz:
|
|
return nHorzDockBarWidth, self.sizeHorz[1]
|
|
return self.sizeVert[0], nVertDockBarHeight
|
|
|
|
def CalcDynamicLayout(self, length, mode):
|
|
# Support for diagonal sizing.
|
|
if self.IsFloating():
|
|
self.GetParent().GetParent().ModifyStyle(win32ui.MFS_4THICKFRAME, 0)
|
|
if mode & (win32ui.LM_HORZDOCK | win32ui.LM_VERTDOCK):
|
|
flags = (
|
|
win32con.SWP_NOSIZE
|
|
| win32con.SWP_NOMOVE
|
|
| win32con.SWP_NOZORDER
|
|
| win32con.SWP_NOACTIVATE
|
|
| win32con.SWP_FRAMECHANGED
|
|
)
|
|
self.SetWindowPos(
|
|
0,
|
|
(
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
),
|
|
flags,
|
|
)
|
|
self.dockSite.RecalcLayout()
|
|
return self._obj_.CalcDynamicLayout(length, mode)
|
|
|
|
if mode & win32ui.LM_MRUWIDTH:
|
|
return self.sizeFloat
|
|
if mode & win32ui.LM_COMMIT:
|
|
self.sizeFloat = length, self.sizeFloat[1]
|
|
return self.sizeFloat
|
|
# More diagonal sizing.
|
|
if self.IsFloating():
|
|
dc = self.dockContext
|
|
pt = win32api.GetCursorPos()
|
|
windowRect = self.GetParent().GetParent().GetWindowRect()
|
|
|
|
hittest = dc.nHitTest
|
|
if hittest == win32con.HTTOPLEFT:
|
|
cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
|
|
cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
|
|
self.sizeFloat = cx, cy
|
|
|
|
top = (
|
|
min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight)
|
|
- self.cyBorder
|
|
)
|
|
left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
|
|
dc.rectFrameDragHorz = (
|
|
left,
|
|
top,
|
|
dc.rectFrameDragHorz[2],
|
|
dc.rectFrameDragHorz[3],
|
|
)
|
|
return self.sizeFloat
|
|
if hittest == win32con.HTTOPRIGHT:
|
|
cx = max(pt[0] - windowRect[0], self.cMinWidth)
|
|
cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
|
|
self.sizeFloat = cx, cy
|
|
|
|
top = (
|
|
min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight)
|
|
- self.cyBorder
|
|
)
|
|
dc.rectFrameDragHorz = (
|
|
dc.rectFrameDragHorz[0],
|
|
top,
|
|
dc.rectFrameDragHorz[2],
|
|
dc.rectFrameDragHorz[3],
|
|
)
|
|
return self.sizeFloat
|
|
|
|
if hittest == win32con.HTBOTTOMLEFT:
|
|
cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
|
|
cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
|
|
self.sizeFloat = cx, cy
|
|
|
|
left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
|
|
dc.rectFrameDragHorz = (
|
|
left,
|
|
dc.rectFrameDragHorz[1],
|
|
dc.rectFrameDragHorz[2],
|
|
dc.rectFrameDragHorz[3],
|
|
)
|
|
return self.sizeFloat
|
|
|
|
if hittest == win32con.HTBOTTOMRIGHT:
|
|
cx = max(pt[0] - windowRect[0], self.cMinWidth)
|
|
cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
|
|
self.sizeFloat = cx, cy
|
|
return self.sizeFloat
|
|
|
|
if mode & win32ui.LM_LENGTHY:
|
|
self.sizeFloat = self.sizeFloat[0], max(self.sizeMin[1], length)
|
|
return self.sizeFloat
|
|
else:
|
|
return max(self.sizeMin[0], length), self.sizeFloat[1]
|
|
|
|
def OnWindowPosChanged(self, msg):
|
|
if self.GetSafeHwnd() == 0 or self.dialog is None:
|
|
return 0
|
|
lparam = msg[3]
|
|
""" LPARAM used with WM_WINDOWPOSCHANGED:
|
|
typedef struct {
|
|
HWND hwnd;
|
|
HWND hwndInsertAfter;
|
|
int x;
|
|
int y;
|
|
int cx;
|
|
int cy;
|
|
UINT flags;} WINDOWPOS;
|
|
"""
|
|
format = "PPiiiii"
|
|
bytes = win32ui.GetBytes(lparam, struct.calcsize(format))
|
|
hwnd, hwndAfter, x, y, cx, cy, flags = struct.unpack(format, bytes)
|
|
|
|
if self.bInRecalcNC:
|
|
rc = self.GetClientRect()
|
|
self.dialog.MoveWindow(rc)
|
|
return 0
|
|
# Find on which side are we docked
|
|
nDockBarID = self.GetParent().GetDlgCtrlID()
|
|
# Return if dropped at same location
|
|
# no docking side change and no size change
|
|
if (
|
|
(nDockBarID == self.nDockBarID)
|
|
and (flags & win32con.SWP_NOSIZE)
|
|
and (
|
|
(self._obj_.dwStyle & afxres.CBRS_BORDER_ANY) != afxres.CBRS_BORDER_ANY
|
|
)
|
|
):
|
|
return
|
|
self.nDockBarID = nDockBarID
|
|
|
|
# Force recalc the non-client area
|
|
self.bInRecalcNC = 1
|
|
try:
|
|
swpflags = (
|
|
win32con.SWP_NOSIZE
|
|
| win32con.SWP_NOMOVE
|
|
| win32con.SWP_NOZORDER
|
|
| win32con.SWP_FRAMECHANGED
|
|
)
|
|
self.SetWindowPos(0, (0, 0, 0, 0), swpflags)
|
|
finally:
|
|
self.bInRecalcNC = 0
|
|
return 0
|
|
|
|
# This is a virtual and not a message hook.
|
|
def OnSetCursor(self, window, nHitTest, wMouseMsg):
|
|
if nHitTest != win32con.HTSIZE or self.bTracking:
|
|
return self._obj_.OnSetCursor(window, nHitTest, wMouseMsg)
|
|
|
|
if self.IsHorz():
|
|
win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZENS))
|
|
else:
|
|
win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZEWE))
|
|
return 1
|
|
|
|
# Mouse Handling
|
|
def OnLButtonUp(self, msg):
|
|
if not self.bTracking:
|
|
return 1 # pass it on.
|
|
self.StopTracking(1)
|
|
return 0 # Dont pass on
|
|
|
|
def OnLButtonDown(self, msg):
|
|
# UINT nFlags, CPoint point)
|
|
# only start dragging if clicked in "void" space
|
|
if self.dockBar is not None:
|
|
# start the drag
|
|
pt = msg[5]
|
|
pt = self.ClientToScreen(pt)
|
|
self.dockContext.StartDrag(pt)
|
|
return 0
|
|
return 1
|
|
|
|
def OnNcLButtonDown(self, msg):
|
|
if self.bTracking:
|
|
return 0
|
|
nHitTest = wparam = msg[2]
|
|
pt = msg[5]
|
|
|
|
if nHitTest == win32con.HTSYSMENU and not self.IsFloating():
|
|
self.GetDockingFrame().ShowControlBar(self, 0, 0)
|
|
elif nHitTest == win32con.HTMINBUTTON and not self.IsFloating():
|
|
self.dockContext.ToggleDocking()
|
|
elif (
|
|
nHitTest == win32con.HTCAPTION
|
|
and not self.IsFloating()
|
|
and self.dockBar is not None
|
|
):
|
|
self.dockContext.StartDrag(pt)
|
|
elif nHitTest == win32con.HTSIZE and not self.IsFloating():
|
|
self.StartTracking()
|
|
else:
|
|
return 1
|
|
return 0
|
|
|
|
def OnLButtonDblClk(self, msg):
|
|
# only toggle docking if clicked in "void" space
|
|
if self.dockBar is not None:
|
|
# toggle docking
|
|
self.dockContext.ToggleDocking()
|
|
return 0
|
|
return 1
|
|
|
|
def OnNcLButtonDblClk(self, msg):
|
|
nHitTest = wparam = msg[2]
|
|
# UINT nHitTest, CPoint point)
|
|
if self.dockBar is not None and nHitTest == win32con.HTCAPTION:
|
|
# toggle docking
|
|
self.dockContext.ToggleDocking()
|
|
return 0
|
|
return 1
|
|
|
|
def OnMouseMove(self, msg):
|
|
flags = wparam = msg[2]
|
|
lparam = msg[3]
|
|
if self.IsFloating() or not self.bTracking:
|
|
return 1
|
|
|
|
# Convert unsigned 16 bit to signed 32 bit.
|
|
x = win32api.LOWORD(lparam)
|
|
if x & 32768:
|
|
x = x | -65536
|
|
y = win32api.HIWORD(lparam)
|
|
if y & 32768:
|
|
y = y | -65536
|
|
pt = x, y
|
|
cpt = CenterPoint(self.rectTracker)
|
|
pt = self.ClientToWnd(pt)
|
|
if self.IsHorz():
|
|
if cpt[1] != pt[1]:
|
|
self.OnInvertTracker(self.rectTracker)
|
|
self.rectTracker = OffsetRect(self.rectTracker, (0, pt[1] - cpt[1]))
|
|
self.OnInvertTracker(self.rectTracker)
|
|
else:
|
|
if cpt[0] != pt[0]:
|
|
self.OnInvertTracker(self.rectTracker)
|
|
self.rectTracker = OffsetRect(self.rectTracker, (pt[0] - cpt[0], 0))
|
|
self.OnInvertTracker(self.rectTracker)
|
|
|
|
return 0 # Dont pass it on.
|
|
|
|
# def OnBarStyleChange(self, old, new):
|
|
|
|
def OnNcCalcSize(self, bCalcValid, size_info):
|
|
(rc0, rc1, rc2, pos) = size_info
|
|
self.rectBorder = self.GetWindowRect()
|
|
self.rectBorder = OffsetRect(
|
|
self.rectBorder, (-self.rectBorder[0], -self.rectBorder[1])
|
|
)
|
|
|
|
dwBorderStyle = self._obj_.dwStyle | afxres.CBRS_BORDER_ANY
|
|
|
|
if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP:
|
|
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_BOTTOM
|
|
rc0.left = rc0.left + self.cxGripper
|
|
rc0.bottom = rc0.bottom - self.cxEdge
|
|
rc0.top = rc0.top + self.cxBorder
|
|
rc0.right = rc0.right - self.cxBorder
|
|
self.rectBorder = (
|
|
self.rectBorder[0],
|
|
self.rectBorder[3] - self.cxEdge,
|
|
self.rectBorder[2],
|
|
self.rectBorder[3],
|
|
)
|
|
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
|
|
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_TOP
|
|
rc0.left = rc0.left + self.cxGripper
|
|
rc0.top = rc0.top + self.cxEdge
|
|
rc0.bottom = rc0.bottom - self.cxBorder
|
|
rc0.right = rc0.right - self.cxBorder
|
|
self.rectBorder = (
|
|
self.rectBorder[0],
|
|
self.rectBorder[1],
|
|
self.rectBorder[2],
|
|
self.rectBorder[1] + self.cxEdge,
|
|
)
|
|
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT:
|
|
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_RIGHT
|
|
rc0.right = rc0.right - self.cxEdge
|
|
rc0.left = rc0.left + self.cxBorder
|
|
rc0.bottom = rc0.bottom - self.cxBorder
|
|
rc0.top = rc0.top + self.cxGripper
|
|
self.rectBorder = (
|
|
self.rectBorder[2] - self.cxEdge,
|
|
self.rectBorder[1],
|
|
self.rectBorder[2],
|
|
self.rectBorder[3],
|
|
)
|
|
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
|
|
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_LEFT
|
|
rc0.left = rc0.left + self.cxEdge
|
|
rc0.right = rc0.right - self.cxBorder
|
|
rc0.bottom = rc0.bottom - self.cxBorder
|
|
rc0.top = rc0.top + self.cxGripper
|
|
self.rectBorder = (
|
|
self.rectBorder[0],
|
|
self.rectBorder[1],
|
|
self.rectBorder[0] + self.cxEdge,
|
|
self.rectBorder[3],
|
|
)
|
|
else:
|
|
self.rectBorder = 0, 0, 0, 0
|
|
|
|
self.SetBarStyle(dwBorderStyle)
|
|
return 0
|
|
|
|
def OnNcPaint(self, msg):
|
|
self.EraseNonClient()
|
|
dc = self.GetWindowDC()
|
|
ctl = win32api.GetSysColor(win32con.COLOR_BTNHIGHLIGHT)
|
|
cbr = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
|
|
dc.Draw3dRect(self.rectBorder, ctl, cbr)
|
|
|
|
self.DrawGripper(dc)
|
|
|
|
rect = self.GetClientRect()
|
|
self.InvalidateRect(rect, 1)
|
|
return 0
|
|
|
|
def OnNcHitTest(self, pt): # A virtual, not a hooked message.
|
|
if self.IsFloating():
|
|
return 1
|
|
|
|
ptOrig = pt
|
|
rect = self.GetWindowRect()
|
|
pt = pt[0] - rect[0], pt[1] - rect[1]
|
|
|
|
if PtInRect(self.rectClose, pt):
|
|
return win32con.HTSYSMENU
|
|
elif PtInRect(self.rectUndock, pt):
|
|
return win32con.HTMINBUTTON
|
|
elif PtInRect(self.rectGripper, pt):
|
|
return win32con.HTCAPTION
|
|
elif PtInRect(self.rectBorder, pt):
|
|
return win32con.HTSIZE
|
|
else:
|
|
return self._obj_.OnNcHitTest(ptOrig)
|
|
|
|
def StartTracking(self):
|
|
self.SetCapture()
|
|
|
|
# make sure no updates are pending
|
|
self.RedrawWindow(None, None, win32con.RDW_ALLCHILDREN | win32con.RDW_UPDATENOW)
|
|
self.dockSite.LockWindowUpdate()
|
|
|
|
self.ptOld = CenterPoint(self.rectBorder)
|
|
self.bTracking = 1
|
|
|
|
self.rectTracker = self.rectBorder
|
|
if not self.IsHorz():
|
|
l, t, r, b = self.rectTracker
|
|
b = b - 4
|
|
self.rectTracker = l, t, r, b
|
|
|
|
self.OnInvertTracker(self.rectTracker)
|
|
|
|
def OnCaptureChanged(self, msg):
|
|
hwnd = lparam = msg[3]
|
|
if self.bTracking and hwnd != self.GetSafeHwnd():
|
|
self.StopTracking(0) # cancel tracking
|
|
return 1
|
|
|
|
def StopTracking(self, bAccept):
|
|
self.OnInvertTracker(self.rectTracker)
|
|
self.dockSite.UnlockWindowUpdate()
|
|
self.bTracking = 0
|
|
self.ReleaseCapture()
|
|
if not bAccept:
|
|
return
|
|
|
|
rcc = self.dockSite.GetWindowRect()
|
|
if self.IsHorz():
|
|
newsize = self.sizeHorz[1]
|
|
maxsize = newsize + (rcc[3] - rcc[1])
|
|
minsize = self.sizeMin[1]
|
|
else:
|
|
newsize = self.sizeVert[0]
|
|
maxsize = newsize + (rcc[2] - rcc[0])
|
|
minsize = self.sizeMin[0]
|
|
|
|
pt = CenterPoint(self.rectTracker)
|
|
if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP:
|
|
newsize = newsize + (pt[1] - self.ptOld[1])
|
|
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
|
|
newsize = newsize + (-pt[1] + self.ptOld[1])
|
|
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT:
|
|
newsize = newsize + (pt[0] - self.ptOld[0])
|
|
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
|
|
newsize = newsize + (-pt[0] + self.ptOld[0])
|
|
newsize = max(minsize, min(maxsize, newsize))
|
|
if self.IsHorz():
|
|
self.sizeHorz = self.sizeHorz[0], newsize
|
|
else:
|
|
self.sizeVert = newsize, self.sizeVert[1]
|
|
self.dockSite.RecalcLayout()
|
|
return 0
|
|
|
|
def OnInvertTracker(self, rect):
|
|
assert rect[2] - rect[0] > 0 and rect[3] - rect[1] > 0, "rect is empty"
|
|
assert self.bTracking
|
|
rcc = self.GetWindowRect()
|
|
rcf = self.dockSite.GetWindowRect()
|
|
|
|
rect = OffsetRect(rect, (rcc[0] - rcf[0], rcc[1] - rcf[1]))
|
|
rect = DeflateRect(rect, (1, 1))
|
|
|
|
flags = win32con.DCX_WINDOW | win32con.DCX_CACHE | win32con.DCX_LOCKWINDOWUPDATE
|
|
dc = self.dockSite.GetDCEx(None, flags)
|
|
try:
|
|
brush = win32ui.GetHalftoneBrush()
|
|
oldBrush = dc.SelectObject(brush)
|
|
|
|
dc.PatBlt(
|
|
(rect[0], rect[1]),
|
|
(rect[2] - rect[0], rect[3] - rect[1]),
|
|
win32con.PATINVERT,
|
|
)
|
|
dc.SelectObject(oldBrush)
|
|
finally:
|
|
self.dockSite.ReleaseDC(dc)
|
|
|
|
def IsHorz(self):
|
|
return (
|
|
self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP
|
|
or self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM
|
|
)
|
|
|
|
def ClientToWnd(self, pt):
|
|
x, y = pt
|
|
if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
|
|
y = y + self.cxEdge
|
|
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
|
|
x = x + self.cxEdge
|
|
return x, y
|
|
|
|
def DrawGripper(self, dc):
|
|
# no gripper if floating
|
|
if self._obj_.dwStyle & afxres.CBRS_FLOATING:
|
|
return
|
|
|
|
# -==HACK==-
|
|
# in order to calculate the client area properly after docking,
|
|
# the client area must be recalculated twice (I have no idea why)
|
|
self.dockSite.RecalcLayout()
|
|
# -==END HACK==-
|
|
|
|
gripper = self.GetWindowRect()
|
|
gripper = self.ScreenToClient(gripper)
|
|
gripper = OffsetRect(gripper, (-gripper[0], -gripper[1]))
|
|
gl, gt, gr, gb = gripper
|
|
|
|
if self._obj_.dwStyle & afxres.CBRS_ORIENT_HORZ:
|
|
# gripper at left
|
|
self.rectGripper = gl, gt + 40, gl + 20, gb
|
|
# draw close box
|
|
self.rectClose = gl + 7, gt + 10, gl + 19, gt + 22
|
|
dc.DrawFrameControl(
|
|
self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE
|
|
)
|
|
# draw docking toggle box
|
|
self.rectUndock = OffsetRect(self.rectClose, (0, 13))
|
|
dc.DrawFrameControl(
|
|
self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX
|
|
)
|
|
|
|
gt = gt + 38
|
|
gb = gb - 10
|
|
gl = gl + 10
|
|
gr = gl + 3
|
|
gripper = gl, gt, gr, gb
|
|
dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow)
|
|
dc.Draw3dRect(OffsetRect(gripper, (4, 0)), clrBtnHilight, clrBtnShadow)
|
|
else:
|
|
# gripper at top
|
|
self.rectGripper = gl, gt, gr - 40, gt + 20
|
|
# draw close box
|
|
self.rectClose = gr - 21, gt + 7, gr - 10, gt + 18
|
|
dc.DrawFrameControl(
|
|
self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE
|
|
)
|
|
# draw docking toggle box
|
|
self.rectUndock = OffsetRect(self.rectClose, (-13, 0))
|
|
dc.DrawFrameControl(
|
|
self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX
|
|
)
|
|
gr = gr - 38
|
|
gl = gl + 10
|
|
gt = gt + 10
|
|
gb = gt + 3
|
|
|
|
gripper = gl, gt, gr, gb
|
|
dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow)
|
|
dc.Draw3dRect(OffsetRect(gripper, (0, 4)), clrBtnHilight, clrBtnShadow)
|
|
|
|
def HookMessages(self):
|
|
self.HookMessage(self.OnLButtonUp, win32con.WM_LBUTTONUP)
|
|
self.HookMessage(self.OnLButtonDown, win32con.WM_LBUTTONDOWN)
|
|
self.HookMessage(self.OnLButtonDblClk, win32con.WM_LBUTTONDBLCLK)
|
|
self.HookMessage(self.OnNcLButtonDown, win32con.WM_NCLBUTTONDOWN)
|
|
self.HookMessage(self.OnNcLButtonDblClk, win32con.WM_NCLBUTTONDBLCLK)
|
|
self.HookMessage(self.OnMouseMove, win32con.WM_MOUSEMOVE)
|
|
self.HookMessage(self.OnNcPaint, win32con.WM_NCPAINT)
|
|
self.HookMessage(self.OnCaptureChanged, win32con.WM_CAPTURECHANGED)
|
|
self.HookMessage(self.OnWindowPosChanged, win32con.WM_WINDOWPOSCHANGED)
|
|
|
|
|
|
# self.HookMessage(self.OnSize, win32con.WM_SIZE)
|
|
|
|
|
|
def EditCreator(parent):
|
|
d = win32ui.CreateEdit()
|
|
es = (
|
|
win32con.WS_CHILD
|
|
| win32con.WS_VISIBLE
|
|
| win32con.WS_BORDER
|
|
| win32con.ES_MULTILINE
|
|
| win32con.ES_WANTRETURN
|
|
)
|
|
d.CreateWindow(es, (0, 0, 150, 150), parent, 1000)
|
|
return d
|
|
|
|
|
|
def test():
|
|
import pywin.mfc.dialog
|
|
|
|
global bar
|
|
bar = DockingBar()
|
|
creator = EditCreator
|
|
bar.CreateWindow(win32ui.GetMainFrame(), creator, "Coolbar Demo", 0xFFFFF)
|
|
# win32ui.GetMainFrame().ShowControlBar(bar, 1, 0)
|
|
bar.SetBarStyle(
|
|
bar.GetBarStyle()
|
|
| afxres.CBRS_TOOLTIPS
|
|
| afxres.CBRS_FLYBY
|
|
| afxres.CBRS_SIZE_DYNAMIC
|
|
)
|
|
bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
|
|
win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test()
|