]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/buttons.py
More WXDLLIMPEXP_GIZMOS fixes
[wxWidgets.git] / wxPython / wx / lib / buttons.py
CommitLineData
d14a1e28
RD
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#----------------------------------------------------------------------
b881fc78
RD
13# 11/30/2003 - Jeff Grimmett (grimmtooth@softhome.net)
14#
15# o Updated for wx namespace
16# o Tested with updated demo
17#
d14a1e28
RD
18
19"""
20This module implements various forms of generic buttons, meaning that
21they are not built on native controls but are self-drawn.
22
23The GenButton is the base. It acts like a normal button but you
24are able to better control how it looks, bevel width, colours, etc.
25
26GenBitmapButton is a button with one or more bitmaps that show
27the various states the button can be in.
28
29GenToggleButton stays depressed when clicked, until clicked again.
30
31GenBitmapToggleButton the same but with bitmaps.
32
33"""
34
35import wx
36import imageutils
37
38
39#----------------------------------------------------------------------
40
41class 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
62class 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"):
fdf907ad
RD
69 cstyle = style
70 if cstyle == 0:
71 cstyle = wx.NO_BORDER
72 wx.PyControl.__init__(self, parent, ID, pos, size, cstyle, validator, name)
d14a1e28
RD
73
74 self.up = True
d14a1e28 75 self.hasFocus = False
fdf907ad
RD
76 if style & wx.NO_BORDER:
77 self.bezelWidth = 0
78 self.useFocusInd = False
79 else:
80 self.bezelWidth = 2
81 self.useFocusInd = True
d14a1e28
RD
82
83 self.SetLabel(label)
969d9b6f 84 self.InheritAttributes()
5193b348 85 self.SetBestFittingSize(size)
d14a1e28
RD
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:
969d9b6f 107 size = wx.DefaultSize
5193b348 108 wx.PyControl.SetBestFittingSize(self, size)
d14a1e28
RD
109
110
111 def DoGetBestSize(self):
969d9b6f
RD
112 """
113 Overridden base class virtual. Determines the best size of the
114 button based on the label and bezel size.
115 """
d14a1e28
RD
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
969d9b6f
RD
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
d14a1e28
RD
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):
969d9b6f
RD
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()
d14a1e28
RD
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)
969d9b6f
RD
194
195
d14a1e28
RD
196 def SetBackgroundColour(self, colour):
197 wx.PyControl.SetBackgroundColour(self, colour)
969d9b6f 198 self.InitColours()
d14a1e28 199
969d9b6f
RD
200
201 def SetForegroundColour(self, colour):
202 wx.PyControl.SetForegroundColour(self, colour)
203 self.InitColours()
d14a1e28 204
fb113b16
RD
205 def SetDefault(self):
206 self.GetParent().SetDefaultItem(self)
207
d14a1e28
RD
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):
d7403ad2
RD
229 dc.DrawLine(x1+i, y1, x1+i, y2-i)
230 dc.DrawLine(x1, y1+i, x2-i, y1+i)
d14a1e28
RD
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):
d7403ad2
RD
238 dc.DrawLine(x1+i, y2-i, x2+1, y2-i)
239 dc.DrawLine(x2-i, y1+i, x2-i, y2)
d14a1e28
RD
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
d7403ad2 252 dc.DrawText(label, (width-tw)/2+dw, (height-th)/2+dy)
d14a1e28
RD
253
254
255 def DrawFocusIndicator(self, dc, w, h):
256 bw = self.bezelWidth
d14a1e28
RD
257 self.focusIndPen.SetColour(self.focusClr)
258 dc.SetLogicalFunction(wx.INVERT)
259 dc.SetPen(self.focusIndPen)
260 dc.SetBrush(wx.TRANSPARENT_BRUSH)
d7403ad2 261 dc.DrawRectangle(bw+2,bw+2, w-bw*2-4, h-bw*2-4)
d14a1e28
RD
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
358class 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
d7403ad2 426 dc.DrawBitmap(bmp, (width-bw)/2+dw, (height-bh)/2+dy, hasMask)
d14a1e28
RD
427
428
429#----------------------------------------------------------------------
430
431
432class 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:
d7403ad2 486 dc.DrawBitmap(bmp, pos_x, (height-bh)/2+dy, hasMask) # draw bitmap if available
d14a1e28
RD
487 pos_x = pos_x + 2 # extra spacing from bitmap
488
d7403ad2 489 dc.DrawText(label, pos_x + dw+bw, (height-th)/2+dy) # draw the text
d14a1e28
RD
490
491
492#----------------------------------------------------------------------
493
494
495class __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
552class GenToggleButton(__ToggleMixin, GenButton):
553 pass
554
555class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):
556 pass
557
558class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):
559 pass
560
561#----------------------------------------------------------------------
1fded56b 562
1fded56b 563