]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/buttons.py
Fixed the code to actually work.
[wxWidgets.git] / wxPython / wx / lib / buttons.py
1 #----------------------------------------------------------------------
2 # Name: wx.lib.buttons
3 # Purpose: Various kinds of generic buttons, (not native controls but
4 # self-drawn.)
5 #
6 # Author: Robin Dunn
7 #
8 # Created: 9-Dec-1999
9 # RCS-ID: $Id$
10 # Copyright: (c) 1999 by Total Control Software
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------
13
14 """
15 This module implements various forms of generic buttons, meaning that
16 they are not built on native controls but are self-drawn.
17
18 The GenButton is the base. It acts like a normal button but you
19 are able to better control how it looks, bevel width, colours, etc.
20
21 GenBitmapButton is a button with one or more bitmaps that show
22 the various states the button can be in.
23
24 GenToggleButton stays depressed when clicked, until clicked again.
25
26 GenBitmapToggleButton the same but with bitmaps.
27
28 """
29
30 import wx
31 import imageutils
32
33
34 #----------------------------------------------------------------------
35
36 class GenButtonEvent(wx.PyCommandEvent):
37 def __init__(self, eventType, ID):
38 wx.PyCommandEvent.__init__(self, eventType, ID)
39 self.isDown = False
40 self.theButton = None
41
42 def SetIsDown(self, isDown):
43 self.isDown = isDown
44
45 def GetIsDown(self):
46 return self.isDown
47
48 def SetButtonObj(self, btn):
49 self.theButton = btn
50
51 def GetButtonObj(self):
52 return self.theButton
53
54
55 #----------------------------------------------------------------------
56
57 class GenButton(wx.PyControl):
58 labelDelta = 1
59
60 def __init__(self, parent, ID, label,
61 pos = wx.DefaultPosition, size = wx.DefaultSize,
62 style = 0, validator = wx.DefaultValidator,
63 name = "genbutton"):
64 if style == 0:
65 style = wx.NO_BORDER
66 wx.PyControl.__init__(self, parent, ID, pos, size, style, validator, name)
67
68 self.up = True
69 self.bezelWidth = 2
70 self.hasFocus = False
71 self.useFocusInd = True
72
73 self.SetLabel(label)
74 self.SetPosition(pos)
75 font = parent.GetFont()
76 if not font.Ok():
77 font = wx.SystemSettings.GetSystemFont(wx.SYS_DEFAULT_GUI_FONT)
78 self.SetFont(font)
79 self.SetBestSize(size)
80 self.InitColours()
81
82 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
83 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
84 if wx.Platform == '__WXMSW__':
85 self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)
86 self.Bind(wx.EVT_MOTION, self.OnMotion)
87 self.Bind(wx.EVT_SET_FOCUS, self.OnGainFocus)
88 self.Bind(wx.EVT_KILL_FOCUS, self.OnLoseFocus)
89 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
90 self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
91 self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
92 self.Bind(wx.EVT_PAINT, self.OnPaint)
93
94
95 def SetBestSize(self, size=None):
96 """
97 Given the current font and bezel width settings, calculate
98 and set a good size.
99 """
100 if size is None:
101 size = wx.Size(-1,-1)
102 if type(size) == type(()):
103 size = wx.Size(size[0], size[1])
104 size = wx.Size(size.width, size.height) # make a copy
105
106 best = self.GetBestSize()
107 if size.width == -1:
108 size.width = best.width
109 if size.height == -1:
110 size.height = best.height
111
112 self.SetSize(size)
113
114
115 def DoGetBestSize(self):
116 """Overridden base class virtual. Determines the best size of the
117 button based on the label and bezel size."""
118 w, h, useMin = self._GetLabelSize()
119 defSize = wx.Button.GetDefaultSize()
120 width = 12 + w
121 if useMin and width < defSize.width:
122 width = defSize.width
123 height = 11 + h
124 if useMin and height < defSize.height:
125 height = defSize.height
126 width = width + self.bezelWidth - 1
127 height = height + self.bezelWidth - 1
128 return (width, height)
129
130
131 def AcceptsFocus(self):
132 """Overridden base class virtual."""
133 return self.IsShown() and self.IsEnabled()
134
135
136 def Enable(self, enable=True):
137 wx.PyControl.Enable(self, enable)
138 self.Refresh()
139
140
141 def SetBezelWidth(self, width):
142 """Set the width of the 3D effect"""
143 self.bezelWidth = width
144
145 def GetBezelWidth(self):
146 """Return the width of the 3D effect"""
147 return self.bezelWidth
148
149 def SetUseFocusIndicator(self, flag):
150 """Specifiy if a focus indicator (dotted line) should be used"""
151 self.useFocusInd = flag
152
153 def GetUseFocusIndicator(self):
154 """Return focus indicator flag"""
155 return self.useFocusInd
156
157
158 def InitColours(self):
159 faceClr = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)
160 textClr = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT)
161 self.faceDnClr = faceClr
162 self.SetBackgroundColour(faceClr)
163 self.SetForegroundColour(textClr)
164
165 shadowClr = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)
166 highlightClr = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)
167 self.shadowPen = wx.Pen(shadowClr, 1, wx.SOLID)
168 self.highlightPen = wx.Pen(highlightClr, 1, wx.SOLID)
169 if wx.Platform == "__WXMAC__":
170 self.focusIndPen = wx.Pen(textClr, 1, wx.SOLID)
171 else:
172 self.focusIndPen = wx.Pen(textClr, 1, wx.USER_DASH)
173 self.focusIndPen.SetDashes([1,1])
174 self.focusIndPen.SetCap(wx.CAP_BUTT)
175 self.focusClr = highlightClr
176
177
178 def SetBackgroundColour(self, colour):
179 wx.PyControl.SetBackgroundColour(self, colour)
180 colour = self.GetBackgroundColour()
181
182 # Calculate a new set of highlight and shadow colours based on
183 # the new background colour. Works okay if the colour is dark...
184 r, g, b = colour.Get()
185 fr, fg, fb = min(255,r+32), min(255,g+32), min(255,b+32)
186 self.faceDnClr = wx.Colour(fr, fg, fb)
187 sr, sg, sb = max(0,r-32), max(0,g-32), max(0,b-32)
188 self.shadowPen = wx.Pen(wx.Colour(sr,sg,sb), 1, wx.SOLID)
189 hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64)
190 self.highlightPen = wx.Pen(wx.Colour(hr,hg,hb), 1, wx.SOLID)
191 self.focusClr = wx.Colour(hr, hg, hb)
192
193
194 def _GetLabelSize(self):
195 """ used internally """
196 w, h = self.GetTextExtent(self.GetLabel())
197 return w, h, True
198
199
200 def Notify(self):
201 evt = GenButtonEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
202 evt.SetIsDown(not self.up)
203 evt.SetButtonObj(self)
204 evt.SetEventObject(self)
205 self.GetEventHandler().ProcessEvent(evt)
206
207
208 def DrawBezel(self, dc, x1, y1, x2, y2):
209 # draw the upper left sides
210 if self.up:
211 dc.SetPen(self.highlightPen)
212 else:
213 dc.SetPen(self.shadowPen)
214 for i in range(self.bezelWidth):
215 dc.DrawLine((x1+i, y1), (x1+i, y2-i))
216 dc.DrawLine((x1, y1+i), (x2-i, y1+i))
217
218 # draw the lower right sides
219 if self.up:
220 dc.SetPen(self.shadowPen)
221 else:
222 dc.SetPen(self.highlightPen)
223 for i in range(self.bezelWidth):
224 dc.DrawLine((x1+i, y2-i), (x2+1, y2-i))
225 dc.DrawLine((x2-i, y1+i), (x2-i, y2))
226
227
228 def DrawLabel(self, dc, width, height, dw=0, dy=0):
229 dc.SetFont(self.GetFont())
230 if self.IsEnabled():
231 dc.SetTextForeground(self.GetForegroundColour())
232 else:
233 dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
234 label = self.GetLabel()
235 tw, th = dc.GetTextExtent(label)
236 if not self.up:
237 dw = dy = self.labelDelta
238 dc.DrawText(label, ((width-tw)/2+dw, (height-th)/2+dy))
239
240
241 def DrawFocusIndicator(self, dc, w, h):
242 bw = self.bezelWidth
243 ## if self.hasFocus:
244 ## self.focusIndPen.SetColour(self.GetForegroundColour())
245 ## else:
246 ## #self.focusIndPen.SetColour(self.GetBackgroundColour())
247 ## self.focusIndPen.SetColour(self.GetForegroundColour())
248 self.focusIndPen.SetColour(self.focusClr)
249 dc.SetLogicalFunction(wx.INVERT)
250 dc.SetPen(self.focusIndPen)
251 dc.SetBrush(wx.TRANSPARENT_BRUSH)
252 dc.DrawRectangle((bw+2,bw+2), (w-bw*2-4, h-bw*2-4))
253 dc.SetLogicalFunction(wx.COPY)
254
255
256 def OnPaint(self, event):
257 (width, height) = self.GetClientSizeTuple()
258 x1 = y1 = 0
259 x2 = width-1
260 y2 = height-1
261 dc = wx.BufferedPaintDC(self)
262 if self.up:
263 dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID))
264 else:
265 dc.SetBackground(wx.Brush(self.faceDnClr, wx.SOLID))
266 dc.Clear()
267 self.DrawBezel(dc, x1, y1, x2, y2)
268 self.DrawLabel(dc, width, height)
269 if self.hasFocus and self.useFocusInd:
270 self.DrawFocusIndicator(dc, width, height)
271
272
273 def OnEraseBackground(self, event):
274 pass
275
276
277 def OnLeftDown(self, event):
278 if not self.IsEnabled():
279 return
280 self.up = False
281 self.CaptureMouse()
282 self.SetFocus()
283 self.Refresh()
284 event.Skip()
285
286
287 def OnLeftUp(self, event):
288 if not self.IsEnabled() or not self.HasCapture():
289 return
290 if self.HasCapture():
291 self.ReleaseMouse()
292 if not self.up: # if the button was down when the mouse was released...
293 self.Notify()
294 self.up = True
295 self.Refresh()
296 event.Skip()
297
298
299 def OnMotion(self, event):
300 if not self.IsEnabled() or not self.HasCapture():
301 return
302 if event.LeftIsDown() and self.HasCapture():
303 x,y = event.GetPositionTuple()
304 w,h = self.GetClientSizeTuple()
305 if self.up and x<w and x>=0 and y<h and y>=0:
306 self.up = False
307 self.Refresh()
308 return
309 if not self.up and (x<0 or y<0 or x>=w or y>=h):
310 self.up = True
311 self.Refresh()
312 return
313 event.Skip()
314
315
316 def OnGainFocus(self, event):
317 self.hasFocus = True
318 dc = wx.ClientDC(self)
319 w, h = self.GetClientSizeTuple()
320 if self.useFocusInd:
321 self.DrawFocusIndicator(dc, w, h)
322
323
324 def OnLoseFocus(self, event):
325 self.hasFocus = False
326 dc = wx.ClientDC(self)
327 w, h = self.GetClientSizeTuple()
328 if self.useFocusInd:
329 self.DrawFocusIndicator(dc, w, h)
330
331
332 def OnKeyDown(self, event):
333 if self.hasFocus and event.KeyCode() == ord(" "):
334 self.up = False
335 self.Refresh()
336 event.Skip()
337
338
339 def OnKeyUp(self, event):
340 if self.hasFocus and event.KeyCode() == ord(" "):
341 self.up = True
342 self.Notify()
343 self.Refresh()
344 event.Skip()
345
346
347 #----------------------------------------------------------------------
348
349 class GenBitmapButton(GenButton):
350 def __init__(self, parent, ID, bitmap,
351 pos = wx.DefaultPosition, size = wx.DefaultSize,
352 style = 0, validator = wx.DefaultValidator,
353 name = "genbutton"):
354 self.bmpDisabled = None
355 self.bmpFocus = None
356 self.bmpSelected = None
357 self.SetBitmapLabel(bitmap)
358 GenButton.__init__(self, parent, ID, "", pos, size, style, validator, name)
359
360
361 def GetBitmapLabel(self):
362 return self.bmpLabel
363 def GetBitmapDisabled(self):
364 return self.bmpDisabled
365 def GetBitmapFocus(self):
366 return self.bmpFocus
367 def GetBitmapSelected(self):
368 return self.bmpSelected
369
370
371 def SetBitmapDisabled(self, bitmap):
372 """Set bitmap to display when the button is disabled"""
373 self.bmpDisabled = bitmap
374
375 def SetBitmapFocus(self, bitmap):
376 """Set bitmap to display when the button has the focus"""
377 self.bmpFocus = bitmap
378 self.SetUseFocusIndicator(False)
379
380 def SetBitmapSelected(self, bitmap):
381 """Set bitmap to display when the button is selected (pressed down)"""
382 self.bmpSelected = bitmap
383
384 def SetBitmapLabel(self, bitmap, createOthers=True):
385 """
386 Set the bitmap to display normally.
387 This is the only one that is required. If
388 createOthers is True, then the other bitmaps
389 will be generated on the fly. Currently,
390 only the disabled bitmap is generated.
391 """
392 self.bmpLabel = bitmap
393 if bitmap is not None and createOthers:
394 image = wx.ImageFromBitmap(bitmap)
395 imageutils.grayOut(image)
396 self.SetBitmapDisabled(wx.BitmapFromImage(image))
397
398
399 def _GetLabelSize(self):
400 """ used internally """
401 if not self.bmpLabel:
402 return -1, -1, False
403 return self.bmpLabel.GetWidth()+2, self.bmpLabel.GetHeight()+2, False
404
405 def DrawLabel(self, dc, width, height, dw=0, dy=0):
406 bmp = self.bmpLabel
407 if self.bmpDisabled and not self.IsEnabled():
408 bmp = self.bmpDisabled
409 if self.bmpFocus and self.hasFocus:
410 bmp = self.bmpFocus
411 if self.bmpSelected and not self.up:
412 bmp = self.bmpSelected
413 bw,bh = bmp.GetWidth(), bmp.GetHeight()
414 if not self.up:
415 dw = dy = self.labelDelta
416 hasMask = bmp.GetMask() != None
417 dc.DrawBitmap(bmp, ((width-bw)/2+dw, (height-bh)/2+dy), hasMask)
418
419
420 #----------------------------------------------------------------------
421
422
423 class GenBitmapTextButton(GenBitmapButton): # generic bitmapped button with Text Label
424 def __init__(self, parent, ID, bitmap, label,
425 pos = wx.DefaultPosition, size = wx.DefaultSize,
426 style = 0, validator = wx.DefaultValidator,
427 name = "genbutton"):
428 GenBitmapButton.__init__(self, parent, ID, bitmap, pos, size, style, validator, name)
429 self.SetLabel(label)
430
431
432 def _GetLabelSize(self):
433 """ used internally """
434 w, h = self.GetTextExtent(self.GetLabel())
435 if not self.bmpLabel:
436 return w, h, True # if there isn't a bitmap use the size of the text
437
438 w_bmp = self.bmpLabel.GetWidth()+2
439 h_bmp = self.bmpLabel.GetHeight()+2
440 width = w + w_bmp
441 if h_bmp > h:
442 height = h_bmp
443 else:
444 height = h
445 return width, height, True
446
447
448 def DrawLabel(self, dc, width, height, dw=0, dy=0):
449 bmp = self.bmpLabel
450 if bmp != None: # if the bitmap is used
451 if self.bmpDisabled and not self.IsEnabled():
452 bmp = self.bmpDisabled
453 if self.bmpFocus and self.hasFocus:
454 bmp = self.bmpFocus
455 if self.bmpSelected and not self.up:
456 bmp = self.bmpSelected
457 bw,bh = bmp.GetWidth(), bmp.GetHeight()
458 if not self.up:
459 dw = dy = self.labelDelta
460 hasMask = bmp.GetMask() != None
461 else:
462 bw = bh = 0 # no bitmap -> size is zero
463
464 dc.SetFont(self.GetFont())
465 if self.IsEnabled():
466 dc.SetTextForeground(self.GetForegroundColour())
467 else:
468 dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
469
470 label = self.GetLabel()
471 tw, th = dc.GetTextExtent(label) # size of text
472 if not self.up:
473 dw = dy = self.labelDelta
474
475 pos_x = (width-bw-tw)/2+dw # adjust for bitmap and text to centre
476 if bmp !=None:
477 dc.DrawBitmap(bmp, (pos_x, (height-bh)/2+dy), hasMask) # draw bitmap if available
478 pos_x = pos_x + 2 # extra spacing from bitmap
479
480 dc.DrawText(label, (pos_x + dw+bw, (height-th)/2+dy)) # draw the text
481
482
483 #----------------------------------------------------------------------
484
485
486 class __ToggleMixin:
487 def SetToggle(self, flag):
488 self.up = not flag
489 self.Refresh()
490 SetValue = SetToggle
491
492 def GetToggle(self):
493 return not self.up
494 GetValue = GetToggle
495
496 def OnLeftDown(self, event):
497 if not self.IsEnabled():
498 return
499 self.saveUp = self.up
500 self.up = not self.up
501 self.CaptureMouse()
502 self.SetFocus()
503 self.Refresh()
504
505 def OnLeftUp(self, event):
506 if not self.IsEnabled() or not self.HasCapture():
507 return
508 if self.HasCapture():
509 if self.up != self.saveUp:
510 self.Notify()
511 self.ReleaseMouse()
512 self.Refresh()
513
514 def OnKeyDown(self, event):
515 event.Skip()
516
517 def OnMotion(self, event):
518 if not self.IsEnabled():
519 return
520 if event.LeftIsDown() and self.HasCapture():
521 x,y = event.GetPositionTuple()
522 w,h = self.GetClientSizeTuple()
523 if x<w and x>=0 and y<h and y>=0:
524 self.up = not self.saveUp
525 self.Refresh()
526 return
527 if (x<0 or y<0 or x>=w or y>=h):
528 self.up = self.saveUp
529 self.Refresh()
530 return
531 event.Skip()
532
533 def OnKeyUp(self, event):
534 if self.hasFocus and event.KeyCode() == ord(" "):
535 self.up = not self.up
536 self.Notify()
537 self.Refresh()
538 event.Skip()
539
540
541
542
543 class GenToggleButton(__ToggleMixin, GenButton):
544 pass
545
546 class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):
547 pass
548
549 class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):
550 pass
551
552 #----------------------------------------------------------------------
553
554