Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DockingBar.py 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. # DockingBar.py
  2. # Ported directly (comments and all) from the samples at www.codeguru.com
  3. # WARNING: Use at your own risk, as this interface is highly likely to change.
  4. # Currently we support only one child per DockingBar. Later we need to add
  5. # support for multiple children.
  6. import struct
  7. import win32api
  8. import win32con
  9. import win32ui
  10. from pywin.mfc import afxres, window
  11. clrBtnHilight = win32api.GetSysColor(win32con.COLOR_BTNHILIGHT)
  12. clrBtnShadow = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
  13. def CenterPoint(rect):
  14. width = rect[2] - rect[0]
  15. height = rect[3] - rect[1]
  16. return rect[0] + width // 2, rect[1] + height // 2
  17. def OffsetRect(rect, point):
  18. (x, y) = point
  19. return rect[0] + x, rect[1] + y, rect[2] + x, rect[3] + y
  20. def DeflateRect(rect, point):
  21. (x, y) = point
  22. return rect[0] + x, rect[1] + y, rect[2] - x, rect[3] - y
  23. def PtInRect(rect, pt):
  24. return rect[0] <= pt[0] < rect[2] and rect[1] <= pt[1] < rect[3]
  25. class DockingBar(window.Wnd):
  26. def __init__(self, obj=None):
  27. if obj is None:
  28. obj = win32ui.CreateControlBar()
  29. window.Wnd.__init__(self, obj)
  30. self.dialog = None
  31. self.nDockBarID = 0
  32. self.sizeMin = 32, 32
  33. self.sizeHorz = 200, 200
  34. self.sizeVert = 200, 200
  35. self.sizeFloat = 200, 200
  36. self.bTracking = 0
  37. self.bInRecalcNC = 0
  38. self.cxEdge = 6
  39. self.cxBorder = 3
  40. self.cxGripper = 20
  41. self.brushBkgd = win32ui.CreateBrush()
  42. self.brushBkgd.CreateSolidBrush(win32api.GetSysColor(win32con.COLOR_BTNFACE))
  43. # Support for diagonal resizing
  44. self.cyBorder = 3
  45. self.cCaptionSize = win32api.GetSystemMetrics(win32con.SM_CYSMCAPTION)
  46. self.cMinWidth = win32api.GetSystemMetrics(win32con.SM_CXMIN)
  47. self.cMinHeight = win32api.GetSystemMetrics(win32con.SM_CYMIN)
  48. self.rectUndock = (0, 0, 0, 0)
  49. def OnUpdateCmdUI(self, target, bDisableIfNoHndler):
  50. return self.UpdateDialogControls(target, bDisableIfNoHndler)
  51. def CreateWindow(
  52. self,
  53. parent,
  54. childCreator,
  55. title,
  56. id,
  57. style=win32con.WS_CHILD | win32con.WS_VISIBLE | afxres.CBRS_LEFT,
  58. childCreatorArgs=(),
  59. ):
  60. assert not (
  61. (style & afxres.CBRS_SIZE_FIXED) and (style & afxres.CBRS_SIZE_DYNAMIC)
  62. ), "Invalid style"
  63. self.rectClose = self.rectBorder = self.rectGripper = self.rectTracker = (
  64. 0,
  65. 0,
  66. 0,
  67. 0,
  68. )
  69. # save the style
  70. self._obj_.dwStyle = style & afxres.CBRS_ALL
  71. cursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
  72. wndClass = win32ui.RegisterWndClass(
  73. win32con.CS_DBLCLKS, cursor, self.brushBkgd.GetSafeHandle(), 0
  74. )
  75. self._obj_.CreateWindow(wndClass, title, style, (0, 0, 0, 0), parent, id)
  76. # Create the child dialog
  77. self.dialog = childCreator(*(self,) + childCreatorArgs)
  78. # use the dialog dimensions as default base dimensions
  79. assert self.dialog.IsWindow(), (
  80. "The childCreator function %s did not create a window!" % childCreator
  81. )
  82. rect = self.dialog.GetWindowRect()
  83. self.sizeHorz = self.sizeVert = self.sizeFloat = (
  84. rect[2] - rect[0],
  85. rect[3] - rect[1],
  86. )
  87. self.sizeHorz = self.sizeHorz[0], self.sizeHorz[1] + self.cxEdge + self.cxBorder
  88. self.sizeVert = self.sizeVert[0] + self.cxEdge + self.cxBorder, self.sizeVert[1]
  89. self.HookMessages()
  90. def CalcFixedLayout(self, bStretch, bHorz):
  91. rectTop = self.dockSite.GetControlBar(
  92. afxres.AFX_IDW_DOCKBAR_TOP
  93. ).GetWindowRect()
  94. rectLeft = self.dockSite.GetControlBar(
  95. afxres.AFX_IDW_DOCKBAR_LEFT
  96. ).GetWindowRect()
  97. if bStretch:
  98. nHorzDockBarWidth = 32767
  99. nVertDockBarHeight = 32767
  100. else:
  101. nHorzDockBarWidth = rectTop[2] - rectTop[0] + 4
  102. nVertDockBarHeight = rectLeft[3] - rectLeft[1] + 4
  103. if self.IsFloating():
  104. return self.sizeFloat
  105. if bHorz:
  106. return nHorzDockBarWidth, self.sizeHorz[1]
  107. return self.sizeVert[0], nVertDockBarHeight
  108. def CalcDynamicLayout(self, length, mode):
  109. # Support for diagonal sizing.
  110. if self.IsFloating():
  111. self.GetParent().GetParent().ModifyStyle(win32ui.MFS_4THICKFRAME, 0)
  112. if mode & (win32ui.LM_HORZDOCK | win32ui.LM_VERTDOCK):
  113. flags = (
  114. win32con.SWP_NOSIZE
  115. | win32con.SWP_NOMOVE
  116. | win32con.SWP_NOZORDER
  117. | win32con.SWP_NOACTIVATE
  118. | win32con.SWP_FRAMECHANGED
  119. )
  120. self.SetWindowPos(
  121. 0,
  122. (
  123. 0,
  124. 0,
  125. 0,
  126. 0,
  127. ),
  128. flags,
  129. )
  130. self.dockSite.RecalcLayout()
  131. return self._obj_.CalcDynamicLayout(length, mode)
  132. if mode & win32ui.LM_MRUWIDTH:
  133. return self.sizeFloat
  134. if mode & win32ui.LM_COMMIT:
  135. self.sizeFloat = length, self.sizeFloat[1]
  136. return self.sizeFloat
  137. # More diagonal sizing.
  138. if self.IsFloating():
  139. dc = self.dockContext
  140. pt = win32api.GetCursorPos()
  141. windowRect = self.GetParent().GetParent().GetWindowRect()
  142. hittest = dc.nHitTest
  143. if hittest == win32con.HTTOPLEFT:
  144. cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
  145. cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
  146. self.sizeFloat = cx, cy
  147. top = (
  148. min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight)
  149. - self.cyBorder
  150. )
  151. left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
  152. dc.rectFrameDragHorz = (
  153. left,
  154. top,
  155. dc.rectFrameDragHorz[2],
  156. dc.rectFrameDragHorz[3],
  157. )
  158. return self.sizeFloat
  159. if hittest == win32con.HTTOPRIGHT:
  160. cx = max(pt[0] - windowRect[0], self.cMinWidth)
  161. cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
  162. self.sizeFloat = cx, cy
  163. top = (
  164. min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight)
  165. - self.cyBorder
  166. )
  167. dc.rectFrameDragHorz = (
  168. dc.rectFrameDragHorz[0],
  169. top,
  170. dc.rectFrameDragHorz[2],
  171. dc.rectFrameDragHorz[3],
  172. )
  173. return self.sizeFloat
  174. if hittest == win32con.HTBOTTOMLEFT:
  175. cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
  176. cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
  177. self.sizeFloat = cx, cy
  178. left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
  179. dc.rectFrameDragHorz = (
  180. left,
  181. dc.rectFrameDragHorz[1],
  182. dc.rectFrameDragHorz[2],
  183. dc.rectFrameDragHorz[3],
  184. )
  185. return self.sizeFloat
  186. if hittest == win32con.HTBOTTOMRIGHT:
  187. cx = max(pt[0] - windowRect[0], self.cMinWidth)
  188. cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
  189. self.sizeFloat = cx, cy
  190. return self.sizeFloat
  191. if mode & win32ui.LM_LENGTHY:
  192. self.sizeFloat = self.sizeFloat[0], max(self.sizeMin[1], length)
  193. return self.sizeFloat
  194. else:
  195. return max(self.sizeMin[0], length), self.sizeFloat[1]
  196. def OnWindowPosChanged(self, msg):
  197. if self.GetSafeHwnd() == 0 or self.dialog is None:
  198. return 0
  199. lparam = msg[3]
  200. """ LPARAM used with WM_WINDOWPOSCHANGED:
  201. typedef struct {
  202. HWND hwnd;
  203. HWND hwndInsertAfter;
  204. int x;
  205. int y;
  206. int cx;
  207. int cy;
  208. UINT flags;} WINDOWPOS;
  209. """
  210. format = "PPiiiii"
  211. bytes = win32ui.GetBytes(lparam, struct.calcsize(format))
  212. hwnd, hwndAfter, x, y, cx, cy, flags = struct.unpack(format, bytes)
  213. if self.bInRecalcNC:
  214. rc = self.GetClientRect()
  215. self.dialog.MoveWindow(rc)
  216. return 0
  217. # Find on which side are we docked
  218. nDockBarID = self.GetParent().GetDlgCtrlID()
  219. # Return if dropped at same location
  220. # no docking side change and no size change
  221. if (
  222. (nDockBarID == self.nDockBarID)
  223. and (flags & win32con.SWP_NOSIZE)
  224. and (
  225. (self._obj_.dwStyle & afxres.CBRS_BORDER_ANY) != afxres.CBRS_BORDER_ANY
  226. )
  227. ):
  228. return
  229. self.nDockBarID = nDockBarID
  230. # Force recalc the non-client area
  231. self.bInRecalcNC = 1
  232. try:
  233. swpflags = (
  234. win32con.SWP_NOSIZE
  235. | win32con.SWP_NOMOVE
  236. | win32con.SWP_NOZORDER
  237. | win32con.SWP_FRAMECHANGED
  238. )
  239. self.SetWindowPos(0, (0, 0, 0, 0), swpflags)
  240. finally:
  241. self.bInRecalcNC = 0
  242. return 0
  243. # This is a virtual and not a message hook.
  244. def OnSetCursor(self, window, nHitTest, wMouseMsg):
  245. if nHitTest != win32con.HTSIZE or self.bTracking:
  246. return self._obj_.OnSetCursor(window, nHitTest, wMouseMsg)
  247. if self.IsHorz():
  248. win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZENS))
  249. else:
  250. win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZEWE))
  251. return 1
  252. # Mouse Handling
  253. def OnLButtonUp(self, msg):
  254. if not self.bTracking:
  255. return 1 # pass it on.
  256. self.StopTracking(1)
  257. return 0 # Dont pass on
  258. def OnLButtonDown(self, msg):
  259. # UINT nFlags, CPoint point)
  260. # only start dragging if clicked in "void" space
  261. if self.dockBar is not None:
  262. # start the drag
  263. pt = msg[5]
  264. pt = self.ClientToScreen(pt)
  265. self.dockContext.StartDrag(pt)
  266. return 0
  267. return 1
  268. def OnNcLButtonDown(self, msg):
  269. if self.bTracking:
  270. return 0
  271. nHitTest = wparam = msg[2]
  272. pt = msg[5]
  273. if nHitTest == win32con.HTSYSMENU and not self.IsFloating():
  274. self.GetDockingFrame().ShowControlBar(self, 0, 0)
  275. elif nHitTest == win32con.HTMINBUTTON and not self.IsFloating():
  276. self.dockContext.ToggleDocking()
  277. elif (
  278. nHitTest == win32con.HTCAPTION
  279. and not self.IsFloating()
  280. and self.dockBar is not None
  281. ):
  282. self.dockContext.StartDrag(pt)
  283. elif nHitTest == win32con.HTSIZE and not self.IsFloating():
  284. self.StartTracking()
  285. else:
  286. return 1
  287. return 0
  288. def OnLButtonDblClk(self, msg):
  289. # only toggle docking if clicked in "void" space
  290. if self.dockBar is not None:
  291. # toggle docking
  292. self.dockContext.ToggleDocking()
  293. return 0
  294. return 1
  295. def OnNcLButtonDblClk(self, msg):
  296. nHitTest = wparam = msg[2]
  297. # UINT nHitTest, CPoint point)
  298. if self.dockBar is not None and nHitTest == win32con.HTCAPTION:
  299. # toggle docking
  300. self.dockContext.ToggleDocking()
  301. return 0
  302. return 1
  303. def OnMouseMove(self, msg):
  304. flags = wparam = msg[2]
  305. lparam = msg[3]
  306. if self.IsFloating() or not self.bTracking:
  307. return 1
  308. # Convert unsigned 16 bit to signed 32 bit.
  309. x = win32api.LOWORD(lparam)
  310. if x & 32768:
  311. x = x | -65536
  312. y = win32api.HIWORD(lparam)
  313. if y & 32768:
  314. y = y | -65536
  315. pt = x, y
  316. cpt = CenterPoint(self.rectTracker)
  317. pt = self.ClientToWnd(pt)
  318. if self.IsHorz():
  319. if cpt[1] != pt[1]:
  320. self.OnInvertTracker(self.rectTracker)
  321. self.rectTracker = OffsetRect(self.rectTracker, (0, pt[1] - cpt[1]))
  322. self.OnInvertTracker(self.rectTracker)
  323. else:
  324. if cpt[0] != pt[0]:
  325. self.OnInvertTracker(self.rectTracker)
  326. self.rectTracker = OffsetRect(self.rectTracker, (pt[0] - cpt[0], 0))
  327. self.OnInvertTracker(self.rectTracker)
  328. return 0 # Dont pass it on.
  329. # def OnBarStyleChange(self, old, new):
  330. def OnNcCalcSize(self, bCalcValid, size_info):
  331. (rc0, rc1, rc2, pos) = size_info
  332. self.rectBorder = self.GetWindowRect()
  333. self.rectBorder = OffsetRect(
  334. self.rectBorder, (-self.rectBorder[0], -self.rectBorder[1])
  335. )
  336. dwBorderStyle = self._obj_.dwStyle | afxres.CBRS_BORDER_ANY
  337. if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP:
  338. dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_BOTTOM
  339. rc0.left = rc0.left + self.cxGripper
  340. rc0.bottom = rc0.bottom - self.cxEdge
  341. rc0.top = rc0.top + self.cxBorder
  342. rc0.right = rc0.right - self.cxBorder
  343. self.rectBorder = (
  344. self.rectBorder[0],
  345. self.rectBorder[3] - self.cxEdge,
  346. self.rectBorder[2],
  347. self.rectBorder[3],
  348. )
  349. elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
  350. dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_TOP
  351. rc0.left = rc0.left + self.cxGripper
  352. rc0.top = rc0.top + self.cxEdge
  353. rc0.bottom = rc0.bottom - self.cxBorder
  354. rc0.right = rc0.right - self.cxBorder
  355. self.rectBorder = (
  356. self.rectBorder[0],
  357. self.rectBorder[1],
  358. self.rectBorder[2],
  359. self.rectBorder[1] + self.cxEdge,
  360. )
  361. elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT:
  362. dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_RIGHT
  363. rc0.right = rc0.right - self.cxEdge
  364. rc0.left = rc0.left + self.cxBorder
  365. rc0.bottom = rc0.bottom - self.cxBorder
  366. rc0.top = rc0.top + self.cxGripper
  367. self.rectBorder = (
  368. self.rectBorder[2] - self.cxEdge,
  369. self.rectBorder[1],
  370. self.rectBorder[2],
  371. self.rectBorder[3],
  372. )
  373. elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
  374. dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_LEFT
  375. rc0.left = rc0.left + self.cxEdge
  376. rc0.right = rc0.right - self.cxBorder
  377. rc0.bottom = rc0.bottom - self.cxBorder
  378. rc0.top = rc0.top + self.cxGripper
  379. self.rectBorder = (
  380. self.rectBorder[0],
  381. self.rectBorder[1],
  382. self.rectBorder[0] + self.cxEdge,
  383. self.rectBorder[3],
  384. )
  385. else:
  386. self.rectBorder = 0, 0, 0, 0
  387. self.SetBarStyle(dwBorderStyle)
  388. return 0
  389. def OnNcPaint(self, msg):
  390. self.EraseNonClient()
  391. dc = self.GetWindowDC()
  392. ctl = win32api.GetSysColor(win32con.COLOR_BTNHIGHLIGHT)
  393. cbr = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
  394. dc.Draw3dRect(self.rectBorder, ctl, cbr)
  395. self.DrawGripper(dc)
  396. rect = self.GetClientRect()
  397. self.InvalidateRect(rect, 1)
  398. return 0
  399. def OnNcHitTest(self, pt): # A virtual, not a hooked message.
  400. if self.IsFloating():
  401. return 1
  402. ptOrig = pt
  403. rect = self.GetWindowRect()
  404. pt = pt[0] - rect[0], pt[1] - rect[1]
  405. if PtInRect(self.rectClose, pt):
  406. return win32con.HTSYSMENU
  407. elif PtInRect(self.rectUndock, pt):
  408. return win32con.HTMINBUTTON
  409. elif PtInRect(self.rectGripper, pt):
  410. return win32con.HTCAPTION
  411. elif PtInRect(self.rectBorder, pt):
  412. return win32con.HTSIZE
  413. else:
  414. return self._obj_.OnNcHitTest(ptOrig)
  415. def StartTracking(self):
  416. self.SetCapture()
  417. # make sure no updates are pending
  418. self.RedrawWindow(None, None, win32con.RDW_ALLCHILDREN | win32con.RDW_UPDATENOW)
  419. self.dockSite.LockWindowUpdate()
  420. self.ptOld = CenterPoint(self.rectBorder)
  421. self.bTracking = 1
  422. self.rectTracker = self.rectBorder
  423. if not self.IsHorz():
  424. l, t, r, b = self.rectTracker
  425. b = b - 4
  426. self.rectTracker = l, t, r, b
  427. self.OnInvertTracker(self.rectTracker)
  428. def OnCaptureChanged(self, msg):
  429. hwnd = lparam = msg[3]
  430. if self.bTracking and hwnd != self.GetSafeHwnd():
  431. self.StopTracking(0) # cancel tracking
  432. return 1
  433. def StopTracking(self, bAccept):
  434. self.OnInvertTracker(self.rectTracker)
  435. self.dockSite.UnlockWindowUpdate()
  436. self.bTracking = 0
  437. self.ReleaseCapture()
  438. if not bAccept:
  439. return
  440. rcc = self.dockSite.GetWindowRect()
  441. if self.IsHorz():
  442. newsize = self.sizeHorz[1]
  443. maxsize = newsize + (rcc[3] - rcc[1])
  444. minsize = self.sizeMin[1]
  445. else:
  446. newsize = self.sizeVert[0]
  447. maxsize = newsize + (rcc[2] - rcc[0])
  448. minsize = self.sizeMin[0]
  449. pt = CenterPoint(self.rectTracker)
  450. if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP:
  451. newsize = newsize + (pt[1] - self.ptOld[1])
  452. elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
  453. newsize = newsize + (-pt[1] + self.ptOld[1])
  454. elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_LEFT:
  455. newsize = newsize + (pt[0] - self.ptOld[0])
  456. elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
  457. newsize = newsize + (-pt[0] + self.ptOld[0])
  458. newsize = max(minsize, min(maxsize, newsize))
  459. if self.IsHorz():
  460. self.sizeHorz = self.sizeHorz[0], newsize
  461. else:
  462. self.sizeVert = newsize, self.sizeVert[1]
  463. self.dockSite.RecalcLayout()
  464. return 0
  465. def OnInvertTracker(self, rect):
  466. assert rect[2] - rect[0] > 0 and rect[3] - rect[1] > 0, "rect is empty"
  467. assert self.bTracking
  468. rcc = self.GetWindowRect()
  469. rcf = self.dockSite.GetWindowRect()
  470. rect = OffsetRect(rect, (rcc[0] - rcf[0], rcc[1] - rcf[1]))
  471. rect = DeflateRect(rect, (1, 1))
  472. flags = win32con.DCX_WINDOW | win32con.DCX_CACHE | win32con.DCX_LOCKWINDOWUPDATE
  473. dc = self.dockSite.GetDCEx(None, flags)
  474. try:
  475. brush = win32ui.GetHalftoneBrush()
  476. oldBrush = dc.SelectObject(brush)
  477. dc.PatBlt(
  478. (rect[0], rect[1]),
  479. (rect[2] - rect[0], rect[3] - rect[1]),
  480. win32con.PATINVERT,
  481. )
  482. dc.SelectObject(oldBrush)
  483. finally:
  484. self.dockSite.ReleaseDC(dc)
  485. def IsHorz(self):
  486. return (
  487. self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP
  488. or self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM
  489. )
  490. def ClientToWnd(self, pt):
  491. x, y = pt
  492. if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
  493. y = y + self.cxEdge
  494. elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
  495. x = x + self.cxEdge
  496. return x, y
  497. def DrawGripper(self, dc):
  498. # no gripper if floating
  499. if self._obj_.dwStyle & afxres.CBRS_FLOATING:
  500. return
  501. # -==HACK==-
  502. # in order to calculate the client area properly after docking,
  503. # the client area must be recalculated twice (I have no idea why)
  504. self.dockSite.RecalcLayout()
  505. # -==END HACK==-
  506. gripper = self.GetWindowRect()
  507. gripper = self.ScreenToClient(gripper)
  508. gripper = OffsetRect(gripper, (-gripper[0], -gripper[1]))
  509. gl, gt, gr, gb = gripper
  510. if self._obj_.dwStyle & afxres.CBRS_ORIENT_HORZ:
  511. # gripper at left
  512. self.rectGripper = gl, gt + 40, gl + 20, gb
  513. # draw close box
  514. self.rectClose = gl + 7, gt + 10, gl + 19, gt + 22
  515. dc.DrawFrameControl(
  516. self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE
  517. )
  518. # draw docking toggle box
  519. self.rectUndock = OffsetRect(self.rectClose, (0, 13))
  520. dc.DrawFrameControl(
  521. self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX
  522. )
  523. gt = gt + 38
  524. gb = gb - 10
  525. gl = gl + 10
  526. gr = gl + 3
  527. gripper = gl, gt, gr, gb
  528. dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow)
  529. dc.Draw3dRect(OffsetRect(gripper, (4, 0)), clrBtnHilight, clrBtnShadow)
  530. else:
  531. # gripper at top
  532. self.rectGripper = gl, gt, gr - 40, gt + 20
  533. # draw close box
  534. self.rectClose = gr - 21, gt + 7, gr - 10, gt + 18
  535. dc.DrawFrameControl(
  536. self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE
  537. )
  538. # draw docking toggle box
  539. self.rectUndock = OffsetRect(self.rectClose, (-13, 0))
  540. dc.DrawFrameControl(
  541. self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX
  542. )
  543. gr = gr - 38
  544. gl = gl + 10
  545. gt = gt + 10
  546. gb = gt + 3
  547. gripper = gl, gt, gr, gb
  548. dc.Draw3dRect(gripper, clrBtnHilight, clrBtnShadow)
  549. dc.Draw3dRect(OffsetRect(gripper, (0, 4)), clrBtnHilight, clrBtnShadow)
  550. def HookMessages(self):
  551. self.HookMessage(self.OnLButtonUp, win32con.WM_LBUTTONUP)
  552. self.HookMessage(self.OnLButtonDown, win32con.WM_LBUTTONDOWN)
  553. self.HookMessage(self.OnLButtonDblClk, win32con.WM_LBUTTONDBLCLK)
  554. self.HookMessage(self.OnNcLButtonDown, win32con.WM_NCLBUTTONDOWN)
  555. self.HookMessage(self.OnNcLButtonDblClk, win32con.WM_NCLBUTTONDBLCLK)
  556. self.HookMessage(self.OnMouseMove, win32con.WM_MOUSEMOVE)
  557. self.HookMessage(self.OnNcPaint, win32con.WM_NCPAINT)
  558. self.HookMessage(self.OnCaptureChanged, win32con.WM_CAPTURECHANGED)
  559. self.HookMessage(self.OnWindowPosChanged, win32con.WM_WINDOWPOSCHANGED)
  560. # self.HookMessage(self.OnSize, win32con.WM_SIZE)
  561. def EditCreator(parent):
  562. d = win32ui.CreateEdit()
  563. es = (
  564. win32con.WS_CHILD
  565. | win32con.WS_VISIBLE
  566. | win32con.WS_BORDER
  567. | win32con.ES_MULTILINE
  568. | win32con.ES_WANTRETURN
  569. )
  570. d.CreateWindow(es, (0, 0, 150, 150), parent, 1000)
  571. return d
  572. def test():
  573. import pywin.mfc.dialog
  574. global bar
  575. bar = DockingBar()
  576. creator = EditCreator
  577. bar.CreateWindow(win32ui.GetMainFrame(), creator, "Coolbar Demo", 0xFFFFF)
  578. # win32ui.GetMainFrame().ShowControlBar(bar, 1, 0)
  579. bar.SetBarStyle(
  580. bar.GetBarStyle()
  581. | afxres.CBRS_TOOLTIPS
  582. | afxres.CBRS_FLYBY
  583. | afxres.CBRS_SIZE_DYNAMIC
  584. )
  585. bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
  586. win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
  587. if __name__ == "__main__":
  588. test()