X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8b9a4190f70909de9568f45389e7aa3ecbc66b8a..609b563c8b9ade4fcde9160f11c7deaf16a9f7b6:/wxPython/wx/lib/buttons.py diff --git a/wxPython/wx/lib/buttons.py b/wxPython/wx/lib/buttons.py index cff9142f00..833f176a57 100644 --- a/wxPython/wx/lib/buttons.py +++ b/wxPython/wx/lib/buttons.py @@ -1,8 +1,556 @@ +#---------------------------------------------------------------------- +# Name: wx.lib.buttons +# Purpose: Various kinds of generic buttons, (not native controls but +# self-drawn.) +# +# Author: Robin Dunn +# +# Created: 9-Dec-1999 +# RCS-ID: $Id$ +# Copyright: (c) 1999 by Total Control Software +# Licence: wxWindows license +#---------------------------------------------------------------------- +# 11/30/2003 - Jeff Grimmett (grimmtooth@softhome.net) +# +# o Updated for wx namespace +# o Tested with updated demo +# + +""" +This module implements various forms of generic buttons, meaning that +they are not built on native controls but are self-drawn. + +The GenButton is the base. It acts like a normal button but you +are able to better control how it looks, bevel width, colours, etc. + +GenBitmapButton is a button with one or more bitmaps that show +the various states the button can be in. + +GenToggleButton stays depressed when clicked, until clicked again. + +GenBitmapToggleButton the same but with bitmaps. + +""" + +import wx +import imageutils + + +#---------------------------------------------------------------------- + +class GenButtonEvent(wx.PyCommandEvent): + def __init__(self, eventType, ID): + wx.PyCommandEvent.__init__(self, eventType, ID) + self.isDown = False + self.theButton = None + + def SetIsDown(self, isDown): + self.isDown = isDown + + def GetIsDown(self): + return self.isDown + + def SetButtonObj(self, btn): + self.theButton = btn + + def GetButtonObj(self): + return self.theButton + + +#---------------------------------------------------------------------- + +class GenButton(wx.PyControl): + labelDelta = 1 + + def __init__(self, parent, ID, label, + pos = wx.DefaultPosition, size = wx.DefaultSize, + style = 0, validator = wx.DefaultValidator, + name = "genbutton"): + if style == 0: + style = wx.NO_BORDER + wx.PyControl.__init__(self, parent, ID, pos, size, style, validator, name) + + self.up = True + self.bezelWidth = 2 + self.hasFocus = False + self.useFocusInd = True + + self.SetLabel(label) + self.InheritAttributes() + self.SetBestFittingSize(size) + self.InitColours() + + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + if wx.Platform == '__WXMSW__': + self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown) + self.Bind(wx.EVT_MOTION, self.OnMotion) + self.Bind(wx.EVT_SET_FOCUS, self.OnGainFocus) + self.Bind(wx.EVT_KILL_FOCUS, self.OnLoseFocus) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) + self.Bind(wx.EVT_PAINT, self.OnPaint) + + + def SetBestSize(self, size=None): + """ + Given the current font and bezel width settings, calculate + and set a good size. + """ + if size is None: + size = wx.DefaultSize + wx.PyControl.SetBestFittingSize(self, size) + + + def DoGetBestSize(self): + """ + Overridden base class virtual. Determines the best size of the + button based on the label and bezel size. + """ + w, h, useMin = self._GetLabelSize() + defSize = wx.Button.GetDefaultSize() + width = 12 + w + if useMin and width < defSize.width: + width = defSize.width + height = 11 + h + if useMin and height < defSize.height: + height = defSize.height + width = width + self.bezelWidth - 1 + height = height + self.bezelWidth - 1 + return (width, height) + + + def AcceptsFocus(self): + """Overridden base class virtual.""" + return self.IsShown() and self.IsEnabled() + + + def GetDefaultAttributes(self): + """ + Overridden base class virtual. By default we should use + the same font/colour attributes as the native Button. + """ + return wx.Button.GetClassDefaultAttributes() + + + def ShouldInheritColours(self): + """ + Overridden base class virtual. Buttons usually don't inherit + the parent's colours. + """ + return False + + + def Enable(self, enable=True): + wx.PyControl.Enable(self, enable) + self.Refresh() + + + def SetBezelWidth(self, width): + """Set the width of the 3D effect""" + self.bezelWidth = width + + def GetBezelWidth(self): + """Return the width of the 3D effect""" + return self.bezelWidth + + def SetUseFocusIndicator(self, flag): + """Specifiy if a focus indicator (dotted line) should be used""" + self.useFocusInd = flag + + def GetUseFocusIndicator(self): + """Return focus indicator flag""" + return self.useFocusInd + + + def InitColours(self): + """ + Calculate a new set of highlight and shadow colours based on + the background colour. Works okay if the colour is dark... + """ + faceClr = self.GetBackgroundColour() + r, g, b = faceClr.Get() + fr, fg, fb = min(255,r+32), min(255,g+32), min(255,b+32) + self.faceDnClr = wx.Colour(fr, fg, fb) + sr, sg, sb = max(0,r-32), max(0,g-32), max(0,b-32) + self.shadowPen = wx.Pen(wx.Colour(sr,sg,sb), 1, wx.SOLID) + hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64) + self.highlightPen = wx.Pen(wx.Colour(hr,hg,hb), 1, wx.SOLID) + self.focusClr = wx.Colour(hr, hg, hb) + + textClr = self.GetForegroundColour() + if wx.Platform == "__WXMAC__": + self.focusIndPen = wx.Pen(textClr, 1, wx.SOLID) + else: + self.focusIndPen = wx.Pen(textClr, 1, wx.USER_DASH) + self.focusIndPen.SetDashes([1,1]) + self.focusIndPen.SetCap(wx.CAP_BUTT) + + + def SetBackgroundColour(self, colour): + wx.PyControl.SetBackgroundColour(self, colour) + self.InitColours() + + + def SetForegroundColour(self, colour): + wx.PyControl.SetForegroundColour(self, colour) + self.InitColours() + + + def _GetLabelSize(self): + """ used internally """ + w, h = self.GetTextExtent(self.GetLabel()) + return w, h, True + + + def Notify(self): + evt = GenButtonEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId()) + evt.SetIsDown(not self.up) + evt.SetButtonObj(self) + evt.SetEventObject(self) + self.GetEventHandler().ProcessEvent(evt) + + + def DrawBezel(self, dc, x1, y1, x2, y2): + # draw the upper left sides + if self.up: + dc.SetPen(self.highlightPen) + else: + dc.SetPen(self.shadowPen) + for i in range(self.bezelWidth): + dc.DrawLine(x1+i, y1, x1+i, y2-i) + dc.DrawLine(x1, y1+i, x2-i, y1+i) + + # draw the lower right sides + if self.up: + dc.SetPen(self.shadowPen) + else: + dc.SetPen(self.highlightPen) + for i in range(self.bezelWidth): + dc.DrawLine(x1+i, y2-i, x2+1, y2-i) + dc.DrawLine(x2-i, y1+i, x2-i, y2) + + + def DrawLabel(self, dc, width, height, dw=0, dy=0): + dc.SetFont(self.GetFont()) + if self.IsEnabled(): + dc.SetTextForeground(self.GetForegroundColour()) + else: + dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) + label = self.GetLabel() + tw, th = dc.GetTextExtent(label) + if not self.up: + dw = dy = self.labelDelta + dc.DrawText(label, (width-tw)/2+dw, (height-th)/2+dy) + + + def DrawFocusIndicator(self, dc, w, h): + bw = self.bezelWidth + self.focusIndPen.SetColour(self.focusClr) + dc.SetLogicalFunction(wx.INVERT) + dc.SetPen(self.focusIndPen) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawRectangle(bw+2,bw+2, w-bw*2-4, h-bw*2-4) + dc.SetLogicalFunction(wx.COPY) + + + def OnPaint(self, event): + (width, height) = self.GetClientSizeTuple() + x1 = y1 = 0 + x2 = width-1 + y2 = height-1 + dc = wx.BufferedPaintDC(self) + if self.up: + dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID)) + else: + dc.SetBackground(wx.Brush(self.faceDnClr, wx.SOLID)) + dc.Clear() + self.DrawBezel(dc, x1, y1, x2, y2) + self.DrawLabel(dc, width, height) + if self.hasFocus and self.useFocusInd: + self.DrawFocusIndicator(dc, width, height) + + + def OnEraseBackground(self, event): + pass + + + def OnLeftDown(self, event): + if not self.IsEnabled(): + return + self.up = False + self.CaptureMouse() + self.SetFocus() + self.Refresh() + event.Skip() + + + def OnLeftUp(self, event): + if not self.IsEnabled() or not self.HasCapture(): + return + if self.HasCapture(): + self.ReleaseMouse() + if not self.up: # if the button was down when the mouse was released... + self.Notify() + self.up = True + self.Refresh() + event.Skip() + + + def OnMotion(self, event): + if not self.IsEnabled() or not self.HasCapture(): + return + if event.LeftIsDown() and self.HasCapture(): + x,y = event.GetPositionTuple() + w,h = self.GetClientSizeTuple() + if self.up and x=0 and y=0: + self.up = False + self.Refresh() + return + if not self.up and (x<0 or y<0 or x>=w or y>=h): + self.up = True + self.Refresh() + return + event.Skip() + + + def OnGainFocus(self, event): + self.hasFocus = True + dc = wx.ClientDC(self) + w, h = self.GetClientSizeTuple() + if self.useFocusInd: + self.DrawFocusIndicator(dc, w, h) + + + def OnLoseFocus(self, event): + self.hasFocus = False + dc = wx.ClientDC(self) + w, h = self.GetClientSizeTuple() + if self.useFocusInd: + self.DrawFocusIndicator(dc, w, h) + + + def OnKeyDown(self, event): + if self.hasFocus and event.KeyCode() == ord(" "): + self.up = False + self.Refresh() + event.Skip() + + + def OnKeyUp(self, event): + if self.hasFocus and event.KeyCode() == ord(" "): + self.up = True + self.Notify() + self.Refresh() + event.Skip() + + +#---------------------------------------------------------------------- + +class GenBitmapButton(GenButton): + def __init__(self, parent, ID, bitmap, + pos = wx.DefaultPosition, size = wx.DefaultSize, + style = 0, validator = wx.DefaultValidator, + name = "genbutton"): + self.bmpDisabled = None + self.bmpFocus = None + self.bmpSelected = None + self.SetBitmapLabel(bitmap) + GenButton.__init__(self, parent, ID, "", pos, size, style, validator, name) + + + def GetBitmapLabel(self): + return self.bmpLabel + def GetBitmapDisabled(self): + return self.bmpDisabled + def GetBitmapFocus(self): + return self.bmpFocus + def GetBitmapSelected(self): + return self.bmpSelected + + + def SetBitmapDisabled(self, bitmap): + """Set bitmap to display when the button is disabled""" + self.bmpDisabled = bitmap + + def SetBitmapFocus(self, bitmap): + """Set bitmap to display when the button has the focus""" + self.bmpFocus = bitmap + self.SetUseFocusIndicator(False) + + def SetBitmapSelected(self, bitmap): + """Set bitmap to display when the button is selected (pressed down)""" + self.bmpSelected = bitmap + + def SetBitmapLabel(self, bitmap, createOthers=True): + """ + Set the bitmap to display normally. + This is the only one that is required. If + createOthers is True, then the other bitmaps + will be generated on the fly. Currently, + only the disabled bitmap is generated. + """ + self.bmpLabel = bitmap + if bitmap is not None and createOthers: + image = wx.ImageFromBitmap(bitmap) + imageutils.grayOut(image) + self.SetBitmapDisabled(wx.BitmapFromImage(image)) + + + def _GetLabelSize(self): + """ used internally """ + if not self.bmpLabel: + return -1, -1, False + return self.bmpLabel.GetWidth()+2, self.bmpLabel.GetHeight()+2, False + + def DrawLabel(self, dc, width, height, dw=0, dy=0): + bmp = self.bmpLabel + if self.bmpDisabled and not self.IsEnabled(): + bmp = self.bmpDisabled + if self.bmpFocus and self.hasFocus: + bmp = self.bmpFocus + if self.bmpSelected and not self.up: + bmp = self.bmpSelected + bw,bh = bmp.GetWidth(), bmp.GetHeight() + if not self.up: + dw = dy = self.labelDelta + hasMask = bmp.GetMask() != None + dc.DrawBitmap(bmp, (width-bw)/2+dw, (height-bh)/2+dy, hasMask) + + +#---------------------------------------------------------------------- + + +class GenBitmapTextButton(GenBitmapButton): # generic bitmapped button with Text Label + def __init__(self, parent, ID, bitmap, label, + pos = wx.DefaultPosition, size = wx.DefaultSize, + style = 0, validator = wx.DefaultValidator, + name = "genbutton"): + GenBitmapButton.__init__(self, parent, ID, bitmap, pos, size, style, validator, name) + self.SetLabel(label) + + + def _GetLabelSize(self): + """ used internally """ + w, h = self.GetTextExtent(self.GetLabel()) + if not self.bmpLabel: + return w, h, True # if there isn't a bitmap use the size of the text + + w_bmp = self.bmpLabel.GetWidth()+2 + h_bmp = self.bmpLabel.GetHeight()+2 + width = w + w_bmp + if h_bmp > h: + height = h_bmp + else: + height = h + return width, height, True + + + def DrawLabel(self, dc, width, height, dw=0, dy=0): + bmp = self.bmpLabel + if bmp != None: # if the bitmap is used + if self.bmpDisabled and not self.IsEnabled(): + bmp = self.bmpDisabled + if self.bmpFocus and self.hasFocus: + bmp = self.bmpFocus + if self.bmpSelected and not self.up: + bmp = self.bmpSelected + bw,bh = bmp.GetWidth(), bmp.GetHeight() + if not self.up: + dw = dy = self.labelDelta + hasMask = bmp.GetMask() != None + else: + bw = bh = 0 # no bitmap -> size is zero + + dc.SetFont(self.GetFont()) + if self.IsEnabled(): + dc.SetTextForeground(self.GetForegroundColour()) + else: + dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) + + label = self.GetLabel() + tw, th = dc.GetTextExtent(label) # size of text + if not self.up: + dw = dy = self.labelDelta + + pos_x = (width-bw-tw)/2+dw # adjust for bitmap and text to centre + if bmp !=None: + dc.DrawBitmap(bmp, pos_x, (height-bh)/2+dy, hasMask) # draw bitmap if available + pos_x = pos_x + 2 # extra spacing from bitmap + + dc.DrawText(label, pos_x + dw+bw, (height-th)/2+dy) # draw the text + + +#---------------------------------------------------------------------- + + +class __ToggleMixin: + def SetToggle(self, flag): + self.up = not flag + self.Refresh() + SetValue = SetToggle + + def GetToggle(self): + return not self.up + GetValue = GetToggle + + def OnLeftDown(self, event): + if not self.IsEnabled(): + return + self.saveUp = self.up + self.up = not self.up + self.CaptureMouse() + self.SetFocus() + self.Refresh() + + def OnLeftUp(self, event): + if not self.IsEnabled() or not self.HasCapture(): + return + if self.HasCapture(): + if self.up != self.saveUp: + self.Notify() + self.ReleaseMouse() + self.Refresh() + + def OnKeyDown(self, event): + event.Skip() + + def OnMotion(self, event): + if not self.IsEnabled(): + return + if event.LeftIsDown() and self.HasCapture(): + x,y = event.GetPositionTuple() + w,h = self.GetClientSizeTuple() + if x=0 and y=0: + self.up = not self.saveUp + self.Refresh() + return + if (x<0 or y<0 or x>=w or y>=h): + self.up = self.saveUp + self.Refresh() + return + event.Skip() + + def OnKeyUp(self, event): + if self.hasFocus and event.KeyCode() == ord(" "): + self.up = not self.up + self.Notify() + self.Refresh() + event.Skip() + + + + +class GenToggleButton(__ToggleMixin, GenButton): + pass + +class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton): + pass + +class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton): + pass + +#---------------------------------------------------------------------- -"""Renamer stub: provides a way to drop the wx prefix from wxPython objects.""" -from wx import _rename -from wxPython.lib import buttons -_rename(globals(), buttons.__dict__, modulename='lib.buttons') -del buttons -del _rename