mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-28 14:12:52 -06:00
2802 lines
107 KiB
Python
2802 lines
107 KiB
Python
# -*- coding: utf-8 -*-
|
|
#----------------------------------------------------------------------------
|
|
# Name: tabart.py
|
|
# Purpose:
|
|
#
|
|
# Author: Andrea Gavana <andrea.gavana@gmail.com>
|
|
#
|
|
# Created:
|
|
# Version:
|
|
# Date: 31 March 2009
|
|
# Licence: wxWindows license
|
|
# Tags: phoenix-port, unittest, documented, py3-port
|
|
#----------------------------------------------------------------------------
|
|
"""
|
|
Tab art provider code - a tab provider provides all drawing functionality to
|
|
the :class:`~lib.agw.aui.auibook.AuiNotebook`. This allows the
|
|
:class:`~lib.agw.aui.auibook.AuiNotebook` to have a plugable look-and-feel.
|
|
|
|
By default, a :class:`~lib.agw.aui.auibook.AuiNotebook` uses an instance of this class
|
|
called :class:`AuiDefaultTabArt` 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 tab art class.
|
|
Call :meth:`AuiNotebook.SetArtProvider() <lib.agw.aui.auibook.AuiNotebook.SetArtProvider>`
|
|
to make use this new tab art.
|
|
"""
|
|
|
|
__author__ = "Andrea Gavana <andrea.gavana@gmail.com>"
|
|
__date__ = "31 March 2009"
|
|
|
|
|
|
import wx
|
|
|
|
if wx.Platform == '__WXMAC__':
|
|
try:
|
|
import Carbon.Appearance
|
|
except ImportError:
|
|
CARBON = False
|
|
else:
|
|
CARBON = True
|
|
|
|
from .aui_utilities import BitmapFromBits, StepColour, IndentPressedBitmap, ChopText
|
|
from .aui_utilities import GetBaseColour, DrawMACCloseButton, LightColour, TakeScreenShot
|
|
from .aui_utilities import CopyAttributes
|
|
|
|
from .aui_constants import *
|
|
|
|
|
|
# -- GUI helper classes and functions --
|
|
class AuiCommandCapture(wx.EvtHandler):
|
|
""" A class to handle the dropdown window menu. """
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
wx.EvtHandler.__init__(self)
|
|
self._last_id = 0
|
|
|
|
|
|
def GetCommandId(self):
|
|
""" Returns the event command identifier. """
|
|
|
|
return self._last_id
|
|
|
|
|
|
def ProcessEvent(self, event):
|
|
"""
|
|
Processes an event, searching event tables and calling zero or more suitable
|
|
event handler function(s).
|
|
|
|
:param `event`: the event to process.
|
|
|
|
:note: Normally, your application would not call this function: it is called
|
|
in the wxPython implementation to dispatch incoming user interface events
|
|
to the framework (and application).
|
|
However, you might need to call it if implementing new functionality (such as
|
|
a new control) where you define new event types, as opposed to allowing the
|
|
user to override functions.
|
|
|
|
An instance where you might actually override the :meth:`ProcessEvent` function is where
|
|
you want to direct event processing to event handlers not normally noticed by
|
|
wxPython. For example, in the document/view architecture, documents and views
|
|
are potential event handlers. When an event reaches a frame, :meth:`ProcessEvent` will
|
|
need to be called on the associated document and view in case event handler
|
|
functions are associated with these objects.
|
|
|
|
The normal order of event table searching is as follows:
|
|
|
|
1. If the object is disabled (via a call to :meth:`EvtHandler.SetEvtHandlerEnabled`) the function
|
|
skips to step (6).
|
|
2. If the object is a :class:`Window`, :meth:`ProcessEvent` is recursively called on the window's
|
|
:class:`Validator`. If this returns ``True``, the function exits.
|
|
3. wxWidgets `SearchEventTable` is called for this event handler. If this fails, the
|
|
base class table is tried, and so on until no more tables exist or an appropriate
|
|
function was found, in which case the function exits.
|
|
4. The search is applied down the entire chain of event handlers (usually the chain
|
|
has a length of one). If this succeeds, the function exits.
|
|
5. If the object is a :class:`Window` and the event is a :class:`CommandEvent`, :meth:`ProcessEvent` is
|
|
recursively applied to the parent window's event handler. If this returns ``True``,
|
|
the function exits.
|
|
6. Finally, :meth:`ProcessEvent` is called on the :class:`App` object.
|
|
"""
|
|
|
|
if event.GetEventType() == wx.wxEVT_COMMAND_MENU_SELECTED:
|
|
self._last_id = event.GetId()
|
|
return True
|
|
|
|
if self.GetNextHandler():
|
|
return self.GetNextHandler().ProcessEvent(event)
|
|
|
|
return False
|
|
|
|
|
|
class AuiDefaultTabArt(object):
|
|
"""
|
|
Tab art provider code - a tab provider provides all drawing functionality to the :class:`~lib.agw.aui.auibook.AuiNotebook`.
|
|
This allows the :class:`~lib.agw.aui.auibook.AuiNotebook` to have a plugable look-and-feel.
|
|
|
|
By default, a :class:`~lib.agw.aui.auibook.AuiNotebook` uses an instance of this class called
|
|
:class:`AuiDefaultTabArt` 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 tab art class. Call :meth:`AuiNotebook.SetArtProvider() <lib.agw.aui.auibook.AuiNotebook.SetArtProvider>` to make use this
|
|
new tab art.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
self._normal_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
self._selected_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
self._selected_font.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
self._measuring_font = self._selected_font
|
|
|
|
self._fixed_tab_width = 100
|
|
self._tab_ctrl_height = 0
|
|
self._buttonRect = wx.Rect()
|
|
|
|
self.SetDefaultColours()
|
|
|
|
if wx.Platform == "__WXMAC__":
|
|
bmp_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)
|
|
self._active_close_bmp = DrawMACCloseButton(bmp_colour)
|
|
self._disabled_close_bmp = DrawMACCloseButton(wx.Colour(128, 128, 128))
|
|
else:
|
|
self._active_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.BLACK)
|
|
self._disabled_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
self._hover_close_bmp = self._active_close_bmp
|
|
self._pressed_close_bmp = self._active_close_bmp
|
|
|
|
self._active_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.BLACK)
|
|
self._disabled_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
self._active_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.BLACK)
|
|
self._disabled_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
self._active_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.BLACK)
|
|
self._disabled_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
if wx.Platform == "__WXMAC__":
|
|
k = Carbon.Appearance.kThemeBrushFocusHighlight if CARBON else 19
|
|
# Get proper highlight colour for focus rectangle from the
|
|
# current Mac theme. kThemeBrushFocusHighlight is
|
|
# available on Mac OS 8.5 and higher
|
|
if hasattr(wx, 'MacThemeColour'):
|
|
c = wx.MacThemeColour(k)
|
|
else:
|
|
brush = wx.Brush(wx.BLACK)
|
|
brush.MacSetTheme(k)
|
|
c = brush.GetColour()
|
|
self._focusPen = wx.Pen(c, 2, wx.SOLID)
|
|
else:
|
|
self._focusPen = wx.Pen(wx.BLACK, 1, wx.USER_DASH)
|
|
self._focusPen.SetDashes([1, 1])
|
|
self._focusPen.SetCap(wx.CAP_BUTT)
|
|
|
|
|
|
def SetBaseColour(self, base_colour):
|
|
"""
|
|
Sets a new base colour.
|
|
|
|
:param `base_colour`: an instance of :class:`Colour`.
|
|
"""
|
|
|
|
self._base_colour = base_colour
|
|
self._base_colour_pen = wx.Pen(self._base_colour)
|
|
self._base_colour_brush = wx.Brush(self._base_colour)
|
|
|
|
|
|
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()
|
|
|
|
self.SetBaseColour( base_colour )
|
|
self._border_colour = StepColour(base_colour, 75)
|
|
self._border_pen = wx.Pen(self._border_colour)
|
|
|
|
self._background_top_colour = StepColour(self._base_colour, 90)
|
|
self._background_bottom_colour = StepColour(self._base_colour, 170)
|
|
|
|
self._tab_top_colour = self._base_colour
|
|
self._tab_bottom_colour = wx.WHITE
|
|
self._tab_gradient_highlight_colour = wx.WHITE
|
|
|
|
self._tab_inactive_top_colour = self._base_colour
|
|
self._tab_inactive_bottom_colour = StepColour(self._tab_inactive_top_colour, 160)
|
|
|
|
self._tab_text_colour = lambda page: page.text_colour
|
|
self._tab_disabled_text_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
|
|
|
|
|
|
def Clone(self):
|
|
""" Clones the art object. """
|
|
|
|
art = type(self)()
|
|
art.SetNormalFont(self.GetNormalFont())
|
|
art.SetSelectedFont(self.GetSelectedFont())
|
|
art.SetMeasuringFont(self.GetMeasuringFont())
|
|
|
|
art = CopyAttributes(art, self)
|
|
return art
|
|
|
|
|
|
def SetAGWFlags(self, agwFlags):
|
|
"""
|
|
Sets the tab art flags.
|
|
|
|
:param integer `agwFlags`: a combination of the following values:
|
|
|
|
==================================== ==================================
|
|
Flag name Description
|
|
==================================== ==================================
|
|
``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
|
|
``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
|
|
``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
|
|
``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
|
|
``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
|
|
``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
|
|
``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
|
|
``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
|
|
``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
|
|
``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
|
|
``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
|
|
``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
|
|
``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
|
|
``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close :class:`~lib.agw.aui.auibook.AuiNotebook` tabs by mouse middle button click
|
|
``AUI_NB_SUB_NOTEBOOK`` This style is used by :class:`~lib.agw.aui.framemanager.AuiManager` to create automatic AuiNotebooks
|
|
``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
|
|
``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
|
|
``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
|
|
``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
|
|
``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less
|
|
full screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
|
|
``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
|
|
``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
|
|
``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
|
|
==================================== ==================================
|
|
|
|
"""
|
|
|
|
self._agwFlags = agwFlags
|
|
|
|
|
|
def GetAGWFlags(self):
|
|
"""
|
|
Returns the tab art flags.
|
|
|
|
:see: :meth:`~AuiDefaultTabArt.SetAGWFlags` for a list of possible return values.
|
|
"""
|
|
|
|
return self._agwFlags
|
|
|
|
|
|
def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
|
|
"""
|
|
Sets the tab sizing information.
|
|
|
|
:param Size `tab_ctrl_size`: the size of the tab control area;
|
|
:param integer `tab_count`: the number of tabs;
|
|
:param tuple `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
|
|
to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
|
|
"""
|
|
|
|
self._fixed_tab_width = 100
|
|
minTabWidth, maxTabWidth = minMaxTabWidth
|
|
|
|
tot_width = tab_ctrl_size.x - self.GetIndentSize() - 4
|
|
agwFlags = self.GetAGWFlags()
|
|
|
|
if agwFlags & AUI_NB_CLOSE_BUTTON:
|
|
tot_width -= self._active_close_bmp.GetWidth()
|
|
if agwFlags & AUI_NB_WINDOWLIST_BUTTON:
|
|
tot_width -= self._active_windowlist_bmp.GetWidth()
|
|
|
|
if tab_count > 0:
|
|
self._fixed_tab_width = tot_width/tab_count
|
|
|
|
if self._fixed_tab_width < 100:
|
|
self._fixed_tab_width = 100
|
|
|
|
if self._fixed_tab_width > tot_width/2:
|
|
self._fixed_tab_width = tot_width/2
|
|
|
|
if self._fixed_tab_width > 220:
|
|
self._fixed_tab_width = 220
|
|
|
|
if minTabWidth > -1:
|
|
self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
|
|
if maxTabWidth > -1:
|
|
self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
|
|
|
|
self._tab_ctrl_height = tab_ctrl_size.y
|
|
|
|
|
|
def DrawBackground(self, dc, wnd, rect):
|
|
"""
|
|
Draws the tab area background.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param Rect `rect`: the tab control rectangle.
|
|
"""
|
|
|
|
self._buttonRect = wx.Rect()
|
|
|
|
# draw background
|
|
agwFlags = self.GetAGWFlags()
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
r = wx.Rect(rect.x, rect.y, rect.width+2, rect.height)
|
|
|
|
# TODO: else if (agwFlags & AUI_NB_LEFT)
|
|
# TODO: else if (agwFlags & AUI_NB_RIGHT)
|
|
else: #for AUI_NB_TOP
|
|
r = wx.Rect(rect.x, rect.y, rect.width+2, rect.height-3)
|
|
|
|
dc.GradientFillLinear(r, self._background_top_colour, self._background_bottom_colour, wx.SOUTH)
|
|
|
|
# draw base lines
|
|
|
|
dc.SetPen(self._border_pen)
|
|
y = rect.GetHeight()
|
|
w = rect.GetWidth()
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
dc.SetBrush(wx.Brush(self._background_bottom_colour))
|
|
dc.DrawRectangle(-1, 0, w+2, 4)
|
|
|
|
# TODO: else if (agwFlags & AUI_NB_LEFT)
|
|
# TODO: else if (agwFlags & AUI_NB_RIGHT)
|
|
|
|
else: # for AUI_NB_TOP
|
|
dc.SetBrush(self._base_colour_brush)
|
|
dc.DrawRectangle(-1, y-4, w+2, 4)
|
|
|
|
|
|
def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
|
|
"""
|
|
Draws a single tab.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param `page`: the tab control page associated with the tab;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param bool `paint_control`: whether to draw the control inside a tab (if any) on a :class:`MemoryDC`.
|
|
"""
|
|
|
|
# if the caption is empty, measure some temporary text
|
|
caption = page.caption
|
|
if not caption:
|
|
caption = "Xj"
|
|
|
|
dc.SetFont(self._selected_font)
|
|
selected_textx, selected_texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
dc.SetFont(self._normal_font)
|
|
normal_textx, normal_texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
control = page.control
|
|
|
|
# figure out the size of the tab
|
|
tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
|
|
page.active, close_button_state, control)
|
|
|
|
tab_height = self._tab_ctrl_height - 3
|
|
tab_width = tab_size[0]
|
|
tab_x = in_rect.x
|
|
tab_y = in_rect.y + in_rect.height - tab_height
|
|
|
|
caption = page.caption
|
|
|
|
# select pen, brush and font for the tab to be drawn
|
|
|
|
if page.active:
|
|
|
|
dc.SetFont(self._selected_font)
|
|
textx, texty = selected_textx, selected_texty
|
|
|
|
else:
|
|
|
|
dc.SetFont(self._normal_font)
|
|
textx, texty = normal_textx, normal_texty
|
|
|
|
if not page.enabled:
|
|
dc.SetTextForeground(self._tab_disabled_text_colour)
|
|
pagebitmap = page.dis_bitmap
|
|
else:
|
|
dc.SetTextForeground(self._tab_text_colour(page))
|
|
pagebitmap = page.bitmap
|
|
|
|
# create points that will make the tab outline
|
|
|
|
clip_width = tab_width
|
|
if tab_x + clip_width > in_rect.x + in_rect.width:
|
|
clip_width = in_rect.x + in_rect.width - tab_x
|
|
|
|
# since the above code above doesn't play well with WXDFB or WXCOCOA,
|
|
# we'll just use a rectangle for the clipping region for now --
|
|
dc.SetClippingRegion(tab_x, tab_y, clip_width+1, tab_height-3)
|
|
|
|
border_points = [wx.Point() for i in range(6)]
|
|
agwFlags = self.GetAGWFlags()
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
|
|
border_points[0] = wx.Point(tab_x, tab_y)
|
|
border_points[1] = wx.Point(tab_x, tab_y+tab_height-6)
|
|
border_points[2] = wx.Point(tab_x+2, tab_y+tab_height-4)
|
|
border_points[3] = wx.Point(tab_x+tab_width-2, tab_y+tab_height-4)
|
|
border_points[4] = wx.Point(tab_x+tab_width, tab_y+tab_height-6)
|
|
border_points[5] = wx.Point(tab_x+tab_width, tab_y)
|
|
|
|
else: #if (agwFlags & AUI_NB_TOP)
|
|
|
|
border_points[0] = wx.Point(tab_x, tab_y+tab_height-4)
|
|
border_points[1] = wx.Point(tab_x, tab_y+2)
|
|
border_points[2] = wx.Point(tab_x+2, tab_y)
|
|
border_points[3] = wx.Point(tab_x+tab_width-2, tab_y)
|
|
border_points[4] = wx.Point(tab_x+tab_width, tab_y+2)
|
|
border_points[5] = wx.Point(tab_x+tab_width, tab_y+tab_height-4)
|
|
|
|
# TODO: else if (agwFlags & AUI_NB_LEFT)
|
|
# TODO: else if (agwFlags & AUI_NB_RIGHT)
|
|
|
|
drawn_tab_yoff = border_points[1].y
|
|
drawn_tab_height = border_points[0].y - border_points[1].y
|
|
|
|
if page.active:
|
|
|
|
# draw active tab
|
|
|
|
# draw base background colour
|
|
r = wx.Rect(tab_x, tab_y, tab_width, tab_height)
|
|
dc.SetPen(self._base_colour_pen)
|
|
dc.SetBrush(self._base_colour_brush)
|
|
dc.DrawRectangle(r.x+1, r.y+1, r.width-1, r.height-4)
|
|
|
|
# this white helps fill out the gradient at the top of the tab
|
|
dc.SetPen( wx.Pen(self._tab_gradient_highlight_colour) )
|
|
dc.SetBrush( wx.Brush(self._tab_gradient_highlight_colour) )
|
|
dc.DrawRectangle(r.x+2, r.y+1, r.width-3, r.height-4)
|
|
|
|
# these two points help the rounded corners appear more antialiased
|
|
dc.SetPen(self._base_colour_pen)
|
|
dc.DrawPoint(r.x+2, r.y+1)
|
|
dc.DrawPoint(r.x+r.width-2, r.y+1)
|
|
|
|
# set rectangle down a bit for gradient drawing
|
|
r.SetHeight(r.GetHeight()/2)
|
|
r.x += 2
|
|
r.width -= 2
|
|
r.y += r.height
|
|
r.y -= 2
|
|
|
|
# draw gradient background
|
|
top_colour = self._tab_bottom_colour
|
|
bottom_colour = self._tab_top_colour
|
|
dc.GradientFillLinear(r, bottom_colour, top_colour, wx.NORTH)
|
|
|
|
else:
|
|
|
|
# draw inactive tab
|
|
|
|
r = wx.Rect(tab_x, tab_y+1, tab_width, tab_height-3)
|
|
|
|
# start the gradent up a bit and leave the inside border inset
|
|
# by a pixel for a 3D look. Only the top half of the inactive
|
|
# tab will have a slight gradient
|
|
r.x += 3
|
|
r.y += 1
|
|
r.width -= 4
|
|
r.height /= 2
|
|
r.height -= 1
|
|
|
|
# -- draw top gradient fill for glossy look
|
|
top_colour = self._tab_inactive_top_colour
|
|
bottom_colour = self._tab_inactive_bottom_colour
|
|
dc.GradientFillLinear(r, bottom_colour, top_colour, wx.NORTH)
|
|
|
|
r.y += r.height
|
|
r.y -= 1
|
|
|
|
# -- draw bottom fill for glossy look
|
|
top_colour = self._tab_inactive_bottom_colour
|
|
bottom_colour = self._tab_inactive_bottom_colour
|
|
dc.GradientFillLinear(r, top_colour, bottom_colour, wx.SOUTH)
|
|
|
|
# draw tab outline
|
|
dc.SetPen(self._border_pen)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.DrawPolygon(border_points)
|
|
|
|
# there are two horizontal grey lines at the bottom of the tab control,
|
|
# this gets rid of the top one of those lines in the tab control
|
|
if page.active:
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
dc.SetPen(wx.Pen(self._background_bottom_colour))
|
|
|
|
# TODO: else if (agwFlags & AUI_NB_LEFT)
|
|
# TODO: else if (agwFlags & AUI_NB_RIGHT)
|
|
else: # for AUI_NB_TOP
|
|
dc.SetPen(self._base_colour_pen)
|
|
|
|
dc.DrawLine(border_points[0].x+1,
|
|
border_points[0].y,
|
|
border_points[5].x,
|
|
border_points[5].y)
|
|
|
|
text_offset = tab_x + 8
|
|
close_button_width = 0
|
|
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
text_offset += close_button_width - 5
|
|
|
|
bitmap_offset = 0
|
|
|
|
if pagebitmap.IsOk():
|
|
|
|
bitmap_offset = tab_x + 8
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
|
|
bitmap_offset += close_button_width - 5
|
|
|
|
# draw bitmap
|
|
dc.DrawBitmap(pagebitmap,
|
|
bitmap_offset,
|
|
drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2),
|
|
True)
|
|
|
|
text_offset = bitmap_offset + pagebitmap.GetWidth()
|
|
text_offset += 3 # bitmap padding
|
|
|
|
else:
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
|
|
text_offset = tab_x + 8
|
|
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
|
|
|
|
ypos = drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2) - 1
|
|
|
|
offset_focus = text_offset
|
|
if control:
|
|
if control.GetPosition() != wx.Point(text_offset+1, ypos):
|
|
control.SetPosition(wx.Point(text_offset+1, ypos))
|
|
|
|
if not control.IsShown():
|
|
control.Show()
|
|
|
|
if paint_control:
|
|
bmp = TakeScreenShot(control.GetScreenRect())
|
|
dc.DrawBitmap(bmp, text_offset+1, ypos, True)
|
|
|
|
controlW, controlH = control.GetSize()
|
|
text_offset += controlW + 4
|
|
textx += controlW + 4
|
|
|
|
# draw tab text
|
|
rectx, recty, dummy = dc.GetFullMultiLineTextExtent(draw_text)
|
|
dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
|
|
|
|
# draw focus rectangle
|
|
if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
|
|
self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff, drawn_tab_height, rectx, recty)
|
|
|
|
out_button_rect = wx.Rect()
|
|
|
|
# draw close button if necessary
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
|
|
bmp = self._disabled_close_bmp
|
|
|
|
if close_button_state == AUI_BUTTON_STATE_HOVER:
|
|
bmp = self._hover_close_bmp
|
|
elif close_button_state == AUI_BUTTON_STATE_PRESSED:
|
|
bmp = self._pressed_close_bmp
|
|
|
|
shift = (agwFlags & AUI_NB_BOTTOM and [1] or [0])[0]
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
rect = wx.Rect(tab_x + 4, tab_y + (tab_height - bmp.GetHeight())/2 - shift,
|
|
close_button_width, tab_height)
|
|
else:
|
|
rect = wx.Rect(tab_x + tab_width - close_button_width - 1,
|
|
tab_y + (tab_height - bmp.GetHeight())/2 - shift,
|
|
close_button_width, tab_height)
|
|
|
|
rect = IndentPressedBitmap(rect, close_button_state)
|
|
dc.DrawBitmap(bmp, rect.x, rect.y, True)
|
|
|
|
out_button_rect = rect
|
|
|
|
out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
|
|
|
|
dc.DestroyClippingRegion()
|
|
|
|
return out_tab_rect, out_button_rect, x_extent
|
|
|
|
|
|
def SetCustomButton(self, bitmap_id, button_state, bmp):
|
|
"""
|
|
Sets a custom bitmap for the close, left, right and window list buttons.
|
|
|
|
:param integer `bitmap_id`: the button identifier;
|
|
:param integer `button_state`: the button state;
|
|
:param Bitmap `bmp`: the custom bitmap to use for the button.
|
|
"""
|
|
|
|
if bitmap_id == AUI_BUTTON_CLOSE:
|
|
if button_state == AUI_BUTTON_STATE_NORMAL:
|
|
self._active_close_bmp = bmp
|
|
self._hover_close_bmp = self._active_close_bmp
|
|
self._pressed_close_bmp = self._active_close_bmp
|
|
self._disabled_close_bmp = self._active_close_bmp
|
|
|
|
elif button_state == AUI_BUTTON_STATE_HOVER:
|
|
self._hover_close_bmp = bmp
|
|
elif button_state == AUI_BUTTON_STATE_PRESSED:
|
|
self._pressed_close_bmp = bmp
|
|
else:
|
|
self._disabled_close_bmp = bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_LEFT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
self._disabled_left_bmp = bmp
|
|
else:
|
|
self._active_left_bmp = bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_RIGHT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
self._disabled_right_bmp = bmp
|
|
else:
|
|
self._active_right_bmp = bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_WINDOWLIST:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
self._disabled_windowlist_bmp = bmp
|
|
else:
|
|
self._active_windowlist_bmp = bmp
|
|
|
|
|
|
def GetIndentSize(self):
|
|
""" Returns the tabs indent size. """
|
|
|
|
return 5
|
|
|
|
|
|
def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
|
|
"""
|
|
Returns the tab size for the given caption, bitmap and button state.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param string `caption`: the tab text caption;
|
|
:param Bitmap `bitmap`: the bitmap displayed on the tab;
|
|
:param bool `active`: whether the tab is selected or not;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param Window `control`: a :class:`Window` instance inside a tab (or ``None``).
|
|
"""
|
|
|
|
dc.SetFont(self._measuring_font)
|
|
measured_textx, measured_texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
# add padding around the text
|
|
tab_width = measured_textx
|
|
tab_height = measured_texty
|
|
|
|
# if the close button is showing, add space for it
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
tab_width += self._active_close_bmp.GetWidth() + 3
|
|
|
|
# if there's a bitmap, add space for it
|
|
if bitmap.IsOk():
|
|
tab_width += bitmap.GetWidth()
|
|
tab_width += 3 # right side bitmap padding
|
|
tab_height = max(tab_height, bitmap.GetHeight())
|
|
|
|
# add padding
|
|
tab_width += 16
|
|
tab_height += 10
|
|
|
|
agwFlags = self.GetAGWFlags()
|
|
if agwFlags & AUI_NB_TAB_FIXED_WIDTH:
|
|
tab_width = self._fixed_tab_width
|
|
|
|
if control:
|
|
tab_width += control.GetSize().GetWidth() + 4
|
|
|
|
x_extent = tab_width
|
|
|
|
return (tab_width, tab_height), x_extent
|
|
|
|
|
|
def DrawButton(self, dc, wnd, in_rect, button, orientation):
|
|
"""
|
|
Draws a button on the tab or on the tab area, depending on the button identifier.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param `button`: an instance of the button class;
|
|
:param integer `orientation`: the tab orientation.
|
|
"""
|
|
|
|
bitmap_id, button_state = button.id, button.cur_state
|
|
|
|
if bitmap_id == AUI_BUTTON_CLOSE:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_close_bmp
|
|
elif button_state & AUI_BUTTON_STATE_HOVER:
|
|
bmp = self._hover_close_bmp
|
|
elif button_state & AUI_BUTTON_STATE_PRESSED:
|
|
bmp = self._pressed_close_bmp
|
|
else:
|
|
bmp = self._active_close_bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_LEFT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_left_bmp
|
|
else:
|
|
bmp = self._active_left_bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_RIGHT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_right_bmp
|
|
else:
|
|
bmp = self._active_right_bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_WINDOWLIST:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_windowlist_bmp
|
|
else:
|
|
bmp = self._active_windowlist_bmp
|
|
|
|
else:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = button.dis_bitmap
|
|
else:
|
|
bmp = button.bitmap
|
|
|
|
if not bmp.IsOk():
|
|
return
|
|
|
|
rect = wx.Rect(*in_rect)
|
|
|
|
if orientation == wx.LEFT:
|
|
|
|
rect.SetX(in_rect.x)
|
|
rect.SetY(((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2))
|
|
rect.SetWidth(bmp.GetWidth())
|
|
rect.SetHeight(bmp.GetHeight())
|
|
|
|
else:
|
|
|
|
rect = wx.Rect(in_rect.x + in_rect.width - bmp.GetWidth(),
|
|
((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2),
|
|
bmp.GetWidth(), bmp.GetHeight())
|
|
|
|
rect = IndentPressedBitmap(rect, button_state)
|
|
dc.DrawBitmap(bmp, rect.x, rect.y, True)
|
|
|
|
out_rect = rect
|
|
|
|
if bitmap_id == AUI_BUTTON_RIGHT:
|
|
self._buttonRect = wx.Rect(rect.x, rect.y, 30, rect.height)
|
|
|
|
return out_rect
|
|
|
|
|
|
def DrawFocusRectangle(self, dc, page, wnd, draw_text, text_offset, bitmap_offset, drawn_tab_yoff, drawn_tab_height, textx, texty):
|
|
"""
|
|
Draws the focus rectangle on a tab.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `page`: the page associated with the tab;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param string `draw_text`: the text that has been drawn on the tab;
|
|
:param integer `text_offset`: the text offset on the tab;
|
|
:param integer `bitmap_offset`: the bitmap offset on the tab;
|
|
:param integer `drawn_tab_yoff`: the y offset of the tab text;
|
|
:param integer `drawn_tab_height`: the height of the tab;
|
|
:param integer `textx`: the x text extent;
|
|
:param integer `texty`: the y text extent.
|
|
"""
|
|
|
|
if self.GetAGWFlags() & AUI_NB_NO_TAB_FOCUS:
|
|
return
|
|
|
|
if page.active and wx.Window.FindFocus() == wnd:
|
|
|
|
focusRectText = wx.Rect(text_offset, (drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2)),
|
|
textx, texty)
|
|
|
|
if page.bitmap.IsOk():
|
|
focusRectBitmap = wx.Rect(bitmap_offset, drawn_tab_yoff + (drawn_tab_height/2) - (page.bitmap.GetHeight()/2),
|
|
page.bitmap.GetWidth(), page.bitmap.GetHeight())
|
|
|
|
if page.bitmap.IsOk() and draw_text == "":
|
|
focusRect = wx.Rect(*focusRectBitmap)
|
|
elif not page.bitmap.IsOk() and draw_text != "":
|
|
focusRect = wx.Rect(*focusRectText)
|
|
elif page.bitmap.IsOk() and draw_text != "":
|
|
focusRect = focusRectText.Union(focusRectBitmap)
|
|
|
|
focusRect.Inflate(2, 2)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(self._focusPen)
|
|
dc.DrawRoundedRectangle(focusRect, 2)
|
|
|
|
|
|
def GetBestTabCtrlSize(self, wnd, pages, required_bmp_size):
|
|
"""
|
|
Returns the best tab control size.
|
|
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param list `pages`: the pages associated with the tabs;
|
|
:param Size `required_bmp_size`: the size of the bitmap on the tabs.
|
|
"""
|
|
|
|
dc = wx.ClientDC(wnd)
|
|
dc.SetFont(self._measuring_font)
|
|
|
|
# sometimes a standard bitmap size needs to be enforced, especially
|
|
# if some tabs have bitmaps and others don't. This is important because
|
|
# it prevents the tab control from resizing when tabs are added.
|
|
|
|
measure_bmp = wx.NullBitmap
|
|
|
|
if required_bmp_size.IsFullySpecified():
|
|
measure_bmp = wx.Bitmap(required_bmp_size.x, required_bmp_size.y)
|
|
|
|
max_y = 0
|
|
|
|
for page in pages:
|
|
|
|
if measure_bmp.IsOk():
|
|
bmp = measure_bmp
|
|
else:
|
|
bmp = page.bitmap
|
|
|
|
# we don't use the caption text because we don't
|
|
# want tab heights to be different in the case
|
|
# of a very short piece of text on one tab and a very
|
|
# tall piece of text on another tab
|
|
s, x_ext = self.GetTabSize(dc, wnd, page.caption, bmp, True, AUI_BUTTON_STATE_HIDDEN, None)
|
|
max_y = max(max_y, s[1])
|
|
|
|
if page.control:
|
|
controlW, controlH = page.control.GetSize()
|
|
max_y = max(max_y, controlH+4)
|
|
|
|
return max_y + 2
|
|
|
|
|
|
def SetNormalFont(self, font):
|
|
"""
|
|
Sets the normal font for drawing tab labels.
|
|
|
|
:param Font `font`: the new font to use to draw tab labels in their normal, un-selected state.
|
|
"""
|
|
|
|
self._normal_font = font
|
|
|
|
|
|
def SetSelectedFont(self, font):
|
|
"""
|
|
Sets the selected tab font for drawing tab labels.
|
|
|
|
:param Font `font`: the new font to use to draw tab labels in their selected state.
|
|
"""
|
|
|
|
self._selected_font = font
|
|
|
|
|
|
def SetMeasuringFont(self, font):
|
|
"""
|
|
Sets the font for calculating text measurements.
|
|
|
|
:param Font `font`: the new font to use to measure tab labels text extents.
|
|
"""
|
|
|
|
self._measuring_font = font
|
|
|
|
|
|
def GetNormalFont(self):
|
|
""" Returns the normal font for drawing tab labels. """
|
|
|
|
return self._normal_font
|
|
|
|
|
|
def GetSelectedFont(self):
|
|
""" Returns the selected tab font for drawing tab labels. """
|
|
|
|
return self._selected_font
|
|
|
|
|
|
def GetMeasuringFont(self):
|
|
""" Returns the font for calculating text measurements. """
|
|
|
|
return self._measuring_font
|
|
|
|
|
|
def ShowDropDown(self, wnd, pages, active_idx):
|
|
"""
|
|
Shows the drop-down window menu on the tab area.
|
|
|
|
:param `wnd`: a :class:`Window` derived window instance;
|
|
:param list `pages`: the pages associated with the tabs;
|
|
:param integer `active_idx`: the active tab index.
|
|
"""
|
|
|
|
useImages = self.GetAGWFlags() & AUI_NB_USE_IMAGES_DROPDOWN
|
|
menuPopup = wx.Menu()
|
|
|
|
longest = 0
|
|
for i, page in enumerate(pages):
|
|
|
|
caption = page.caption
|
|
|
|
# if there is no caption, make it a space. This will prevent
|
|
# an assert in the menu code.
|
|
if caption == "":
|
|
caption = " "
|
|
|
|
# Save longest caption width for calculating menu width with
|
|
width = wnd.GetTextExtent(caption)[0]
|
|
if width > longest:
|
|
longest = width
|
|
|
|
if useImages:
|
|
menuItem = wx.MenuItem(menuPopup, 1000+i, caption)
|
|
if page.bitmap:
|
|
menuItem.SetBitmap(page.bitmap)
|
|
|
|
menuPopup.Append(menuItem)
|
|
|
|
else:
|
|
|
|
menuPopup.AppendCheckItem(1000+i, caption)
|
|
|
|
menuPopup.Enable(1000+i, page.enabled)
|
|
|
|
if active_idx != -1 and not useImages:
|
|
|
|
menuPopup.Check(1000+active_idx, True)
|
|
|
|
# find out the screen coordinate at the bottom of the tab ctrl
|
|
cli_rect = wnd.GetClientRect()
|
|
|
|
# Calculate the approximate size of the popupmenu for setting the
|
|
# position of the menu when its shown.
|
|
# Account for extra padding on left/right of text on mac menus
|
|
if wx.Platform in ['__WXMAC__', '__WXMSW__']:
|
|
longest += 32
|
|
|
|
# Bitmap/Checkmark width + padding
|
|
longest += 20
|
|
|
|
if self.GetAGWFlags() & AUI_NB_CLOSE_BUTTON:
|
|
longest += 16
|
|
|
|
pt = wx.Point(cli_rect.x + cli_rect.GetWidth() - longest,
|
|
cli_rect.y + cli_rect.height)
|
|
|
|
cc = AuiCommandCapture()
|
|
wnd.PushEventHandler(cc)
|
|
wnd.PopupMenu(menuPopup, pt)
|
|
command = cc.GetCommandId()
|
|
wnd.PopEventHandler(True)
|
|
|
|
if command >= 1000:
|
|
return command - 1000
|
|
|
|
return -1
|
|
|
|
|
|
class AuiSimpleTabArt(object):
|
|
""" A simple-looking implementation of a tab art. """
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
self._normal_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
self._selected_font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
self._selected_font.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
self._measuring_font = self._selected_font
|
|
|
|
self._agwFlags = 0
|
|
self._fixed_tab_width = 100
|
|
|
|
base_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
|
|
|
|
background_colour = base_colour
|
|
normaltab_colour = base_colour
|
|
selectedtab_colour = wx.WHITE
|
|
|
|
self._bkbrush = wx.Brush(background_colour)
|
|
self._normal_bkbrush = wx.Brush(normaltab_colour)
|
|
self._normal_bkpen = wx.Pen(normaltab_colour)
|
|
self._selected_bkbrush = wx.Brush(selectedtab_colour)
|
|
self._selected_bkpen = wx.Pen(selectedtab_colour)
|
|
|
|
self._active_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.BLACK)
|
|
self._disabled_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
self._active_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.BLACK)
|
|
self._disabled_left_bmp = BitmapFromBits(nb_left_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
self._active_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.BLACK)
|
|
self._disabled_right_bmp = BitmapFromBits(nb_right_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
self._active_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.BLACK)
|
|
self._disabled_windowlist_bmp = BitmapFromBits(nb_list_bits, 16, 16, wx.Colour(128, 128, 128))
|
|
|
|
|
|
def Clone(self):
|
|
""" Clones the art object. """
|
|
|
|
art = type(self)()
|
|
art.SetNormalFont(self.GetNormalFont())
|
|
art.SetSelectedFont(self.GetSelectedFont())
|
|
art.SetMeasuringFont(self.GetMeasuringFont())
|
|
|
|
art = CopyAttributes(art, self)
|
|
return art
|
|
|
|
|
|
def SetAGWFlags(self, agwFlags):
|
|
"""
|
|
Sets the tab art flags.
|
|
|
|
:param integer `agwFlags`: a combination of the following values:
|
|
|
|
==================================== ==================================
|
|
Flag name Description
|
|
==================================== ==================================
|
|
``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
|
|
``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
|
|
``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
|
|
``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
|
|
``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
|
|
``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
|
|
``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
|
|
``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
|
|
``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
|
|
``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
|
|
``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
|
|
``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
|
|
``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
|
|
``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close :class:`~lib.agw.aui.auibook.AuiNotebook` tabs by mouse middle button click
|
|
``AUI_NB_SUB_NOTEBOOK`` This style is used by :class:`~lib.agw.aui.framemanager.AuiManager` to create automatic AuiNotebooks
|
|
``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
|
|
``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
|
|
``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
|
|
``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
|
|
``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full
|
|
screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
|
|
``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
|
|
``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
|
|
``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
|
|
==================================== ==================================
|
|
|
|
"""
|
|
|
|
self._agwFlags = agwFlags
|
|
|
|
|
|
def GetAGWFlags(self):
|
|
"""
|
|
Returns the tab art flags.
|
|
|
|
:see: :meth:`~AuiSimpleTabArt.SetAGWFlags` for a list of possible return values.
|
|
"""
|
|
|
|
return self._agwFlags
|
|
|
|
|
|
def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
|
|
"""
|
|
Sets the tab sizing information.
|
|
|
|
:param Size `tab_ctrl_size`: the size of the tab control area;
|
|
:param integer `tab_count`: the number of tabs;
|
|
:param tuple `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
|
|
to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
|
|
"""
|
|
|
|
self._fixed_tab_width = 100
|
|
minTabWidth, maxTabWidth = minMaxTabWidth
|
|
|
|
tot_width = tab_ctrl_size.x - self.GetIndentSize() - 4
|
|
|
|
if self._agwFlags & AUI_NB_CLOSE_BUTTON:
|
|
tot_width -= self._active_close_bmp.GetWidth()
|
|
if self._agwFlags & AUI_NB_WINDOWLIST_BUTTON:
|
|
tot_width -= self._active_windowlist_bmp.GetWidth()
|
|
|
|
if tab_count > 0:
|
|
self._fixed_tab_width = tot_width/tab_count
|
|
|
|
if self._fixed_tab_width < 100:
|
|
self._fixed_tab_width = 100
|
|
|
|
if self._fixed_tab_width > tot_width/2:
|
|
self._fixed_tab_width = tot_width/2
|
|
|
|
if self._fixed_tab_width > 220:
|
|
self._fixed_tab_width = 220
|
|
|
|
if minTabWidth > -1:
|
|
self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
|
|
if maxTabWidth > -1:
|
|
self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
|
|
|
|
self._tab_ctrl_height = tab_ctrl_size.y
|
|
|
|
|
|
def DrawBackground(self, dc, wnd, rect):
|
|
"""
|
|
Draws the tab area background.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param Rect `rect`: the tab control rectangle.
|
|
"""
|
|
|
|
# draw background
|
|
dc.SetBrush(self._bkbrush)
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
dc.DrawRectangle(-1, -1, rect.GetWidth()+2, rect.GetHeight()+2)
|
|
|
|
# draw base line
|
|
dc.SetPen(wx.GREY_PEN)
|
|
dc.DrawLine(0, rect.GetHeight()-1, rect.GetWidth(), rect.GetHeight()-1)
|
|
|
|
|
|
def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
|
|
"""
|
|
Draws a single tab.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param `page`: the tab control page associated with the tab;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param bool `paint_control`: whether to draw the control inside a tab (if any) on a :class:`MemoryDC`.
|
|
"""
|
|
|
|
# if the caption is empty, measure some temporary text
|
|
caption = page.caption
|
|
if caption == "":
|
|
caption = "Xj"
|
|
|
|
agwFlags = self.GetAGWFlags()
|
|
|
|
dc.SetFont(self._selected_font)
|
|
selected_textx, selected_texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
dc.SetFont(self._normal_font)
|
|
normal_textx, normal_texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
control = page.control
|
|
|
|
# figure out the size of the tab
|
|
tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
|
|
page.active, close_button_state, control)
|
|
|
|
tab_height = tab_size[1]
|
|
tab_width = tab_size[0]
|
|
tab_x = in_rect.x
|
|
tab_y = in_rect.y + in_rect.height - tab_height
|
|
|
|
caption = page.caption
|
|
# select pen, brush and font for the tab to be drawn
|
|
|
|
if page.active:
|
|
|
|
dc.SetPen(self._selected_bkpen)
|
|
dc.SetBrush(self._selected_bkbrush)
|
|
dc.SetFont(self._selected_font)
|
|
textx = selected_textx
|
|
texty = selected_texty
|
|
|
|
else:
|
|
|
|
dc.SetPen(self._normal_bkpen)
|
|
dc.SetBrush(self._normal_bkbrush)
|
|
dc.SetFont(self._normal_font)
|
|
textx = normal_textx
|
|
texty = normal_texty
|
|
|
|
if not page.enabled:
|
|
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
|
|
else:
|
|
dc.SetTextForeground(page.text_colour)
|
|
|
|
# -- draw line --
|
|
|
|
points = [wx.Point() for i in range(7)]
|
|
points[0].x = tab_x
|
|
points[0].y = tab_y + tab_height - 1
|
|
points[1].x = tab_x + tab_height - 3
|
|
points[1].y = tab_y + 2
|
|
points[2].x = tab_x + tab_height + 3
|
|
points[2].y = tab_y
|
|
points[3].x = tab_x + tab_width - 2
|
|
points[3].y = tab_y
|
|
points[4].x = tab_x + tab_width
|
|
points[4].y = tab_y + 2
|
|
points[5].x = tab_x + tab_width
|
|
points[5].y = tab_y + tab_height - 1
|
|
points[6] = points[0]
|
|
|
|
dc.SetClippingRegion(in_rect)
|
|
dc.DrawPolygon(points)
|
|
|
|
dc.SetPen(wx.GREY_PEN)
|
|
dc.DrawLines(points)
|
|
|
|
close_button_width = 0
|
|
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
if control:
|
|
text_offset = tab_x + (tab_height/2) + close_button_width - (textx/2) - 2
|
|
else:
|
|
text_offset = tab_x + (tab_height/2) + ((tab_width+close_button_width)/2) - (textx/2) - 2
|
|
else:
|
|
if control:
|
|
text_offset = tab_x + (tab_height/2) + close_button_width - (textx/2)
|
|
else:
|
|
text_offset = tab_x + (tab_height/2) + ((tab_width-close_button_width)/2) - (textx/2)
|
|
|
|
else:
|
|
|
|
text_offset = tab_x + (tab_height/3) + (tab_width/2) - (textx/2)
|
|
if control:
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
text_offset = tab_x + (tab_height/3) - (textx/2) + close_button_width + 2
|
|
else:
|
|
text_offset = tab_x + (tab_height/3) - (textx/2)
|
|
|
|
# set minimum text offset
|
|
if text_offset < tab_x + tab_height:
|
|
text_offset = tab_x + tab_height
|
|
|
|
# chop text if necessary
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x))
|
|
else:
|
|
draw_text = ChopText(dc, caption,
|
|
tab_width - (text_offset-tab_x) - close_button_width)
|
|
|
|
ypos = (tab_y + tab_height)/2 - (texty/2) + 1
|
|
|
|
if control:
|
|
if control.GetPosition() != wx.Point(text_offset+1, ypos):
|
|
control.SetPosition(wx.Point(text_offset+1, ypos))
|
|
|
|
if not control.IsShown():
|
|
control.Show()
|
|
|
|
if paint_control:
|
|
bmp = TakeScreenShot(control.GetScreenRect())
|
|
dc.DrawBitmap(bmp, text_offset+1, ypos, True)
|
|
|
|
controlW, controlH = control.GetSize()
|
|
text_offset += controlW + 4
|
|
|
|
# draw tab text
|
|
rectx, recty, dummy = dc.GetFullMultiLineTextExtent(draw_text)
|
|
dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
|
|
|
|
# draw focus rectangle
|
|
if page.active and wx.Window.FindFocus() == wnd and (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
|
|
|
|
focusRect = wx.Rect(text_offset, ((tab_y + tab_height)/2 - (texty/2) + 1),
|
|
selected_textx, selected_texty)
|
|
|
|
focusRect.Inflate(2, 2)
|
|
# TODO:
|
|
# This should be uncommented when DrawFocusRect will become
|
|
# available in wxPython
|
|
# wx.RendererNative.Get().DrawFocusRect(wnd, dc, focusRect, 0)
|
|
|
|
out_button_rect = wx.Rect()
|
|
# draw close button if necessary
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
|
|
if page.active:
|
|
bmp = self._active_close_bmp
|
|
else:
|
|
bmp = self._disabled_close_bmp
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
rect = wx.Rect(tab_x + tab_height - 2,
|
|
tab_y + (tab_height/2) - (bmp.GetHeight()/2) + 1,
|
|
close_button_width, tab_height - 1)
|
|
else:
|
|
rect = wx.Rect(tab_x + tab_width - close_button_width - 1,
|
|
tab_y + (tab_height/2) - (bmp.GetHeight()/2) + 1,
|
|
close_button_width, tab_height - 1)
|
|
|
|
self.DrawButtons(dc, rect, bmp, wx.WHITE, close_button_state)
|
|
out_button_rect = wx.Rect(*rect)
|
|
|
|
out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
|
|
dc.DestroyClippingRegion()
|
|
|
|
return out_tab_rect, out_button_rect, x_extent
|
|
|
|
|
|
def DrawButtons(self, dc, _rect, bmp, bkcolour, button_state):
|
|
"""
|
|
Convenience method to draw tab buttons.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param Rect `_rect`: the tab rectangle;
|
|
:param Bitmap `bmp`: the tab bitmap;
|
|
:param Colour `bkcolour`: the tab background colour;
|
|
:param integer `button_state`: the state of the tab button.
|
|
"""
|
|
|
|
rect = wx.Rect(*_rect)
|
|
|
|
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]:
|
|
dc.SetBrush(wx.Brush(StepColour(bkcolour, 120)))
|
|
dc.SetPen(wx.Pen(StepColour(bkcolour, 75)))
|
|
|
|
# draw the background behind the button
|
|
dc.DrawRectangle(rect.x, rect.y, 15, 15)
|
|
|
|
# draw the button itself
|
|
dc.DrawBitmap(bmp, rect.x, rect.y, True)
|
|
|
|
|
|
def GetIndentSize(self):
|
|
""" Returns the tabs indent size. """
|
|
|
|
return 0
|
|
|
|
|
|
def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
|
|
"""
|
|
Returns the tab size for the given caption, bitmap and button state.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param string `caption`: the tab text caption;
|
|
:param Bitmap `bitmap`: the bitmap displayed on the tab;
|
|
:param bool `active`: whether the tab is selected or not;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param Window `control`: a :class:`Window` instance inside a tab (or ``None``).
|
|
"""
|
|
|
|
dc.SetFont(self._measuring_font)
|
|
measured_textx, measured_texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
tab_height = measured_texty + 4
|
|
tab_width = measured_textx + tab_height + 5
|
|
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
tab_width += self._active_close_bmp.GetWidth()
|
|
|
|
if self._agwFlags & AUI_NB_TAB_FIXED_WIDTH:
|
|
tab_width = self._fixed_tab_width
|
|
|
|
if control:
|
|
controlW, controlH = control.GetSize()
|
|
tab_width += controlW + 4
|
|
|
|
x_extent = tab_width - (tab_height/2) - 1
|
|
|
|
return (tab_width, tab_height), x_extent
|
|
|
|
|
|
def DrawButton(self, dc, wnd, in_rect, button, orientation):
|
|
"""
|
|
Draws a button on the tab or on the tab area, depending on the button identifier.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param `button`: an instance of the button class;
|
|
:param integer `orientation`: the tab orientation.
|
|
"""
|
|
|
|
bitmap_id, button_state = button.id, button.cur_state
|
|
|
|
if bitmap_id == AUI_BUTTON_CLOSE:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_close_bmp
|
|
else:
|
|
bmp = self._active_close_bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_LEFT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_left_bmp
|
|
else:
|
|
bmp = self._active_left_bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_RIGHT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_right_bmp
|
|
else:
|
|
bmp = self._active_right_bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_WINDOWLIST:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = self._disabled_windowlist_bmp
|
|
else:
|
|
bmp = self._active_windowlist_bmp
|
|
|
|
else:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
bmp = button.dis_bitmap
|
|
else:
|
|
bmp = button.bitmap
|
|
|
|
if not bmp.IsOk():
|
|
return
|
|
|
|
rect = wx.Rect(*in_rect)
|
|
|
|
if orientation == wx.LEFT:
|
|
|
|
rect.SetX(in_rect.x)
|
|
rect.SetY(((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2))
|
|
rect.SetWidth(bmp.GetWidth())
|
|
rect.SetHeight(bmp.GetHeight())
|
|
|
|
else:
|
|
|
|
rect = wx.Rect(in_rect.x + in_rect.width - bmp.GetWidth(),
|
|
((in_rect.y + in_rect.height)/2) - (bmp.GetHeight()/2),
|
|
bmp.GetWidth(), bmp.GetHeight())
|
|
|
|
self.DrawButtons(dc, rect, bmp, wx.WHITE, button_state)
|
|
|
|
out_rect = wx.Rect(*rect)
|
|
return out_rect
|
|
|
|
|
|
def ShowDropDown(self, wnd, pages, active_idx):
|
|
"""
|
|
Shows the drop-down window menu on the tab area.
|
|
|
|
:param `wnd`: a :class:`Window` derived window instance;
|
|
:param list `pages`: the pages associated with the tabs;
|
|
:param integer `active_idx`: the active tab index.
|
|
"""
|
|
|
|
menuPopup = wx.Menu()
|
|
useImages = self.GetAGWFlags() & AUI_NB_USE_IMAGES_DROPDOWN
|
|
|
|
for i, page in enumerate(pages):
|
|
|
|
if useImages:
|
|
menuItem = wx.MenuItem(menuPopup, 1000+i, page.caption)
|
|
if page.bitmap:
|
|
menuItem.SetBitmap(page.bitmap)
|
|
|
|
menuPopup.Append(menuItem)
|
|
|
|
else:
|
|
|
|
menuPopup.AppendCheckItem(1000+i, page.caption)
|
|
|
|
menuPopup.Enable(1000+i, page.enabled)
|
|
|
|
if active_idx != -1 and not useImages:
|
|
menuPopup.Check(1000+active_idx, True)
|
|
|
|
# find out where to put the popup menu of window
|
|
# items. Subtract 100 for now to center the menu
|
|
# a bit, until a better mechanism can be implemented
|
|
pt = wx.GetMousePosition()
|
|
pt = wnd.ScreenToClient(pt)
|
|
|
|
if pt.x < 100:
|
|
pt.x = 0
|
|
else:
|
|
pt.x -= 100
|
|
|
|
# find out the screen coordinate at the bottom of the tab ctrl
|
|
cli_rect = wnd.GetClientRect()
|
|
pt.y = cli_rect.y + cli_rect.height
|
|
|
|
cc = AuiCommandCapture()
|
|
wnd.PushEventHandler(cc)
|
|
wnd.PopupMenu(menuPopup, pt)
|
|
command = cc.GetCommandId()
|
|
wnd.PopEventHandler(True)
|
|
|
|
if command >= 1000:
|
|
return command-1000
|
|
|
|
return -1
|
|
|
|
|
|
def GetBestTabCtrlSize(self, wnd, pages, required_bmp_size):
|
|
"""
|
|
Returns the best tab control size.
|
|
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param list `pages`: the pages associated with the tabs;
|
|
:param Size `required_bmp_size`: the size of the bitmap on the tabs.
|
|
"""
|
|
|
|
dc = wx.ClientDC(wnd)
|
|
dc.SetFont(self._measuring_font)
|
|
s, x_extent = self.GetTabSize(dc, wnd, "ABCDEFGHIj", wx.NullBitmap, True,
|
|
AUI_BUTTON_STATE_HIDDEN, None)
|
|
|
|
max_y = s[1]
|
|
|
|
for page in pages:
|
|
if page.control:
|
|
controlW, controlH = page.control.GetSize()
|
|
max_y = max(max_y, controlH+4)
|
|
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(page.caption)
|
|
max_y = max(max_y, texty)
|
|
|
|
return max_y + 3
|
|
|
|
|
|
def SetNormalFont(self, font):
|
|
"""
|
|
Sets the normal font for drawing tab labels.
|
|
|
|
:param Font `font`: the new font to use to draw tab labels in their normal, un-selected state.
|
|
"""
|
|
|
|
self._normal_font = font
|
|
|
|
|
|
def SetSelectedFont(self, font):
|
|
"""
|
|
Sets the selected tab font for drawing tab labels.
|
|
|
|
:param Font `font`: the new font to use to draw tab labels in their selected state.
|
|
"""
|
|
|
|
self._selected_font = font
|
|
|
|
|
|
def SetMeasuringFont(self, font):
|
|
"""
|
|
Sets the font for calculating text measurements.
|
|
|
|
:param Font `font`: the new font to use to measure tab labels text extents.
|
|
"""
|
|
|
|
self._measuring_font = font
|
|
|
|
|
|
def GetNormalFont(self):
|
|
""" Returns the normal font for drawing tab labels. """
|
|
|
|
return self._normal_font
|
|
|
|
|
|
def GetSelectedFont(self):
|
|
""" Returns the selected tab font for drawing tab labels. """
|
|
|
|
return self._selected_font
|
|
|
|
|
|
def GetMeasuringFont(self):
|
|
""" Returns the font for calculating text measurements. """
|
|
|
|
return self._measuring_font
|
|
|
|
|
|
def SetCustomButton(self, bitmap_id, button_state, bmp):
|
|
"""
|
|
Sets a custom bitmap for the close, left, right and window list buttons.
|
|
|
|
:param integer `bitmap_id`: the button identifier;
|
|
:param integer `button_state`: the button state;
|
|
:param Bitmap `bmp`: the custom bitmap to use for the button.
|
|
"""
|
|
|
|
if bitmap_id == AUI_BUTTON_CLOSE:
|
|
if button_state == AUI_BUTTON_STATE_NORMAL:
|
|
self._active_close_bmp = bmp
|
|
self._hover_close_bmp = self._active_close_bmp
|
|
self._pressed_close_bmp = self._active_close_bmp
|
|
self._disabled_close_bmp = self._active_close_bmp
|
|
|
|
elif button_state == AUI_BUTTON_STATE_HOVER:
|
|
self._hover_close_bmp = bmp
|
|
elif button_state == AUI_BUTTON_STATE_PRESSED:
|
|
self._pressed_close_bmp = bmp
|
|
else:
|
|
self._disabled_close_bmp = bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_LEFT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
self._disabled_left_bmp = bmp
|
|
else:
|
|
self._active_left_bmp = bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_RIGHT:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
self._disabled_right_bmp = bmp
|
|
else:
|
|
self._active_right_bmp = bmp
|
|
|
|
elif bitmap_id == AUI_BUTTON_WINDOWLIST:
|
|
if button_state & AUI_BUTTON_STATE_DISABLED:
|
|
self._disabled_windowlist_bmp = bmp
|
|
else:
|
|
self._active_windowlist_bmp = bmp
|
|
|
|
|
|
class VC71TabArt(AuiDefaultTabArt):
|
|
""" A class to draw tabs using the Visual Studio 2003 (VC71) style. """
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
AuiDefaultTabArt.__init__(self)
|
|
|
|
|
|
def Clone(self):
|
|
""" Clones the art object. """
|
|
|
|
art = type(self)()
|
|
art.SetNormalFont(self.GetNormalFont())
|
|
art.SetSelectedFont(self.GetSelectedFont())
|
|
art.SetMeasuringFont(self.GetMeasuringFont())
|
|
|
|
art = CopyAttributes(art, self)
|
|
return art
|
|
|
|
|
|
def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
|
|
"""
|
|
Draws a single tab.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param `page`: the tab control page associated with the tab;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param bool `paint_control`: whether to draw the control inside a tab (if any) on a :class:`MemoryDC`.
|
|
"""
|
|
|
|
# Visual studio 7.1 style
|
|
# This code is based on the renderer included in FlatNotebook
|
|
|
|
# figure out the size of the tab
|
|
|
|
control = page.control
|
|
tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
|
|
close_button_state, control)
|
|
|
|
tab_height = self._tab_ctrl_height - 3
|
|
tab_width = tab_size[0]
|
|
tab_x = in_rect.x
|
|
tab_y = in_rect.y + in_rect.height - tab_height
|
|
clip_width = tab_width
|
|
|
|
if tab_x + clip_width > in_rect.x + in_rect.width - 4:
|
|
clip_width = (in_rect.x + in_rect.width) - tab_x - 4
|
|
|
|
dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
|
|
agwFlags = self.GetAGWFlags()
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
tab_y -= 1
|
|
|
|
dc.SetPen((page.active and [wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DHIGHLIGHT))] or \
|
|
[wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW))])[0])
|
|
dc.SetBrush((page.active and [wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))] or \
|
|
[wx.TRANSPARENT_BRUSH])[0])
|
|
|
|
if page.active:
|
|
|
|
tabH = tab_height - 2
|
|
dc.DrawRectangle(tab_x, tab_y, tab_width, tabH)
|
|
|
|
rightLineY1 = (agwFlags & AUI_NB_BOTTOM and [vertical_border_padding - 2] or \
|
|
[vertical_border_padding - 1])[0]
|
|
rightLineY2 = tabH + 3
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DSHADOW)))
|
|
dc.DrawLine(tab_x + tab_width - 1, rightLineY1 + 1, tab_x + tab_width - 1, rightLineY2)
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
dc.DrawLine(tab_x + 1, rightLineY2 - 3 , tab_x + tab_width - 1, rightLineY2 - 3)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DDKSHADOW)))
|
|
dc.DrawLine(tab_x + tab_width, rightLineY1, tab_x + tab_width, rightLineY2)
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
dc.DrawLine(tab_x, rightLineY2 - 2, tab_x + tab_width, rightLineY2 - 2)
|
|
|
|
else:
|
|
|
|
# We dont draw a rectangle for non selected tabs, but only
|
|
# vertical line on the right
|
|
blackLineY1 = (agwFlags & AUI_NB_BOTTOM and [vertical_border_padding + 2] or \
|
|
[vertical_border_padding + 1])[0]
|
|
blackLineY2 = tab_height - 5
|
|
dc.DrawLine(tab_x + tab_width, blackLineY1, tab_x + tab_width, blackLineY2)
|
|
|
|
border_points = [0, 0]
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
|
|
border_points[0] = wx.Point(tab_x, tab_y)
|
|
border_points[1] = wx.Point(tab_x, tab_y + tab_height - 6)
|
|
|
|
else: # if (agwFlags & AUI_NB_TOP)
|
|
|
|
border_points[0] = wx.Point(tab_x, tab_y + tab_height - 4)
|
|
border_points[1] = wx.Point(tab_x, tab_y + 2)
|
|
|
|
drawn_tab_yoff = border_points[1].y
|
|
drawn_tab_height = border_points[0].y - border_points[1].y
|
|
|
|
text_offset = tab_x + 8
|
|
close_button_width = 0
|
|
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
text_offset += close_button_width - 5
|
|
|
|
if not page.enabled:
|
|
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
|
|
pagebitmap = page.dis_bitmap
|
|
else:
|
|
dc.SetTextForeground(page.text_colour)
|
|
pagebitmap = page.bitmap
|
|
|
|
shift = 0
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
shift = (page.active and [1] or [2])[0]
|
|
|
|
bitmap_offset = 0
|
|
if pagebitmap.IsOk():
|
|
bitmap_offset = tab_x + 8
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
|
|
bitmap_offset += close_button_width - 5
|
|
|
|
# draw bitmap
|
|
dc.DrawBitmap(pagebitmap, bitmap_offset,
|
|
drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
|
|
True)
|
|
|
|
text_offset = bitmap_offset + pagebitmap.GetWidth()
|
|
text_offset += 3 # bitmap padding
|
|
|
|
else:
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
|
|
text_offset = tab_x + 8
|
|
|
|
# if the caption is empty, measure some temporary text
|
|
caption = page.caption
|
|
|
|
if caption == "":
|
|
caption = "Xj"
|
|
|
|
if page.active:
|
|
dc.SetFont(self._selected_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
else:
|
|
dc.SetFont(self._normal_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
|
|
|
|
ypos = drawn_tab_yoff + (drawn_tab_height)/2 - (texty/2) - 1 + shift
|
|
|
|
offset_focus = text_offset
|
|
|
|
if control:
|
|
if control.GetPosition() != wx.Point(text_offset+1, ypos):
|
|
control.SetPosition(wx.Point(text_offset+1, ypos))
|
|
|
|
if not control.IsShown():
|
|
control.Show()
|
|
|
|
if paint_control:
|
|
bmp = TakeScreenShot(control.GetScreenRect())
|
|
dc.DrawBitmap(bmp, text_offset+1, ypos, True)
|
|
|
|
controlW, controlH = control.GetSize()
|
|
text_offset += controlW + 4
|
|
textx += controlW + 4
|
|
|
|
# draw tab text
|
|
rectx, recty, dummy = dc.GetFullMultiLineTextExtent(draw_text)
|
|
dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
|
|
|
|
out_button_rect = wx.Rect()
|
|
|
|
# draw focus rectangle
|
|
if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
|
|
self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
|
|
drawn_tab_height+shift, rectx, recty)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
|
|
bmp = self._disabled_close_bmp
|
|
|
|
if close_button_state == AUI_BUTTON_STATE_HOVER:
|
|
bmp = self._hover_close_bmp
|
|
elif close_button_state == AUI_BUTTON_STATE_PRESSED:
|
|
bmp = self._pressed_close_bmp
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
rect = wx.Rect(tab_x + 4,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
|
|
close_button_width, tab_height)
|
|
else:
|
|
rect = wx.Rect(tab_x + tab_width - close_button_width - 3,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
|
|
close_button_width, tab_height)
|
|
|
|
# Indent the button if it is pressed down:
|
|
rect = IndentPressedBitmap(rect, close_button_state)
|
|
dc.DrawBitmap(bmp, rect.x, rect.y, True)
|
|
|
|
out_button_rect = rect
|
|
|
|
out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
|
|
dc.DestroyClippingRegion()
|
|
|
|
return out_tab_rect, out_button_rect, x_extent
|
|
|
|
|
|
class FF2TabArt(AuiDefaultTabArt):
|
|
""" A class to draw tabs using the Firefox 2 (FF2) style. """
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
AuiDefaultTabArt.__init__(self)
|
|
|
|
|
|
def Clone(self):
|
|
""" Clones the art object. """
|
|
|
|
art = type(self)()
|
|
art.SetNormalFont(self.GetNormalFont())
|
|
art.SetSelectedFont(self.GetSelectedFont())
|
|
art.SetMeasuringFont(self.GetMeasuringFont())
|
|
|
|
art = CopyAttributes(art, self)
|
|
return art
|
|
|
|
|
|
def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control):
|
|
"""
|
|
Returns the tab size for the given caption, bitmap and button state.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param string `caption`: the tab text caption;
|
|
:param Bitmap `bitmap`: the bitmap displayed on the tab;
|
|
:param bool `active`: whether the tab is selected or not;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param Window `control`: a :class:`Window` instance inside a tab (or ``None``).
|
|
"""
|
|
|
|
tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
|
|
active, close_button_state, control)
|
|
|
|
tab_width, tab_height = tab_size
|
|
|
|
# add some vertical padding
|
|
tab_height += 2
|
|
|
|
return (tab_width, tab_height), x_extent
|
|
|
|
|
|
def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
|
|
"""
|
|
Draws a single tab.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param `page`: the tab control page associated with the tab;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param bool `paint_control`: whether to draw the control inside a tab (if any) on a :class:`MemoryDC`.
|
|
"""
|
|
|
|
# Firefox 2 style
|
|
|
|
control = page.control
|
|
|
|
# figure out the size of the tab
|
|
tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
|
|
page.active, close_button_state, control)
|
|
|
|
tab_height = self._tab_ctrl_height - 2
|
|
tab_width = tab_size[0]
|
|
tab_x = in_rect.x
|
|
tab_y = in_rect.y + in_rect.height - tab_height
|
|
|
|
clip_width = tab_width
|
|
if tab_x + clip_width > in_rect.x + in_rect.width - 4:
|
|
clip_width = (in_rect.x + in_rect.width) - tab_x - 4
|
|
|
|
dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
|
|
|
|
tabPoints = [wx.Point() for i in range(7)]
|
|
|
|
adjust = 0
|
|
if not page.active:
|
|
adjust = 1
|
|
|
|
agwFlags = self.GetAGWFlags()
|
|
|
|
tabPoints[0].x = tab_x + 3
|
|
tabPoints[0].y = (agwFlags & AUI_NB_BOTTOM and [3] or [tab_height - 2])[0]
|
|
|
|
tabPoints[1].x = tabPoints[0].x
|
|
tabPoints[1].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - (vertical_border_padding + 2) - adjust] or \
|
|
[(vertical_border_padding + 2) + adjust])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x+2
|
|
tabPoints[2].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding - adjust] or \
|
|
[vertical_border_padding + adjust])[0]
|
|
|
|
tabPoints[3].x = tab_x + tab_width - 2
|
|
tabPoints[3].y = tabPoints[2].y
|
|
|
|
tabPoints[4].x = tabPoints[3].x + 2
|
|
tabPoints[4].y = tabPoints[1].y
|
|
|
|
tabPoints[5].x = tabPoints[4].x
|
|
tabPoints[5].y = tabPoints[0].y
|
|
|
|
tabPoints[6].x = tabPoints[0].x
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
rr = wx.Rect(tabPoints[2], tabPoints[5])
|
|
self.DrawTabBackground(dc, rr, page.active, (agwFlags & AUI_NB_BOTTOM) == 0)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
|
|
# Draw the tab as rounded rectangle
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
if page.active:
|
|
dc.DrawLine(tabPoints[0].x + 1, tabPoints[0].y, tabPoints[5].x , tabPoints[0].y)
|
|
|
|
drawn_tab_yoff = tabPoints[1].y
|
|
drawn_tab_height = tabPoints[0].y - tabPoints[2].y
|
|
|
|
text_offset = tab_x + 8
|
|
close_button_width = 0
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
text_offset += close_button_width - 4
|
|
|
|
if not page.enabled:
|
|
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
|
|
pagebitmap = page.dis_bitmap
|
|
else:
|
|
dc.SetTextForeground(page.text_colour)
|
|
pagebitmap = page.bitmap
|
|
|
|
shift = -1
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
shift = 2
|
|
|
|
bitmap_offset = 0
|
|
if pagebitmap.IsOk():
|
|
bitmap_offset = tab_x + 8
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
|
|
bitmap_offset += close_button_width - 4
|
|
|
|
# draw bitmap
|
|
dc.DrawBitmap(pagebitmap, bitmap_offset,
|
|
drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
|
|
True)
|
|
|
|
text_offset = bitmap_offset + pagebitmap.GetWidth()
|
|
text_offset += 3 # bitmap padding
|
|
|
|
else:
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
|
|
text_offset = tab_x + 8
|
|
|
|
# if the caption is empty, measure some temporary text
|
|
caption = page.caption
|
|
if caption == "":
|
|
caption = "Xj"
|
|
|
|
if page.active:
|
|
dc.SetFont(self._selected_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
else:
|
|
dc.SetFont(self._normal_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width + 1)
|
|
else:
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
|
|
|
|
ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1 + shift
|
|
|
|
offset_focus = text_offset
|
|
|
|
if control:
|
|
if control.GetPosition() != wx.Point(text_offset+1, ypos):
|
|
control.SetPosition(wx.Point(text_offset+1, ypos))
|
|
|
|
if not control.IsShown():
|
|
control.Show()
|
|
|
|
if paint_control:
|
|
bmp = TakeScreenShot(control.GetScreenRect())
|
|
dc.DrawBitmap(bmp, text_offset+1, ypos, True)
|
|
|
|
controlW, controlH = control.GetSize()
|
|
text_offset += controlW + 4
|
|
textx += controlW + 4
|
|
|
|
# draw tab text
|
|
rectx, recty, dummy = dc.GetFullMultiLineTextExtent(draw_text)
|
|
dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
|
|
|
|
# draw focus rectangle
|
|
if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
|
|
self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
|
|
drawn_tab_height, rectx, recty)
|
|
|
|
out_button_rect = wx.Rect()
|
|
# draw 'x' on tab (if enabled)
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
bmp = self._disabled_close_bmp
|
|
|
|
if close_button_state == AUI_BUTTON_STATE_HOVER:
|
|
bmp = self._hover_close_bmp
|
|
elif close_button_state == AUI_BUTTON_STATE_PRESSED:
|
|
bmp = self._pressed_close_bmp
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
rect = wx.Rect(tab_x + 5,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
|
|
close_button_width, tab_height)
|
|
else:
|
|
rect = wx.Rect(tab_x + tab_width - close_button_width - 3,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
|
|
close_button_width, tab_height)
|
|
|
|
# Indent the button if it is pressed down:
|
|
rect = IndentPressedBitmap(rect, close_button_state)
|
|
dc.DrawBitmap(bmp, rect.x, rect.y, True)
|
|
out_button_rect = rect
|
|
|
|
out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
|
|
dc.DestroyClippingRegion()
|
|
|
|
return out_tab_rect, out_button_rect, x_extent
|
|
|
|
|
|
def DrawTabBackground(self, dc, rect, focus, upperTabs):
|
|
"""
|
|
Draws the tab background for the Firefox 2 style.
|
|
This is more consistent with :class:`~lib.agw.flatnotebook.FlatNotebook` than before.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param Rect `rect`: rectangle the tab should be confined to;
|
|
:param bool `focus`: whether the tab has focus or not;
|
|
:param bool `upperTabs`: whether the style is ``AUI_NB_TOP`` or ``AUI_NB_BOTTOM``.
|
|
"""
|
|
|
|
# Define the rounded rectangle base on the given rect
|
|
# we need an array of 9 points for it
|
|
regPts = [wx.Point() for indx in range(9)]
|
|
|
|
if focus:
|
|
if upperTabs:
|
|
leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*8)
|
|
rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*8)
|
|
else:
|
|
leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*5)
|
|
rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*5)
|
|
else:
|
|
leftPt = wx.Point(rect.x, rect.y + (rect.height / 2))
|
|
rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 2))
|
|
|
|
# Define the top region
|
|
top = wx.Rect(rect.GetTopLeft(), rightPt)
|
|
bottom = wx.Rect(leftPt, rect.GetBottomRight())
|
|
|
|
topStartColour = wx.WHITE
|
|
|
|
if not focus:
|
|
topStartColour = LightColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE), 50)
|
|
|
|
topEndColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
|
|
bottomStartColour = topEndColour
|
|
bottomEndColour = topEndColour
|
|
|
|
# Incase we use bottom tabs, switch the colours
|
|
if upperTabs:
|
|
if focus:
|
|
dc.GradientFillLinear(top, topStartColour, topEndColour, wx.SOUTH)
|
|
dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
|
|
else:
|
|
dc.GradientFillLinear(top, topEndColour , topStartColour, wx.SOUTH)
|
|
dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
|
|
|
|
else:
|
|
if focus:
|
|
dc.GradientFillLinear(bottom, topEndColour, bottomEndColour, wx.SOUTH)
|
|
dc.GradientFillLinear(top, topStartColour, topStartColour, wx.SOUTH)
|
|
else:
|
|
dc.GradientFillLinear(bottom, bottomStartColour, bottomEndColour, wx.SOUTH)
|
|
dc.GradientFillLinear(top, topEndColour, topStartColour, wx.SOUTH)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
|
|
|
|
class VC8TabArt(AuiDefaultTabArt):
|
|
""" A class to draw tabs using the Visual Studio 2005 (VC8) style. """
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
AuiDefaultTabArt.__init__(self)
|
|
|
|
|
|
def Clone(self):
|
|
""" Clones the art object. """
|
|
|
|
art = type(self)()
|
|
art.SetNormalFont(self.GetNormalFont())
|
|
art.SetSelectedFont(self.GetSelectedFont())
|
|
art.SetMeasuringFont(self.GetMeasuringFont())
|
|
|
|
art = CopyAttributes(art, self)
|
|
return art
|
|
|
|
|
|
def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
|
|
"""
|
|
Sets the tab sizing information.
|
|
|
|
:param Size `tab_ctrl_size`: the size of the tab control area;
|
|
:param integer `tab_count`: the number of tabs;
|
|
:param tuple `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
|
|
to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
|
|
"""
|
|
|
|
AuiDefaultTabArt.SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth)
|
|
|
|
minTabWidth, maxTabWidth = minMaxTabWidth
|
|
if minTabWidth > -1:
|
|
self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
|
|
if maxTabWidth > -1:
|
|
self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
|
|
|
|
self._fixed_tab_width -= 5
|
|
|
|
|
|
def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
|
|
"""
|
|
Returns the tab size for the given caption, bitmap and button state.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param string `caption`: the tab text caption;
|
|
:param Bitmap `bitmap`: the bitmap displayed on the tab;
|
|
:param bool `active`: whether the tab is selected or not;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param Window `control`: a :class:`Window` instance inside a tab (or ``None``).
|
|
"""
|
|
|
|
tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
|
|
active, close_button_state, control)
|
|
|
|
tab_width, tab_height = tab_size
|
|
|
|
# add some padding
|
|
tab_width += 10
|
|
|
|
if not bitmap.IsOk():
|
|
tab_width += 5
|
|
|
|
tab_height += 2
|
|
|
|
return (tab_width, tab_height), x_extent
|
|
|
|
|
|
def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
|
|
"""
|
|
Draws a single tab.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param `page`: the tab control page associated with the tab;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param bool `paint_control`: whether to draw the control inside a tab (if any) on a :class:`MemoryDC`.
|
|
"""
|
|
|
|
# Visual Studio 8 style
|
|
|
|
control = page.control
|
|
|
|
# figure out the size of the tab
|
|
tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap,
|
|
page.active, close_button_state, control)
|
|
|
|
tab_height = self._tab_ctrl_height - 1
|
|
tab_width = tab_size[0]
|
|
tab_x = in_rect.x
|
|
tab_y = in_rect.y + in_rect.height - tab_height
|
|
|
|
clip_width = tab_width + 3
|
|
if tab_x + clip_width > in_rect.x + in_rect.width - 4:
|
|
clip_width = (in_rect.x + in_rect.width) - tab_x - 4
|
|
|
|
tabPoints = [wx.Point() for i in range(8)]
|
|
|
|
# If we draw the first tab or the active tab,
|
|
# we draw a full tab, else we draw a truncated tab
|
|
#
|
|
# X(2) X(3)
|
|
# X(1) X(4)
|
|
#
|
|
# X(5)
|
|
#
|
|
# X(0),(7) X(6)
|
|
#
|
|
#
|
|
|
|
adjust = 0
|
|
if not page.active:
|
|
adjust = 1
|
|
|
|
agwFlags = self.GetAGWFlags()
|
|
tabPoints[0].x = (agwFlags & AUI_NB_BOTTOM and [tab_x] or [tab_x + adjust])[0]
|
|
tabPoints[0].y = (agwFlags & AUI_NB_BOTTOM and [2] or [tab_height - 3])[0]
|
|
|
|
tabPoints[1].x = tabPoints[0].x + tab_height - vertical_border_padding - 3 - adjust
|
|
tabPoints[1].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - (vertical_border_padding+2)] or \
|
|
[(vertical_border_padding+2)])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x + 4
|
|
tabPoints[2].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding] or \
|
|
[vertical_border_padding])[0]
|
|
|
|
tabPoints[3].x = tabPoints[2].x + tab_width - tab_height + vertical_border_padding
|
|
tabPoints[3].y = (agwFlags & AUI_NB_BOTTOM and [tab_height - vertical_border_padding] or \
|
|
[vertical_border_padding])[0]
|
|
|
|
tabPoints[4].x = tabPoints[3].x + 1
|
|
tabPoints[4].y = (agwFlags & AUI_NB_BOTTOM and [tabPoints[3].y - 1] or [tabPoints[3].y + 1])[0]
|
|
|
|
tabPoints[5].x = tabPoints[4].x + 1
|
|
tabPoints[5].y = (agwFlags & AUI_NB_BOTTOM and [(tabPoints[4].y - 1)] or [tabPoints[4].y + 1])[0]
|
|
|
|
tabPoints[6].x = tabPoints[2].x + tab_width - tab_height + 2 + vertical_border_padding
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
tabPoints[7].x = tabPoints[0].x
|
|
tabPoints[7].y = tabPoints[0].y
|
|
|
|
self.FillVC8GradientColour(dc, tabPoints, page.active)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
if page.active:
|
|
# Delete the bottom line (or the upper one, incase we use wxBOTTOM)
|
|
dc.SetPen(wx.WHITE_PEN)
|
|
dc.DrawLine(tabPoints[0].x, tabPoints[0].y, tabPoints[6].x, tabPoints[6].y)
|
|
|
|
dc.SetClippingRegion(tab_x, tab_y, clip_width + 2, tab_height - 3)
|
|
|
|
drawn_tab_yoff = tabPoints[1].y
|
|
drawn_tab_height = tabPoints[0].y - tabPoints[2].y
|
|
|
|
text_offset = tab_x + 20
|
|
close_button_width = 0
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
text_offset += close_button_width
|
|
|
|
if not page.enabled:
|
|
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
|
|
pagebitmap = page.dis_bitmap
|
|
else:
|
|
dc.SetTextForeground(page.text_colour)
|
|
pagebitmap = page.bitmap
|
|
|
|
shift = 0
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
shift = (page.active and [1] or [2])[0]
|
|
|
|
bitmap_offset = 0
|
|
if pagebitmap.IsOk():
|
|
bitmap_offset = tab_x + 20
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
|
|
bitmap_offset += close_button_width
|
|
|
|
# draw bitmap
|
|
dc.DrawBitmap(pagebitmap, bitmap_offset,
|
|
drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2) + shift,
|
|
True)
|
|
|
|
text_offset = bitmap_offset + pagebitmap.GetWidth()
|
|
text_offset += 3 # bitmap padding
|
|
|
|
else:
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
|
|
text_offset = tab_x + tab_height
|
|
|
|
# if the caption is empty, measure some temporary text
|
|
caption = page.caption
|
|
if caption == "":
|
|
caption = "Xj"
|
|
|
|
if page.active:
|
|
dc.SetFont(self._selected_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
else:
|
|
dc.SetFont(self._normal_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x))
|
|
else:
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width)
|
|
|
|
ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1 + shift
|
|
|
|
offset_focus = text_offset
|
|
|
|
if control:
|
|
if control.GetPosition() != wx.Point(text_offset+1, ypos):
|
|
control.SetPosition(wx.Point(text_offset+1, ypos))
|
|
|
|
if not control.IsShown():
|
|
control.Show()
|
|
|
|
if paint_control:
|
|
bmp = TakeScreenShot(control.GetScreenRect())
|
|
dc.DrawBitmap(bmp, text_offset+1, ypos, True)
|
|
|
|
controlW, controlH = control.GetSize()
|
|
text_offset += controlW + 4
|
|
textx += controlW + 4
|
|
|
|
# draw tab text
|
|
rectx, recty, dummy = dc.GetFullMultiLineTextExtent(draw_text)
|
|
dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
|
|
|
|
# draw focus rectangle
|
|
if (agwFlags & AUI_NB_NO_TAB_FOCUS) == 0:
|
|
self.DrawFocusRectangle(dc, page, wnd, draw_text, offset_focus, bitmap_offset, drawn_tab_yoff+shift,
|
|
drawn_tab_height+shift, rectx, recty)
|
|
|
|
out_button_rect = wx.Rect()
|
|
# draw 'x' on tab (if enabled)
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
bmp = self._disabled_close_bmp
|
|
|
|
if close_button_state == AUI_BUTTON_STATE_HOVER:
|
|
bmp = self._hover_close_bmp
|
|
elif close_button_state == AUI_BUTTON_STATE_PRESSED:
|
|
bmp = self._pressed_close_bmp
|
|
|
|
if page.active:
|
|
xpos = tab_x + tab_width - close_button_width + 3
|
|
else:
|
|
xpos = tab_x + tab_width - close_button_width - 5
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
rect = wx.Rect(tab_x + 20,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
|
|
close_button_width, tab_height)
|
|
else:
|
|
rect = wx.Rect(xpos,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + shift,
|
|
close_button_width, tab_height)
|
|
|
|
# Indent the button if it is pressed down:
|
|
rect = IndentPressedBitmap(rect, close_button_state)
|
|
dc.DrawBitmap(bmp, rect.x, rect.y, True)
|
|
out_button_rect = rect
|
|
|
|
out_tab_rect = wx.Rect(tab_x, tab_y, x_extent, tab_height)
|
|
dc.DestroyClippingRegion()
|
|
|
|
return out_tab_rect, out_button_rect, x_extent
|
|
|
|
|
|
def FillVC8GradientColour(self, dc, tabPoints, active):
|
|
"""
|
|
Fills the tab with the Visual Studio 2005 gradient background.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param list `tabPoints`: a list of :class:`Point` objects describing the tab shape;
|
|
:param bool `active`: whether the tab is selected or not.
|
|
"""
|
|
|
|
xList = [pt.x for pt in tabPoints]
|
|
yList = [pt.y for pt in tabPoints]
|
|
|
|
minx, maxx = min(xList), max(xList)
|
|
miny, maxy = min(yList), max(yList)
|
|
|
|
rect = wx.Rect(minx, maxy, maxx-minx, miny-maxy+1)
|
|
region = wx.Region(tabPoints)
|
|
|
|
if self._buttonRect.width > 0:
|
|
buttonRegion = wx.Region(*self._buttonRect)
|
|
region.Xor(buttonRegion)
|
|
|
|
dc.SetDeviceClippingRegion(region)
|
|
|
|
if active:
|
|
bottom_colour = top_colour = wx.WHITE
|
|
else:
|
|
bottom_colour = StepColour(self._base_colour, 90)
|
|
top_colour = StepColour(self._base_colour, 170)
|
|
|
|
dc.GradientFillLinear(rect, top_colour, bottom_colour, wx.SOUTH)
|
|
dc.DestroyClippingRegion()
|
|
|
|
|
|
class ChromeTabArt(AuiDefaultTabArt):
|
|
"""
|
|
A class to draw tabs using the Google Chrome browser style.
|
|
It uses custom bitmap to render the tabs, so that the look and feel is as close
|
|
as possible to the Chrome style.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
AuiDefaultTabArt.__init__(self)
|
|
|
|
self.SetBitmaps(mirror=False)
|
|
|
|
closeBmp = tab_close.GetBitmap()
|
|
closeHBmp = tab_close_h.GetBitmap()
|
|
closePBmp = tab_close_p.GetBitmap()
|
|
|
|
self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_NORMAL, closeBmp)
|
|
self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_HOVER, closeHBmp)
|
|
self.SetCustomButton(AUI_BUTTON_CLOSE, AUI_BUTTON_STATE_PRESSED, closePBmp)
|
|
|
|
|
|
def SetAGWFlags(self, agwFlags):
|
|
"""
|
|
Sets the tab art flags.
|
|
|
|
:param integer `agwFlags`: a combination of the following values:
|
|
|
|
==================================== ==================================
|
|
Flag name Description
|
|
==================================== ==================================
|
|
``AUI_NB_TOP`` With this style, tabs are drawn along the top of the notebook
|
|
``AUI_NB_LEFT`` With this style, tabs are drawn along the left of the notebook. Not implemented yet.
|
|
``AUI_NB_RIGHT`` With this style, tabs are drawn along the right of the notebook. Not implemented yet.
|
|
``AUI_NB_BOTTOM`` With this style, tabs are drawn along the bottom of the notebook
|
|
``AUI_NB_TAB_SPLIT`` Allows the tab control to be split by dragging a tab
|
|
``AUI_NB_TAB_MOVE`` Allows a tab to be moved horizontally by dragging
|
|
``AUI_NB_TAB_EXTERNAL_MOVE`` Allows a tab to be moved to another tab control
|
|
``AUI_NB_TAB_FIXED_WIDTH`` With this style, all tabs have the same width
|
|
``AUI_NB_SCROLL_BUTTONS`` With this style, left and right scroll buttons are displayed
|
|
``AUI_NB_WINDOWLIST_BUTTON`` With this style, a drop-down list of windows is available
|
|
``AUI_NB_CLOSE_BUTTON`` With this style, a close button is available on the tab bar
|
|
``AUI_NB_CLOSE_ON_ACTIVE_TAB`` With this style, a close button is available on the active tab
|
|
``AUI_NB_CLOSE_ON_ALL_TABS`` With this style, a close button is available on all tabs
|
|
``AUI_NB_MIDDLE_CLICK_CLOSE`` Allows to close :class:`~lib.agw.aui.auibook.AuiNotebook` tabs by mouse middle button click
|
|
``AUI_NB_SUB_NOTEBOOK`` This style is used by :class:`~lib.agw.aui.framemanager.AuiManager` to create automatic AuiNotebooks
|
|
``AUI_NB_HIDE_ON_SINGLE_TAB`` Hides the tab window if only one tab is present
|
|
``AUI_NB_SMART_TABS`` Use Smart Tabbing, like ``Alt`` + ``Tab`` on Windows
|
|
``AUI_NB_USE_IMAGES_DROPDOWN`` Uses images on dropdown window list menu instead of check items
|
|
``AUI_NB_CLOSE_ON_TAB_LEFT`` Draws the tab close button on the left instead of on the right (a la Camino browser)
|
|
``AUI_NB_TAB_FLOAT`` Allows the floating of single tabs. Known limitation: when the notebook is more or less full
|
|
screen, tabs cannot be dragged far enough outside of the notebook to become floating pages
|
|
``AUI_NB_DRAW_DND_TAB`` Draws an image representation of a tab while dragging (on by default)
|
|
``AUI_NB_ORDER_BY_ACCESS`` Tab navigation order by last access time for the tabs
|
|
``AUI_NB_NO_TAB_FOCUS`` Don't draw tab focus rectangle
|
|
==================================== ==================================
|
|
|
|
:note: Overridden from :class:`AuiDefaultTabArt`.
|
|
"""
|
|
|
|
if agwFlags & AUI_NB_TOP:
|
|
self.SetBitmaps(mirror=False)
|
|
elif agwFlags & AUI_NB_BOTTOM:
|
|
self.SetBitmaps(mirror=True)
|
|
|
|
AuiDefaultTabArt.SetAGWFlags(self, agwFlags)
|
|
|
|
|
|
def SetBitmaps(self, mirror):
|
|
"""
|
|
Assigns the tab custom bitmaps
|
|
|
|
:param bool `mirror`: whether to vertically mirror the bitmap or not.
|
|
"""
|
|
|
|
bmps = [tab_active_left.GetBitmap(), tab_active_center.GetBitmap(),
|
|
tab_active_right.GetBitmap(), tab_inactive_left.GetBitmap(),
|
|
tab_inactive_center.GetBitmap(), tab_inactive_right.GetBitmap()]
|
|
|
|
if mirror:
|
|
for indx, bmp in enumerate(bmps):
|
|
img = bmp.ConvertToImage()
|
|
img = img.Mirror(horizontally=False)
|
|
bmps[indx] = img.ConvertToBitmap()
|
|
|
|
self._leftActiveBmp = bmps[0]
|
|
self._centerActiveBmp = bmps[1]
|
|
self._rightActiveBmp = bmps[2]
|
|
self._leftInactiveBmp = bmps[3]
|
|
self._centerInactiveBmp = bmps[4]
|
|
self._rightInactiveBmp = bmps[5]
|
|
|
|
|
|
def Clone(self):
|
|
""" Clones the art object. """
|
|
|
|
art = type(self)()
|
|
art.SetNormalFont(self.GetNormalFont())
|
|
art.SetSelectedFont(self.GetSelectedFont())
|
|
art.SetMeasuringFont(self.GetMeasuringFont())
|
|
|
|
art = CopyAttributes(art, self)
|
|
return art
|
|
|
|
|
|
def SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth):
|
|
"""
|
|
Sets the tab sizing information.
|
|
|
|
:param Size `tab_ctrl_size`: the size of the tab control area;
|
|
:param integer `tab_count`: the number of tabs;
|
|
:param tuple `minMaxTabWidth`: a tuple containing the minimum and maximum tab widths
|
|
to be used when the ``AUI_NB_TAB_FIXED_WIDTH`` style is active.
|
|
"""
|
|
|
|
AuiDefaultTabArt.SetSizingInfo(self, tab_ctrl_size, tab_count, minMaxTabWidth)
|
|
|
|
minTabWidth, maxTabWidth = minMaxTabWidth
|
|
if minTabWidth > -1:
|
|
self._fixed_tab_width = max(self._fixed_tab_width, minTabWidth)
|
|
if maxTabWidth > -1:
|
|
self._fixed_tab_width = min(self._fixed_tab_width, maxTabWidth)
|
|
|
|
self._fixed_tab_width -= 5
|
|
|
|
|
|
def GetTabSize(self, dc, wnd, caption, bitmap, active, close_button_state, control=None):
|
|
"""
|
|
Returns the tab size for the given caption, bitmap and button state.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param string `caption`: the tab text caption;
|
|
:param Bitmap `bitmap`: the bitmap displayed on the tab;
|
|
:param bool `active`: whether the tab is selected or not;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param Window `control`: a :class:`Window` instance inside a tab (or ``None``).
|
|
"""
|
|
|
|
tab_size, x_extent = AuiDefaultTabArt.GetTabSize(self, dc, wnd, caption, bitmap,
|
|
active, close_button_state, control)
|
|
|
|
tab_width, tab_height = tab_size
|
|
|
|
# add some padding
|
|
tab_width += self._leftActiveBmp.GetWidth()
|
|
tab_height += 2
|
|
|
|
tab_height = max(tab_height, self._centerActiveBmp.GetHeight())
|
|
|
|
return (tab_width, tab_height), x_extent
|
|
|
|
|
|
def DrawTab(self, dc, wnd, page, in_rect, close_button_state, paint_control=False):
|
|
"""
|
|
Draws a single tab.
|
|
|
|
:param `dc`: a :class:`DC` device context;
|
|
:param `wnd`: a :class:`Window` instance object;
|
|
:param `page`: the tab control page associated with the tab;
|
|
:param Rect `in_rect`: rectangle the tab should be confined to;
|
|
:param integer `close_button_state`: the state of the close button on the tab;
|
|
:param bool `paint_control`: whether to draw the control inside a tab (if any) on a :class:`MemoryDC`.
|
|
"""
|
|
|
|
# Chrome tab style
|
|
|
|
control = page.control
|
|
# figure out the size of the tab
|
|
tab_size, x_extent = self.GetTabSize(dc, wnd, page.caption, page.bitmap, page.active,
|
|
close_button_state, control)
|
|
|
|
agwFlags = self.GetAGWFlags()
|
|
|
|
tab_height = self._tab_ctrl_height - 1
|
|
tab_width = tab_size[0]
|
|
tab_x = in_rect.x
|
|
tab_y = in_rect.y + in_rect.height - tab_height
|
|
clip_width = tab_width
|
|
|
|
if tab_x + clip_width > in_rect.x + in_rect.width - 4:
|
|
clip_width = (in_rect.x + in_rect.width) - tab_x - 4
|
|
|
|
dc.SetClippingRegion(tab_x, tab_y, clip_width + 1, tab_height - 3)
|
|
drawn_tab_yoff = 1
|
|
|
|
if page.active:
|
|
left = self._leftActiveBmp
|
|
center = self._centerActiveBmp
|
|
right = self._rightActiveBmp
|
|
else:
|
|
left = self._leftInactiveBmp
|
|
center = self._centerInactiveBmp
|
|
right = self._rightInactiveBmp
|
|
|
|
dc.DrawBitmap(left, tab_x, tab_y)
|
|
leftw = left.GetWidth()
|
|
centerw = center.GetWidth()
|
|
rightw = right.GetWidth()
|
|
|
|
available = tab_x + tab_width - rightw
|
|
posx = tab_x + leftw
|
|
|
|
while 1:
|
|
if posx >= available:
|
|
break
|
|
dc.DrawBitmap(center, posx, tab_y)
|
|
posx += centerw
|
|
|
|
dc.DrawBitmap(right, posx, tab_y)
|
|
|
|
drawn_tab_height = center.GetHeight()
|
|
text_offset = tab_x + leftw
|
|
|
|
close_button_width = 0
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
text_offset += close_button_width
|
|
|
|
if not page.enabled:
|
|
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
|
|
pagebitmap = page.dis_bitmap
|
|
else:
|
|
dc.SetTextForeground(page.text_colour)
|
|
pagebitmap = page.bitmap
|
|
|
|
bitmap_offset = 0
|
|
if pagebitmap.IsOk():
|
|
bitmap_offset = tab_x + leftw
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT and close_button_width:
|
|
bitmap_offset += close_button_width
|
|
|
|
# draw bitmap
|
|
dc.DrawBitmap(pagebitmap, bitmap_offset,
|
|
drawn_tab_yoff + (drawn_tab_height/2) - (pagebitmap.GetHeight()/2),
|
|
True)
|
|
|
|
text_offset = bitmap_offset + pagebitmap.GetWidth()
|
|
text_offset += 3 # bitmap padding
|
|
|
|
else:
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT == 0 or not close_button_width:
|
|
text_offset = tab_x + leftw
|
|
|
|
# if the caption is empty, measure some temporary text
|
|
caption = page.caption
|
|
if caption == "":
|
|
caption = "Xj"
|
|
|
|
if page.active:
|
|
dc.SetFont(self._selected_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
else:
|
|
dc.SetFont(self._normal_font)
|
|
textx, texty, dummy = dc.GetFullMultiLineTextExtent(caption)
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - leftw)
|
|
else:
|
|
draw_text = ChopText(dc, caption, tab_width - (text_offset-tab_x) - close_button_width - leftw)
|
|
|
|
ypos = drawn_tab_yoff + drawn_tab_height/2 - texty/2 - 1
|
|
|
|
if control:
|
|
if control.GetPosition() != wx.Point(text_offset+1, ypos):
|
|
control.SetPosition(wx.Point(text_offset+1, ypos))
|
|
|
|
if not control.IsShown():
|
|
control.Show()
|
|
|
|
if paint_control:
|
|
bmp = TakeScreenShot(control.GetScreenRect())
|
|
dc.DrawBitmap(bmp, text_offset+1, ypos, True)
|
|
|
|
controlW, controlH = control.GetSize()
|
|
text_offset += controlW + 4
|
|
|
|
# draw tab text
|
|
rectx, recty, dummy = dc.GetFullMultiLineTextExtent(draw_text)
|
|
dc.DrawLabel(draw_text, wx.Rect(text_offset, ypos, rectx, recty))
|
|
|
|
out_button_rect = wx.Rect()
|
|
# draw 'x' on tab (if enabled)
|
|
if close_button_state != AUI_BUTTON_STATE_HIDDEN:
|
|
|
|
close_button_width = self._active_close_bmp.GetWidth()
|
|
bmp = self._disabled_close_bmp
|
|
|
|
if close_button_state == AUI_BUTTON_STATE_HOVER:
|
|
bmp = self._hover_close_bmp
|
|
elif close_button_state == AUI_BUTTON_STATE_PRESSED:
|
|
bmp = self._pressed_close_bmp
|
|
|
|
if agwFlags & AUI_NB_CLOSE_ON_TAB_LEFT:
|
|
rect = wx.Rect(tab_x + leftw - 2,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + 1,
|
|
close_button_width, tab_height)
|
|
else:
|
|
rect = wx.Rect(tab_x + tab_width - close_button_width - rightw + 2,
|
|
drawn_tab_yoff + (drawn_tab_height / 2) - (bmp.GetHeight() / 2) + 1,
|
|
close_button_width, tab_height)
|
|
|
|
if agwFlags & AUI_NB_BOTTOM:
|
|
rect.y -= 1
|
|
|
|
# Indent the button if it is pressed down:
|
|
rect = IndentPressedBitmap(rect, close_button_state)
|
|
dc.DrawBitmap(bmp, rect.x, rect.y, True)
|
|
out_button_rect = rect
|
|
|
|
out_tab_rect = wx.Rect(tab_x, tab_y, tab_width, tab_height)
|
|
dc.DestroyClippingRegion()
|
|
|
|
return out_tab_rect, out_button_rect, x_extent
|
|
|
|
|