chiark / gitweb /
Add working scale entry tools.
[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
246         def setSelected(self, value):
247                 self._selected = value
248
249         def setHidden(self, value):
250                 self._hidden = value
251
252         def getSelected(self):
253                 return self._selected
254
255         def getMinSize(self):
256                 return self._base._buttonSize, self._base._buttonSize
257
258         def _getPixelPos(self):
259                 x0, y0, w, h = self.getSize()
260                 return x0 + w / 2, y0 + h / 2
261
262         def draw(self):
263                 if self._hidden:
264                         return
265
266                 cx = (self._imageID % 4) / 4
267                 cy = int(self._imageID / 4) / 4
268                 bs = self._base._buttonSize
269                 pos = self._getPixelPos()
270
271                 glPushMatrix()
272                 glTranslatef(pos[0], pos[1], 0)
273                 glBindTexture(GL_TEXTURE_2D, self._base._glButtonsTexture)
274                 glEnable(GL_TEXTURE_2D)
275                 scale = 0.8
276                 if self._selected:
277                         scale = 1.0
278                 elif self._focus:
279                         scale = 0.9
280                 glScalef(bs * scale, bs * scale, bs * scale)
281                 glColor4ub(255,255,255,255)
282                 glBegin(GL_QUADS)
283                 glTexCoord2f(cx+0.25, cy)
284                 glVertex2f( 0.5,-0.5)
285                 glTexCoord2f(cx, cy)
286                 glVertex2f(-0.5,-0.5)
287                 glTexCoord2f(cx, cy+0.25)
288                 glVertex2f(-0.5, 0.5)
289                 glTexCoord2f(cx+0.25, cy+0.25)
290                 glVertex2f( 0.5, 0.5)
291                 glEnd()
292                 glDisable(GL_TEXTURE_2D)
293                 if self._focus:
294                         glColor4ub(0,0,0,255)
295                         glTranslatef(0, -0.55, 0)
296                         opengl.glDrawStringCenter(self._tooltip)
297                 glPopMatrix()
298
299         def _checkHit(self, x, y):
300                 if self._hidden:
301                         return False
302                 bs = self.getMinSize()[0]
303                 pos = self._getPixelPos()
304                 return -bs * 0.5 <= x - pos[0] <= bs * 0.5 and -bs * 0.5 <= y - pos[1] <= bs * 0.5
305
306         def OnMouseMotion(self, x, y):
307                 if self._checkHit(x, y):
308                         self._focus = True
309                         return True
310                 self._focus = False
311                 return False
312
313         def OnMouseDown(self, x, y):
314                 if self._checkHit(x, y):
315                         self._callback()
316                         return True
317                 return False
318
319 class glFrame(glGuiContainer):
320         def __init__(self, parent, pos):
321                 super(glFrame, self).__init__(parent, pos)
322                 self._selected = False
323                 self._focus = False
324                 self._hidden = False
325
326         def setSelected(self, value):
327                 self._selected = value
328
329         def setHidden(self, value):
330                 self._hidden = value
331
332         def getSelected(self):
333                 return self._selected
334
335         def getMinSize(self):
336                 return self._base._buttonSize, self._base._buttonSize
337
338         def _getPixelPos(self):
339                 x0, y0, w, h = self.getSize()
340                 return x0, y0
341
342         def draw(self):
343                 if self._hidden:
344                         return
345
346                 bs = self._parent._buttonSize
347                 pos = self._getPixelPos()
348
349                 glPushMatrix()
350                 glTranslatef(pos[0], pos[1], 0)
351                 glBindTexture(GL_TEXTURE_2D, self._parent._glButtonsTexture)
352                 glEnable(GL_TEXTURE_2D)
353
354                 size = self._layout.getLayoutSize()
355                 glColor4ub(255,255,255,128)
356                 glBegin(GL_QUADS)
357                 glTexCoord2f(1, 0)
358                 glVertex2f( size[0], 0)
359                 glTexCoord2f(0, 0)
360                 glVertex2f( 0, 0)
361                 glTexCoord2f(0, 1)
362                 glVertex2f( 0, size[1])
363                 glTexCoord2f(1, 1)
364                 glVertex2f( size[0], size[1])
365                 glEnd()
366                 glDisable(GL_TEXTURE_2D)
367                 glPopMatrix()
368                 #Draw the controls on the frame
369                 super(glFrame, self).draw()
370
371         def _checkHit(self, x, y):
372                 if self._hidden:
373                         return False
374                 pos = self._getPixelPos()
375                 w, h = self._layout.getLayoutSize()
376                 return 0 <= x - pos[0] <= w and 0 <= y - pos[1] <= h
377
378         def OnMouseMotion(self, x, y):
379                 super(glFrame, self).OnMouseMotion(x, y)
380                 if self._checkHit(x, y):
381                         self._focus = True
382                         return True
383                 self._focus = False
384                 return False
385
386         def OnMouseDown(self, x, y):
387                 if self._checkHit(x, y):
388                         super(glFrame, self).OnMouseDown(x, y)
389                         return True
390                 return False
391
392 class glLabel(glGuiControl):
393         def __init__(self, parent, label, pos):
394                 self._label = label
395                 super(glLabel, self).__init__(parent, pos)
396
397         def getMinSize(self):
398                 w, h = opengl.glGetStringSize(self._label)
399                 return w + 10, h + 4
400
401         def _getPixelPos(self):
402                 x0, y0, w, h = self.getSize()
403                 return x0, y0
404
405         def draw(self):
406                 x, y, w, h = self.getSize()
407
408                 glPushMatrix()
409                 glTranslatef(x, y, 0)
410
411                 glColor4ub(255,255,255,128)
412                 glBegin(GL_QUADS)
413                 glTexCoord2f(1, 0)
414                 glVertex2f( w, 0)
415                 glTexCoord2f(0, 0)
416                 glVertex2f( 0, 0)
417                 glTexCoord2f(0, 1)
418                 glVertex2f( 0, h)
419                 glTexCoord2f(1, 1)
420                 glVertex2f( w, h)
421                 glEnd()
422
423                 glTranslate(5, h - 5, 0)
424                 glColor4ub(0,0,0,255)
425                 opengl.glDrawStringLeft(self._label)
426                 glPopMatrix()
427
428         def _checkHit(self, x, y):
429                 return False
430
431         def OnMouseMotion(self, x, y):
432                 return False
433
434         def OnMouseDown(self, x, y):
435                 return False
436
437 class glTextCtrl(glGuiControl):
438         def __init__(self, parent, value, pos, callback):
439                 self._callback = callback
440                 self._value = str(value)
441                 self._selectPos = 0
442                 self._maxLen = 6
443                 self._inCallback = False
444                 super(glTextCtrl, self).__init__(parent, pos)
445
446         def setValue(self, value):
447                 if self._inCallback:
448                         return
449                 self._value = str(value)
450
451         def getMinSize(self):
452                 w, h = opengl.glGetStringSize("VALUES")
453                 return w + 10, h + 4
454
455         def _getPixelPos(self):
456                 x0, y0, w, h = self.getSize()
457                 return x0, y0
458
459         def draw(self):
460                 x, y, w, h = self.getSize()
461
462                 glPushMatrix()
463                 glTranslatef(x, y, 0)
464
465                 if self._base._focus == self:
466                         glColor4ub(255,255,255,255)
467                 else:
468                         glColor4ub(255,255,255,192)
469                 glBegin(GL_QUADS)
470                 glTexCoord2f(1, 0)
471                 glVertex2f( w, 0)
472                 glTexCoord2f(0, 0)
473                 glVertex2f( 0, 0)
474                 glTexCoord2f(0, 1)
475                 glVertex2f( 0, h)
476                 glTexCoord2f(1, 1)
477                 glVertex2f( w, h)
478                 glEnd()
479
480                 glTranslate(5, h - 5, 0)
481                 glColor4ub(0,0,0,255)
482                 opengl.glDrawStringLeft(self._value)
483                 if self._base._focus == self:
484                         glTranslate(opengl.glGetStringSize(self._value[0:self._selectPos])[0] - 2, -1, 0)
485                         opengl.glDrawStringLeft('|')
486                 glPopMatrix()
487
488         def _checkHit(self, x, y):
489                 x1, y1, w, h = self.getSize()
490                 return 0 <= x - x1 <= w and 0 <= y - y1 <= h
491
492         def OnMouseMotion(self, x, y):
493                 return False
494
495         def OnMouseDown(self, x, y):
496                 if self._checkHit(x, y):
497                         self._base._focus = self
498                         self._selectPos = len(self._value)
499                         return True
500                 return False
501
502         def OnKeyChar(self, c):
503                 self._inCallback = True
504                 if c == wx.WXK_LEFT:
505                         self._selectPos -= 1
506                         self._selectPos = max(0, self._selectPos)
507                 if c == wx.WXK_RIGHT:
508                         self._selectPos += 1
509                         self._selectPos = min(self._selectPos, len(self._value))
510                 if c == wx.WXK_BACK and self._selectPos > 0:
511                         self._value = self._value[0:self._selectPos - 1] + self._value[self._selectPos:]
512                         self._selectPos -= 1
513                         self._callback(self._value)
514                 if c == wx.WXK_DELETE:
515                         self._value = self._value[0:self._selectPos] + self._value[self._selectPos + 1:]
516                         self._callback(self._value)
517                 if (ord('0') <= c <= ord('9') or c == ord('.')) and len(self._value) < self._maxLen:
518                         self._value = self._value[0:self._selectPos] + chr(c) + self._value[self._selectPos:]
519                         self._selectPos += 1
520                         self._callback(self._value)
521                 self._inCallback = False
522
523 class glCheckbox(glGuiControl):
524         def __init__(self, parent, value, pos, callback):
525                 self._callback = callback
526                 self._value = value
527                 self._selectPos = 0
528                 self._maxLen = 6
529                 self._inCallback = False
530                 super(glCheckbox, self).__init__(parent, pos)
531
532         def setValue(self, value):
533                 if self._inCallback:
534                         return
535                 self._value = str(value)
536
537         def getValue(self):
538                 return self._value
539
540         def getMinSize(self):
541                 return 20, 20
542
543         def _getPixelPos(self):
544                 x0, y0, w, h = self.getSize()
545                 return x0, y0
546
547         def draw(self):
548                 x, y, w, h = self.getSize()
549
550                 glPushMatrix()
551                 glTranslatef(x, y, 0)
552
553                 if self._value:
554                         glColor4ub(0,255,0,255)
555                 else:
556                         glColor4ub(255,0,0,255)
557                 glBegin(GL_QUADS)
558                 glTexCoord2f(1, 0)
559                 glVertex2f( w, 0)
560                 glTexCoord2f(0, 0)
561                 glVertex2f( 0, 0)
562                 glTexCoord2f(0, 1)
563                 glVertex2f( 0, h)
564                 glTexCoord2f(1, 1)
565                 glVertex2f( w, h)
566                 glEnd()
567
568                 glPopMatrix()
569
570         def _checkHit(self, x, y):
571                 x1, y1, w, h = self.getSize()
572                 return 0 <= x - x1 <= w and 0 <= y - y1 <= h
573
574         def OnMouseMotion(self, x, y):
575                 return False
576
577         def OnMouseDown(self, x, y):
578                 if self._checkHit(x, y):
579                         self._value = not self._value
580                         return True
581                 return False