chiark / gitweb /
Add tab key to the glNumberCtrl.
[cura.git] / Cura / gui / util / openglGui.py
1 from __future__ import absolute_import
2 from __future__ import division
3
4 import wx
5 from wx import glcanvas
6 import OpenGL
7 OpenGL.ERROR_CHECKING = False
8 from OpenGL.GL import *
9
10 from Cura.gui.util import opengl
11
12 class glGuiControl(object):
13         def __init__(self, parent, pos):
14                 self._parent = parent
15                 self._base = parent._base
16                 self._pos = pos
17                 self._size = (0,0, 1, 1)
18                 self._parent.add(self)
19
20         def setSize(self, x, y, w, h):
21                 self._size = (x, y, w, h)
22
23         def getSize(self):
24                 return self._size
25
26         def getMinSize(self):
27                 return 1, 1
28
29         def updateLayout(self):
30                 pass
31
32         def focusNext(self):
33                 for n in xrange(self._parent._glGuiControlList.index(self) + 1, len(self._parent._glGuiControlList)):
34                         if self._parent._glGuiControlList[n].setFocus():
35                                 return
36                 for n in xrange(0, self._parent._glGuiControlList.index(self)):
37                         if self._parent._glGuiControlList[n].setFocus():
38                                 return
39
40         def focusPrevious(self):
41                 for n in xrange(self._parent._glGuiControlList.index(self) -1, -1, -1):
42                         if self._parent._glGuiControlList[n].setFocus():
43                                 return
44                 for n in xrange(len(self._parent._glGuiControlList) - 1, self._parent._glGuiControlList.index(self), -1):
45                         if self._parent._glGuiControlList[n].setFocus():
46                                 return
47
48         def setFocus(self):
49                 return False
50
51 class glGuiContainer(glGuiControl):
52         def __init__(self, parent, pos):
53                 self._glGuiControlList = []
54                 glGuiLayoutButtons(self)
55                 super(glGuiContainer, self).__init__(parent, pos)
56
57         def add(self, ctrl):
58                 self._glGuiControlList.append(ctrl)
59                 self.updateLayout()
60
61         def OnMouseDown(self, x, y):
62                 for ctrl in self._glGuiControlList:
63                         if ctrl.OnMouseDown(x, y):
64                                 return True
65                 return False
66
67         def OnMouseMotion(self, x, y):
68                 handled = False
69                 for ctrl in self._glGuiControlList:
70                         if ctrl.OnMouseMotion(x, y):
71                                 handled = True
72                 return handled
73
74         def draw(self):
75                 for ctrl in self._glGuiControlList:
76                         ctrl.draw()
77
78         def updateLayout(self):
79                 self._layout.update()
80                 for ctrl in self._glGuiControlList:
81                         ctrl.updateLayout()
82
83 class glGuiPanel(glcanvas.GLCanvas):
84         def __init__(self, parent):
85                 attribList = (glcanvas.WX_GL_RGBA, glcanvas.WX_GL_DOUBLEBUFFER, glcanvas.WX_GL_DEPTH_SIZE, 24, glcanvas.WX_GL_STENCIL_SIZE, 8)
86                 glcanvas.GLCanvas.__init__(self, parent, style=wx.WANTS_CHARS, attribList = attribList)
87                 self._base = self
88                 self._focus = None
89                 self._container = None
90                 self._container = glGuiContainer(self, (0,0))
91
92                 self._context = glcanvas.GLContext(self)
93                 self._glButtonsTexture = None
94                 self._buttonSize = 64
95
96                 wx.EVT_PAINT(self, self._OnGuiPaint)
97                 wx.EVT_SIZE(self, self._OnSize)
98                 wx.EVT_ERASE_BACKGROUND(self, self._OnEraseBackground)
99                 wx.EVT_LEFT_DOWN(self, self._OnGuiMouseLeftDown)
100                 wx.EVT_MOTION(self, self._OnGuiMouseMotion)
101                 wx.EVT_CHAR(self, self.OnKeyChar)
102                 wx.EVT_KILL_FOCUS(self, self.OnFocusLost)
103
104         def OnKeyChar(self, e):
105                 if self._focus is not None:
106                         self._focus.OnKeyChar(e.GetKeyCode())
107                         self.Refresh()
108
109         def OnFocusLost(self, e):
110                 self._focus = None
111                 self.Refresh()
112
113         def _OnGuiMouseLeftDown(self,e):
114                 self.SetFocus()
115                 if self._container.OnMouseDown(e.GetX(), e.GetY()):
116                         self.Refresh()
117                         return
118                 self.OnMouseLeftDown(e)
119
120         def _OnGuiMouseMotion(self,e):
121                 self.Refresh()
122                 if not self._container.OnMouseMotion(e.GetX(), e.GetY()):
123                         self.OnMouseMotion(e)
124
125         def _OnGuiPaint(self, e):
126                 h = self.GetSize().GetHeight()
127                 w = self.GetSize().GetWidth()
128                 oldButtonSize = self._buttonSize
129                 if h / 3 > w / 4:
130                         w = h * 4 / 3
131                 if w < 64 * 10:
132                         self._buttonSize = 48
133                 elif w < 64 * 15:
134                         self._buttonSize = 64
135                 elif w < 64 * 20:
136                         self._buttonSize = 80
137                 else:
138                         self._buttonSize = 96
139                 if self._buttonSize != oldButtonSize:
140                         self._container.updateLayout()
141
142                 dc = wx.PaintDC(self)
143                 self.SetCurrent(self._context)
144                 self.OnPaint(e)
145                 self._drawGui()
146                 glFlush()
147                 self.SwapBuffers()
148
149         def _drawGui(self):
150                 if self._glButtonsTexture is None:
151                         self._glButtonsTexture = opengl.loadGLTexture('glButtons.png')
152
153                 glDisable(GL_DEPTH_TEST)
154                 glEnable(GL_BLEND)
155                 glDisable(GL_LIGHTING)
156                 glColor4ub(255,255,255,255)
157
158                 glMatrixMode(GL_PROJECTION)
159                 glLoadIdentity()
160                 size = self.GetSize()
161                 glOrtho(0, size.GetWidth()-1, size.GetHeight()-1, 0, -1000.0, 1000.0)
162                 glMatrixMode(GL_MODELVIEW)
163                 glLoadIdentity()
164
165                 self._container.draw()
166
167         def _OnEraseBackground(self,event):
168                 #Workaround for windows background redraw flicker.
169                 pass
170
171         def _OnSize(self,e):
172                 self._container.setSize(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
173                 self._container.updateLayout()
174                 self.Refresh()
175
176         def OnMouseLeftDown(self,e):
177                 pass
178         def OnMouseMotion(self, e):
179                 pass
180         def OnPaint(self, e):
181                 pass
182
183         def add(self, ctrl):
184                 if self._container is not None:
185                         self._container.add(ctrl)
186
187 class glGuiLayoutButtons(object):
188         def __init__(self, parent):
189                 self._parent = parent
190                 self._parent._layout = self
191
192         def update(self):
193                 bs = self._parent._base._buttonSize
194                 x0, y0, w, h = self._parent.getSize()
195                 gridSize = bs * 1.3
196                 for ctrl in self._parent._glGuiControlList:
197                         pos = ctrl._pos
198                         if pos[0] < 0:
199                                 x = w + pos[0] * gridSize - bs * 0.2
200                         else:
201                                 x = pos[0] * gridSize + bs * 0.2
202                         if pos[1] < 0:
203                                 y = h + pos[1] * gridSize - bs * 0.2
204                         else:
205                                 y = pos[1] * gridSize + bs * 0.2
206                         ctrl.setSize(x, y, gridSize, gridSize)
207
208         def getLayoutSize(self):
209                 _, _, w, h = self._parent.getSize()
210                 return w, h
211
212 class glGuiLayoutGrid(object):
213         def __init__(self, parent):
214                 self._parent = parent
215                 self._parent._layout = self
216                 self._size = 0,0
217
218         def update(self):
219                 borderSize = self._parent._base._buttonSize * 0.2
220                 x0, y0, w, h = self._parent.getSize()
221                 x0 += borderSize
222                 y0 += borderSize
223                 widths = {}
224                 heights = {}
225                 for ctrl in self._parent._glGuiControlList:
226                         x, y = ctrl._pos
227                         w, h = ctrl.getMinSize()
228                         if not x in widths:
229                                 widths[x] = w
230                         else:
231                                 widths[x] = max(widths[x], w)
232                         if not y in heights:
233                                 heights[y] = h
234                         else:
235                                 heights[y] = max(heights[y], h)
236                 for ctrl in self._parent._glGuiControlList:
237                         x, y = ctrl._pos
238                         x1 = x0
239                         y1 = y0
240                         for n in xrange(0, x):
241                                 if not n in widths:
242                                         widths[n] = 3
243                                 x1 += widths[n]
244                         for n in xrange(0, y):
245                                 if not n in heights:
246                                         heights[n] = 3
247                                 y1 += heights[n]
248                         ctrl.setSize(x1, y1, widths[x], heights[y])
249                 self._size = sum(widths.values()) + borderSize * 2, sum(heights.values()) + borderSize * 2
250
251         def getLayoutSize(self):
252                 return self._size
253
254 class glButton(glGuiControl):
255         def __init__(self, parent, imageID, tooltip, pos, callback):
256                 super(glButton, self).__init__(parent, pos)
257                 self._tooltip = tooltip
258                 self._parent = parent
259                 self._imageID = imageID
260                 self._callback = callback
261                 self._selected = False
262                 self._focus = False
263                 self._hidden = False
264                 self._disabled = False
265
266         def setSelected(self, value):
267                 self._selected = value
268
269         def setHidden(self, value):
270                 self._hidden = value
271
272         def setDisabled(self, value):
273                 self._disabled = value
274
275         def getSelected(self):
276                 return self._selected
277
278         def getMinSize(self):
279                 return self._base._buttonSize, self._base._buttonSize
280
281         def _getPixelPos(self):
282                 x0, y0, w, h = self.getSize()
283                 return x0 + w / 2, y0 + h / 2
284
285         def draw(self):
286                 if self._hidden:
287                         return
288
289                 cx = (self._imageID % 4) / 4
290                 cy = int(self._imageID / 4) / 4
291                 bs = self._base._buttonSize
292                 pos = self._getPixelPos()
293
294                 glPushMatrix()
295                 glTranslatef(pos[0], pos[1], 0)
296                 glBindTexture(GL_TEXTURE_2D, self._base._glButtonsTexture)
297                 glEnable(GL_TEXTURE_2D)
298                 scale = 0.8
299                 if self._selected:
300                         scale = 1.0
301                 elif self._focus:
302                         scale = 0.9
303                 glScalef(bs * scale, bs * scale, bs * scale)
304                 if self._disabled:
305                         glColor4ub(128,128,128,128)
306                 else:
307                         glColor4ub(255,255,255,255)
308                 glBegin(GL_QUADS)
309                 glTexCoord2f(cx+0.25, cy)
310                 glVertex2f( 0.5,-0.5)
311                 glTexCoord2f(cx, cy)
312                 glVertex2f(-0.5,-0.5)
313                 glTexCoord2f(cx, cy+0.25)
314                 glVertex2f(-0.5, 0.5)
315                 glTexCoord2f(cx+0.25, cy+0.25)
316                 glVertex2f( 0.5, 0.5)
317                 glEnd()
318                 glDisable(GL_TEXTURE_2D)
319                 if self._focus:
320                         glColor4ub(0,0,0,255)
321                         glTranslatef(0, -0.55, 0)
322                         opengl.glDrawStringCenter(self._tooltip)
323                 glPopMatrix()
324
325         def _checkHit(self, x, y):
326                 if self._hidden:
327                         return False
328                 bs = self.getMinSize()[0]
329                 pos = self._getPixelPos()
330                 return -bs * 0.5 <= x - pos[0] <= bs * 0.5 and -bs * 0.5 <= y - pos[1] <= bs * 0.5
331
332         def OnMouseMotion(self, x, y):
333                 if self._checkHit(x, y):
334                         self._focus = True
335                         return True
336                 self._focus = False
337                 return False
338
339         def OnMouseDown(self, x, y):
340                 if self._checkHit(x, y):
341                         self._callback()
342                         return True
343                 return False
344
345 class glFrame(glGuiContainer):
346         def __init__(self, parent, pos):
347                 super(glFrame, self).__init__(parent, pos)
348                 self._selected = False
349                 self._focus = False
350                 self._hidden = False
351
352         def setSelected(self, value):
353                 self._selected = value
354
355         def setHidden(self, value):
356                 self._hidden = value
357
358         def getSelected(self):
359                 return self._selected
360
361         def getMinSize(self):
362                 return self._base._buttonSize, self._base._buttonSize
363
364         def _getPixelPos(self):
365                 x0, y0, w, h = self.getSize()
366                 return x0, y0
367
368         def draw(self):
369                 if self._hidden:
370                         return
371
372                 bs = self._parent._buttonSize
373                 pos = self._getPixelPos()
374
375                 glPushMatrix()
376                 glTranslatef(pos[0], pos[1], 0)
377                 glBindTexture(GL_TEXTURE_2D, self._parent._glButtonsTexture)
378                 glEnable(GL_TEXTURE_2D)
379
380                 size = self._layout.getLayoutSize()
381                 glColor4ub(255,255,255,128)
382                 glBegin(GL_QUADS)
383                 glTexCoord2f(1, 0)
384                 glVertex2f( size[0], 0)
385                 glTexCoord2f(0, 0)
386                 glVertex2f( 0, 0)
387                 glTexCoord2f(0, 1)
388                 glVertex2f( 0, size[1])
389                 glTexCoord2f(1, 1)
390                 glVertex2f( size[0], size[1])
391                 glEnd()
392                 glDisable(GL_TEXTURE_2D)
393                 glPopMatrix()
394                 #Draw the controls on the frame
395                 super(glFrame, self).draw()
396
397         def _checkHit(self, x, y):
398                 if self._hidden:
399                         return False
400                 pos = self._getPixelPos()
401                 w, h = self._layout.getLayoutSize()
402                 return 0 <= x - pos[0] <= w and 0 <= y - pos[1] <= h
403
404         def OnMouseMotion(self, x, y):
405                 super(glFrame, self).OnMouseMotion(x, y)
406                 if self._checkHit(x, y):
407                         self._focus = True
408                         return True
409                 self._focus = False
410                 return False
411
412         def OnMouseDown(self, x, y):
413                 if self._checkHit(x, y):
414                         super(glFrame, self).OnMouseDown(x, y)
415                         return True
416                 return False
417
418 class glLabel(glGuiControl):
419         def __init__(self, parent, label, pos):
420                 self._label = label
421                 super(glLabel, self).__init__(parent, pos)
422
423         def getMinSize(self):
424                 w, h = opengl.glGetStringSize(self._label)
425                 return w + 10, h + 4
426
427         def _getPixelPos(self):
428                 x0, y0, w, h = self.getSize()
429                 return x0, y0
430
431         def draw(self):
432                 x, y, w, h = self.getSize()
433
434                 glPushMatrix()
435                 glTranslatef(x, y, 0)
436
437                 glColor4ub(255,255,255,128)
438                 glBegin(GL_QUADS)
439                 glTexCoord2f(1, 0)
440                 glVertex2f( w, 0)
441                 glTexCoord2f(0, 0)
442                 glVertex2f( 0, 0)
443                 glTexCoord2f(0, 1)
444                 glVertex2f( 0, h)
445                 glTexCoord2f(1, 1)
446                 glVertex2f( w, h)
447                 glEnd()
448
449                 glTranslate(5, h - 5, 0)
450                 glColor4ub(0,0,0,255)
451                 opengl.glDrawStringLeft(self._label)
452                 glPopMatrix()
453
454         def _checkHit(self, x, y):
455                 return False
456
457         def OnMouseMotion(self, x, y):
458                 return False
459
460         def OnMouseDown(self, x, y):
461                 return False
462
463 class glNumberCtrl(glGuiControl):
464         def __init__(self, parent, value, pos, callback):
465                 self._callback = callback
466                 self._value = str(value)
467                 self._selectPos = 0
468                 self._maxLen = 6
469                 self._inCallback = False
470                 super(glNumberCtrl, self).__init__(parent, pos)
471
472         def setValue(self, value):
473                 if self._inCallback:
474                         return
475                 self._value = str(value)
476
477         def getMinSize(self):
478                 w, h = opengl.glGetStringSize("VALUES")
479                 return w + 10, h + 4
480
481         def _getPixelPos(self):
482                 x0, y0, w, h = self.getSize()
483                 return x0, y0
484
485         def draw(self):
486                 x, y, w, h = self.getSize()
487
488                 glPushMatrix()
489                 glTranslatef(x, y, 0)
490
491                 if self._base._focus == self:
492                         glColor4ub(255,255,255,255)
493                 else:
494                         glColor4ub(255,255,255,192)
495                 glBegin(GL_QUADS)
496                 glTexCoord2f(1, 0)
497                 glVertex2f( w, 0)
498                 glTexCoord2f(0, 0)
499                 glVertex2f( 0, 0)
500                 glTexCoord2f(0, 1)
501                 glVertex2f( 0, h)
502                 glTexCoord2f(1, 1)
503                 glVertex2f( w, h)
504                 glEnd()
505
506                 glTranslate(5, h - 5, 0)
507                 glColor4ub(0,0,0,255)
508                 opengl.glDrawStringLeft(self._value)
509                 if self._base._focus == self:
510                         glTranslate(opengl.glGetStringSize(self._value[0:self._selectPos])[0] - 2, -1, 0)
511                         opengl.glDrawStringLeft('|')
512                 glPopMatrix()
513
514         def _checkHit(self, x, y):
515                 x1, y1, w, h = self.getSize()
516                 return 0 <= x - x1 <= w and 0 <= y - y1 <= h
517
518         def OnMouseMotion(self, x, y):
519                 return False
520
521         def OnMouseDown(self, x, y):
522                 if self._checkHit(x, y):
523                         self.setFocus()
524                         return True
525                 return False
526
527         def OnKeyChar(self, c):
528                 self._inCallback = True
529                 if c == wx.WXK_LEFT:
530                         self._selectPos -= 1
531                         self._selectPos = max(0, self._selectPos)
532                 if c == wx.WXK_RIGHT:
533                         self._selectPos += 1
534                         self._selectPos = min(self._selectPos, len(self._value))
535                 if c == wx.WXK_UP:
536                         try:
537                                 value = float(self._value)
538                         except:
539                                 pass
540                         else:
541                                 value += 0.1
542                                 self._value = str(value)
543                                 self._callback(self._value)
544                 if c == wx.WXK_DOWN:
545                         try:
546                                 value = float(self._value)
547                         except:
548                                 pass
549                         else:
550                                 value -= 0.1
551                                 if value > 0:
552                                         self._value = str(value)
553                                         self._callback(self._value)
554                 if c == wx.WXK_BACK and self._selectPos > 0:
555                         self._value = self._value[0:self._selectPos - 1] + self._value[self._selectPos:]
556                         self._selectPos -= 1
557                         self._callback(self._value)
558                 if c == wx.WXK_DELETE:
559                         self._value = self._value[0:self._selectPos] + self._value[self._selectPos + 1:]
560                         self._callback(self._value)
561                 if c == wx.WXK_TAB:
562                         if wx.GetKeyState(wx.WXK_SHIFT):
563                                 self.focusPrevious()
564                         else:
565                                 self.focusNext()
566                 if (ord('0') <= c <= ord('9') or c == ord('.')) and len(self._value) < self._maxLen:
567                         self._value = self._value[0:self._selectPos] + chr(c) + self._value[self._selectPos:]
568                         self._selectPos += 1
569                         self._callback(self._value)
570                 self._inCallback = False
571
572         def setFocus(self):
573                 self._base._focus = self
574                 self._selectPos = len(self._value)
575                 return True
576
577 class glCheckbox(glGuiControl):
578         def __init__(self, parent, value, pos, callback):
579                 self._callback = callback
580                 self._value = value
581                 self._selectPos = 0
582                 self._maxLen = 6
583                 self._inCallback = False
584                 super(glCheckbox, self).__init__(parent, pos)
585
586         def setValue(self, value):
587                 if self._inCallback:
588                         return
589                 self._value = str(value)
590
591         def getValue(self):
592                 return self._value
593
594         def getMinSize(self):
595                 return 20, 20
596
597         def _getPixelPos(self):
598                 x0, y0, w, h = self.getSize()
599                 return x0, y0
600
601         def draw(self):
602                 x, y, w, h = self.getSize()
603
604                 glPushMatrix()
605                 glTranslatef(x, y, 0)
606
607                 if self._value:
608                         glColor4ub(0,255,0,255)
609                 else:
610                         glColor4ub(255,0,0,255)
611                 glBegin(GL_QUADS)
612                 glTexCoord2f(1, 0)
613                 glVertex2f( w, 0)
614                 glTexCoord2f(0, 0)
615                 glVertex2f( 0, 0)
616                 glTexCoord2f(0, 1)
617                 glVertex2f( 0, h)
618                 glTexCoord2f(1, 1)
619                 glVertex2f( w, h)
620                 glEnd()
621
622                 glPopMatrix()
623
624         def _checkHit(self, x, y):
625                 x1, y1, w, h = self.getSize()
626                 return 0 <= x - x1 <= w and 0 <= y - y1 <= h
627
628         def OnMouseMotion(self, x, y):
629                 return False
630
631         def OnMouseDown(self, x, y):
632                 if self._checkHit(x, y):
633                         self._value = not self._value
634                         return True
635                 return False