# -*- coding: utf-8 -*- #---------------------------------------------------------------------------- # Name: dockart.py # Purpose: # # Author: Andrea Gavana # # Created: # Version: # Date: 31 March 2009 # Licence: wxWindows license # Tags: phoenix-port, unittest, documented, py3-port #---------------------------------------------------------------------------- """ Dock art provider code - a dock provider provides all drawing functionality to the AUI dock manager. This allows the dock manager to have a plugable look-and-feel. By default, a :class:`~lib.agw.aui.framemanager` uses an instance of this class called :mod:`~lib.agw.aui.dockart` which provides bitmap art and a colour scheme that is adapted to the major platforms' look. You can either derive from that class to alter its behaviour or write a completely new dock art class. Call :meth:`AuiManager.SetArtProvider() ` to make use this new dock art. """ __author__ = "Andrea Gavana " __date__ = "31 March 2009" import wx from .aui_utilities import BitmapFromBits, StepColour, ChopText, GetBaseColour from .aui_utilities import DrawGradientRectangle, DrawMACCloseButton from .aui_utilities import DarkenBitmap, LightContrastColour from .aui_constants import * optionActive = 2**14 """ Indicates that a pane is active and should display an active caption (if present). """ _ctypes = False # Try to import winxptheme for ModernDockArt if wx.Platform == "__WXMSW__": try: import ctypes import winxptheme _ctypes = True except ImportError: pass # -- AuiDefaultDockArt class implementation -- class AuiDefaultDockArt(object): """ Dock art provider code - a dock provider provides all drawing functionality to the AUI dock manager. This allows the dock manager to have a plugable look-and-feel. By default, a :class:`~lib.agw.aui.framemanager.AuiManager` uses an instance of this class called :class:`AuiDefaultDockArt` which provides bitmap art and a colour scheme that is adapted to the major platforms' look. You can either derive from that class to alter its behaviour or write a completely new dock art class. Call :meth:`AuiManager.SetArtProvider() ` to make use this new dock art. **Metric Ordinals** These are the possible pane dock art settings for :class:`AuiDefaultDockArt`: ================================================ ====================================== Metric Ordinal Constant Description ================================================ ====================================== ``AUI_DOCKART_SASH_SIZE`` Customizes the sash size ``AUI_DOCKART_CAPTION_SIZE`` Customizes the caption size ``AUI_DOCKART_GRIPPER_SIZE`` Customizes the gripper size ``AUI_DOCKART_PANE_BORDER_SIZE`` Customizes the pane border size ``AUI_DOCKART_PANE_BUTTON_SIZE`` Customizes the pane button size ``AUI_DOCKART_BACKGROUND_COLOUR`` Customizes the background colour ``AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR`` Customizes the background gradient colour ``AUI_DOCKART_SASH_COLOUR`` Customizes the sash colour ``AUI_DOCKART_ACTIVE_CAPTION_COLOUR`` Customizes the active caption colour ``AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR`` Customizes the active caption gradient colour ``AUI_DOCKART_INACTIVE_CAPTION_COLOUR`` Customizes the inactive caption colour ``AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR`` Customizes the inactive gradient caption colour ``AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR`` Customizes the active caption text colour ``AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR`` Customizes the inactive caption text colour ``AUI_DOCKART_BORDER_COLOUR`` Customizes the border colour ``AUI_DOCKART_GRIPPER_COLOUR`` Customizes the gripper colour ``AUI_DOCKART_CAPTION_FONT`` Customizes the caption font ``AUI_DOCKART_GRADIENT_TYPE`` Customizes the gradient type (no gradient, vertical or horizontal) ``AUI_DOCKART_DRAW_SASH_GRIP`` Draw a sash grip on the sash ``AUI_DOCKART_HINT_WINDOW_COLOUR`` Customizes the hint window background colour (currently light blue) ================================================ ====================================== **Gradient Types** These are the possible gradient dock art settings for :class:`AuiDefaultDockArt`: ============================================ ====================================== Gradient Constant Description ============================================ ====================================== ``AUI_GRADIENT_NONE`` No gradient on the captions ``AUI_GRADIENT_VERTICAL`` Vertical gradient on the captions ``AUI_GRADIENT_HORIZONTAL`` Horizontal gradient on the captions ============================================ ====================================== **Button States** These are the possible pane button / :class:`~lib.agw.aui.auibook.AuiNotebook` button / :class:`~lib.agw.aui.auibar.AuiToolBar` button states: ============================================ ====================================== Button State Constant Description ============================================ ====================================== ``AUI_BUTTON_STATE_NORMAL`` Normal button state ``AUI_BUTTON_STATE_HOVER`` Hovered button state ``AUI_BUTTON_STATE_PRESSED`` Pressed button state ``AUI_BUTTON_STATE_DISABLED`` Disabled button state ``AUI_BUTTON_STATE_HIDDEN`` Hidden button state ``AUI_BUTTON_STATE_CHECKED`` Checked button state ============================================ ====================================== **Button Identifiers** These are the possible pane button / :class:`~lib.agw.aui.auibook.AuiNotebook` button / :class:`~lib.agw.aui.auibar.AuiToolBar` button identifiers: ============================================ ====================================== Button Identifier Description ============================================ ====================================== ``AUI_BUTTON_CLOSE`` Shows a close button on the pane ``AUI_BUTTON_MAXIMIZE_RESTORE`` Shows a maximize/restore button on the pane ``AUI_BUTTON_MINIMIZE`` Shows a minimize button on the pane ``AUI_BUTTON_PIN`` Shows a pin button on the pane ``AUI_BUTTON_OPTIONS`` Shows an option button on the pane (not implemented) ``AUI_BUTTON_WINDOWLIST`` Shows a window list button on the pane (for :class:`~lib.agw.aui.auibook.AuiNotebook`) ``AUI_BUTTON_LEFT`` Shows a left button on the pane (for :class:`~lib.agw.aui.auibook.AuiNotebook`) ``AUI_BUTTON_RIGHT`` Shows a right button on the pane (for :class:`~lib.agw.aui.auibook.AuiNotebook`) ``AUI_BUTTON_UP`` Shows an up button on the pane (not implemented) ``AUI_BUTTON_DOWN`` Shows a down button on the pane (not implemented) ``AUI_BUTTON_CUSTOM1`` Shows a custom button on the pane (not implemented) ``AUI_BUTTON_CUSTOM2`` Shows a custom button on the pane (not implemented) ``AUI_BUTTON_CUSTOM3`` Shows a custom button on the pane (not implemented) ============================================ ====================================== """ def __init__(self): """ Default class constructor. """ self.Init() isMac = wx.Platform == "__WXMAC__" if isMac: self._caption_font = wx.SMALL_FONT else: self._caption_font = wx.Font(8, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False) self.SetDefaultPaneBitmaps(isMac) self._restore_bitmap = wx.Bitmap(restore_xpm) # default metric values self._sash_size = 4 if isMac: # This really should be implemented in wx.SystemSettings # There is no way to do this that I am aware outside of using # the cocoa python bindings. 8 pixels looks correct on my system # so hard coding it for now. # How do I translate this?!? Not sure of the below implementation... # SInt32 height; # GetThemeMetric( kThemeMetricSmallPaneSplitterHeight , &height ); # self._sash_size = height; self._sash_size = 8 # Carbon.Appearance.kThemeMetricPaneSplitterHeight elif wx.Platform == "__WXGTK__": self._sash_size = wx.RendererNative.Get().GetSplitterParams(wx.GetTopLevelWindows()[0]).widthSash else: self._sash_size = 4 self._caption_size = 19 self._border_size = 1 self._button_size = 14 self._gripper_size = 9 self._gradient_type = AUI_GRADIENT_VERTICAL self._draw_sash = False def Init(self): """ Initializes the dock art. """ self.SetDefaultColours() isMac = wx.Platform == "__WXMAC__" if isMac: self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) else: self._active_caption_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION) self._active_caption_gradient_colour = LightContrastColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)) self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) self._inactive_caption_text_colour = wx.BLACK def SetDefaultColours(self, base_colour=None): """ Sets the default colours, which are calculated from the given base colour. :param `base_colour`: an instance of :class:`Colour`. If defaulted to ``None``, a colour is generated accordingly to the platform and theme. """ if base_colour is None: base_colour = GetBaseColour() darker1_colour = StepColour(base_colour, 85) darker2_colour = StepColour(base_colour, 75) darker3_colour = StepColour(base_colour, 60) darker4_colour = StepColour(base_colour, 40) self._background_colour = base_colour self._background_gradient_colour = StepColour(base_colour, 180) self._inactive_caption_colour = darker1_colour self._inactive_caption_gradient_colour = StepColour(base_colour, 97) self._sash_brush = wx.Brush(base_colour) self._background_brush = wx.Brush(base_colour) self._border_pen = wx.Pen(darker2_colour) self._gripper_brush = wx.Brush(base_colour) self._gripper_pen1 = wx.Pen(darker4_colour) self._gripper_pen2 = wx.Pen(darker3_colour) self._gripper_pen3 = wx.WHITE_PEN self._hint_background_colour = colourHintBackground def GetMetric(self, id): """ Gets the value of a certain setting. :param integer `id`: can be one of the size values in `Metric Ordinals`. """ if id == AUI_DOCKART_SASH_SIZE: return self._sash_size elif id == AUI_DOCKART_CAPTION_SIZE: return self._caption_size elif id == AUI_DOCKART_GRIPPER_SIZE: return self._gripper_size elif id == AUI_DOCKART_PANE_BORDER_SIZE: return self._border_size elif id == AUI_DOCKART_PANE_BUTTON_SIZE: return self._button_size elif id == AUI_DOCKART_GRADIENT_TYPE: return self._gradient_type elif id == AUI_DOCKART_DRAW_SASH_GRIP: return self._draw_sash else: raise Exception("Invalid Metric Ordinal.") def SetMetric(self, id, new_val): """ Sets the value of a certain setting using `new_val` :param integer `id`: can be one of the size values in `Metric Ordinals`; :param `new_val`: the new value of the setting. """ if id == AUI_DOCKART_SASH_SIZE: self._sash_size = new_val elif id == AUI_DOCKART_CAPTION_SIZE: self._caption_size = new_val elif id == AUI_DOCKART_GRIPPER_SIZE: self._gripper_size = new_val elif id == AUI_DOCKART_PANE_BORDER_SIZE: self._border_size = new_val elif id == AUI_DOCKART_PANE_BUTTON_SIZE: self._button_size = new_val elif id == AUI_DOCKART_GRADIENT_TYPE: self._gradient_type = new_val elif id == AUI_DOCKART_DRAW_SASH_GRIP: self._draw_sash = new_val else: raise Exception("Invalid Metric Ordinal.") def GetColor(self, id): """ Gets the colour of a certain setting. :param integer `id`: can be one of the colour values in `Metric Ordinals`. """ if id == AUI_DOCKART_BACKGROUND_COLOUR: return self._background_brush.GetColour() elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR: return self._background_gradient_colour elif id == AUI_DOCKART_SASH_COLOUR: return self._sash_brush.GetColour() elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR: return self._inactive_caption_colour elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR: return self._inactive_caption_gradient_colour elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR: return self._inactive_caption_text_colour elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR: return self._active_caption_colour elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR: return self._active_caption_gradient_colour elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR: return self._active_caption_text_colour elif id == AUI_DOCKART_BORDER_COLOUR: return self._border_pen.GetColour() elif id == AUI_DOCKART_GRIPPER_COLOUR: return self._gripper_brush.GetColour() elif id == AUI_DOCKART_HINT_WINDOW_COLOUR: return self._hint_background_colour else: raise Exception("Invalid Colour Ordinal.") def SetColor(self, id, colour): """ Sets the colour of a certain setting. :param integer `id`: can be one of the colour values in `Metric Ordinals`; :param `colour`: the new value of the setting. :type `colour`: :class:`Colour` or tuple or integer """ colour = wx.Colour(colour) if id == AUI_DOCKART_BACKGROUND_COLOUR: self._background_brush.SetColour(colour) elif id == AUI_DOCKART_BACKGROUND_GRADIENT_COLOUR: self._background_gradient_colour = colour elif id == AUI_DOCKART_SASH_COLOUR: self._sash_brush.SetColour(colour) elif id == AUI_DOCKART_INACTIVE_CAPTION_COLOUR: self._inactive_caption_colour = colour if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__": # No custom bitmaps for the pane close button # Change the MAC close bitmap colour self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, colour) elif id == AUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR: self._inactive_caption_gradient_colour = colour elif id == AUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR: self._inactive_caption_text_colour = colour elif id == AUI_DOCKART_ACTIVE_CAPTION_COLOUR: self._active_caption_colour = colour if not self._custom_pane_bitmaps and wx.Platform == "__WXMAC__": # No custom bitmaps for the pane close button # Change the MAC close bitmap colour self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, colour) elif id == AUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR: self._active_caption_gradient_colour = colour elif id == AUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR: self._active_caption_text_colour = colour elif id == AUI_DOCKART_BORDER_COLOUR: self._border_pen.SetColour(colour) elif id == AUI_DOCKART_GRIPPER_COLOUR: self._gripper_brush.SetColour(colour) self._gripper_pen1.SetColour(StepColour(colour, 40)) self._gripper_pen2.SetColour(StepColour(colour, 60)) elif id == AUI_DOCKART_HINT_WINDOW_COLOUR: self._hint_background_colour = colour else: raise Exception("Invalid Colour Ordinal.") GetColour = GetColor SetColour = SetColor def SetFont(self, id, font): """ Sets a font setting. :param integer `id`: must be ``AUI_DOCKART_CAPTION_FONT``; :param `font`: an instance of :class:`Font`. """ if id == AUI_DOCKART_CAPTION_FONT: self._caption_font = font def GetFont(self, id): """ Gets a font setting. :param integer `id`: must be ``AUI_DOCKART_CAPTION_FONT``, otherwise :class:`NullFont` is returned. """ if id == AUI_DOCKART_CAPTION_FONT: return self._caption_font return wx.NullFont def DrawSash(self, dc, window, orient, rect): """ Draws a sash between two windows. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param integer `orient`: the sash orientation; :param Rect `rect`: the sash rectangle. """ # AG: How do we make this work?!? # RendererNative does not use the sash_brush chosen by the user # and the rect.GetSize() is ignored as the sash is always drawn # 3 pixel wide # wx.RendererNative.Get().DrawSplitterSash(window, dc, rect.GetSize(), pos, orient) dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(self._sash_brush) dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) draw_sash = self.GetMetric(AUI_DOCKART_DRAW_SASH_GRIP) if draw_sash: self.DrawSashGripper(dc, orient, rect) def DrawBackground(self, dc, window, orient, rect): """ Draws a background. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param integer `orient`: the gradient (if any) orientation; :param Rect `rect`: the background rectangle. """ dc.SetPen(wx.TRANSPARENT_PEN) if wx.Platform == "__WXMAC__": # we have to clear first, otherwise we are drawing a light striped pattern # over an already darker striped background dc.SetBrush(wx.WHITE_BRUSH) dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) DrawGradientRectangle(dc, rect, self._background_brush.GetColour(), self._background_gradient_colour, AUI_GRADIENT_HORIZONTAL, rect.x, 700) def DrawBorder(self, dc, window, rect, pane): """ Draws the pane border. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param Rect `rect`: the border rectangle; :param `pane`: the pane for which the border is drawn. """ drect = wx.Rect(*rect) dc.SetPen(self._border_pen) dc.SetBrush(wx.TRANSPARENT_BRUSH) border_width = self.GetMetric(AUI_DOCKART_PANE_BORDER_SIZE) if pane.IsToolbar(): for ii in range(0, border_width): dc.SetPen(wx.WHITE_PEN) dc.DrawLine(drect.x, drect.y, drect.x+drect.width, drect.y) dc.DrawLine(drect.x, drect.y, drect.x, drect.y+drect.height) dc.SetPen(self._border_pen) dc.DrawLine(drect.x, drect.y+drect.height-1, drect.x+drect.width, drect.y+drect.height-1) dc.DrawLine(drect.x+drect.width-1, drect.y, drect.x+drect.width-1, drect.y+drect.height) drect.Deflate(1, 1) else: for ii in range(0, border_width): dc.DrawRectangle(drect.x, drect.y, drect.width, drect.height) drect.Deflate(1, 1) def DrawCaptionBackground(self, dc, rect, pane): """ Draws the text caption background in the pane. :param `dc`: a :class:`DC` device context; :param Rect `rect`: the text caption rectangle; :param `pane`: the pane for which the text background is drawn. """ active = pane.state & optionActive if self._gradient_type == AUI_GRADIENT_NONE: if active: dc.SetBrush(wx.Brush(self._active_caption_colour)) else: dc.SetBrush(wx.Brush(self._inactive_caption_colour)) dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) else: switch_gradient = pane.HasCaptionLeft() gradient_type = self._gradient_type if switch_gradient: gradient_type = (self._gradient_type == AUI_GRADIENT_HORIZONTAL and [AUI_GRADIENT_VERTICAL] or \ [AUI_GRADIENT_HORIZONTAL])[0] if active: if wx.Platform == "__WXMAC__": DrawGradientRectangle(dc, rect, self._active_caption_colour, self._active_caption_gradient_colour, gradient_type) else: DrawGradientRectangle(dc, rect, self._active_caption_gradient_colour, self._active_caption_colour, gradient_type) else: if wx.Platform == "__WXMAC__": DrawGradientRectangle(dc, rect, self._inactive_caption_gradient_colour, self._inactive_caption_colour, gradient_type) else: DrawGradientRectangle(dc, rect, self._inactive_caption_colour, self._inactive_caption_gradient_colour, gradient_type) def DrawIcon(self, dc, rect, pane): """ Draws the icon in the pane caption area. :param `dc`: a :class:`DC` device context; :param Rect `rect`: the pane caption rectangle; :param `pane`: the pane for which the icon is drawn. """ # Draw the icon centered vertically if pane.icon.IsOk(): if pane.HasCaptionLeft(): bmp = wx.ImageFromBitmap(pane.icon).Rotate90(clockwise=False) dc.DrawBitmap(bmp.ConvertToBitmap(), rect.x+(rect.width-pane.icon.GetWidth())/2, rect.y+rect.height-2-pane.icon.GetHeight(), True) else: dc.DrawBitmap(pane.icon, rect.x+2, rect.y+(rect.height-pane.icon.GetHeight())/2, True) def DrawCaption(self, dc, window, text, rect, pane): """ Draws the text in the pane caption. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param string `text`: the text to be displayed; :param Rect `rect`: the pane caption rectangle; :param `pane`: the pane for which the text is drawn. """ dc.SetPen(wx.TRANSPARENT_PEN) dc.SetFont(self._caption_font) self.DrawCaptionBackground(dc, rect, pane) if pane.state & optionActive: dc.SetTextForeground(self._active_caption_text_colour) else: dc.SetTextForeground(self._inactive_caption_text_colour) w, h = dc.GetTextExtent("ABCDEFHXfgkj") clip_rect = wx.Rect(*rect) btns = pane.CountButtons() captionLeft = pane.HasCaptionLeft() variable = (captionLeft and [rect.height] or [rect.width])[0] variable -= 3 # text offset variable -= 2 # button padding caption_offset = 0 if pane.icon: if captionLeft: caption_offset += pane.icon.GetHeight() + 3 else: caption_offset += pane.icon.GetWidth() + 3 self.DrawIcon(dc, rect, pane) variable -= caption_offset variable -= btns*(self._button_size + self._border_size) draw_text = ChopText(dc, text, variable) if captionLeft: dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-1, rect.y+rect.height-3-caption_offset, 90) else: dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-1) def RequestUserAttention(self, dc, window, text, rect, pane): """ Requests the user attention by intermittently highlighting the pane caption. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param string `text`: the text to be displayed; :param Rect `rect`: the pane caption rectangle; :param `pane`: the pane for which we want to attract the user attention. """ state = pane.state pane.state &= ~optionActive for indx in range(6): active = (indx%2 == 0 and [True] or [False])[0] if active: pane.state |= optionActive else: pane.state &= ~optionActive self.DrawCaptionBackground(dc, rect, pane) self.DrawCaption(dc, window, text, rect, pane) wx.SafeYield() wx.MilliSleep(350) pane.state = state def DrawGripper(self, dc, window, rect, pane): """ Draws a gripper on the pane. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param Rect `rect`: the pane caption rectangle; :param `pane`: the pane for which the gripper is drawn. """ dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(self._gripper_brush) dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) if not pane.HasGripperTop(): y = 4 while 1: dc.SetPen(self._gripper_pen1) dc.DrawPoint(rect.x+3, rect.y+y) dc.SetPen(self._gripper_pen2) dc.DrawPoint(rect.x+3, rect.y+y+1) dc.DrawPoint(rect.x+4, rect.y+y) dc.SetPen(self._gripper_pen3) dc.DrawPoint(rect.x+5, rect.y+y+1) dc.DrawPoint(rect.x+5, rect.y+y+2) dc.DrawPoint(rect.x+4, rect.y+y+2) y = y + 4 if y > rect.GetHeight() - 4: break else: x = 4 while 1: dc.SetPen(self._gripper_pen1) dc.DrawPoint(rect.x+x, rect.y+3) dc.SetPen(self._gripper_pen2) dc.DrawPoint(rect.x+x+1, rect.y+3) dc.DrawPoint(rect.x+x, rect.y+4) dc.SetPen(self._gripper_pen3) dc.DrawPoint(rect.x+x+1, rect.y+5) dc.DrawPoint(rect.x+x+2, rect.y+5) dc.DrawPoint(rect.x+x+2, rect.y+4) x = x + 4 if x > rect.GetWidth() - 4: break def DrawPaneButton(self, dc, window, button, button_state, _rect, pane): """ Draws a pane button in the pane caption area. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param integer `button`: the button to be drawn; :param integer `button_state`: the pane button state; :param Rect `_rect`: the pane caption rectangle; :param `pane`: the pane for which the button is drawn. """ if not pane: return if button == AUI_BUTTON_CLOSE: if pane.state & optionActive: bmp = self._active_close_bitmap else: bmp = self._inactive_close_bitmap elif button == AUI_BUTTON_PIN: if pane.state & optionActive: bmp = self._active_pin_bitmap else: bmp = self._inactive_pin_bitmap elif button == AUI_BUTTON_MAXIMIZE_RESTORE: if pane.IsMaximized(): if pane.state & optionActive: bmp = self._active_restore_bitmap else: bmp = self._inactive_restore_bitmap else: if pane.state & optionActive: bmp = self._active_maximize_bitmap else: bmp = self._inactive_maximize_bitmap elif button == AUI_BUTTON_MINIMIZE: if pane.state & optionActive: bmp = self._active_minimize_bitmap else: bmp = self._inactive_minimize_bitmap isVertical = pane.HasCaptionLeft() rect = wx.Rect(*_rect) if isVertical: old_x = rect.x rect.x = rect.x + (rect.width/2) - (bmp.GetWidth()/2) rect.width = old_x + rect.width - rect.x - 1 else: old_y = rect.y rect.y = rect.y + (rect.height/2) - (bmp.GetHeight()/2) rect.height = old_y + rect.height - rect.y - 1 if button_state == AUI_BUTTON_STATE_PRESSED: rect.x += 1 rect.y += 1 if button_state in [AUI_BUTTON_STATE_HOVER, AUI_BUTTON_STATE_PRESSED]: if pane.state & optionActive: dc.SetBrush(wx.Brush(StepColour(self._active_caption_colour, 120))) dc.SetPen(wx.Pen(StepColour(self._active_caption_colour, 70))) else: dc.SetBrush(wx.Brush(StepColour(self._inactive_caption_colour, 120))) dc.SetPen(wx.Pen(StepColour(self._inactive_caption_colour, 70))) if wx.Platform != "__WXMAC__": # draw the background behind the button dc.DrawRectangle(rect.x, rect.y, 15, 15) else: # Darker the bitmap a bit bmp = DarkenBitmap(bmp, self._active_caption_colour, StepColour(self._active_caption_colour, 110)) if isVertical: bmp = wx.ImageFromBitmap(bmp).Rotate90(clockwise=False).ConvertToBitmap() # draw the button itself dc.DrawBitmap(bmp, rect.x, rect.y, True) def DrawSashGripper(self, dc, orient, rect): """ Draws a sash gripper on a sash between two windows. :param `dc`: a :class:`DC` device context; :param integer `orient`: the sash orientation; :param Rect `rect`: the sash rectangle. """ dc.SetBrush(self._gripper_brush) if orient == wx.HORIZONTAL: # horizontal sash x = rect.x + int((1.0/4.0)*rect.width) xend = rect.x + int((3.0/4.0)*rect.width) y = rect.y + (rect.height/2) - 1 while 1: dc.SetPen(self._gripper_pen3) dc.DrawRectangle(x, y, 2, 2) dc.SetPen(self._gripper_pen2) dc.DrawPoint(x+1, y+1) x = x + 5 if x >= xend: break else: y = rect.y + int((1.0/4.0)*rect.height) yend = rect.y + int((3.0/4.0)*rect.height) x = rect.x + (rect.width/2) - 1 while 1: dc.SetPen(self._gripper_pen3) dc.DrawRectangle(x, y, 2, 2) dc.SetPen(self._gripper_pen2) dc.DrawPoint(x+1, y+1) y = y + 5 if y >= yend: break def SetDefaultPaneBitmaps(self, isMac): """ Assigns the default pane bitmaps. :param bool `isMac`: whether we are on wxMAC or not. """ if isMac: self._inactive_close_bitmap = DrawMACCloseButton(wx.WHITE, self._inactive_caption_colour) self._active_close_bitmap = DrawMACCloseButton(wx.WHITE, self._active_caption_colour) else: self._inactive_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._inactive_caption_text_colour) self._active_close_bitmap = BitmapFromBits(close_bits, 16, 16, self._active_caption_text_colour) if isMac: self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE) self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, wx.WHITE) else: self._inactive_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._inactive_caption_text_colour) self._active_maximize_bitmap = BitmapFromBits(max_bits, 16, 16, self._active_caption_text_colour) if isMac: self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE) self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, wx.WHITE) else: self._inactive_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._inactive_caption_text_colour) self._active_restore_bitmap = BitmapFromBits(restore_bits, 16, 16, self._active_caption_text_colour) if isMac: self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE) self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, wx.WHITE) else: self._inactive_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._inactive_caption_text_colour) self._active_minimize_bitmap = BitmapFromBits(minimize_bits, 16, 16, self._active_caption_text_colour) self._inactive_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._inactive_caption_text_colour) self._active_pin_bitmap = BitmapFromBits(pin_bits, 16, 16, self._active_caption_text_colour) self._custom_pane_bitmaps = False def SetCustomPaneBitmap(self, bmp, button, active, maximize=False): """ Sets a custom button bitmap for the pane button. :param Bitmap `bmp`: the actual bitmap to set; :param integer `button`: the button identifier; :param bool `active`: whether it is the bitmap for the active button or not; :param bool `maximize`: used to distinguish between the maximize and restore bitmaps. """ if bmp.GetWidth() > 16 or bmp.GetHeight() > 16: raise Exception("The input bitmap is too big") if button == AUI_BUTTON_CLOSE: if active: self._active_close_bitmap = bmp else: self._inactive_close_bitmap = bmp if wx.Platform == "__WXMAC__": self._custom_pane_bitmaps = True elif button == AUI_BUTTON_PIN: if active: self._active_pin_bitmap = bmp else: self._inactive_pin_bitmap = bmp elif button == AUI_BUTTON_MAXIMIZE_RESTORE: if maximize: if active: self._active_maximize_bitmap = bmp else: self._inactive_maximize_bitmap = bmp else: if active: self._active_restore_bitmap = bmp else: self._inactive_restore_bitmap = bmp elif button == AUI_BUTTON_MINIMIZE: if active: self._active_minimize_bitmap = bmp else: self._inactive_minimize_bitmap = bmp if _ctypes: class RECT(ctypes.Structure): """ Used to handle :class:`ModernDockArt` on Windows XP/Vista/7. """ _fields_ = [('left', ctypes.c_ulong),('top', ctypes.c_ulong),('right', ctypes.c_ulong),('bottom', ctypes.c_ulong)] def dump(self): """ Dumps `self` as a :class:`Rect`. """ return list(map(int, (self.left, self.top, self.right, self.bottom))) class SIZE(ctypes.Structure): """ Used to handle :class:`ModernDockArt` on Windows XP/Vista/7. """ _fields_ = [('x', ctypes.c_long),('y', ctypes.c_long)] class ModernDockArt(AuiDefaultDockArt): """ ModernDockArt is a custom `AuiDockArt` class, that implements a look similar to Firefox and other recents applications. Is uses the `winxptheme `_ module and XP themes whenever possible, so it should look good even if the user has a custom theme. :note: This dock art is Windows only and will only work if you have installed Mark Hammond's `pywin32` module (http://sourceforge.net/projects/pywin32/). """ def __init__(self, win): """ Default class constructor. :param Window `win`: the window managed by :class:`~lib.agw.aui.framemanager.AuiManager`. """ AuiDefaultDockArt.__init__(self) self.win = win # Get the size of a small close button (themed) hwnd = self.win.GetHandle() self.usingTheme = False if _ctypes: self.hTheme1 = winxptheme.OpenThemeData(hwnd, "Window") self.usingTheme = True if not self.hTheme1: self.usingTheme = False self._button_size = 13 self._button_border_size = 3 self._caption_text_indent = 6 self._caption_size = 22 # We only highlight the active pane with the caption text being in bold. # So we do not want a special colour for active elements. self._active_close_bitmap = self._inactive_close_bitmap self.Init() def Init(self): """ Initializes the dock art. """ AuiDefaultDockArt.Init(self) self._active_caption_colour = self._inactive_caption_colour self._active_caption_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_CAPTIONTEXT) self._inactive_caption_text_colour = self._active_caption_text_colour def DrawCaption(self, dc, window, text, rect, pane): """ Draws the text in the pane caption. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param string `text`: the text to be displayed; :param Rect `rect`: the pane caption rectangle; :param `pane`: the pane for which the text is drawn. """ dc.SetPen(wx.TRANSPARENT_PEN) self.DrawCaptionBackground(dc, rect, pane) active = ((pane.state & optionActive) and [True] or [False])[0] self._caption_font.SetWeight(wx.FONTWEIGHT_BOLD) dc.SetFont(self._caption_font) if active: dc.SetTextForeground(self._active_caption_text_colour) else: dc.SetTextForeground(self._inactive_caption_text_colour) w, h = dc.GetTextExtent("ABCDEFHXfgkj") clip_rect = wx.Rect(*rect) btns = pane.CountButtons() captionLeft = pane.HasCaptionLeft() variable = (captionLeft and [rect.height] or [rect.width])[0] variable -= 3 # text offset variable -= 2 # button padding caption_offset = 0 if pane.icon: if captionLeft: caption_offset += pane.icon.GetHeight() + 3 else: caption_offset += pane.icon.GetWidth() + 3 self.DrawIcon(dc, rect, pane) diff = -2 if self.usingTheme: diff = -1 variable -= caption_offset variable -= btns*(self._button_size + self._button_border_size) draw_text = ChopText(dc, text, variable) if captionLeft: dc.DrawRotatedText(draw_text, rect.x+(rect.width/2)-(h/2)-diff, rect.y+rect.height-3-caption_offset, 90) else: dc.DrawText(draw_text, rect.x+3+caption_offset, rect.y+(rect.height/2)-(h/2)-diff) def DrawCaptionBackground(self, dc, rect, pane): """ Draws the text caption background in the pane. :param `dc`: a :class:`DC` device context; :param Rect `rect`: the text caption rectangle; :param `pane`: the pane for which we are drawing the caption background. """ dc.SetBrush(self._background_brush) dc.DrawRectangle(rect.x, rect.y, rect.width, rect.height) active = ((pane.state & optionActive) and [True] or [False])[0] if self.usingTheme: rectangle = wx.Rect() rc = RECT(rectangle.x, rectangle.y, rectangle.width, rectangle.height) # If rect x/y values are negative rc.right/bottom values will overflow and winxptheme.DrawThemeBackground # will raise a TypeError. Ensure they are never negative. rect.x = max(0, rect.x) rect.y = max(0, rect.y) rc.top = rect.x rc.left = rect.y rc.right = rect.x + rect.width rc.bottom = rect.y + rect.height if active: winxptheme.DrawThemeBackground(self.hTheme1, int(dc.GetHandle()), 5, 1, (rc.top, rc.left, rc.right, rc.bottom), None) else: winxptheme.DrawThemeBackground(self.hTheme1, int(dc.GetHandle()), 5, 2, (rc.top, rc.left, rc.right, rc.bottom), None) else: AuiDefaultDockArt.DrawCaptionBackground(self, dc, rect, pane) def RequestUserAttention(self, dc, window, text, rect, pane): """ Requests the user attention by intermittently highlighting the pane caption. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param string `text`: the text to be displayed; :param Rect `rect`: the pane caption rectangle; :param `pane`: the pane for which the text is drawn. """ state = pane.state pane.state &= ~optionActive for indx in range(6): active = (indx%2 == 0 and [True] or [False])[0] if active: pane.state |= optionActive else: pane.state &= ~optionActive self.DrawCaptionBackground(dc, rect, pane) self.DrawCaption(dc, window, text, rect, pane) wx.SafeYield() wx.MilliSleep(350) pane.state = state def DrawPaneButton(self, dc, window, button, button_state, rect, pane): """ Draws a pane button in the pane caption area. :param `dc`: a :class:`DC` device context; :param `window`: an instance of :class:`Window`; :param integer `button`: the button to be drawn; :param integer `button_state`: the pane button state; :param Rect `rect`: the pane caption rectangle; :param `pane`: the pane for which the button is drawn. """ if self.usingTheme: hTheme = self.hTheme1 # Get the real button position (compensating for borders) drect = wx.Rect(rect.x, rect.y, self._button_size, self._button_size) # Draw the themed close button rc = RECT(0, 0, 0, 0) if pane.HasCaptionLeft(): rc.top = rect.x + self._button_border_size rc.left = int(rect.y + 1.5*self._button_border_size) rc.right = rect.x + self._button_size + self._button_border_size rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size) else: rc.top = rect.x - self._button_border_size rc.left = int(rect.y + 1.5*self._button_border_size) rc.right = rect.x + self._button_size- self._button_border_size rc.bottom = int(rect.y + self._button_size + 1.5*self._button_border_size) if button == AUI_BUTTON_CLOSE: btntype = 19 elif button == AUI_BUTTON_PIN: btntype = 23 elif button == AUI_BUTTON_MAXIMIZE_RESTORE: if not pane.IsMaximized(): btntype = 17 else: btntype = 21 else: btntype = 15 state = 4 # CBS_DISABLED if pane.state & optionActive: if button_state == AUI_BUTTON_STATE_NORMAL: state = 1 # CBS_NORMAL elif button_state == AUI_BUTTON_STATE_HOVER: state = 2 # CBS_HOT elif button_state == AUI_BUTTON_STATE_PRESSED: state = 3 # CBS_PUSHED else: raise Exception("ERROR: Unknown State.") else: # inactive pane if button_state == AUI_BUTTON_STATE_NORMAL: state = 5 # CBS_NORMAL elif button_state == AUI_BUTTON_STATE_HOVER: state = 6 # CBS_HOT elif button_state == AUI_BUTTON_STATE_PRESSED: state = 7 # CBS_PUSHED else: raise Exception("ERROR: Unknown State.") try: winxptheme.DrawThemeBackground(hTheme, int(dc.GetHandle()), btntype, state, (rc.top, rc.left, rc.right, rc.bottom), None) except TypeError: return else: # Fallback to default closebutton if themes are not enabled rect2 = wx.Rect(rect.x-4, rect.y+2, rect.width, rect.height) AuiDefaultDockArt.DrawPaneButton(self, dc, window, button, button_state, rect2, pane)