Wed, 20 Jan 2021 10:15:13 +0100
updated and added new files for printrun
15 | 1 | # This file is part of the Printrun suite. |
2 | # | |
3 | # Printrun is free software: you can redistribute it and/or modify | |
4 | # it under the terms of the GNU General Public License as published by | |
5 | # the Free Software Foundation, either version 3 of the License, or | |
6 | # (at your option) any later version. | |
7 | # | |
8 | # Printrun is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License | |
14 | # along with Printrun. If not, see <http://www.gnu.org/licenses/>. | |
15 | ||
16 | import wx | |
17 | import math | |
18 | from .bufferedcanvas import BufferedCanvas | |
19 | from printrun.utils import imagefile | |
20 | ||
21 | def sign(n): | |
22 | if n < 0: return -1 | |
23 | elif n > 0: return 1 | |
24 | else: return 0 | |
25 | ||
46 | 26 | DASHES = [4, 7] |
27 | # Brush and pen for grey overlay when mouse hovers over | |
28 | HOVER_PEN_COLOR = wx.Colour(100, 100, 100, 172) | |
29 | HOVER_BRUSH_COLOR = wx.Colour(0, 0, 0, 128) | |
30 | ||
31 | class FocusCanvas(BufferedCanvas): | |
32 | def __init__(self, *args, **kwds): | |
33 | super().__init__(*args, **kwds) | |
34 | self.Bind(wx.EVT_SET_FOCUS, self.onFocus) | |
35 | ||
36 | def onFocus(self, evt): | |
37 | self.Refresh() | |
38 | evt.Skip() | |
39 | ||
40 | def drawFocusRect(self, dc): | |
41 | if self.HasFocus(): | |
42 | pen = wx.Pen(wx.BLACK, 1, wx.PENSTYLE_USER_DASH) | |
43 | pen.SetDashes(DASHES) | |
44 | dc.Pen = pen | |
45 | dc.Brush = wx.Brush(wx.TRANSPARENT_BRUSH) | |
46 | dc.DrawRectangle(self.ClientRect) | |
47 | ||
48 | class XYButtons(FocusCanvas): | |
15 | 49 | keypad_positions = { |
46 | 50 | 0: (104, 99), |
15 | 51 | 1: (86, 83), |
52 | 2: (68, 65), | |
53 | 3: (53, 50) | |
54 | } | |
46 | 55 | keypad_radius = 9 |
15 | 56 | corner_size = (49, 49) |
57 | corner_inset = (7, 13) | |
58 | label_overlay_positions = { | |
59 | 1: (145, 98.5, 9), | |
60 | 2: (160.5, 83.5, 10.6), | |
61 | 3: (178, 66, 13), | |
62 | 4: (197.3, 46.3, 13.3) | |
63 | } | |
64 | concentric_circle_radii = [0, 17, 45, 69, 94, 115] | |
65 | concentric_inset = 11 | |
66 | center = (124, 121) | |
67 | spacer = 7 | |
68 | imagename = "control_xy.png" | |
69 | corner_to_axis = { | |
70 | -1: "center", | |
71 | 0: "x", | |
72 | 1: "z", | |
73 | 2: "y", | |
74 | 3: "all", | |
75 | } | |
76 | ||
77 | def __init__(self, parent, moveCallback = None, cornerCallback = None, spacebarCallback = None, bgcolor = "#FFFFFF", ID=-1, zcallback=None): | |
78 | self.bg_bmp = wx.Image(imagefile(self.imagename), wx.BITMAP_TYPE_PNG).ConvertToBitmap() | |
79 | self.keypad_bmp = wx.Image(imagefile("arrow_keys.png"), wx.BITMAP_TYPE_PNG).ConvertToBitmap() | |
80 | self.keypad_idx = -1 | |
46 | 81 | self.hovered_keypad = None |
15 | 82 | self.quadrant = None |
83 | self.concentric = None | |
84 | self.corner = None | |
85 | self.moveCallback = moveCallback | |
86 | self.cornerCallback = cornerCallback | |
87 | self.spacebarCallback = spacebarCallback | |
88 | self.zCallback = zcallback | |
89 | self.enabled = False | |
90 | # Remember the last clicked buttons, so we can repeat when spacebar pressed | |
91 | self.lastMove = None | |
92 | self.lastCorner = None | |
93 | ||
94 | self.bgcolor = wx.Colour() | |
46 | 95 | self.bgcolor.Set(bgcolor) |
15 | 96 | self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128) |
97 | ||
46 | 98 | super().__init__(parent, ID, size=self.bg_bmp.GetSize()) |
15 | 99 | |
100 | self.bind_events() | |
101 | ||
102 | def bind_events(self): | |
103 | # Set up mouse and keyboard event capture | |
104 | self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) | |
105 | self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown) | |
106 | self.Bind(wx.EVT_MOTION, self.OnMotion) | |
107 | self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) | |
46 | 108 | self.Bind(wx.EVT_CHAR_HOOK, self.OnKey) |
109 | self.Bind(wx.EVT_KILL_FOCUS, self.onKillFocus) | |
110 | ||
111 | def onKillFocus(self, evt): | |
112 | self.setKeypadIndex(-1) | |
113 | evt.Skip() | |
15 | 114 | |
115 | def disable(self): | |
46 | 116 | self.Enabled = self.enabled = False |
15 | 117 | self.update() |
118 | ||
119 | def enable(self): | |
46 | 120 | self.Enabled = self.enabled = True |
15 | 121 | self.update() |
122 | ||
123 | def repeatLast(self): | |
124 | if self.lastMove: | |
125 | self.moveCallback(*self.lastMove) | |
126 | if self.lastCorner: | |
127 | self.cornerCallback(self.corner_to_axis[self.lastCorner]) | |
128 | ||
129 | def clearRepeat(self): | |
130 | self.lastMove = None | |
131 | self.lastCorner = None | |
132 | ||
133 | def distanceToLine(self, pos, x1, y1, x2, y2): | |
134 | xlen = x2 - x1 | |
135 | ylen = y2 - y1 | |
136 | pxlen = x1 - pos.x | |
137 | pylen = y1 - pos.y | |
138 | return abs(xlen * pylen - ylen * pxlen) / math.sqrt(xlen ** 2 + ylen ** 2) | |
139 | ||
140 | def distanceToPoint(self, x1, y1, x2, y2): | |
141 | return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) | |
142 | ||
46 | 143 | def cycleKeypadIndex(self, forward): |
144 | idx = self.keypad_idx + (1 if forward else -1) | |
145 | # do not really cycle to allow exiting of jog controls widget | |
146 | return idx if idx < len(self.keypad_positions) else -1 | |
15 | 147 | |
148 | def setKeypadIndex(self, idx): | |
149 | self.keypad_idx = idx | |
150 | self.update() | |
151 | ||
46 | 152 | def getMovement(self, event): |
15 | 153 | xdir = [1, 0, -1, 0, 0, 0][self.quadrant] |
154 | ydir = [0, 1, 0, -1, 0, 0][self.quadrant] | |
155 | zdir = [0, 0, 0, 0, 1, -1][self.quadrant] | |
156 | magnitude = math.pow(10, self.concentric - 2) | |
46 | 157 | magnitude *= event.ShiftDown() and 2 or event.ControlDown() and 0.5 or 1 |
158 | ||
159 | if zdir: | |
15 | 160 | magnitude = min(magnitude, 10) |
161 | return (magnitude * xdir, magnitude * ydir, magnitude * zdir) | |
162 | ||
163 | def lookupConcentric(self, radius): | |
164 | idx = 0 | |
165 | for r in self.concentric_circle_radii[1:]: | |
166 | if radius < r: | |
167 | return idx | |
168 | idx += 1 | |
169 | return len(self.concentric_circle_radii) | |
170 | ||
171 | def getQuadrantConcentricFromPosition(self, pos): | |
172 | rel_x = pos[0] - self.center[0] | |
173 | rel_y = pos[1] - self.center[1] | |
174 | if rel_x > rel_y and rel_x > -rel_y: | |
175 | quadrant = 0 # Right | |
176 | elif rel_x <= rel_y and rel_x > -rel_y: | |
177 | quadrant = 3 # Down | |
178 | elif rel_x > rel_y and rel_x < -rel_y: | |
179 | quadrant = 1 # Up | |
180 | else: | |
181 | quadrant = 2 # Left | |
182 | ||
46 | 183 | radius = math.sqrt(rel_x ** 2 + rel_y ** 2) |
15 | 184 | idx = self.lookupConcentric(radius) |
185 | return (quadrant, idx) | |
186 | ||
187 | def mouseOverKeypad(self, mpos): | |
188 | for idx, kpos in self.keypad_positions.items(): | |
189 | radius = self.distanceToPoint(mpos[0], mpos[1], kpos[0], kpos[1]) | |
46 | 190 | if radius < XYButtons.keypad_radius: |
15 | 191 | return idx |
192 | return None | |
193 | ||
194 | def drawPartialPie(self, gc, center, r1, r2, angle1, angle2): | |
195 | p1 = wx.Point(center.x + r1 * math.cos(angle1), center.y + r1 * math.sin(angle1)) | |
196 | ||
197 | path = gc.CreatePath() | |
198 | path.MoveToPoint(p1.x, p1.y) | |
199 | path.AddArc(center.x, center.y, r1, angle1, angle2, True) | |
200 | path.AddArc(center.x, center.y, r2, angle2, angle1, False) | |
201 | path.AddLineToPoint(p1.x, p1.y) | |
202 | gc.DrawPath(path) | |
203 | ||
204 | def highlightQuadrant(self, gc, quadrant, concentric): | |
46 | 205 | if not 0 <= quadrant <= 3: |
206 | return | |
15 | 207 | assert(concentric >= 0 and concentric <= 4) |
208 | ||
209 | inner_ring_radius = self.concentric_inset | |
210 | # fudge = math.pi*0.002 | |
211 | fudge = -0.02 | |
212 | center = wx.Point(self.center[0], self.center[1]) | |
213 | if quadrant == 0: | |
214 | a1, a2 = (-math.pi * 0.25, math.pi * 0.25) | |
215 | center.x += inner_ring_radius | |
216 | elif quadrant == 1: | |
217 | a1, a2 = (math.pi * 1.25, math.pi * 1.75) | |
218 | center.y -= inner_ring_radius | |
219 | elif quadrant == 2: | |
220 | a1, a2 = (math.pi * 0.75, math.pi * 1.25) | |
221 | center.x -= inner_ring_radius | |
222 | elif quadrant == 3: | |
223 | a1, a2 = (math.pi * 0.25, math.pi * 0.75) | |
224 | center.y += inner_ring_radius | |
225 | ||
226 | r1 = self.concentric_circle_radii[concentric] | |
227 | r2 = self.concentric_circle_radii[concentric + 1] | |
228 | ||
229 | self.drawPartialPie(gc, center, r1 - inner_ring_radius, r2 - inner_ring_radius, a1 + fudge, a2 - fudge) | |
230 | ||
231 | def drawCorner(self, gc, x, y, angle = 0.0): | |
232 | w, h = self.corner_size | |
233 | ||
234 | gc.PushState() | |
235 | gc.Translate(x, y) | |
236 | gc.Rotate(angle) | |
237 | path = gc.CreatePath() | |
238 | path.MoveToPoint(-w / 2, -h / 2) | |
239 | path.AddLineToPoint(w / 2, -h / 2) | |
240 | path.AddLineToPoint(w / 2, -h / 2 + h / 4) | |
241 | path.AddLineToPoint(w / 12, h / 12) | |
242 | path.AddLineToPoint(-w / 2 + w / 4, h / 2) | |
243 | path.AddLineToPoint(-w / 2, h / 2) | |
244 | path.AddLineToPoint(-w / 2, -h / 2) | |
245 | gc.DrawPath(path) | |
246 | gc.PopState() | |
247 | ||
248 | def highlightCorner(self, gc, corner = 0): | |
249 | w, h = self.corner_size | |
250 | xinset, yinset = self.corner_inset | |
251 | cx, cy = self.center | |
46 | 252 | ww, wh = self.GetSize() |
15 | 253 | |
254 | if corner == 0: | |
255 | x, y = (cx - ww / 2 + xinset + 1, cy - wh / 2 + yinset) | |
256 | self.drawCorner(gc, x + w / 2, y + h / 2, 0) | |
257 | elif corner == 1: | |
258 | x, y = (cx + ww / 2 - xinset, cy - wh / 2 + yinset) | |
259 | self.drawCorner(gc, x - w / 2, y + h / 2, math.pi / 2) | |
260 | elif corner == 2: | |
261 | x, y = (cx + ww / 2 - xinset, cy + wh / 2 - yinset - 1) | |
262 | self.drawCorner(gc, x - w / 2, y - h / 2, math.pi) | |
263 | elif corner == 3: | |
264 | x, y = (cx - ww / 2 + xinset + 1, cy + wh / 2 - yinset - 1) | |
265 | self.drawCorner(gc, x + w / 2, y - h / 2, math.pi * 3 / 2) | |
266 | ||
267 | def drawCenteredDisc(self, gc, radius): | |
268 | cx, cy = self.center | |
269 | gc.DrawEllipse(cx - radius, cy - radius, radius * 2, radius * 2) | |
270 | ||
271 | def draw(self, dc, w, h): | |
272 | dc.SetBackground(wx.Brush(self.bgcolor)) | |
273 | dc.Clear() | |
274 | gc = wx.GraphicsContext.Create(dc) | |
275 | ||
276 | if self.bg_bmp: | |
277 | w, h = (self.bg_bmp.GetWidth(), self.bg_bmp.GetHeight()) | |
278 | gc.DrawBitmap(self.bg_bmp, 0, 0, w, h) | |
279 | ||
280 | if self.enabled and self.IsEnabled(): | |
46 | 281 | gc.SetPen(wx.Pen(HOVER_PEN_COLOR, 4)) |
282 | gc.SetBrush(wx.Brush(HOVER_BRUSH_COLOR)) | |
15 | 283 | |
284 | if self.concentric is not None: | |
285 | if self.concentric < len(self.concentric_circle_radii): | |
286 | if self.concentric == 0: | |
287 | self.drawCenteredDisc(gc, self.concentric_circle_radii[1]) | |
288 | elif self.quadrant is not None: | |
289 | self.highlightQuadrant(gc, self.quadrant, self.concentric) | |
290 | elif self.corner is not None: | |
291 | self.highlightCorner(gc, self.corner) | |
292 | ||
293 | if self.keypad_idx >= 0: | |
294 | padw, padh = (self.keypad_bmp.GetWidth(), self.keypad_bmp.GetHeight()) | |
295 | pos = self.keypad_positions[self.keypad_idx] | |
296 | pos = (pos[0] - padw / 2 - 3, pos[1] - padh / 2 - 3) | |
297 | gc.DrawBitmap(self.keypad_bmp, pos[0], pos[1], padw, padh) | |
298 | ||
46 | 299 | if self.hovered_keypad is not None and self.hovered_keypad != self.keypad_idx: |
300 | pos = self.keypad_positions[self.hovered_keypad] | |
301 | r = XYButtons.keypad_radius | |
302 | gc.DrawEllipse(pos[0]-r/2, pos[1]-r/2, r, r) | |
303 | ||
15 | 304 | # Draw label overlays |
305 | gc.SetPen(wx.Pen(wx.Colour(255, 255, 255, 128), 1)) | |
306 | gc.SetBrush(wx.Brush(wx.Colour(255, 255, 255, 128 + 64))) | |
307 | for idx, kpos in self.label_overlay_positions.items(): | |
308 | if idx != self.concentric: | |
309 | r = kpos[2] | |
310 | gc.DrawEllipse(kpos[0] - r, kpos[1] - r, r * 2, r * 2) | |
311 | else: | |
312 | gc.SetPen(wx.Pen(self.bgcolor, 0)) | |
313 | gc.SetBrush(wx.Brush(self.bgcolormask)) | |
314 | gc.DrawRectangle(0, 0, w, h) | |
46 | 315 | |
316 | self.drawFocusRect(dc) | |
317 | ||
15 | 318 | # Used to check exact position of keypad dots, should we ever resize the bg image |
319 | # for idx, kpos in self.label_overlay_positions.items(): | |
320 | # dc.DrawCircle(kpos[0], kpos[1], kpos[2]) | |
321 | ||
322 | # ------ # | |
323 | # Events # | |
324 | # ------ # | |
325 | def OnKey(self, evt): | |
46 | 326 | # print('XYButtons key', evt.GetKeyCode()) |
15 | 327 | if not self.enabled: |
46 | 328 | evt.Skip() |
15 | 329 | return |
46 | 330 | key = evt.KeyCode |
15 | 331 | if self.keypad_idx >= 0: |
46 | 332 | if key == wx.WXK_TAB: |
333 | keypad = self.cycleKeypadIndex(not evt.ShiftDown()) | |
334 | self.setKeypadIndex(keypad) | |
335 | if keypad == -1: | |
336 | # exit widget after largest step | |
337 | # evt.Skip() | |
338 | # On MS Windows if tab event is delivered, | |
339 | # it is not handled | |
340 | self.Navigate(not evt.ShiftDown()) | |
341 | return | |
342 | elif key == wx.WXK_ESCAPE: | |
343 | self.setKeypadIndex(-1) | |
344 | elif key == wx.WXK_UP: | |
15 | 345 | self.quadrant = 1 |
46 | 346 | elif key == wx.WXK_DOWN: |
15 | 347 | self.quadrant = 3 |
46 | 348 | elif key == wx.WXK_LEFT: |
15 | 349 | self.quadrant = 2 |
46 | 350 | elif key == wx.WXK_RIGHT: |
15 | 351 | self.quadrant = 0 |
46 | 352 | elif key == wx.WXK_PAGEUP: |
15 | 353 | self.quadrant = 4 |
46 | 354 | elif key == wx.WXK_PAGEDOWN: |
15 | 355 | self.quadrant = 5 |
356 | else: | |
357 | evt.Skip() | |
358 | return | |
359 | ||
46 | 360 | self.concentric = self.keypad_idx + 1 |
361 | ||
362 | if self.quadrant is not None: | |
363 | x, y, z = self.getMovement(evt) | |
364 | if (x or y) and self.moveCallback: | |
365 | self.moveCallback(x, y) | |
366 | if z and self.zCallback: | |
367 | self.zCallback(z) | |
368 | self.Refresh() | |
369 | elif key == wx.WXK_SPACE: | |
15 | 370 | self.spacebarCallback() |
46 | 371 | elif key == wx.WXK_TAB: |
372 | self.setKeypadIndex(len(self.keypad_positions)-1 if evt.ShiftDown() else 0) | |
373 | else: | |
374 | # handle arrows elsewhere | |
375 | evt.Skip() | |
15 | 376 | |
377 | def OnMotion(self, event): | |
378 | if not self.enabled: | |
379 | return | |
380 | ||
381 | oldcorner = self.corner | |
382 | oldq, oldc = self.quadrant, self.concentric | |
46 | 383 | old_hovered_keypad = self.hovered_keypad |
15 | 384 | |
385 | mpos = event.GetPosition() | |
46 | 386 | self.hovered_keypad = self.mouseOverKeypad(mpos) |
15 | 387 | self.quadrant = None |
388 | self.concentric = None | |
46 | 389 | if self.hovered_keypad is None: |
15 | 390 | center = wx.Point(self.center[0], self.center[1]) |
391 | riseDist = self.distanceToLine(mpos, center.x - 1, center.y - 1, center.x + 1, center.y + 1) | |
392 | fallDist = self.distanceToLine(mpos, center.x - 1, center.y + 1, center.x + 1, center.y - 1) | |
393 | self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) | |
394 | ||
395 | # If mouse hovers in space between quadrants, don't commit to a quadrant | |
396 | if riseDist <= self.spacer or fallDist <= self.spacer: | |
397 | self.quadrant = None | |
398 | ||
399 | cx, cy = self.center | |
400 | if mpos.x < cx and mpos.y < cy: | |
401 | self.corner = 0 | |
402 | if mpos.x >= cx and mpos.y < cy: | |
403 | self.corner = 1 | |
404 | if mpos.x >= cx and mpos.y >= cy: | |
405 | self.corner = 2 | |
406 | if mpos.x < cx and mpos.y >= cy: | |
407 | self.corner = 3 | |
408 | ||
46 | 409 | if oldq != self.quadrant or oldc != self.concentric or oldcorner != self.corner \ |
410 | or old_hovered_keypad != self.hovered_keypad: | |
15 | 411 | self.update() |
412 | ||
413 | def OnLeftDown(self, event): | |
414 | if not self.enabled: | |
415 | return | |
416 | ||
417 | # Take focus when clicked so that arrow keys can control movement | |
418 | self.SetFocus() | |
419 | ||
420 | mpos = event.GetPosition() | |
421 | ||
422 | idx = self.mouseOverKeypad(mpos) | |
423 | if idx is None: | |
424 | self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) | |
425 | if self.concentric is not None: | |
426 | if self.concentric < len(self.concentric_circle_radii): | |
427 | if self.concentric == 0: | |
428 | self.lastCorner = -1 | |
429 | self.lastMove = None | |
430 | self.cornerCallback(self.corner_to_axis[-1]) | |
431 | elif self.quadrant is not None: | |
46 | 432 | x, y, z = self.getMovement(event) |
15 | 433 | if self.moveCallback: |
434 | self.lastMove = (x, y) | |
435 | self.lastCorner = None | |
436 | self.moveCallback(x, y) | |
437 | elif self.corner is not None: | |
438 | if self.cornerCallback: | |
439 | self.lastCorner = self.corner | |
440 | self.lastMove = None | |
441 | self.cornerCallback(self.corner_to_axis[self.corner]) | |
442 | else: | |
46 | 443 | self.setKeypadIndex(-1 if self.keypad_idx == idx else idx) |
15 | 444 | |
445 | def OnLeaveWindow(self, evt): | |
446 | self.quadrant = None | |
447 | self.concentric = None | |
448 | self.update() | |
449 | ||
450 | class XYButtonsMini(XYButtons): | |
451 | imagename = "control_mini.png" | |
452 | center = (57, 56.5) | |
453 | concentric_circle_radii = [0, 30.3] | |
454 | corner_inset = (5, 5) | |
455 | corner_size = (50, 50) | |
456 | outer_radius = 31 | |
457 | corner_to_axis = { | |
458 | 0: "x", | |
459 | 1: "z", | |
460 | 2: "y", | |
461 | 3: "center", | |
462 | } | |
463 | ||
464 | def bind_events(self): | |
465 | # Set up mouse and keyboard event capture | |
466 | self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) | |
467 | self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown) | |
468 | self.Bind(wx.EVT_MOTION, self.OnMotion) | |
469 | self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) | |
470 | ||
471 | def OnMotion(self, event): | |
472 | if not self.enabled: | |
473 | return | |
474 | ||
475 | oldcorner = self.corner | |
476 | oldq, oldc = self.quadrant, self.concentric | |
477 | ||
478 | mpos = event.GetPosition() | |
479 | ||
480 | self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) | |
481 | ||
482 | cx, cy = XYButtonsMini.center | |
483 | if mpos.x < cx and mpos.y < cy: | |
484 | self.corner = 0 | |
485 | if mpos.x >= cx and mpos.y < cy: | |
486 | self.corner = 1 | |
487 | if mpos.x >= cx and mpos.y >= cy: | |
488 | self.corner = 2 | |
489 | if mpos.x < cx and mpos.y >= cy: | |
490 | self.corner = 3 | |
491 | ||
492 | if oldq != self.quadrant or oldc != self.concentric or oldcorner != self.corner: | |
493 | self.update() | |
494 | ||
495 | def OnLeftDown(self, event): | |
496 | if not self.enabled: | |
497 | return | |
498 | ||
499 | # Take focus when clicked so that arrow keys can control movement | |
500 | self.SetFocus() | |
501 | ||
502 | mpos = event.GetPosition() | |
503 | ||
504 | self.quadrant, self.concentric = self.getQuadrantConcentricFromPosition(mpos) | |
505 | if self.concentric is not None: | |
506 | if self.concentric < len(self.concentric_circle_radii): | |
507 | self.cornerCallback("all") | |
508 | elif self.corner is not None: | |
509 | if self.cornerCallback: | |
510 | self.lastCorner = self.corner | |
511 | self.lastMove = None | |
512 | self.cornerCallback(self.corner_to_axis[self.corner]) | |
513 | ||
514 | def drawCorner(self, gc, x, y, angle = 0.0): | |
515 | w, h = self.corner_size | |
516 | ||
517 | gc.PushState() | |
518 | gc.Translate(x, y) | |
519 | gc.Rotate(angle) | |
520 | path = gc.CreatePath() | |
521 | path.MoveToPoint(-w / 2, -h / 2) | |
522 | path.AddLineToPoint(w / 2, -h / 2) | |
523 | path.AddLineToPoint(w / 2, -h / 2 + h / 4) | |
524 | path.AddArc(w / 2, h / 2, self.outer_radius, 3 * math.pi / 2, math.pi, False) | |
525 | path.AddLineToPoint(-w / 2, h / 2) | |
526 | path.AddLineToPoint(-w / 2, -h / 2) | |
527 | gc.DrawPath(path) | |
528 | gc.PopState() | |
529 | ||
530 | def draw(self, dc, w, h): | |
531 | dc.SetBackground(wx.Brush(self.bgcolor)) | |
532 | dc.Clear() | |
533 | gc = wx.GraphicsContext.Create(dc) | |
534 | ||
535 | if self.bg_bmp: | |
536 | w, h = (self.bg_bmp.GetWidth(), self.bg_bmp.GetHeight()) | |
537 | gc.DrawBitmap(self.bg_bmp, 0, 0, w, h) | |
538 | ||
539 | if self.enabled and self.IsEnabled(): | |
46 | 540 | gc.SetPen(wx.Pen(HOVER_PEN_COLOR, 4)) |
541 | gc.SetBrush(wx.Brush(HOVER_BRUSH_COLOR)) | |
15 | 542 | |
543 | if self.concentric is not None: | |
544 | if self.concentric < len(self.concentric_circle_radii): | |
545 | self.drawCenteredDisc(gc, self.concentric_circle_radii[-1]) | |
546 | elif self.corner is not None: | |
547 | self.highlightCorner(gc, self.corner) | |
548 | else: | |
549 | gc.SetPen(wx.Pen(self.bgcolor, 0)) | |
550 | gc.SetBrush(wx.Brush(self.bgcolormask)) | |
551 | gc.DrawRectangle(0, 0, w, h) |