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