a55ba391c5fdd5f32e44b657bac749eef6b7c46f
[wxWidgets.git] / wxPython / wxPython / lib / buttons.py
1 #----------------------------------------------------------------------
2 # Name: wxPython.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 wxGenButton 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 wxGenBitmapButton is a button with one or more bitmaps that show
22 the various states the button can be in.
23
24 wxGenToggleButton stays depressed when clicked, until clicked again.
25
26 wxGenBitmapToggleButton the same but with bitmaps.
27
28 """
29
30 from wxPython.wx import *
31
32 #----------------------------------------------------------------------
33
34 class wxGenButtonEvent(wxPyCommandEvent):
35 def __init__(self, eventType, ID):
36 wxPyCommandEvent.__init__(self, eventType, ID)
37 self.isDown = false
38 self.theButton = None
39
40 def SetIsDown(self, isDown):
41 self.isDown = isDown
42
43 def GetIsDown(self):
44 return self.isDown
45
46 def SetButtonObj(self, btn):
47 self.theButton = btn
48
49 def GetButtonObj(self):
50 return self.theButton
51
52
53 #----------------------------------------------------------------------
54
55 class wxGenButton(wxControl):
56 labelDelta = 1
57
58 def __init__(self, parent, ID, label,
59 pos = wxDefaultPosition, size = wxDefaultSize,
60 style = 0, validator = wxDefaultValidator,
61 name = "genbutton"):
62 if style == 0:
63 style = wxNO_BORDER
64 wxControl.__init__(self, parent, ID, pos, size, style, validator, name)
65
66 self.up = true
67 self.bezelWidth = 2
68 self.hasFocus = false
69 self.useFocusInd = true
70
71 self.SetLabel(label)
72 self.SetPosition(pos)
73 font = parent.GetFont()
74 if not font.Ok():
75 font = wxSystemSettings_GetSystemFont(wxSYS_DEFAULT_GUI_FONT)
76 self.SetFont(font)
77 self.SetBestSize(size)
78 self.InitColours()
79
80 EVT_LEFT_DOWN(self, self.OnLeftDown)
81 EVT_LEFT_UP(self, self.OnLeftUp)
82 EVT_MOTION(self, self.OnMotion)
83 EVT_SET_FOCUS(self, self.OnGainFocus)
84 EVT_KILL_FOCUS(self, self.OnLoseFocus)
85 EVT_KEY_DOWN(self, self.OnKeyDown)
86 EVT_KEY_UP(self, self.OnKeyUp)
87 EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
88 EVT_PAINT(self, self.OnPaint)
89
90
91 def SetBestSize(self, size=None):
92 """
93 Given the current font and bezel width settings, calculate
94 and set a good size.
95 """
96 if size is None:
97 size = wxSize(-1,-1)
98 if type(size) == type(()):
99 size = wxSize(size[0], size[1])
100
101 # make a new size so we don't mess with the one passed in
102 size = wxSize(size.width, size.height)
103
104 w, h, useMin = self._GetLabelSize()
105 defSize = wxButton_GetDefaultSize()
106 if size.width == -1:
107 size.width = 12 + w
108 if useMin and size.width < defSize.width:
109 size.width = defSize.width
110 if size.height == -1:
111 size.height = 11 + h
112 if useMin and size.height < defSize.height:
113 size.height = defSize.height
114
115 size.width = size.width + self.bezelWidth - 1
116 size.height = size.height + self.bezelWidth - 1
117
118 self.SetSize(size)
119
120
121 def SetBezelWidth(self, width):
122 """Set the width of the 3D effect"""
123 self.bezelWidth = width
124
125 def GetBezelWidth(self):
126 """Return the width of the 3D effect"""
127 return self.bezelWidth
128
129 def SetUseFocusIndicator(self, flag):
130 """Specifiy if a focus indicator (dotted line) should be used"""
131 self.useFocusInd = flag
132
133 def GetUseFocusIndicator(self):
134 """Return focus indicator flag"""
135 return self.useFocusInd
136
137
138 def InitColours(self):
139 faceClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNFACE)
140 textClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNTEXT)
141 self.faceDnClr = faceClr
142 self.SetBackgroundColour(faceClr)
143 self.SetForegroundColour(textClr)
144
145 shadowClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNSHADOW)
146 highlightClr = wxSystemSettings_GetSystemColour(wxSYS_COLOUR_BTNHIGHLIGHT)
147 self.shadowPen = wxPen(shadowClr, 1, wxSOLID)
148 self.highlightPen = wxPen(highlightClr, 1, wxSOLID)
149 ##self.focusIndPen = wxPen(textClr, 1, wxUSER_DASH)
150 self.focusIndPen = wxPen(textClr, 1, wxDOT)
151
152
153 def SetBackgroundColour(self, colour):
154 wxControl.SetBackgroundColour(self, colour)
155
156 # Calculate a new set of highlight and shadow colours based on
157 # the new background colour. Works okay if the colour is dark...
158 r, g, b = colour.Get()
159 fr, fg, fb = min(255,r+32), min(255,g+32), min(255,b+32)
160 self.faceDnClr = wxColour(fr, fg, fb)
161 sr, sg, sb = max(0,r-32), max(0,g-32), max(0,b-32)
162 self.shadowPen = wxPen(wxColour(sr,sg,sb), 1, wxSOLID)
163 hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64)
164 self.highlightPen = wxPen(wxColour(hr,hg,hb), 1, wxSOLID)
165
166
167 def _GetLabelSize(self):
168 """ used internally """
169 w, h = self.GetTextExtent(self.GetLabel())
170 return w, h, true
171
172
173 def Notify(self):
174 evt = wxGenButtonEvent(wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
175 evt.SetIsDown(not self.up)
176 evt.SetButtonObj(self)
177 self.GetEventHandler().ProcessEvent(evt)
178
179
180 def DrawBezel(self, dc, x1, y1, x2, y2):
181 # draw the upper left sides
182 if self.up:
183 dc.SetPen(self.highlightPen)
184 else:
185 dc.SetPen(self.shadowPen)
186 for i in range(self.bezelWidth):
187 dc.DrawLine(x1+i, y1, x1+i, y2-i)
188 dc.DrawLine(x1, y1+i, x2-i, y1+i)
189
190 # draw the lower right sides
191 if self.up:
192 dc.SetPen(self.shadowPen)
193 else:
194 dc.SetPen(self.highlightPen)
195 for i in range(self.bezelWidth):
196 dc.DrawLine(x1+i, y2-i, x2+1, y2-i)
197 dc.DrawLine(x2-i, y1+i, x2-i, y2)
198
199
200 def DrawLabel(self, dc, width, height, dw=0, dy=0):
201 dc.SetFont(self.GetFont())
202 if self.IsEnabled():
203 dc.SetTextForeground(self.GetForegroundColour())
204 else:
205 dc.SetTextForeground(wxSystemSettings_GetSystemColour(wxSYS_COLOUR_GRAYTEXT))
206 label = self.GetLabel()
207 tw, th = dc.GetTextExtent(label)
208 if not self.up:
209 dw = dy = self.labelDelta
210 dc.DrawText(label, (width-tw)/2+dw, (height-th)/2+dy)
211
212
213 def DrawFocusIndicator(self, dc, w, h):
214 bw = self.bezelWidth
215 if self.hasFocus:
216 self.focusIndPen.SetColour(self.GetForegroundColour())
217 else:
218 self.focusIndPen.SetColour(self.GetBackgroundColour())
219 ##self.focusIndPen.SetDashes([1,2,1,2]) # This isn't quite working the way I expected...
220 dc.SetPen(self.focusIndPen)
221 dc.SetBrush(wxTRANSPARENT_BRUSH)
222 dc.DrawRectangle(bw+2,bw+2, w-bw*2-4, h-bw*2-4)
223
224
225 def OnPaint(self, event):
226 (width, height) = self.GetClientSizeTuple()
227 x1 = y1 = 0
228 x2 = width-1
229 y2 = height-1
230 dc = wxBufferedPaintDC(self)
231 if self.up:
232 dc.SetBackground(wxBrush(self.GetBackgroundColour(), wxSOLID))
233 else:
234 dc.SetBackground(wxBrush(self.faceDnClr, wxSOLID))
235 dc.Clear()
236 self.DrawBezel(dc, x1, y1, x2, y2)
237 self.DrawLabel(dc, width, height)
238 if self.hasFocus and self.useFocusInd:
239 self.DrawFocusIndicator(dc, width, height)
240
241
242 def OnEraseBackground(self, event):
243 pass
244
245
246 def OnLeftDown(self, event):
247 if not self.IsEnabled():
248 return
249 self.up = false
250 self.CaptureMouse()
251 self.SetFocus()
252 self.Refresh()
253 event.Skip()
254
255
256 def OnLeftUp(self, event):
257 if not self.IsEnabled():
258 return
259 self.ReleaseMouse()
260 if not self.up: # if the button was down when the mouse was released...
261 self.Notify()
262 self.up = true
263 self.Refresh()
264 event.Skip()
265
266 def OnMotion(self, event):
267 if not self.IsEnabled():
268 return
269 if event.LeftIsDown():
270 x,y = event.GetPositionTuple()
271 w,h = self.GetClientSizeTuple()
272 if self.up and x<w and x>=0 and y<h and y>=0:
273 self.up = false
274 self.Refresh()
275 return
276 if not self.up and (x<0 or y<0 or x>=w or y>=h):
277 self.up = true
278 self.Refresh()
279 return
280 event.Skip()
281
282
283 def OnGainFocus(self, event):
284 self.hasFocus = true
285 dc = wxClientDC(self)
286 w, h = self.GetClientSizeTuple()
287 if self.useFocusInd:
288 self.DrawFocusIndicator(dc, w, h)
289
290
291 def OnLoseFocus(self, event):
292 self.hasFocus = false
293 dc = wxClientDC(self)
294 w, h = self.GetClientSizeTuple()
295 if self.useFocusInd:
296 self.DrawFocusIndicator(dc, w, h)
297
298
299 def OnKeyDown(self, event):
300 if self.hasFocus and event.KeyCode() == ord(" "):
301 self.up = false
302 self.Refresh()
303 event.Skip()
304
305
306 def OnKeyUp(self, event):
307 if self.hasFocus and event.KeyCode() == ord(" "):
308 self.up = true
309 self.Notify()
310 self.Refresh()
311 event.Skip()
312
313
314 #----------------------------------------------------------------------
315
316 class wxGenBitmapButton(wxGenButton):
317 def __init__(self, parent, ID, bitmap,
318 pos = wxDefaultPosition, size = wxDefaultSize,
319 style = 0, validator = wxDefaultValidator,
320 name = "genbutton"):
321 self.bmpLabel = bitmap
322 self.bmpDisabled = None
323 self.bmpFocus = None
324 self.bmpSelected = None
325 wxGenButton.__init__(self, parent, ID, "", pos, size, style, validator, name)
326
327
328 def GetBitmapLabel(self):
329 return self.bmpLabel
330 def GetBitmapDisabled(self):
331 return self.bmpDisabled
332 def GetBitmapFocus(self):
333 return self.bmpFocus
334 def GetBitmapSelected(self):
335 return self.bmpSelected
336
337
338 def SetBitmapDisabled(self, bitmap):
339 """Set bitmap to display when the button is disabled"""
340 self.bmpDisabled = bitmap
341
342 def SetBitmapFocus(self, bitmap):
343 """Set bitmap to display when the button has the focus"""
344 self.bmpFocus = bitmap
345 self.SetUseFocusIndicator(false)
346
347 def SetBitmapSelected(self, bitmap):
348 """Set bitmap to display when the button is selected (pressed down)"""
349 self.bmpSelected = bitmap
350
351 def SetBitmapLabel(self, bitmap):
352 """Set the bitmap to display normally. This is the only one that is required."""
353 self.bmpLabel = bitmap
354
355
356 def _GetLabelSize(self):
357 """ used internally """
358 if not self.bmpLabel:
359 return -1, -1, false
360 return self.bmpLabel.GetWidth()+2, self.bmpLabel.GetHeight()+2, false
361
362 def DrawLabel(self, dc, width, height, dw=0, dy=0):
363 bmp = self.bmpLabel
364 if self.bmpDisabled and not self.IsEnabled():
365 bmp = self.bmpDisabled
366 if self.bmpFocus and self.hasFocus:
367 bmp = self.bmpFocus
368 if self.bmpSelected and not self.up:
369 bmp = self.bmpSelected
370 bw,bh = bmp.GetWidth(), bmp.GetHeight()
371 if not self.up:
372 dw = dy = self.labelDelta
373 hasMask = bmp.GetMask() != None
374 dc.DrawBitmap(bmp, (width-bw)/2+dw, (height-bh)/2+dy, hasMask)
375
376
377 #----------------------------------------------------------------------
378
379
380 class wxGenBitmapTextButton(wxGenBitmapButton): # generic bitmapped button with Text Label
381 def __init__(self, parent, ID, bitmap, label,
382 pos = wxDefaultPosition, size = wxDefaultSize,
383 style = 0, validator = wxDefaultValidator,
384 name = "genbutton"):
385 wxGenBitmapButton.__init__(self, parent, ID, bitmap, pos, size, style, validator, name)
386 self.SetLabel(label)
387
388
389 def _GetLabelSize(self):
390 """ used internally """
391 w, h = self.GetTextExtent(self.GetLabel())
392 if not self.bmpLabel:
393 return w, h, true # if there isn't a bitmap use the size of the text
394
395 w_bmp = self.bmpLabel.GetWidth()+2
396 h_bmp = self.bmpLabel.GetHeight()+2
397 width = w + w_bmp
398 if h_bmp > h:
399 height = h_bmp
400 else:
401 height = h
402 return width, height, true
403
404
405 def DrawLabel(self, dc, width, height, dw=0, dy=0):
406 bmp = self.bmpLabel
407 if bmp != None: # if the bitmap is used
408 if self.bmpDisabled and not self.IsEnabled():
409 bmp = self.bmpDisabled
410 if self.bmpFocus and self.hasFocus:
411 bmp = self.bmpFocus
412 if self.bmpSelected and not self.up:
413 bmp = self.bmpSelected
414 bw,bh = bmp.GetWidth(), bmp.GetHeight()
415 if not self.up:
416 dw = dy = self.labelDelta
417 hasMask = bmp.GetMask() != None
418 else:
419 bw = bh = 0 # no bitmap -> size is zero
420
421 dc.SetFont(self.GetFont())
422 if self.IsEnabled():
423 dc.SetTextForeground(self.GetForegroundColour())
424 else:
425 dc.SetTextForeground(wxSystemSettings_GetSystemColour(wxSYS_COLOUR_GRAYTEXT))
426
427 label = self.GetLabel()
428 tw, th = dc.GetTextExtent(label) # size of text
429 if not self.up:
430 dw = dy = self.labelDelta
431
432 pos_x = (width-bw-tw)/2+dw # adjust for bitmap and text to centre
433 if bmp !=None:
434 dc.DrawBitmap(bmp, pos_x, (height-bh)/2+dy, hasMask) # draw bitmap if available
435 pos_x = pos_x + 2 # extra spacing from bitmap
436
437 dc.DrawText(label, pos_x + dw+bw, (height-th)/2+dy) # draw the text
438
439
440 #----------------------------------------------------------------------
441
442
443 class __ToggleMixin:
444 def SetToggle(self, flag):
445 self.up = not flag
446
447 def GetToggle(self):
448 return not self.up
449
450 def OnLeftDown(self, event):
451 if not self.IsEnabled():
452 return
453 self.saveUp = self.up
454 self.up = not self.up
455 self.CaptureMouse()
456 self.SetFocus()
457 self.Refresh()
458
459 def OnLeftUp(self, event):
460 if not self.IsEnabled():
461 return
462 if self.up != self.saveUp:
463 self.Notify()
464 self.ReleaseMouse()
465 self.Refresh()
466
467 def OnKeyDown(self, event):
468 event.Skip()
469
470 def OnKeyUp(self, event):
471 if self.hasFocus and event.KeyCode() == ord(" "):
472 self.up = not self.up
473 self.Notify()
474 self.Refresh()
475 event.Skip()
476
477
478
479
480 class wxGenToggleButton(__ToggleMixin, wxGenButton):
481 pass
482
483 class wxGenBitmapToggleButton(__ToggleMixin, wxGenBitmapButton):
484 pass
485
486 class wxGenBitmapTextToggleButton(__ToggleMixin, wxGenBitmapTextButton):
487 pass
488
489 #----------------------------------------------------------------------
490
491