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