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