mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-27 21:52:25 -06:00
6675 lines
215 KiB
Python
6675 lines
215 KiB
Python
# --------------------------------------------------------------------------- #
|
|
# FLATNOTEBOOK Widget wxPython IMPLEMENTATION
|
|
#
|
|
# Original C++ Code From Eran. You Can Find It At:
|
|
#
|
|
# http://wxforum.shadonet.com/viewtopic.php?t=5761&start=0
|
|
#
|
|
# License: wxWidgets license
|
|
#
|
|
#
|
|
# Python Code By:
|
|
#
|
|
# Andrea Gavana, @ 02 Oct 2006
|
|
# Latest Revision: 27 Dec 2012, 21.00 GMT
|
|
#
|
|
#
|
|
# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
|
|
# Write To Me At:
|
|
#
|
|
# andrea.gavana@gmail.com
|
|
# andrea.gavana@maerskoil.com
|
|
#
|
|
# Or, Obviously, To The wxPython Mailing List!!!
|
|
#
|
|
# Tags: phoenix-port, unittest, documented, py3-port
|
|
#
|
|
# End Of Comments
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
"""
|
|
:class:`FlatNotebook` is a full, generic and owner-drawn implementation of :class:`Notebook`.
|
|
|
|
|
|
Description
|
|
===========
|
|
|
|
:class:`FlatNotebook` is a full implementation of the :class:`Notebook`, and designed to be
|
|
a drop-in replacement for :class:`Notebook`. The API functions are similar so one can
|
|
expect the function to behave in the same way.
|
|
|
|
Some features:
|
|
|
|
- The buttons are highlighted a la Firefox style;
|
|
- The scrolling is done for bulks of tabs (so, the scrolling is faster and better);
|
|
- The buttons area is never overdrawn by tabs (unlike many other implementations I saw);
|
|
- It is a generic control;
|
|
- Currently there are 6 different styles - VC8, VC 71, Standard, Fancy, Firefox 2 and Ribbon;
|
|
- Mouse middle click can be used to close tabs;
|
|
- A function to add right click menu for tabs (simple as :meth:`~FlatNotebook.SetRightClickMenu`);
|
|
- All styles has bottom style as well (they can be drawn in the bottom of screen);
|
|
- An option to hide 'X' button or navigation buttons (separately);
|
|
- Gradient colouring of the selected tabs and border;
|
|
- Support for drag 'n' drop of tabs, both in the same notebook or to another notebook;
|
|
- Possibility to have closing button on the active tab directly;
|
|
- Support for disabled tabs;
|
|
- Colours for active/inactive tabs, and captions;
|
|
- Background of tab area can be painted in gradient (VC8 style only);
|
|
- Colourful tabs - a random gentle colour is generated for each new tab (very cool, VC8 style only);
|
|
- Support for showing pages in "column/row mode", which means that all the pages will be
|
|
shown in "tile" mode while the tabs are hidden;
|
|
- Possibility to add a custom panel to show a logo or HTML documentation or
|
|
whatever you like when there are no pages left in :class:`FlatNotebook`;
|
|
- Try setting the tab area colour for the Ribbon Style.
|
|
|
|
|
|
And much more.
|
|
|
|
|
|
Usage
|
|
=====
|
|
|
|
Usage example::
|
|
|
|
import wx
|
|
import wx.lib.agw.flatnotebook as fnb
|
|
|
|
class MyFrame(wx.Frame):
|
|
|
|
def __init__(self, parent):
|
|
|
|
wx.Frame.__init(self, parent, -1, "FlatNotebook Demo")
|
|
|
|
panel = wx.Panel(self)
|
|
|
|
notebook = fnb.FlatNotebook(panel, -1)
|
|
|
|
for i in range(3):
|
|
caption = "Page %d"%(i+1)
|
|
notebook.AddPage(self.CreatePage(notebook, caption), caption)
|
|
|
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
sizer.Add(notebook, 1, wx.ALL | wx.EXPAND, 5)
|
|
panel.SetSizer(sizer)
|
|
|
|
|
|
def CreatePage(self, notebook, caption):
|
|
'''
|
|
Creates a simple :class:`Panel` containing a :class:`TextCtrl`.
|
|
|
|
:param `notebook`: an instance of `FlatNotebook`;
|
|
:param `caption`: a simple label.
|
|
'''
|
|
|
|
p = wx.Panel(notebook)
|
|
wx.StaticText(p, -1, caption, (20,20))
|
|
wx.TextCtrl(p, -1, "", (20,40), (150,-1))
|
|
return p
|
|
|
|
|
|
# our normal wxApp-derived class, as usual
|
|
|
|
app = wx.App(0)
|
|
|
|
frame = MyFrame(None)
|
|
app.SetTopWindow(frame)
|
|
frame.Show()
|
|
|
|
app.MainLoop()
|
|
|
|
|
|
|
|
Window Styles
|
|
=============
|
|
|
|
This class supports the following window styles:
|
|
|
|
================================ =========== ==================================================
|
|
Window Styles Hex Value Description
|
|
================================ =========== ==================================================
|
|
``FNB_VC71`` 0x1 Use Visual Studio 2003 (VC7.1) style for tabs.
|
|
``FNB_FANCY_TABS`` 0x2 Use fancy style - square tabs filled with gradient colouring.
|
|
``FNB_TABS_BORDER_SIMPLE`` 0x4 Draw thin border around the page.
|
|
``FNB_NO_X_BUTTON`` 0x8 Do not display the 'X' button.
|
|
``FNB_NO_NAV_BUTTONS`` 0x10 Do not display the right/left arrows.
|
|
``FNB_MOUSE_MIDDLE_CLOSES_TABS`` 0x20 Use the mouse middle button for cloing tabs.
|
|
``FNB_BOTTOM`` 0x40 Place tabs at bottom - the default is to place them at top.
|
|
``FNB_NODRAG`` 0x80 Disable dragging of tabs.
|
|
``FNB_VC8`` 0x100 Use Visual Studio 2005 (VC8) style for tabs.
|
|
``FNB_X_ON_TAB`` 0x200 Place 'X' close button on the active tab.
|
|
``FNB_BACKGROUND_GRADIENT`` 0x400 Use gradients to paint the tabs background.
|
|
``FNB_COLOURFUL_TABS`` 0x800 Use colourful tabs (VC8 style only).
|
|
``FNB_DCLICK_CLOSES_TABS`` 0x1000 Style to close tab using double click.
|
|
``FNB_SMART_TABS`` 0x2000 Use `Smart Tabbing`, like ``Alt`` + ``Tab`` on Windows.
|
|
``FNB_DROPDOWN_TABS_LIST`` 0x4000 Use a dropdown menu on the left in place of the arrows.
|
|
``FNB_ALLOW_FOREIGN_DND`` 0x8000 Allows drag 'n' drop operations between different :class:`FlatNotebook`.
|
|
``FNB_HIDE_ON_SINGLE_TAB`` 0x10000 Hides the Page Container when there is one or fewer tabs.
|
|
``FNB_DEFAULT_STYLE`` 0x10020 :class:`FlatNotebook` default style.
|
|
``FNB_FF2`` 0x20000 Use Firefox 2 style for tabs.
|
|
``FNB_NO_TAB_FOCUS`` 0x40000 Does not allow tabs to have focus.
|
|
``FNB_RIBBON_TABS`` 0x80000 Use the Ribbon Tabs style
|
|
``FNB_HIDE_TABS`` 0x100000 Hides the Page Container allowing only keyboard navigation
|
|
``FNB_NAV_BUTTONS_WHEN_NEEDED`` 0x200000 Hides the navigation left/right arrows if all tabs fit
|
|
================================ =========== ==================================================
|
|
|
|
|
|
Events Processing
|
|
=================
|
|
|
|
This class processes the following events:
|
|
|
|
========================================= ==================================================
|
|
Event Name Description
|
|
========================================= ==================================================
|
|
``EVT_FLATNOTEBOOK_PAGE_CHANGED`` Notify client objects when the active page in :class:`FlatNotebook` has changed.
|
|
``EVT_FLATNOTEBOOK_PAGE_CHANGING`` Notify client objects when the active page in :class:`FlatNotebook` is about to change.
|
|
``EVT_FLATNOTEBOOK_PAGE_CLOSED`` Notify client objects when a page in :class:`FlatNotebook` has been closed.
|
|
``EVT_FLATNOTEBOOK_PAGE_CLOSING`` Notify client objects when a page in :class:`FlatNotebook` is closing.
|
|
``EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU`` Notify client objects when a pop-up menu should appear next to a tab.
|
|
``EVT_FLATNOTEBOOK_PAGE_DROPPED`` Notify client objects when a tab has been dropped and re-arranged (on the *same* notebook)
|
|
``EVT_FLATNOTEBOOK_PAGE_DROPPED_FOREIGN`` Notify client objects when a tab has been dropped and re-arranged (from a foreign notebook)
|
|
========================================= ==================================================
|
|
|
|
|
|
License And Version
|
|
===================
|
|
|
|
:class:`FlatNotebook` is distributed under the wxPython license.
|
|
|
|
Latest Revision: Andrea Gavana @ 27 Dec 2012, 21.00 GMT
|
|
|
|
Version 3.2
|
|
"""
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
# Beginning Of FLATNOTEBOOK wxPython Code
|
|
#----------------------------------------------------------------------
|
|
|
|
import wx
|
|
import random
|
|
import math
|
|
import weakref
|
|
import pickle
|
|
|
|
import wx.lib.six as six
|
|
|
|
# Used on OSX to get access to carbon api constants
|
|
if wx.Platform == '__WXMAC__':
|
|
try:
|
|
import Carbon.Appearance
|
|
except ImportError:
|
|
CARBON = False
|
|
else:
|
|
CARBON = True
|
|
|
|
# Check for the new method in 2.7 (not present in 2.6.3.3)
|
|
if wx.VERSION_STRING < "2.7":
|
|
wx.Rect.Contains = lambda self, point: wx.Rect.Inside(self, point)
|
|
|
|
FNB_HEIGHT_SPACER = 10
|
|
""" Padding for the tab widths/heights, in pixels. """
|
|
|
|
# Use Visual Studio 2003 (VC7.1) style for tabs
|
|
FNB_VC71 = 1
|
|
"""Use Visual Studio 2003 (VC7.1) style for tabs"""
|
|
|
|
# Use fancy style - square tabs filled with gradient colouring
|
|
FNB_FANCY_TABS = 2
|
|
"""Use fancy style - square tabs filled with gradient colouring"""
|
|
|
|
# Draw thin border around the page
|
|
FNB_TABS_BORDER_SIMPLE = 4
|
|
"""Draw thin border around the page"""
|
|
|
|
# Do not display the 'X' button
|
|
FNB_NO_X_BUTTON = 8
|
|
"""Do not display the 'X' button"""
|
|
|
|
# Do not display the Right / Left arrows
|
|
FNB_NO_NAV_BUTTONS = 16
|
|
"""Do not display the right/left arrows"""
|
|
|
|
# Use the mouse middle button for cloing tabs
|
|
FNB_MOUSE_MIDDLE_CLOSES_TABS = 32
|
|
"""Use the mouse middle button for cloing tabs"""
|
|
|
|
# Place tabs at bottom - the default is to place them
|
|
# at top
|
|
FNB_BOTTOM = 64
|
|
"""Place tabs at bottom - the default is to place them at top"""
|
|
|
|
# Disable dragging of tabs
|
|
FNB_NODRAG = 128
|
|
"""Disable dragging of tabs"""
|
|
|
|
# Use Visual Studio 2005 (VC8) style for tabs
|
|
FNB_VC8 = 256
|
|
"""Use Visual Studio 2005 (VC8) style for tabs"""
|
|
|
|
# Firefox 2 tabs style
|
|
FNB_FF2 = 131072
|
|
"""Use Firefox 2 style for tabs"""
|
|
|
|
# Place 'X' on a tab
|
|
FNB_X_ON_TAB = 512
|
|
"""Place 'X' close button on the active tab"""
|
|
|
|
FNB_BACKGROUND_GRADIENT = 1024
|
|
"""Use gradients to paint the tabs background"""
|
|
|
|
FNB_COLOURFUL_TABS = 2048
|
|
"""Use colourful tabs (VC8 style only)"""
|
|
|
|
# Style to close tab using double click - styles 1024, 2048 are reserved
|
|
FNB_DCLICK_CLOSES_TABS = 4096
|
|
"""Style to close tab using double click"""
|
|
|
|
FNB_SMART_TABS = 8192
|
|
"""Use `Smart Tabbing`, like ``Alt`` + ``Tab`` on Windows"""
|
|
|
|
FNB_DROPDOWN_TABS_LIST = 16384
|
|
"""Use a dropdown menu on the left in place of the arrows"""
|
|
|
|
FNB_ALLOW_FOREIGN_DND = 32768
|
|
"""Allows drag and drop operations between different :class:`FlatNotebook`"""
|
|
|
|
FNB_HIDE_ON_SINGLE_TAB = 65536
|
|
"""Hides the :class:`PageContainer` when there is one or fewer tabs"""
|
|
|
|
FNB_NO_TAB_FOCUS = 262144
|
|
""" Does not allow tabs to have focus"""
|
|
|
|
FNB_RIBBON_TABS = 0x80000
|
|
"""Use Ribbon style for tabs"""
|
|
|
|
FNB_HIDE_TABS = 0x100000
|
|
"""Hides the tabs allowing only keyboard navigation between pages"""
|
|
|
|
FNB_NAV_BUTTONS_WHEN_NEEDED = 0x200000
|
|
""" Hides the navigation left/right arrows if all tabs fit. """
|
|
|
|
VERTICAL_BORDER_PADDING = 4
|
|
""" Padding between the text and the tab border. """
|
|
|
|
# Button size is a 16x16 xpm bitmap
|
|
BUTTON_SPACE = 16
|
|
"""Button size is a 16x16 xpm bitmap"""
|
|
|
|
MASK_COLOUR = wx.Colour(0, 128, 128)
|
|
"""Mask colour for the arrow bitmaps"""
|
|
|
|
# Button status
|
|
FNB_BTN_PRESSED = 2
|
|
"""Navigation button is pressed"""
|
|
FNB_BTN_HOVER = 1
|
|
"""Navigation button is hovered"""
|
|
FNB_BTN_NONE = 0
|
|
"""No navigation"""
|
|
|
|
# Hit Test results
|
|
FNB_TAB = 1 # On a tab
|
|
"""Indicates mouse coordinates inside a tab"""
|
|
FNB_X = 2 # On the X button
|
|
"""Indicates mouse coordinates inside the X region"""
|
|
FNB_TAB_X = 3 # On the 'X' button (tab's X button)
|
|
"""Indicates mouse coordinates inside the X region in a tab"""
|
|
FNB_LEFT_ARROW = 4 # On the rotate left arrow button
|
|
"""Indicates mouse coordinates inside the left arrow region"""
|
|
FNB_RIGHT_ARROW = 5 # On the rotate right arrow button
|
|
"""Indicates mouse coordinates inside the right arrow region"""
|
|
FNB_DROP_DOWN_ARROW = 6 # On the drop down arrow button
|
|
"""Indicates mouse coordinates inside the drop down arrow region"""
|
|
FNB_NOWHERE = 0 # Anywhere else
|
|
"""Indicates mouse coordinates not on any tab of the notebook"""
|
|
|
|
FNB_DEFAULT_STYLE = FNB_MOUSE_MIDDLE_CLOSES_TABS | FNB_HIDE_ON_SINGLE_TAB
|
|
""":class:`FlatNotebook` default style"""
|
|
|
|
# FlatNotebook Events:
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CHANGED: Event Fired When You Switch Page;
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CHANGING: Event Fired When You Are About To Switch
|
|
# Pages, But You Can Still "Veto" The Page Changing By Avoiding To Call
|
|
# event.Skip() In Your Event Handler;
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CLOSING: Event Fired When A Page Is Closing, But
|
|
# You Can Still "Veto" The Page Changing By Avoiding To Call event.Skip()
|
|
# In Your Event Handler;
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CLOSED: Event Fired When A Page Is Closed.
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU: Event Fired When A Menu Pops-up In A Tab.
|
|
# wxEVT_FLATNOTEBOOK_PAGE_DROPPED: Event Fired When A Tab Is Dropped On The Same Notebook
|
|
|
|
wxEVT_FLATNOTEBOOK_PAGE_CHANGED = wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
|
|
wxEVT_FLATNOTEBOOK_PAGE_CHANGING = wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
|
|
wxEVT_FLATNOTEBOOK_PAGE_CLOSING = wx.NewEventType()
|
|
wxEVT_FLATNOTEBOOK_PAGE_CLOSED = wx.NewEventType()
|
|
wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.NewEventType()
|
|
wxEVT_FLATNOTEBOOK_PAGE_DROPPED = wx.NewEventType()
|
|
wxEVT_FLATNOTEBOOK_PAGE_DROPPED_FOREIGN = wx.NewEventType()
|
|
|
|
#-----------------------------------#
|
|
# FlatNotebookEvent
|
|
#-----------------------------------#
|
|
|
|
EVT_FLATNOTEBOOK_PAGE_CHANGED = wx.EVT_NOTEBOOK_PAGE_CHANGED
|
|
""" Notify client objects when the active page in :class:`FlatNotebook` has changed."""
|
|
EVT_FLATNOTEBOOK_PAGE_CHANGING = wx.EVT_NOTEBOOK_PAGE_CHANGING
|
|
""" Notify client objects when the active page in :class:`FlatNotebook` is about to change."""
|
|
EVT_FLATNOTEBOOK_PAGE_CLOSING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, 1)
|
|
""" Notify client objects when a page in :class:`FlatNotebook` is closing."""
|
|
EVT_FLATNOTEBOOK_PAGE_CLOSED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, 1)
|
|
""" Notify client objects when a page in :class:`FlatNotebook` has been closed."""
|
|
EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, 1)
|
|
""" Notify client objects when a pop-up menu should appear next to a tab."""
|
|
EVT_FLATNOTEBOOK_PAGE_DROPPED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_DROPPED, 1)
|
|
""" Notify client objects when a tab has been dropped and re-arranged (on the *same* notebook)."""
|
|
EVT_FLATNOTEBOOK_PAGE_DROPPED_FOREIGN = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_DROPPED_FOREIGN, 1)
|
|
""" Notify client objects when a tab has been dropped and re-arranged (from a foreign notebook)."""
|
|
|
|
# Some icons in XPM format
|
|
|
|
left_arrow_disabled_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #555555",
|
|
b"# c #000000",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````.```````",
|
|
b"```````..```````",
|
|
b"``````.`.```````",
|
|
b"`````.``.```````",
|
|
b"````.```.```````",
|
|
b"`````.``.```````",
|
|
b"``````.`.```````",
|
|
b"```````..```````",
|
|
b"````````.```````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
x_button_pressed_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #9e9ede",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"`..............`",
|
|
b"`.############.`",
|
|
b"`.############.`",
|
|
b"`.############.`",
|
|
b"`.###aa####aa#.`",
|
|
b"`.####aa##aa##.`",
|
|
b"`.#####aaaa###.`",
|
|
b"`.######aa####.`",
|
|
b"`.#####aaaa###.`",
|
|
b"`.####aa##aa##.`",
|
|
b"`.###aa####aa#.`",
|
|
b"`.############.`",
|
|
b"`..............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
|
|
left_arrow_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #555555",
|
|
b"# c #000000",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````.```````",
|
|
b"```````..```````",
|
|
b"``````...```````",
|
|
b"`````....```````",
|
|
b"````.....```````",
|
|
b"`````....```````",
|
|
b"``````...```````",
|
|
b"```````..```````",
|
|
b"````````.```````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
x_button_hilite_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #c9dafb",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"`..............`",
|
|
b"`.############.`",
|
|
b"`.############.`",
|
|
b"`.##aa####aa##.`",
|
|
b"`.###aa##aa###.`",
|
|
b"`.####aaaa####.`",
|
|
b"`.#####aa#####.`",
|
|
b"`.####aaaa####.`",
|
|
b"`.###aa##aa###.`",
|
|
b"`.##aa####aa##.`",
|
|
b"`.############.`",
|
|
b"`.############.`",
|
|
b"`..............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
x_button_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #555555",
|
|
b"# c #000000",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````..````..````",
|
|
b"`````..``..`````",
|
|
b"``````....``````",
|
|
b"```````..```````",
|
|
b"``````....``````",
|
|
b"`````..``..`````",
|
|
b"````..````..````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
left_arrow_pressed_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #9e9ede",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"`..............`",
|
|
b"`.############.`",
|
|
b"`.############.`",
|
|
b"`.#######a####.`",
|
|
b"`.######aa####.`",
|
|
b"`.#####aaa####.`",
|
|
b"`.####aaaa####.`",
|
|
b"`.###aaaaa####.`",
|
|
b"`.####aaaa####.`",
|
|
b"`.#####aaa####.`",
|
|
b"`.######aa####.`",
|
|
b"`.#######a####.`",
|
|
b"`..............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
left_arrow_hilite_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #c9dafb",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"`..............`",
|
|
b"`.############.`",
|
|
b"`.######a#####.`",
|
|
b"`.#####aa#####.`",
|
|
b"`.####aaa#####.`",
|
|
b"`.###aaaa#####.`",
|
|
b"`.##aaaaa#####.`",
|
|
b"`.###aaaa#####.`",
|
|
b"`.####aaa#####.`",
|
|
b"`.#####aa#####.`",
|
|
b"`.######a#####.`",
|
|
b"`.############.`",
|
|
b"`..............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
right_arrow_disabled_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #555555",
|
|
b"# c #000000",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"```````.````````",
|
|
b"```````..```````",
|
|
b"```````.`.``````",
|
|
b"```````.``.`````",
|
|
b"```````.```.````",
|
|
b"```````.``.`````",
|
|
b"```````.`.``````",
|
|
b"```````..```````",
|
|
b"```````.````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
right_arrow_hilite_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #c9dafb",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"`..............`",
|
|
b"`.############.`",
|
|
b"`.####a#######.`",
|
|
b"`.####aa######.`",
|
|
b"`.####aaa#####.`",
|
|
b"`.####aaaa####.`",
|
|
b"`.####aaaaa###.`",
|
|
b"`.####aaaa####.`",
|
|
b"`.####aaa#####.`",
|
|
b"`.####aa######.`",
|
|
b"`.####a#######.`",
|
|
b"`.############.`",
|
|
b"`..............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
right_arrow_pressed_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #9e9ede",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"`..............`",
|
|
b"`.############.`",
|
|
b"`.############.`",
|
|
b"`.#####a######.`",
|
|
b"`.#####aa#####.`",
|
|
b"`.#####aaa####.`",
|
|
b"`.#####aaaa###.`",
|
|
b"`.#####aaaaa##.`",
|
|
b"`.#####aaaa###.`",
|
|
b"`.#####aaa####.`",
|
|
b"`.#####aa#####.`",
|
|
b"`.#####a######.`",
|
|
b"`..............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
|
|
right_arrow_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #555555",
|
|
b"# c #000000",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"```````.````````",
|
|
b"```````..```````",
|
|
b"```````...``````",
|
|
b"```````....`````",
|
|
b"```````.....````",
|
|
b"```````....`````",
|
|
b"```````...``````",
|
|
b"```````..```````",
|
|
b"```````.````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
down_arrow_hilite_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #c9dafb",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"``.............`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.#aaaaaaaaa#.`",
|
|
b"``.##aaaaaaa##.`",
|
|
b"``.###aaaaa###.`",
|
|
b"``.####aaa####.`",
|
|
b"``.#####a#####.`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
down_arrow_pressed_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #4766e0",
|
|
b"# c #9e9ede",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"``.............`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.###########.`",
|
|
b"``.#aaaaaaaaa#.`",
|
|
b"``.##aaaaaaa##.`",
|
|
b"``.###aaaaa###.`",
|
|
b"``.####aaa####.`",
|
|
b"``.#####a#####.`",
|
|
b"``.###########.`",
|
|
b"``.............`",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
|
|
down_arrow_xpm = [
|
|
b" 16 16 8 1",
|
|
b"` c #008080",
|
|
b". c #000000",
|
|
b"# c #000000",
|
|
b"a c #000000",
|
|
b"b c #000000",
|
|
b"c c #000000",
|
|
b"d c #000000",
|
|
b"e c #000000",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````.........```",
|
|
b"`````.......````",
|
|
b"``````.....`````",
|
|
b"```````...``````",
|
|
b"````````.```````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````",
|
|
b"````````````````"
|
|
]
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
from wx.lib.embeddedimage import PyEmbeddedImage
|
|
|
|
Mondrian = PyEmbeddedImage(
|
|
"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAHFJ"
|
|
"REFUWIXt1jsKgDAQRdF7xY25cpcWC60kioI6Fm/ahHBCMh+BRmGMnAgEWnvPpzK8dvrFCCCA"
|
|
"coD8og4c5Lr6WB3Q3l1TBwLYPuF3YS1gn1HphgEEEABcKERrGy0E3B0HFJg7C1N/f/kTBBBA"
|
|
"+Vi+AMkgFEvBPD17AAAAAElFTkSuQmCC")
|
|
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
def LightColour(colour, percent):
|
|
"""
|
|
Brighten the input colour by a percentage.
|
|
|
|
:param `colour`: a valid :class:`Colour` instance;
|
|
:param `percent`: the percentage by which the input colour should be brightened.
|
|
"""
|
|
|
|
end_colour = wx.WHITE
|
|
|
|
rd = end_colour.Red() - colour.Red()
|
|
gd = end_colour.Green() - colour.Green()
|
|
bd = end_colour.Blue() - colour.Blue()
|
|
|
|
high = 100
|
|
|
|
# We take the percent way of the colour from colour -. white
|
|
i = percent
|
|
r = colour.Red() + ((i*rd*100)/high)/100
|
|
g = colour.Green() + ((i*gd*100)/high)/100
|
|
b = colour.Blue() + ((i*bd*100)/high)/100
|
|
return wx.Colour(r, g, b)
|
|
|
|
|
|
def FormatColour(colour):
|
|
"""
|
|
Convert the input `colour` into a valid :class:`Colour` instance, using whatever typemap
|
|
accepted by wxWidgets/wxPython.
|
|
|
|
:param `colour`: can be an instance of :class:`Colour`, a 3 or 4 integer tuple, a hex
|
|
string, a string representing the colour name or ``None``.
|
|
|
|
:returns: a valid instance of :class:`Colour` or ``None`` if the input `colour` was ``None``
|
|
in the first place.
|
|
"""
|
|
|
|
if colour is not None:
|
|
colour = wx.Colour(colour)
|
|
|
|
return colour
|
|
|
|
|
|
def RandomColour():
|
|
""" Creates a random colour. """
|
|
|
|
r = random.randint(0, 255) # Random value between 0-255
|
|
g = random.randint(0, 255) # Random value between 0-255
|
|
b = random.randint(0, 255) # Random value between 0-255
|
|
|
|
return wx.Colour(r, g, b)
|
|
|
|
|
|
def PaintStraightGradientBox(dc, rect, startColour, endColour, vertical=True):
|
|
"""
|
|
Draws a gradient coloured box from `startColour` to `endColour`.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `rect`: the rectangle to fill with the gradient shading;
|
|
:param `startColour`: the first colour in the gradient shading;
|
|
:param `endColour`: the last colour in the gradient shading;
|
|
:param `vertical`: ``True`` if the gradient shading is north to south, ``False``
|
|
if it is east to west.
|
|
"""
|
|
|
|
rd = endColour.Red() - startColour.Red()
|
|
gd = endColour.Green() - startColour.Green()
|
|
bd = endColour.Blue() - startColour.Blue()
|
|
|
|
# Save the current pen and brush
|
|
savedPen = dc.GetPen()
|
|
savedBrush = dc.GetBrush()
|
|
|
|
if vertical:
|
|
high = rect.GetHeight()-1
|
|
else:
|
|
high = rect.GetWidth()-1
|
|
|
|
if high < 1:
|
|
return
|
|
|
|
for i in range(high+1):
|
|
|
|
r = startColour.Red() + ((i*rd*100)/high)/100
|
|
g = startColour.Green() + ((i*gd*100)/high)/100
|
|
b = startColour.Blue() + ((i*bd*100)/high)/100
|
|
|
|
p = wx.Pen(wx.Colour(r, g, b))
|
|
dc.SetPen(p)
|
|
|
|
if vertical:
|
|
dc.DrawLine(rect.x, rect.y+i, rect.x+rect.width, rect.y+i)
|
|
else:
|
|
dc.DrawLine(rect.x+i, rect.y, rect.x+i, rect.y+rect.height)
|
|
|
|
# Restore the pen and brush
|
|
dc.SetPen(savedPen)
|
|
dc.SetBrush(savedBrush)
|
|
|
|
|
|
def AdjustColour(colour, percent, alpha=wx.ALPHA_OPAQUE):
|
|
"""
|
|
Brighten/darken input colour by `percent` and adjust `alpha` channel if needed.
|
|
|
|
:param `colour`: colour object to adjust, an instance of :class:`Colour`;
|
|
:param `percent`: percent to adjust ``+`` (brighten) or ``-`` (darken);
|
|
:param `alpha`: amount to adjust the alpha channel.
|
|
|
|
:return: The modified colour.
|
|
|
|
"""
|
|
|
|
radj, gadj, badj = [int(val * (abs(percent) / 100.)) for val in colour.Get()]
|
|
|
|
if percent < 0:
|
|
radj, gadj, badj = [val * -1 for val in [radj, gadj, badj]]
|
|
else:
|
|
radj, gadj, badj = [val or 255 for val in [radj, gadj, badj]]
|
|
|
|
red = min(colour.Red() + radj, 255)
|
|
green = min(colour.Green() + gadj, 255)
|
|
blue = min(colour.Blue() + badj, 255)
|
|
return wx.Colour(red, green, blue, alpha)
|
|
|
|
|
|
if wx.VERSION_STRING < "2.8.9.2":
|
|
adjust_colour = AdjustColour
|
|
else:
|
|
from wx.lib.colourutils import AdjustColour as adjust_colour
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Util functions
|
|
# -----------------------------------------------------------------------------
|
|
|
|
def DrawButton(dc, rect, focus, upperTabs):
|
|
"""
|
|
Draws a :class:`FlatNotebook` tab.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `rect`: the tab's client rectangle;
|
|
:param `focus`: ``True`` if the tab has focus, ``False`` otherwise;
|
|
:param `upperTabs`: ``True`` if the tabs are at the top, ``False`` if they are
|
|
at the 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:
|
|
PaintStraightGradientBox(dc, top, topStartColour, topEndColour)
|
|
PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
|
|
else:
|
|
PaintStraightGradientBox(dc, top, topEndColour , topStartColour)
|
|
PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
|
|
|
|
else:
|
|
if focus:
|
|
PaintStraightGradientBox(dc, bottom, topEndColour, bottomEndColour)
|
|
PaintStraightGradientBox(dc, top,topStartColour, topStartColour)
|
|
else:
|
|
PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
|
|
PaintStraightGradientBox(dc, top, topEndColour, topStartColour)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBDropSource
|
|
# Gives Some Custom UI Feedback during the DnD Operations
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBDropSource(wx.DropSource):
|
|
"""
|
|
Give some custom UI feedback during the drag and drop operation in this
|
|
function. It is called on each mouse move, so your implementation must
|
|
not be too slow.
|
|
"""
|
|
|
|
def __init__(self, win):
|
|
"""
|
|
Default class constructor.
|
|
Used internally.
|
|
|
|
:param `win`: the source window for which we wish to provide UI feedback
|
|
during drag and drop operations.
|
|
"""
|
|
|
|
wx.DropSource.__init__(self, win)
|
|
self._win = win
|
|
|
|
|
|
def GiveFeedback(self, effect):
|
|
"""
|
|
You may give some custom UI feedback during the drag and drop operation
|
|
in this function. It is called on each mouse move, so your implementation
|
|
must not be too slow.
|
|
|
|
:param `effect`: the effect to implement. One of ``wx.DragCopy``, ``wx.DragMove``,
|
|
``wx.DragLink`` and ``wx.DragNone``.
|
|
|
|
:return: Return ``False`` if you want default feedback, or ``True`` if you
|
|
implement your own feedback. The return values is ignored under GTK.
|
|
|
|
:note: To show your own custom drag and drop UI feedback, you must override
|
|
this method.
|
|
"""
|
|
|
|
self._win.DrawDragHint()
|
|
return False
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBDragInfo
|
|
# Stores All The Information To Allow Drag And Drop Between Different
|
|
# FlatNotebooks.
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBDragInfo(object):
|
|
"""
|
|
Stores all the information to allow drag and drop between different
|
|
:class:`FlatNotebook` instances.
|
|
"""
|
|
|
|
_map = weakref.WeakValueDictionary()
|
|
|
|
def __init__(self, container, pageindex):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `container`: the drag and drop container, a page in :class:`FlatNotebook`;
|
|
:param `pageindex`: the index of the tab that is actually being dragged.
|
|
"""
|
|
|
|
self._id = id(container)
|
|
FNBDragInfo._map[self._id] = container
|
|
self._pageindex = pageindex
|
|
|
|
|
|
def GetContainer(self):
|
|
""" Returns the :class:`FlatNotebook` page (usually a panel). """
|
|
|
|
return FNBDragInfo._map.get(self._id, None)
|
|
|
|
|
|
def GetPageIndex(self):
|
|
""" Returns the page index associated with a page. """
|
|
|
|
return self._pageindex
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBDropTarget
|
|
# Simply Used To Handle The OnDrop() Method When Dragging And Dropping Between
|
|
# Different FlatNotebooks.
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBDropTarget(wx.DropTarget):
|
|
"""
|
|
Class used to handle the :meth:`FlatNotebook.OnDropTarget() <FlatNotebook.OnDropTarget>` method when dragging and
|
|
dropping between different :class:`FlatNotebook` instances.
|
|
"""
|
|
|
|
def __init__(self, parent):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `parent`: the window handling the drag and drop, an instance of
|
|
:class:`FlatNotebook`.
|
|
"""
|
|
|
|
wx.DropTarget.__init__(self)
|
|
|
|
self._parent = parent
|
|
self._dataobject = wx.CustomDataObject(wx.DataFormat(six.u('FlatNotebook')))
|
|
self.SetDataObject(self._dataobject)
|
|
|
|
|
|
def OnData(self, x, y, dragres):
|
|
"""
|
|
Called after `OnDrop` returns ``True``.
|
|
|
|
By default this will usually call `GetData` and will return the suggested default value `dragres`.
|
|
|
|
:param `x`: the current x position of the mouse while dragging and dropping;
|
|
:param `y`: the current y position of the mouse while dragging and dropping;
|
|
:param `dragres`: an optional default return value.
|
|
"""
|
|
|
|
if not self.GetData():
|
|
return wx.DragNone
|
|
|
|
draginfo = self._dataobject.GetData()
|
|
drginfo = pickle.loads(draginfo)
|
|
|
|
return self._parent.OnDropTarget(x, y, drginfo.GetPageIndex(), drginfo.GetContainer())
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class PageInfo
|
|
# Contains parameters for every FlatNotebook page
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class PageInfo(object):
|
|
"""
|
|
This class holds all the information (caption, image, etc...) belonging to a
|
|
single tab in :class:`FlatNotebook`.
|
|
"""
|
|
|
|
def __init__(self, caption="", imageindex=-1, tabangle=0, enabled=True):
|
|
"""
|
|
Default Class Constructor.
|
|
|
|
:param `caption`: the tab caption;
|
|
:param `imageindex`: the tab image index based on the assigned (set)
|
|
:class:`ImageList` (if any);
|
|
:param `tabangle`: the tab angle (only on standard tabs, from 0 to 15
|
|
degrees);
|
|
:param `enabled`: sets the tab as enabled or disabled.
|
|
"""
|
|
|
|
self._strCaption = caption
|
|
self._TabAngle = tabangle
|
|
self._ImageIndex = imageindex
|
|
self._bEnabled = enabled
|
|
self._pos = wx.Point(-1, -1)
|
|
self._size = wx.Size(-1, -1)
|
|
self._region = wx.Region()
|
|
self._xRect = wx.Rect()
|
|
self._colour = None
|
|
self._hasFocus = False
|
|
self._pageTextColour = None
|
|
|
|
|
|
def SetCaption(self, value):
|
|
"""
|
|
Sets the tab caption.
|
|
|
|
:param `value`: the new tab caption string.
|
|
"""
|
|
|
|
self._strCaption = value
|
|
|
|
|
|
def GetCaption(self):
|
|
""" Returns the tab caption. """
|
|
|
|
return self._strCaption
|
|
|
|
|
|
def SetPosition(self, value):
|
|
"""
|
|
Sets the tab position.
|
|
|
|
:param `value`: an instance of :class:`Point`.
|
|
"""
|
|
|
|
self._pos = value
|
|
|
|
|
|
def GetPosition(self):
|
|
""" Returns the tab position. """
|
|
|
|
return self._pos
|
|
|
|
|
|
def SetSize(self, value):
|
|
"""
|
|
Sets the tab size.
|
|
|
|
:param `value`: an instance of :class:`Size`.
|
|
"""
|
|
|
|
self._size = value
|
|
|
|
|
|
def GetSize(self):
|
|
""" Returns the tab size. """
|
|
|
|
return self._size
|
|
|
|
|
|
def SetTabAngle(self, value):
|
|
"""
|
|
Sets the tab header angle.
|
|
|
|
:param `value`: the tab header angle (0 <= value <= 15 degrees).
|
|
"""
|
|
|
|
self._TabAngle = min(45, value)
|
|
|
|
|
|
def GetTabAngle(self):
|
|
""" Returns the tab angle. """
|
|
|
|
return self._TabAngle
|
|
|
|
|
|
def SetImageIndex(self, value):
|
|
"""
|
|
Sets the tab image index.
|
|
|
|
:param `value`: an index within the :class:`FlatNotebook` image list specifying
|
|
the image to use for this tab.
|
|
"""
|
|
|
|
self._ImageIndex = value
|
|
|
|
|
|
def GetImageIndex(self):
|
|
""" Returns the tab image index. """
|
|
|
|
return self._ImageIndex
|
|
|
|
|
|
def GetPageTextColour(self):
|
|
"""
|
|
Returns the tab text colour if it has been set previously, or ``None``
|
|
otherwise.
|
|
"""
|
|
|
|
return self._pageTextColour
|
|
|
|
|
|
def SetPageTextColour(self, colour):
|
|
"""
|
|
Sets the tab text colour for this tab.
|
|
|
|
:param `colour`: an instance of :class:`Colour`. You can pass ``None`` or
|
|
:class:`NullColour` to return to the default page text colour.
|
|
"""
|
|
|
|
colour = FormatColour(colour)
|
|
|
|
if colour is None or not colour.IsOk():
|
|
self._pageTextColour = None
|
|
else:
|
|
self._pageTextColour = colour
|
|
|
|
|
|
def GetEnabled(self):
|
|
""" Returns whether the tab is enabled or not. """
|
|
|
|
return self._bEnabled
|
|
|
|
|
|
def EnableTab(self, enabled):
|
|
"""
|
|
Sets the tab enabled or disabled.
|
|
|
|
:param `enabled`: ``True`` to enable a tab, ``False`` to disable it.
|
|
"""
|
|
|
|
self._bEnabled = enabled
|
|
|
|
|
|
def SetRegion(self, points=[]):
|
|
"""
|
|
Sets the tab region.
|
|
|
|
:param `points`: a Python list of :class:`Point`
|
|
"""
|
|
|
|
self._region = wx.Region(points)
|
|
|
|
|
|
def GetRegion(self):
|
|
""" Returns the tab region. """
|
|
|
|
return self._region
|
|
|
|
|
|
def SetXRect(self, xrect):
|
|
"""
|
|
Sets the button 'X' area rect.
|
|
|
|
:param `xrect`: an instance of :class:`Rect`, specifying the client rectangle
|
|
of the 'X' button.
|
|
"""
|
|
|
|
self._xRect = xrect
|
|
|
|
|
|
def GetXRect(self):
|
|
""" Returns the button 'X' area rect. """
|
|
|
|
return self._xRect
|
|
|
|
|
|
def GetColour(self):
|
|
""" Returns the tab colour. """
|
|
|
|
return self._colour
|
|
|
|
|
|
def SetColour(self, colour):
|
|
"""
|
|
Sets the tab colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object or any typemap supported by wxWidgets/wxPython
|
|
to generate a colour (i.e., a hex string, a colour name, a 3 or 4 integer tuple).
|
|
"""
|
|
|
|
self._colour = FormatColour(colour)
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FlatNotebookEvent
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FlatNotebookEvent(wx.PyCommandEvent):
|
|
"""
|
|
This events will be sent when a ``EVT_FLATNOTEBOOK_PAGE_CHANGED``,
|
|
``EVT_FLATNOTEBOOK_PAGE_CHANGING``, ``EVT_FLATNOTEBOOK_PAGE_CLOSING``,
|
|
``EVT_FLATNOTEBOOK_PAGE_CLOSED`` and ``EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU`` is
|
|
mapped in the parent.
|
|
"""
|
|
|
|
def __init__(self, eventType, eventId=1, nSel=-1, nOldSel=-1):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `eventType`: the event type;
|
|
:param `eventId`: the event identifier;
|
|
:param `nSel`: the current selection;
|
|
:param `nOldSel`: the old selection.
|
|
"""
|
|
|
|
wx.PyCommandEvent.__init__(self, eventType, eventId)
|
|
self._eventType = eventType
|
|
|
|
self.notify = wx.NotifyEvent(eventType, eventId)
|
|
|
|
|
|
def GetNotifyEvent(self):
|
|
""" Returns the actual :class:`NotifyEvent`. """
|
|
|
|
return self.notify
|
|
|
|
|
|
def IsAllowed(self):
|
|
"""
|
|
Returns ``True`` if the change is allowed (:meth:`~FlatNotebookEvent.Veto` hasn't been called) or
|
|
``False`` otherwise (if it was).
|
|
"""
|
|
|
|
return self.notify.IsAllowed()
|
|
|
|
|
|
def Veto(self):
|
|
"""
|
|
Prevents the change announced by this event from happening.
|
|
|
|
:note: It is in general a good idea to notify the user about the reasons
|
|
for vetoing the change because otherwise the applications behaviour (which
|
|
just refuses to do what the user wants) might be quite surprising.
|
|
"""
|
|
|
|
self.notify.Veto()
|
|
|
|
|
|
def Allow(self):
|
|
"""
|
|
This is the opposite of :meth:`~FlatNotebookEvent.Veto`: it explicitly allows the event to be processed.
|
|
For most events it is not necessary to call this method as the events are
|
|
allowed anyhow but some are forbidden by default (this will be mentioned
|
|
in the corresponding event description).
|
|
"""
|
|
|
|
self.notify.Allow()
|
|
|
|
|
|
def SetSelection(self, nSel):
|
|
"""
|
|
Sets the event selection.
|
|
|
|
:param `nSel`: an integer specifying the new selection.
|
|
"""
|
|
|
|
self._selection = nSel
|
|
|
|
|
|
def SetOldSelection(self, nOldSel):
|
|
"""
|
|
Sets the id of the page selected before the change.
|
|
|
|
:param `nOldSel`: an integer specifying the old selection.
|
|
"""
|
|
|
|
self._oldselection = nOldSel
|
|
|
|
|
|
def GetSelection(self):
|
|
""" Returns the currently selected page, or -1 if none was selected. """
|
|
|
|
return self._selection
|
|
|
|
|
|
def GetOldSelection(self):
|
|
""" Returns the page that was selected before the change, -1 if none was selected. """
|
|
|
|
return self._oldselection
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class TabNavigatorWindow
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FlatNotebookDragEvent(FlatNotebookEvent):
|
|
"""
|
|
This event will be sent when a ``EVT_FLATNOTEBOOK_PAGE_DRAGGED_FOREIGN`` is
|
|
mapped in the parent.
|
|
"""
|
|
|
|
def __init__(self, eventType, eventId=1, nSel=-1, nOldSel=-1):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `eventType`: the event type;
|
|
:param `eventId`: the event identifier;
|
|
:param `nSel`: the current selection;
|
|
:param `nOldSel`: the old selection.
|
|
"""
|
|
|
|
wx.PyCommandEvent.__init__(self, eventType, eventId)
|
|
self._eventType = eventType
|
|
|
|
self.notify = wx.NotifyEvent(eventType, eventId)
|
|
self._oldnotebook = -1
|
|
self._newnotebook = -1
|
|
|
|
|
|
def GetNotebook(self):
|
|
""" Returns the new notebook. """
|
|
|
|
return self._newnotebook
|
|
|
|
|
|
def GetOldNotebook(self):
|
|
""" Returns the old notebook. """
|
|
|
|
return self._oldnotebook
|
|
|
|
|
|
def SetNotebook(self, notebook):
|
|
"""
|
|
Sets the new notebook.
|
|
|
|
:param `notebook`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
self._newnotebook = notebook
|
|
|
|
|
|
def SetOldNotebook(self, old):
|
|
"""
|
|
Sets the old notebook.
|
|
|
|
:param `notebook`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
self._oldnotebook = old
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class TabNavigatorWindow
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class TabNavigatorWindow(wx.Dialog):
|
|
"""
|
|
This class is used to create a modal dialog that enables `Smart Tabbing`,
|
|
similar to what you would get by hitting ``Alt`` + ``Tab`` on Windows.
|
|
"""
|
|
|
|
def __init__(self, parent=None, icon=None):
|
|
"""
|
|
Default class constructor.
|
|
Used internally.
|
|
|
|
:param `parent`: the :class:`TabNavigatorWindow` parent window;
|
|
:param `icon`: a valid :class:`Bitmap` object representing the icon to be displayed
|
|
in the :class:`TabNavigatorWindow`.
|
|
"""
|
|
|
|
wx.Dialog.__init__(self, parent, wx.ID_ANY, "", style=0)
|
|
|
|
self._selectedItem = -1
|
|
self._indexMap = []
|
|
|
|
if icon is None:
|
|
self._bmp = Mondrian.GetBitmap()
|
|
else:
|
|
self._bmp = icon
|
|
|
|
sz = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
self._listBox = wx.ListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, 150), [], wx.LB_SINGLE | wx.NO_BORDER)
|
|
|
|
mem_dc = wx.MemoryDC()
|
|
mem_dc.SelectObject(wx.Bitmap(1,1))
|
|
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
font.SetWeight(wx.BOLD)
|
|
mem_dc.SetFont(font)
|
|
|
|
panelHeight = mem_dc.GetCharHeight()
|
|
panelHeight += 4 # Place a spacer of 2 pixels
|
|
|
|
# Out signpost bitmap is 24 pixels
|
|
if panelHeight < 24:
|
|
panelHeight = 24
|
|
|
|
self._panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, panelHeight))
|
|
|
|
sz.Add(self._panel)
|
|
sz.Add(self._listBox, 1, wx.EXPAND)
|
|
|
|
self.SetSizer(sz)
|
|
|
|
# Connect events to the list box
|
|
self._listBox.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
|
|
self._listBox.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
|
|
self._listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnItemSelected)
|
|
|
|
# Connect paint event to the panel
|
|
self._panel.Bind(wx.EVT_PAINT, self.OnPanelPaint)
|
|
self._panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnPanelEraseBg)
|
|
|
|
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))
|
|
self._listBox.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))
|
|
self.PopulateListControl(parent)
|
|
|
|
self.GetSizer().Fit(self)
|
|
self.GetSizer().SetSizeHints(self)
|
|
self.GetSizer().Layout()
|
|
self.Centre()
|
|
|
|
# Set focus on the list box to avoid having to click on it to change
|
|
# the tab selection under GTK.
|
|
self._listBox.SetFocus()
|
|
|
|
|
|
def OnKeyUp(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KEY_UP`` for the :class:`TabNavigatorWindow`.
|
|
|
|
:param `event`: a :class:`KeyEvent` event to be processed.
|
|
"""
|
|
|
|
if event.GetKeyCode() == wx.WXK_CONTROL:
|
|
self.CloseDialog()
|
|
|
|
|
|
def OnNavigationKey(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_NAVIGATION_KEY`` for the :class:`TabNavigatorWindow`.
|
|
|
|
:param `event`: a :class:`NavigationKeyEvent` event to be processed.
|
|
"""
|
|
|
|
selected = self._listBox.GetSelection()
|
|
bk = self.GetParent()
|
|
maxItems = bk.GetPageCount()
|
|
|
|
if event.GetDirection():
|
|
|
|
# Select next page
|
|
if selected == maxItems - 1:
|
|
itemToSelect = 0
|
|
else:
|
|
itemToSelect = selected + 1
|
|
|
|
else:
|
|
|
|
# Previous page
|
|
if selected == 0:
|
|
itemToSelect = maxItems - 1
|
|
else:
|
|
itemToSelect = selected - 1
|
|
|
|
self._listBox.SetSelection(itemToSelect)
|
|
|
|
|
|
def PopulateListControl(self, book):
|
|
"""
|
|
Populates the :class:`TabNavigatorWindow` listbox with a list of tabs.
|
|
|
|
:param `book`: an instance of :class:`FlatNotebook` containing the tabs to be
|
|
displayed in the listbox.
|
|
"""
|
|
|
|
selection = book.GetSelection()
|
|
count = book.GetPageCount()
|
|
|
|
self._listBox.Append(book.GetPageText(selection))
|
|
self._indexMap.append(selection)
|
|
|
|
prevSel = book.GetPreviousSelection()
|
|
|
|
if prevSel != wx.NOT_FOUND:
|
|
|
|
# Insert the previous selection as second entry
|
|
self._listBox.Append(book.GetPageText(prevSel))
|
|
self._indexMap.append(prevSel)
|
|
|
|
for c in range(count):
|
|
|
|
# Skip selected page
|
|
if c == selection:
|
|
continue
|
|
|
|
# Skip previous selected page as well
|
|
if c == prevSel:
|
|
continue
|
|
|
|
self._listBox.Append(book.GetPageText(c))
|
|
self._indexMap.append(c)
|
|
|
|
# Select the next entry after the current selection
|
|
self._listBox.SetSelection(0)
|
|
dummy = wx.NavigationKeyEvent()
|
|
dummy.SetDirection(True)
|
|
self.OnNavigationKey(dummy)
|
|
|
|
|
|
def OnItemSelected(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_LISTBOX_DCLICK`` for the :class:`TabNavigatorWindow`.
|
|
|
|
:param `event`: a :class:`ListEvent` event to be processed.
|
|
"""
|
|
|
|
self.CloseDialog()
|
|
|
|
|
|
def CloseDialog(self):
|
|
"""
|
|
Closes the :class:`TabNavigatorWindow` dialog, setting the new selection in
|
|
:class:`FlatNotebook`.
|
|
"""
|
|
|
|
bk = self.GetParent()
|
|
self._selectedItem = self._listBox.GetSelection()
|
|
iter = self._indexMap[self._selectedItem]
|
|
bk._pages.FireEvent(iter)
|
|
self.EndModal(wx.ID_OK)
|
|
|
|
|
|
def OnPanelPaint(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_PAINT`` for the :class:`TabNavigatorWindow` top panel.
|
|
|
|
:param `event`: a :class:`PaintEvent` event to be processed.
|
|
"""
|
|
|
|
dc = wx.PaintDC(self._panel)
|
|
rect = self._panel.GetClientRect()
|
|
|
|
bmp = wx.Bitmap(rect.width, rect.height)
|
|
|
|
mem_dc = wx.MemoryDC()
|
|
mem_dc.SelectObject(bmp)
|
|
|
|
endColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)
|
|
startColour = LightColour(endColour, 50)
|
|
PaintStraightGradientBox(mem_dc, rect, startColour, endColour)
|
|
|
|
# Draw the caption title and place the bitmap
|
|
# get the bitmap optimal position, and draw it
|
|
bmpPt, txtPt = wx.Point(), wx.Point()
|
|
bmpPt.y = (rect.height - self._bmp.GetHeight())/2
|
|
bmpPt.x = 3
|
|
mem_dc.DrawBitmap(self._bmp, bmpPt.x, bmpPt.y, True)
|
|
|
|
# get the text position, and draw it
|
|
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
font.SetWeight(wx.BOLD)
|
|
mem_dc.SetFont(font)
|
|
fontHeight = mem_dc.GetCharHeight()
|
|
|
|
txtPt.x = bmpPt.x + self._bmp.GetWidth() + 4
|
|
txtPt.y = (rect.height - fontHeight)/2
|
|
mem_dc.SetTextForeground(wx.WHITE)
|
|
mem_dc.DrawText("Opened tabs:", txtPt.x, txtPt.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
dc.DrawBitmap(bmp, 0, 0)
|
|
|
|
|
|
def OnPanelEraseBg(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ERASE_BACKGROUND`` for the :class:`TabNavigatorWindow` top panel.
|
|
|
|
:param `event`: a :class:`EraseEvent` event to be processed.
|
|
|
|
:note: This method is intentionally empty to reduce flicker.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBRenderer
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBRenderer(object):
|
|
"""
|
|
Parent class for the 6 renderers defined: `Standard`, `VC71`, `Fancy`, `Firefox 2`,
|
|
`VC8` and `Ribbon`. This class implements the common methods of all 6 renderers.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
self._tabHeight = None
|
|
|
|
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 GetLeftButtonPos(self, pageContainer):
|
|
"""
|
|
Returns the left button position in the navigation area.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
if agwStyle & FNB_NO_X_BUTTON:
|
|
return clientWidth - 38
|
|
else:
|
|
return clientWidth - 54
|
|
|
|
|
|
def GetRightButtonPos(self, pageContainer):
|
|
"""
|
|
Returns the right button position in the navigation area.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
if agwStyle & FNB_NO_X_BUTTON:
|
|
return clientWidth - 22
|
|
else:
|
|
return clientWidth - 38
|
|
|
|
|
|
def GetDropArrowButtonPos(self, pageContainer):
|
|
"""
|
|
Returns the drop down button position in the navigation area.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
return self.GetRightButtonPos(pageContainer)
|
|
|
|
|
|
def GetXPos(self, pageContainer):
|
|
"""
|
|
Returns the 'X' button position in the navigation area.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
if agwStyle & FNB_NO_X_BUTTON:
|
|
return clientWidth
|
|
else:
|
|
return clientWidth - 22
|
|
|
|
|
|
def GetButtonsAreaLength(self, pageContainer):
|
|
"""
|
|
Returns the navigation area width.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
|
|
# ''
|
|
if agwStyle & FNB_NO_NAV_BUTTONS and agwStyle & FNB_NO_X_BUTTON and not agwStyle & FNB_DROPDOWN_TABS_LIST:
|
|
return 0
|
|
|
|
# 'x'
|
|
elif agwStyle & FNB_NO_NAV_BUTTONS and not agwStyle & FNB_NO_X_BUTTON and not agwStyle & FNB_DROPDOWN_TABS_LIST:
|
|
return 22
|
|
|
|
# '<>'
|
|
if not agwStyle & FNB_NO_NAV_BUTTONS and agwStyle & FNB_NO_X_BUTTON and not agwStyle & FNB_DROPDOWN_TABS_LIST:
|
|
return 53 - 16
|
|
|
|
# 'vx'
|
|
if agwStyle & FNB_DROPDOWN_TABS_LIST and not agwStyle & FNB_NO_X_BUTTON:
|
|
return 22 + 16
|
|
|
|
# 'v'
|
|
if agwStyle & FNB_DROPDOWN_TABS_LIST and agwStyle & FNB_NO_X_BUTTON:
|
|
return 22
|
|
|
|
# '<>x'
|
|
return 53
|
|
|
|
|
|
def DrawArrowAccordingToState(self, dc, pc, rect):
|
|
"""
|
|
Draws the left and right scrolling arrows.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `pc`: an instance of :class:`FlatNotebook`;
|
|
:param `rect`: the client rectangle containing the scrolling arrows.
|
|
"""
|
|
|
|
lightFactor = (pc.HasAGWFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0]
|
|
PaintStraightGradientBox(dc, rect, pc._tabAreaColour, LightColour(pc._tabAreaColour, lightFactor))
|
|
|
|
|
|
def DrawLeftArrow(self, pageContainer, dc):
|
|
"""
|
|
Draws the left navigation arrow.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
if agwStyle & FNB_NO_NAV_BUTTONS:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
if agwStyle & FNB_NAV_BUTTONS_WHEN_NEEDED:
|
|
if pc._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1) and pc._nFrom == 0:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
if pc._nLeftButtonStatus == FNB_BTN_HOVER:
|
|
arrowBmp = wx.Bitmap(left_arrow_hilite_xpm)
|
|
elif pc._nLeftButtonStatus == FNB_BTN_PRESSED:
|
|
arrowBmp = wx.Bitmap(left_arrow_pressed_xpm)
|
|
else:
|
|
arrowBmp = wx.Bitmap(left_arrow_xpm)
|
|
|
|
if pc._nFrom == 0:
|
|
# Handle disabled arrow
|
|
arrowBmp = wx.Bitmap(left_arrow_disabled_xpm)
|
|
|
|
arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOUR))
|
|
|
|
# Erase old bitmap
|
|
posx = self.GetLeftButtonPos(pc)
|
|
self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(arrowBmp, posx, 6, True)
|
|
|
|
|
|
def DrawRightArrow(self, pageContainer, dc):
|
|
"""
|
|
Draws the right navigation arrow.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
if agwStyle & FNB_NO_NAV_BUTTONS:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
if agwStyle & FNB_NAV_BUTTONS_WHEN_NEEDED:
|
|
if pc._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1) and pc._nFrom == 0:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
if pc._nRightButtonStatus == FNB_BTN_HOVER:
|
|
arrowBmp = wx.Bitmap(right_arrow_hilite_xpm)
|
|
elif pc._nRightButtonStatus == FNB_BTN_PRESSED:
|
|
arrowBmp = wx.Bitmap(right_arrow_pressed_xpm)
|
|
else:
|
|
arrowBmp = wx.Bitmap(right_arrow_xpm)
|
|
|
|
# Check if the right most tab is visible, if it is
|
|
# don't rotate right anymore
|
|
if pc._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1):
|
|
arrowBmp = wx.Bitmap(right_arrow_disabled_xpm)
|
|
|
|
arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOUR))
|
|
|
|
# erase old bitmap
|
|
posx = self.GetRightButtonPos(pc)
|
|
self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(arrowBmp, posx, 6, True)
|
|
|
|
|
|
def DrawDropDownArrow(self, pageContainer, dc):
|
|
"""
|
|
Draws the drop-down arrow in the navigation area.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
# Check if this style is enabled
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
if not agwStyle & FNB_DROPDOWN_TABS_LIST:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
if pc._nArrowDownButtonStatus == FNB_BTN_HOVER:
|
|
downBmp = wx.Bitmap(down_arrow_hilite_xpm)
|
|
elif pc._nArrowDownButtonStatus == FNB_BTN_PRESSED:
|
|
downBmp = wx.Bitmap(down_arrow_pressed_xpm)
|
|
else:
|
|
downBmp = wx.Bitmap(down_arrow_xpm)
|
|
|
|
downBmp.SetMask(wx.Mask(downBmp, MASK_COLOUR))
|
|
|
|
# erase old bitmap
|
|
posx = self.GetDropArrowButtonPos(pc)
|
|
self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(downBmp, posx, 6, True)
|
|
|
|
|
|
def DrawX(self, pageContainer, dc):
|
|
"""
|
|
Draw the 'X' navigation button in the navigation area.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
# Check if this style is enabled
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
if agwStyle & FNB_NO_X_BUTTON:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
if pc._nXButtonStatus == FNB_BTN_HOVER:
|
|
xbmp = wx.Bitmap(x_button_hilite_xpm)
|
|
elif pc._nXButtonStatus == FNB_BTN_PRESSED:
|
|
xbmp = wx.Bitmap(x_button_pressed_xpm)
|
|
else:
|
|
xbmp = wx.Bitmap(x_button_xpm)
|
|
|
|
xbmp.SetMask(wx.Mask(xbmp, MASK_COLOUR))
|
|
|
|
# erase old bitmap
|
|
posx = self.GetXPos(pc)
|
|
self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(xbmp, posx, 6, True)
|
|
|
|
|
|
def DrawTabX(self, pageContainer, dc, rect, tabIdx, btnStatus):
|
|
"""
|
|
Draws the 'X' in the selected tab.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `rect`: the current tab client rectangle;
|
|
:param `tabIdx`: the index of the current tab;
|
|
:param `btnStatus`: the status of the 'X' button in the current tab.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
if not pc.HasAGWFlag(FNB_X_ON_TAB):
|
|
return
|
|
|
|
# We draw the 'x' on the active tab only
|
|
if tabIdx != pc.GetSelection() or tabIdx < 0:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
|
|
if btnStatus == FNB_BTN_HOVER:
|
|
xBmp = wx.Bitmap(x_button_hilite_xpm)
|
|
elif btnStatus == FNB_BTN_PRESSED:
|
|
xBmp = wx.Bitmap(x_button_pressed_xpm)
|
|
else:
|
|
xBmp = wx.Bitmap(x_button_xpm)
|
|
|
|
# Set the masking
|
|
xBmp.SetMask(wx.Mask(xBmp, MASK_COLOUR))
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(xBmp, rect.x, rect.y, True)
|
|
|
|
# Update the vector
|
|
rr = wx.Rect(rect.x, rect.y, 14, 13)
|
|
pc._pagesInfoVec[tabIdx].SetXRect(rr)
|
|
|
|
|
|
def DrawTabsLine(self, pageContainer, dc, selTabX1=-1, selTabX2=-1):
|
|
"""
|
|
Draws a line over the tabs.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `selTabX1`: first x coordinate of the tab line;
|
|
:param `selTabX2`: second x coordinate of the tab line.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
clntRect = pc.GetClientRect()
|
|
clientRect3 = wx.Rect(0, 0, clntRect.width, clntRect.height)
|
|
|
|
if pc.HasAGWFlag(FNB_FF2):
|
|
if not pc.HasAGWFlag(FNB_BOTTOM):
|
|
fillColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
|
|
else:
|
|
fillColour = wx.WHITE
|
|
|
|
dc.SetPen(wx.Pen(fillColour))
|
|
|
|
if pc.HasAGWFlag(FNB_BOTTOM):
|
|
|
|
dc.DrawLine(1, 0, clntRect.width-1, 0)
|
|
dc.DrawLine(1, 1, clntRect.width-1, 1)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
dc.DrawLine(1, 2, clntRect.width-1, 2)
|
|
|
|
dc.SetPen(wx.Pen(fillColour))
|
|
dc.DrawLine(selTabX1 + 2, 2, selTabX2 - 1, 2)
|
|
|
|
else:
|
|
|
|
dc.DrawLine(1, clntRect.height, clntRect.width-1, clntRect.height)
|
|
dc.DrawLine(1, clntRect.height-1, clntRect.width-1, clntRect.height-1)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
dc.DrawLine(1, clntRect.height-2, clntRect.width-1, clntRect.height-2)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
|
|
dc.DrawLine(selTabX1 + 2, clntRect.height-2, selTabX2-1, clntRect.height-2)
|
|
|
|
else:
|
|
|
|
if pc.HasAGWFlag(FNB_BOTTOM):
|
|
|
|
clientRect = wx.Rect(0, 2, clntRect.width, clntRect.height - 2)
|
|
clientRect2 = wx.Rect(0, 1, clntRect.width, clntRect.height - 1)
|
|
|
|
else:
|
|
|
|
clientRect = wx.Rect(0, 0, clntRect.width, clntRect.height - 2)
|
|
clientRect2 = wx.Rect(0, 0, clntRect.width, clntRect.height - 1)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(wx.Pen(pc.GetSingleLineBorderColour()))
|
|
dc.DrawRectangle(clientRect2)
|
|
dc.DrawRectangle(clientRect3)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
dc.DrawRectangle(clientRect)
|
|
|
|
if not pc.HasAGWFlag(FNB_TABS_BORDER_SIMPLE):
|
|
|
|
dc.SetPen(wx.Pen((pc.HasAGWFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc._tabAreaColour])[0]))
|
|
dc.DrawLine(0, 0, 0, clientRect.height+1)
|
|
|
|
if pc.HasAGWFlag(FNB_BOTTOM):
|
|
|
|
dc.DrawLine(0, clientRect.height+1, clientRect.width, clientRect.height+1)
|
|
|
|
else:
|
|
|
|
dc.DrawLine(0, 0, clientRect.width, 0)
|
|
|
|
dc.DrawLine(clientRect.width - 1, 0, clientRect.width - 1, clientRect.height+1)
|
|
|
|
|
|
def CalcTabWidth(self, pageContainer, tabIdx, tabHeight):
|
|
"""
|
|
Calculates the width of the input tab.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `tabIdx`: the index of the input tab;
|
|
:param `tabHeight`: the height of the tab.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
dc = wx.MemoryDC()
|
|
dc.SelectObject(wx.Bitmap(1,1))
|
|
|
|
boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
|
|
if pc.IsDefaultTabs():
|
|
shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
|
|
# Calculate the text length using the bold font, so when selecting a tab
|
|
# its width will not change
|
|
dc.SetFont(boldFont)
|
|
width, pom = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
|
|
# Set a minimum size to a tab
|
|
if width < 20:
|
|
width = 20
|
|
|
|
tabWidth = 2*pc._pParent.GetPadding() + width
|
|
|
|
# Style to add a small 'x' button on the top right
|
|
# of the tab
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
# The xpm image that contains the 'x' button is 9 pixels
|
|
spacer = 9
|
|
if pc.HasAGWFlag(FNB_VC8):
|
|
spacer = 4
|
|
|
|
tabWidth += pc._pParent.GetPadding() + spacer
|
|
|
|
if pc.IsDefaultTabs():
|
|
# Default style
|
|
tabWidth += 2*shapePoints
|
|
|
|
hasImage = pc._ImageList != None and pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
|
|
# For VC71 style, we only add the icon size (16 pixels)
|
|
if hasImage:
|
|
|
|
if not pc.IsDefaultTabs():
|
|
tabWidth += 16 + pc._pParent.GetPadding()
|
|
else:
|
|
# Default style
|
|
tabWidth += 16 + pc._pParent.GetPadding() + shapePoints/2
|
|
|
|
return tabWidth
|
|
|
|
|
|
def CalcTabHeight(self, pageContainer):
|
|
"""
|
|
Calculates the height of the input tab.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`.
|
|
"""
|
|
|
|
if self._tabHeight:
|
|
return self._tabHeight
|
|
|
|
pc = pageContainer
|
|
dc = wx.MemoryDC()
|
|
dc.SelectObject(wx.Bitmap(1,1))
|
|
|
|
# For GTK it seems that we must do this steps in order
|
|
# for the tabs will get the proper height on initialization
|
|
# on MSW, preforming these steps yields wierd results
|
|
normalFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont = normalFont
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
dc.SetFont(boldFont)
|
|
|
|
height = dc.GetCharHeight()
|
|
|
|
tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
# On GTK the tabs are should be larger
|
|
tabHeight += 6
|
|
|
|
self._tabHeight = tabHeight
|
|
|
|
return tabHeight
|
|
|
|
|
|
def DrawTabs(self, pageContainer, dc):
|
|
"""
|
|
Actually draws the tabs in :class:`FlatNotebook`.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
if "__WXMAC__" in wx.PlatformInfo:
|
|
# Works well on MSW & GTK, however this lines should be skipped on MAC
|
|
if not pc._pagesInfoVec or pc._nFrom >= len(pc._pagesInfoVec):
|
|
pc.Hide()
|
|
return
|
|
|
|
# Get the text hight
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
agwStyle = pc.GetParent().GetAGWWindowStyleFlag()
|
|
|
|
# Calculate the number of rows required for drawing the tabs
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
# Set the maximum client size
|
|
pc.SetSizeHints(self.GetButtonsAreaLength(pc), tabHeight)
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
|
|
if agwStyle & FNB_VC71:
|
|
backBrush = wx.Brush(wx.Colour(247, 243, 233))
|
|
else:
|
|
backBrush = wx.Brush(pc._tabAreaColour)
|
|
|
|
noselBrush = wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
|
selBrush = wx.Brush(pc._activeTabColour)
|
|
|
|
size = pc.GetSize()
|
|
|
|
# Background
|
|
dc.SetTextBackground((agwStyle & FNB_VC71 and [wx.Colour(247, 243, 233)] or [pc.GetBackgroundColour()])[0])
|
|
dc.SetTextForeground(pc._activeTextColour)
|
|
dc.SetBrush(backBrush)
|
|
|
|
# If border style is set, set the pen to be border pen
|
|
if pc.HasAGWFlag(FNB_TABS_BORDER_SIMPLE):
|
|
dc.SetPen(borderPen)
|
|
else:
|
|
colr = (pc.HasAGWFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc.GetBackgroundColour()])[0]
|
|
dc.SetPen(wx.Pen(colr))
|
|
|
|
if pc.HasAGWFlag(FNB_FF2):
|
|
lightFactor = (pc.HasAGWFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0]
|
|
PaintStraightGradientBox(dc, pc.GetClientRect(), pc._tabAreaColour, LightColour(pc._tabAreaColour, lightFactor))
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
|
|
dc.DrawRectangle(0, 0, size.x, size.y)
|
|
|
|
# We always draw the bottom/upper line of the tabs
|
|
# regradless the style
|
|
dc.SetPen(borderPen)
|
|
|
|
if not pc.HasAGWFlag(FNB_FF2):
|
|
self.DrawTabsLine(pc, dc)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(borderPen)
|
|
|
|
if pc.HasAGWFlag(FNB_VC71):
|
|
|
|
greyLineYVal = (pc.HasAGWFlag(FNB_BOTTOM) and [0] or [size.y - 2])[0]
|
|
whiteLineYVal = (pc.HasAGWFlag(FNB_BOTTOM) and [3] or [size.y - 3])[0]
|
|
|
|
pen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))
|
|
dc.SetPen(pen)
|
|
|
|
# Draw thik grey line between the windows area and
|
|
# the tab area
|
|
for num in range(3):
|
|
dc.DrawLine(0, greyLineYVal + num, size.x, greyLineYVal + num)
|
|
|
|
wbPen = (pc.HasAGWFlag(FNB_BOTTOM) and [wx.BLACK_PEN] or [wx.WHITE_PEN])[0]
|
|
dc.SetPen(wbPen)
|
|
dc.DrawLine(1, whiteLineYVal, size.x - 1, whiteLineYVal)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(borderPen)
|
|
|
|
# Draw labels
|
|
normalFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
dc.SetFont(boldFont)
|
|
|
|
posx = pc._pParent.GetPadding()
|
|
|
|
# Update all the tabs from 0 to 'pc._nFrom' to be non visible
|
|
for i in range(pc._nFrom):
|
|
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
count = pc._nFrom
|
|
|
|
#----------------------------------------------------------
|
|
# Go over and draw the visible tabs
|
|
#----------------------------------------------------------
|
|
x1 = x2 = -1
|
|
for i in range(pc._nFrom, len(pc._pagesInfoVec)):
|
|
|
|
dc.SetPen(borderPen)
|
|
|
|
if not pc.HasAGWFlag(FNB_FF2):
|
|
dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0])
|
|
|
|
# Now set the font to the correct font
|
|
dc.SetFont((i==pc.GetSelection() and [boldFont] or [normalFont])[0])
|
|
|
|
# Add the padding to the tab width
|
|
# Tab width:
|
|
# +-----------------------------------------------------------+
|
|
# | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
|
|
# +-----------------------------------------------------------+
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
|
|
# Check if we can draw more
|
|
if posx + tabWidth + self.GetButtonsAreaLength(pc) >= clientWidth:
|
|
break
|
|
|
|
count = count + 1
|
|
|
|
# By default we clean the tab region
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Clean the 'x' buttn on the tab.
|
|
# A 'Clean' rectangle, is a rectangle with width or height
|
|
# with values lower than or equal to 0
|
|
pc._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1))
|
|
|
|
# Draw the tab (border, text, image & 'x' on tab)
|
|
self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus)
|
|
|
|
if pc.GetSelection() == i:
|
|
x1 = posx
|
|
x2 = posx + tabWidth + 2
|
|
|
|
# Restore the text forground
|
|
dc.SetTextForeground(pc._activeTextColour)
|
|
|
|
# Update the tab position & size
|
|
posy = (pc.HasAGWFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(posx, posy))
|
|
pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight))
|
|
self.DrawFocusRectangle(dc, pc, pc._pagesInfoVec[i])
|
|
|
|
posx += tabWidth
|
|
|
|
# Update all tabs that can not fit into the screen as non-visible
|
|
for i in range(count, len(pc._pagesInfoVec)):
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Draw the left/right/close buttons
|
|
# Left arrow
|
|
self.DrawLeftArrow(pc, dc)
|
|
self.DrawRightArrow(pc, dc)
|
|
self.DrawX(pc, dc)
|
|
self.DrawDropDownArrow(pc, dc)
|
|
|
|
if pc.HasAGWFlag(FNB_FF2):
|
|
self.DrawTabsLine(pc, dc, x1, x2)
|
|
|
|
|
|
def DrawFocusRectangle(self, dc, pageContainer, page):
|
|
"""
|
|
Draws a focus rectangle like the native :class:`Notebook`.
|
|
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `page`: an instance of :class:`PageInfo`, representing a page in the notebook.
|
|
"""
|
|
|
|
if not page._hasFocus:
|
|
return
|
|
|
|
tabPos = wx.Point(*page.GetPosition())
|
|
if pageContainer.GetParent().GetAGWWindowStyleFlag() & FNB_VC8:
|
|
vc8ShapeLen = self.CalcTabHeight(pageContainer) - VERTICAL_BORDER_PADDING - 2
|
|
tabPos.x += vc8ShapeLen
|
|
|
|
rect = wx.Rect(tabPos, page.GetSize())
|
|
rect = wx.Rect(rect.x+2, rect.y+2, rect.width-4, rect.height-8)
|
|
|
|
if wx.Platform == '__WXMAC__':
|
|
rect.SetWidth(rect.GetWidth() + 1)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(self._focusPen)
|
|
dc.DrawRoundedRectangle(rect, 2)
|
|
|
|
|
|
def DrawDragHint(self, pc, tabIdx):
|
|
"""
|
|
Draws tab drag hint, the default implementation is to do nothing.
|
|
You can override this function to provide a nice feedback to user.
|
|
|
|
:param `pc`: an instance of :class:`FlatNotebook`;
|
|
:param `tabIdx`: the index of the tab we are dragging.
|
|
|
|
:note: To show your own custom drag and drop UI feedback, you must override
|
|
this method in your derived class.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
def NumberTabsCanFit(self, pageContainer, fr=-1):
|
|
"""
|
|
Calculates the number of tabs that can fit on the available space on screen.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `fr`: the current first visible tab.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
vTabInfo = []
|
|
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
|
|
# The drawing starts from posx
|
|
posx = pc._pParent.GetPadding()
|
|
|
|
if fr < 0:
|
|
fr = pc._nFrom
|
|
|
|
for i in range(fr, len(pc._pagesInfoVec)):
|
|
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
if posx + tabWidth + self.GetButtonsAreaLength(pc) >= clientWidth:
|
|
break;
|
|
|
|
# Add a result to the returned vector
|
|
tabRect = wx.Rect(posx, VERTICAL_BORDER_PADDING, tabWidth , tabHeight)
|
|
vTabInfo.append(tabRect)
|
|
|
|
# Advance posx
|
|
posx += tabWidth + FNB_HEIGHT_SPACER
|
|
|
|
return vTabInfo
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBRendererMgr
|
|
# A manager that handles all the renderers defined below and calls the
|
|
# appropriate one when drawing is needed
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBRendererMgr(object):
|
|
"""
|
|
This class represents a manager that handles all the 6 renderers defined
|
|
and calls the appropriate one when drawing is needed.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
# register renderers
|
|
|
|
self._renderers = {}
|
|
self._renderers.update({-1: FNBRendererDefault()})
|
|
self._renderers.update({FNB_VC71: FNBRendererVC71()})
|
|
self._renderers.update({FNB_FANCY_TABS: FNBRendererFancy()})
|
|
self._renderers.update({FNB_VC8: FNBRendererVC8()})
|
|
self._renderers.update({FNB_RIBBON_TABS: FNBRendererRibbonTabs()})
|
|
self._renderers.update({FNB_FF2: FNBRendererFirefox2()})
|
|
|
|
|
|
def GetRenderer(self, style):
|
|
"""
|
|
Returns the current renderer based on the style selected.
|
|
|
|
:param `style`: represents one of the 6 implemented styles for :class:`FlatNotebook`,
|
|
namely one of these bits:
|
|
|
|
===================== ========= ======================
|
|
Tabs style Hex Value Description
|
|
===================== ========= ======================
|
|
``FNB_VC71`` 0x1 Use Visual Studio 2003 (VC7.1) style for tabs
|
|
``FNB_FANCY_TABS`` 0x2 Use fancy style - square tabs filled with gradient colouring
|
|
``FNB_VC8`` 0x100 Use Visual Studio 2005 (VC8) style for tabs
|
|
``FNB_FF2`` 0x20000 Use Firefox 2 style for tabs
|
|
``FNB_RIBBON_TABS`` 0x80000 Use the Ribbon Tabs style to render the tabs
|
|
===================== ========= ======================
|
|
|
|
"""
|
|
|
|
if style & FNB_VC71:
|
|
return self._renderers[FNB_VC71]
|
|
|
|
if style & FNB_FANCY_TABS:
|
|
return self._renderers[FNB_FANCY_TABS]
|
|
|
|
if style & FNB_VC8:
|
|
return self._renderers[FNB_VC8]
|
|
|
|
if style & FNB_FF2:
|
|
return self._renderers[FNB_FF2]
|
|
|
|
if style & FNB_RIBBON_TABS:
|
|
return self._renderers[FNB_RIBBON_TABS]
|
|
|
|
# the default is to return the default renderer
|
|
return self._renderers[-1]
|
|
|
|
|
|
#------------------------------------------
|
|
# Default renderer
|
|
#------------------------------------------
|
|
|
|
class FNBRendererDefault(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the standard renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
"""
|
|
Draws a tab using the `Standard` style.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `posx`: the x position of the tab;
|
|
:param `tabIdx`: the index of the tab;
|
|
:param `tabWidth`: the tab's width;
|
|
:param `tabHeight`: the tab's height;
|
|
:param `btnStatus`: the status of the 'X' button inside this tab.
|
|
"""
|
|
|
|
# Default style
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
tabPoints = [wx.Point() for ii in range(7)]
|
|
tabPoints[0].x = posx
|
|
tabPoints[0].y = (pc.HasAGWFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[1].x = int(posx+(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
tabPoints[1].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x+2
|
|
tabPoints[2].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[3].x = int(posx+tabWidth-(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))-2
|
|
tabPoints[3].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[4].x = tabPoints[3].x+2
|
|
tabPoints[4].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[5].x = int(tabPoints[4].x+(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
tabPoints[5].y = (pc.HasAGWFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[6].x = tabPoints[0].x
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
# Draw the tab as rounded rectangle
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
else:
|
|
|
|
if tabIdx != pc.GetSelection() - 1:
|
|
|
|
# Draw a vertical line to the right of the text
|
|
pt1x = tabPoints[5].x
|
|
pt1y = (pc.HasAGWFlag(FNB_BOTTOM) and [4] or [tabHeight - 6])[0]
|
|
pt2x = tabPoints[5].x
|
|
pt2y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - 4] or [4])[0]
|
|
dc.DrawLine(pt1x, pt1y, pt2x, pt2y)
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
savePen = dc.GetPen()
|
|
whitePen = wx.Pen(wx.WHITE)
|
|
whitePen.SetWidth(1)
|
|
dc.SetPen(whitePen)
|
|
|
|
secPt = wx.Point(tabPoints[5].x + 1, tabPoints[5].y)
|
|
dc.DrawLine(tabPoints[0].x, tabPoints[0].y, secPt.x, secPt.y)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(savePen)
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasAGWFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*pc._pParent._nPadding + 16 + shapePoints/2
|
|
else:
|
|
textOffset = pc._pParent._nPadding + shapePoints/2
|
|
|
|
textOffset += 2
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
pageTextColour = pc._pParent.GetPageTextColour(tabIdx)
|
|
if pageTextColour is not None:
|
|
dc.SetTextForeground(pageTextColour)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------
|
|
# Firefox2 renderer
|
|
#------------------------------------------
|
|
class FNBRendererFirefox2(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the `Firefox 2` renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
"""
|
|
Draws a tab using the `Firefox 2` style.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `posx`: the x position of the tab;
|
|
:param `tabIdx`: the index of the tab;
|
|
:param `tabWidth`: the tab's width;
|
|
:param `tabHeight`: the tab's height;
|
|
:param `btnStatus`: the status of the 'X' button inside this tab.
|
|
"""
|
|
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
tabPoints = [wx.Point() for indx in range(7)]
|
|
tabPoints[0].x = posx + 2
|
|
tabPoints[0].y = (pc.HasAGWFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[1].x = tabPoints[0].x
|
|
tabPoints[1].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x+2
|
|
tabPoints[2].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[3].x = posx + tabWidth - 2
|
|
tabPoints[3].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[4].x = tabPoints[3].x + 2
|
|
tabPoints[4].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[5].x = tabPoints[4].x
|
|
tabPoints[5].y = (pc.HasAGWFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[6].x = tabPoints[0].x
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
#------------------------------------
|
|
# Paint the tab with gradient
|
|
#------------------------------------
|
|
rr = wx.Rect(tabPoints[2], tabPoints[5])
|
|
DrawButton(dc, rr, pc.GetSelection() == tabIdx , not pc.HasAGWFlag(FNB_BOTTOM))
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(borderPen)
|
|
|
|
# Draw the tab as rounded rectangle
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasAGWFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*padding + 16 + shapePoints/2
|
|
else:
|
|
textOffset = padding + shapePoints/2
|
|
|
|
textOffset += 2
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
pageTextColour = pc._pParent.GetPageTextColour(tabIdx)
|
|
if pageTextColour is not None:
|
|
dc.SetTextForeground(pageTextColour)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------------------------------
|
|
# Visual studio 7.1
|
|
#------------------------------------------------------------------
|
|
|
|
class FNBRendererVC71(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the `VC71` renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
"""
|
|
Draws a tab using the `VC71` style.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `posx`: the x position of the tab;
|
|
:param `tabIdx`: the index of the tab;
|
|
:param `tabWidth`: the tab's width;
|
|
:param `tabHeight`: the tab's height;
|
|
:param `btnStatus`: the status of the 'X' button inside this tab.
|
|
"""
|
|
|
|
# Visual studio 7.1 style
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
dc.SetPen((tabIdx == pc.GetSelection() and [wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))] or [borderPen])[0])
|
|
dc.SetBrush((tabIdx == pc.GetSelection() and [wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))] or [wx.Brush(wx.Colour(247, 243, 233))])[0])
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
posy = (pc.HasAGWFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0]
|
|
tabH = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - 5] or [tabHeight - 3])[0]
|
|
dc.DrawRectangle(posx, posy, tabWidth, tabH)
|
|
|
|
# Draw a black line on the left side of the
|
|
# rectangle
|
|
dc.SetPen(wx.BLACK_PEN)
|
|
|
|
blackLineY1 = VERTICAL_BORDER_PADDING
|
|
blackLineY2 = tabH
|
|
dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2)
|
|
|
|
# To give the tab more 3D look we do the following
|
|
# Incase the tab is on top,
|
|
# Draw a thik white line on topof the rectangle
|
|
# Otherwise, draw a thin (1 pixel) black line at the bottom
|
|
|
|
pen = wx.Pen((pc.HasAGWFlag(FNB_BOTTOM) and [wx.BLACK] or [wx.WHITE])[0])
|
|
dc.SetPen(pen)
|
|
whiteLinePosY = (pc.HasAGWFlag(FNB_BOTTOM) and [blackLineY2] or [VERTICAL_BORDER_PADDING ])[0]
|
|
dc.DrawLine(posx , whiteLinePosY, posx + tabWidth + 1, whiteLinePosY)
|
|
|
|
# Draw a white vertical line to the left of the tab
|
|
dc.SetPen(wx.WHITE_PEN)
|
|
if not pc.HasAGWFlag(FNB_BOTTOM):
|
|
blackLineY2 += 1
|
|
|
|
dc.DrawLine(posx, blackLineY1, posx, blackLineY2)
|
|
|
|
else:
|
|
|
|
# We dont draw a rectangle for non selected tabs, but only
|
|
# vertical line on the left
|
|
|
|
blackLineY1 = (pc.HasAGWFlag(FNB_BOTTOM) and [VERTICAL_BORDER_PADDING + 2] or [VERTICAL_BORDER_PADDING + 1])[0]
|
|
blackLineY2 = pc.GetSize().y - 5
|
|
dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2)
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasAGWFlag(FNB_BOTTOM) and [5] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*pc._pParent._nPadding + 16
|
|
else:
|
|
textOffset = pc._pParent._nPadding
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
pageTextColour = pc._pParent.GetPageTextColour(tabIdx)
|
|
if pageTextColour is not None:
|
|
dc.SetTextForeground(pageTextColour)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------------------------------
|
|
# Fancy style
|
|
#------------------------------------------------------------------
|
|
|
|
class FNBRendererFancy(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the `Fancy` renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
"""
|
|
Draws a tab using the `Fancy` style, similar to the `VC71` one but with gradients.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `posx`: the x position of the tab;
|
|
:param `tabIdx`: the index of the tab;
|
|
:param `tabWidth`: the tab's width;
|
|
:param `tabHeight`: the tab's height;
|
|
:param `btnStatus`: the status of the 'X' button inside this tab.
|
|
"""
|
|
|
|
# Fancy tabs - like with VC71 but with the following differences:
|
|
# - The Selected tab is coloured with gradient colour
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
pen = (tabIdx == pc.GetSelection() and [wx.Pen(pc._pParent.GetBorderColour())] or [wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE))])[0]
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
posy = (pc.HasAGWFlag(FNB_BOTTOM) and [2] or [VERTICAL_BORDER_PADDING])[0]
|
|
th = tabHeight - 5
|
|
|
|
rect = wx.Rect(posx, posy, tabWidth, th)
|
|
|
|
col2 = (pc.HasAGWFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourTo()] or [pc._pParent.GetGradientColourFrom()])[0]
|
|
col1 = (pc.HasAGWFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourFrom()] or [pc._pParent.GetGradientColourTo()])[0]
|
|
|
|
PaintStraightGradientBox(dc, rect, col1, col2)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(pen)
|
|
dc.DrawRectangle(rect)
|
|
|
|
# erase the bottom/top line of the rectangle
|
|
dc.SetPen(wx.Pen(pc._pParent.GetGradientColourFrom()))
|
|
if pc.HasAGWFlag(FNB_BOTTOM):
|
|
dc.DrawLine(rect.x, 2, rect.x + rect.width, 2)
|
|
else:
|
|
dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1)
|
|
|
|
else:
|
|
|
|
# We dont draw a rectangle for non selected tabs, but only
|
|
# vertical line on the left
|
|
dc.SetPen(borderPen)
|
|
dc.DrawLine(posx + tabWidth, VERTICAL_BORDER_PADDING + 3, posx + tabWidth, tabHeight - 4)
|
|
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasAGWFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*pc._pParent._nPadding + 16
|
|
else:
|
|
textOffset = pc._pParent._nPadding
|
|
|
|
textOffset += 2
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
pageTextColour = pc._pParent.GetPageTextColour(tabIdx)
|
|
if pageTextColour is not None:
|
|
dc.SetTextForeground(pageTextColour)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------------------------------
|
|
# Visual studio 2005 (VS8)
|
|
#------------------------------------------------------------------
|
|
class FNBRendererVC8(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the `VC8` renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
self._first = True
|
|
self._factor = 1
|
|
|
|
|
|
def DrawTabs(self, pageContainer, dc):
|
|
"""
|
|
Draws all the tabs using `VC8` style.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
if "__WXMAC__" in wx.PlatformInfo:
|
|
# Works well on MSW & GTK, however this lines should be skipped on MAC
|
|
if not pc._pagesInfoVec or pc._nFrom >= len(pc._pagesInfoVec):
|
|
pc.Hide()
|
|
return
|
|
|
|
# Get the text hight
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
|
|
# Set the font for measuring the tab height
|
|
normalFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
|
|
# Calculate the number of rows required for drawing the tabs
|
|
rect = pc.GetClientRect()
|
|
|
|
# Set the maximum client size
|
|
pc.SetSizeHints(self.GetButtonsAreaLength(pc), tabHeight)
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
|
|
# Create brushes
|
|
backBrush = wx.Brush(pc._tabAreaColour)
|
|
noselBrush = wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
|
selBrush = wx.Brush(pc._activeTabColour)
|
|
size = pc.GetSize()
|
|
|
|
# Background
|
|
dc.SetTextBackground(pc.GetBackgroundColour())
|
|
dc.SetTextForeground(pc._activeTextColour)
|
|
|
|
# If border style is set, set the pen to be border pen
|
|
if pc.HasAGWFlag(FNB_TABS_BORDER_SIMPLE):
|
|
dc.SetPen(borderPen)
|
|
else:
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
|
|
lightFactor = (pc.HasAGWFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0]
|
|
|
|
# For VC8 style, we colour the tab area in gradient colouring
|
|
lightcolour = LightColour(pc._tabAreaColour, lightFactor)
|
|
PaintStraightGradientBox(dc, pc.GetClientRect(), pc._tabAreaColour, lightcolour)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.DrawRectangle(0, 0, size.x, size.y)
|
|
|
|
# We always draw the bottom/upper line of the tabs
|
|
# regradless the style
|
|
dc.SetPen(borderPen)
|
|
self.DrawTabsLine(pc, dc)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(borderPen)
|
|
|
|
# Draw labels
|
|
dc.SetFont(boldFont)
|
|
|
|
# Update all the tabs from 0 to 'pc.self._nFrom' to be non visible
|
|
for i in range(pc._nFrom):
|
|
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Draw the visible tabs, in VC8 style, we draw them from right to left
|
|
vTabsInfo = self.NumberTabsCanFit(pc)
|
|
|
|
activeTabPosx = 0
|
|
activeTabWidth = 0
|
|
activeTabHeight = 0
|
|
|
|
for cur in range(len(vTabsInfo)-1, -1, -1):
|
|
|
|
# 'i' points to the index of the currently drawn tab
|
|
# in pc.GetPageInfoVector() vector
|
|
i = pc._nFrom + cur
|
|
dc.SetPen(borderPen)
|
|
dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0])
|
|
|
|
# Now set the font to the correct font
|
|
dc.SetFont((i==pc.GetSelection() and [boldFont] or [normalFont])[0])
|
|
|
|
# Add the padding to the tab width
|
|
# Tab width:
|
|
# +-----------------------------------------------------------+
|
|
# | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
|
|
# +-----------------------------------------------------------+
|
|
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
posx = vTabsInfo[cur].x
|
|
|
|
# By default we clean the tab region
|
|
# incase we use the VC8 style which requires
|
|
# the region, it will be filled by the function
|
|
# drawVc8Tab
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Clean the 'x' buttn on the tab
|
|
# 'Clean' rectanlge is a rectangle with width or height
|
|
# with values lower than or equal to 0
|
|
pc._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1))
|
|
|
|
# Draw the tab
|
|
# Incase we are drawing the active tab
|
|
# we need to redraw so it will appear on top
|
|
# of all other tabs
|
|
|
|
# when using the vc8 style, we keep the position of the active tab so we will draw it again later
|
|
if i == pc.GetSelection() and pc.HasAGWFlag(FNB_VC8):
|
|
|
|
activeTabPosx = posx
|
|
activeTabWidth = tabWidth
|
|
activeTabHeight = tabHeight
|
|
|
|
else:
|
|
|
|
self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus)
|
|
|
|
# Restore the text forground
|
|
dc.SetTextForeground(pc._activeTextColour)
|
|
|
|
# Update the tab position & size
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(posx, VERTICAL_BORDER_PADDING))
|
|
pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight))
|
|
|
|
# Incase we are in VC8 style, redraw the active tab (incase it is visible)
|
|
if pc.GetSelection() >= pc._nFrom and pc.GetSelection() < pc._nFrom + len(vTabsInfo):
|
|
|
|
self.DrawTab(pc, dc, activeTabPosx, pc.GetSelection(), activeTabWidth, activeTabHeight, pc._nTabXButtonStatus)
|
|
|
|
# Update all tabs that can not fit into the screen as non-visible
|
|
for xx in range(pc._nFrom + len(vTabsInfo), len(pc._pagesInfoVec)):
|
|
|
|
pc._pagesInfoVec[xx].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[xx].GetRegion().Clear()
|
|
|
|
# Draw the left/right/close buttons
|
|
# Left arrow
|
|
self.DrawLeftArrow(pc, dc)
|
|
self.DrawRightArrow(pc, dc)
|
|
self.DrawX(pc, dc)
|
|
self.DrawDropDownArrow(pc, dc)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
"""
|
|
Draws a tab using the `VC8` style.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `posx`: the x position of the tab;
|
|
:param `tabIdx`: the index of the tab;
|
|
:param `tabWidth`: the tab's width;
|
|
:param `tabHeight`: the tab's height;
|
|
:param `btnStatus`: the status of the 'X' button inside this tab.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
borderPen = wx.Pen(pc._pParent.GetBorderColour())
|
|
tabPoints = [wx.Point() for ii 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)
|
|
#
|
|
#
|
|
|
|
tabPoints[0].x = (pc.HasAGWFlag(FNB_BOTTOM) and [posx] or [posx+self._factor])[0]
|
|
tabPoints[0].y = (pc.HasAGWFlag(FNB_BOTTOM) and [2] or [tabHeight - 3])[0]
|
|
|
|
tabPoints[1].x = tabPoints[0].x + tabHeight - VERTICAL_BORDER_PADDING - 3 - self._factor
|
|
tabPoints[1].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x + 4
|
|
tabPoints[2].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[3].x = tabPoints[2].x + tabWidth - 2
|
|
tabPoints[3].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[4].x = tabPoints[3].x + 1
|
|
tabPoints[4].y = (pc.HasAGWFlag(FNB_BOTTOM) and [tabPoints[3].y - 1] or [tabPoints[3].y + 1])[0]
|
|
|
|
tabPoints[5].x = tabPoints[4].x + 1
|
|
tabPoints[5].y = (pc.HasAGWFlag(FNB_BOTTOM) and [(tabPoints[4].y - 1)] or [tabPoints[4].y + 1])[0]
|
|
|
|
tabPoints[6].x = tabPoints[2].x + tabWidth
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
tabPoints[7].x = tabPoints[0].x
|
|
tabPoints[7].y = tabPoints[0].y
|
|
|
|
pc._pagesInfoVec[tabIdx].SetRegion(tabPoints)
|
|
|
|
# Draw the polygon
|
|
br = dc.GetBrush()
|
|
dc.SetBrush(wx.Brush((tabIdx == pc.GetSelection() and [pc._activeTabColour] or [pc._colourTo])[0]))
|
|
dc.SetPen(wx.Pen((tabIdx == pc.GetSelection() and [wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [pc._colourBorder])[0]))
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
# Restore the brush
|
|
dc.SetBrush(br)
|
|
rect = pc.GetClientRect()
|
|
|
|
if tabIdx != pc.GetSelection() and not pc.HasAGWFlag(FNB_BOTTOM):
|
|
|
|
# Top default tabs
|
|
dc.SetPen(wx.Pen(pc._pParent.GetBorderColour()))
|
|
lineY = rect.height
|
|
curPen = dc.GetPen()
|
|
curPen.SetWidth(1)
|
|
dc.SetPen(curPen)
|
|
dc.DrawLine(posx, lineY, posx+rect.width, lineY)
|
|
|
|
# Incase we are drawing the selected tab, we draw the border of it as well
|
|
# but without the bottom (upper line incase of wxBOTTOM)
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
dc.SetPen(borderPen)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
# 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)
|
|
|
|
self.FillVC8GradientColour(pc, dc, tabPoints, tabIdx == pc.GetSelection(), tabIdx)
|
|
|
|
# Draw a thin line to the right of the non-selected tab
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
|
|
dc.DrawLine(tabPoints[4].x-1, tabPoints[4].y, tabPoints[5].x-1, tabPoints[5].y)
|
|
dc.DrawLine(tabPoints[5].x-1, tabPoints[5].y, tabPoints[6].x-1, tabPoints[6].y)
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
vc8ShapeLen = tabHeight - VERTICAL_BORDER_PADDING - 2
|
|
if pc.TabHasImage(tabIdx):
|
|
textOffset = 2*pc._pParent.GetPadding() + 16 + vc8ShapeLen
|
|
else:
|
|
textOffset = pc._pParent.GetPadding() + vc8ShapeLen
|
|
|
|
# Draw the image for the tab if any
|
|
imageYCoord = (pc.HasAGWFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if pc.TabHasImage(tabIdx):
|
|
|
|
imageXOffset = textOffset - 16 - pc._pParent.GetPadding()
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
|
|
# if selected tab, draw text in bold
|
|
if tabIdx == pc.GetSelection():
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
|
|
dc.SetFont(boldFont)
|
|
|
|
pageTextColour = pc._pParent.GetPageTextColour(tabIdx)
|
|
if pageTextColour is not None:
|
|
dc.SetTextForeground(pageTextColour)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
self.DrawFocusRectangle(dc, pc, pc._pagesInfoVec[tabIdx])
|
|
|
|
|
|
def FillVC8GradientColour(self, pageContainer, dc, tabPoints, bSelectedTab, tabIdx):
|
|
"""
|
|
Fills a tab with a gradient shading.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `tabPoints`: a Python list of :class:`Point` representing the tab outline;
|
|
:param `bSelectedTab`: ``True`` if the tab is selected, ``False`` otherwise;
|
|
:param `tabIdx`: the index of the tab;
|
|
"""
|
|
|
|
# calculate gradient coefficients
|
|
pc = pageContainer
|
|
|
|
if self._first:
|
|
self._first = False
|
|
pc._colourTo = LightColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE), 0)
|
|
pc._colourFrom = LightColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE), 60)
|
|
|
|
col2 = pc._pParent.GetGradientColourTo()
|
|
col1 = pc._pParent.GetGradientColourFrom()
|
|
|
|
# If colourful tabs style is set, override the tab colour
|
|
if pc.HasAGWFlag(FNB_COLOURFUL_TABS):
|
|
|
|
if not pc._pagesInfoVec[tabIdx].GetColour():
|
|
|
|
# First time, generate colour, and keep it in the vector
|
|
tabColour = RandomColour()
|
|
pc._pagesInfoVec[tabIdx].SetColour(tabColour)
|
|
|
|
if pc.HasAGWFlag(FNB_BOTTOM):
|
|
|
|
col2 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 50)
|
|
col1 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 80)
|
|
|
|
else:
|
|
|
|
col1 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 50)
|
|
col2 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 80)
|
|
|
|
size = abs(tabPoints[2].y - tabPoints[0].y) - 1
|
|
|
|
rf, gf, bf = 0, 0, 0
|
|
rstep = float(col2.Red() - col1.Red())/float(size)
|
|
gstep = float(col2.Green() - col1.Green())/float(size)
|
|
bstep = float(col2.Blue() - col1.Blue())/float(size)
|
|
|
|
y = tabPoints[0].y
|
|
|
|
# If we are drawing the selected tab, we need also to draw a line
|
|
# from 0.tabPoints[0].x and tabPoints[6].x . end, we achieve this
|
|
# by drawing the rectangle with transparent brush
|
|
# the line under the selected tab will be deleted by the drwaing loop
|
|
if bSelectedTab:
|
|
self.DrawTabsLine(pc, dc)
|
|
|
|
while 1:
|
|
|
|
if pc.HasAGWFlag(FNB_BOTTOM):
|
|
|
|
if y > tabPoints[0].y + size:
|
|
break
|
|
|
|
else:
|
|
|
|
if y < tabPoints[0].y - size:
|
|
break
|
|
|
|
currCol = wx.Colour(col1.Red() + int(rf), col1.Green() + int(gf), col1.Blue() + int(bf))
|
|
|
|
dc.SetPen((bSelectedTab and [wx.Pen(pc._activeTabColour)] or [wx.Pen(currCol)])[0])
|
|
startX = self.GetStartX(tabPoints, y, pc.GetParent().GetAGWWindowStyleFlag())
|
|
endX = self.GetEndX(tabPoints, y, pc.GetParent().GetAGWWindowStyleFlag())
|
|
dc.DrawLine(startX, y, endX, y)
|
|
|
|
# Draw the border using the 'edge' point
|
|
dc.SetPen(wx.Pen((bSelectedTab and [wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [pc._colourBorder])[0]))
|
|
|
|
dc.DrawPoint(startX, y)
|
|
dc.DrawPoint(endX, y)
|
|
|
|
# Progress the colour
|
|
rf += rstep
|
|
gf += gstep
|
|
bf += bstep
|
|
|
|
if pc.HasAGWFlag(FNB_BOTTOM):
|
|
y = y + 1
|
|
else:
|
|
y = y - 1
|
|
|
|
|
|
def GetStartX(self, tabPoints, y, style):
|
|
"""
|
|
Returns the `x` start position of a tab.
|
|
|
|
:param `tabPoints`: a Python list of :class:`Point` representing the tab outline;
|
|
:param `y`: the y start position of the tab;
|
|
:param `style`: can be ``FNB_BOTTOM`` or the default (tabs at top).
|
|
"""
|
|
|
|
x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0
|
|
|
|
# We check the 3 points to the left
|
|
|
|
bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0]
|
|
match = False
|
|
|
|
if bBottomStyle:
|
|
|
|
for i in range(3):
|
|
|
|
if y >= tabPoints[i].y and y < tabPoints[i+1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i+1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i+1].y
|
|
match = True
|
|
break
|
|
|
|
else:
|
|
|
|
for i in range(3):
|
|
|
|
if y <= tabPoints[i].y and y > tabPoints[i+1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i+1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i+1].y
|
|
match = True
|
|
break
|
|
|
|
if not match:
|
|
return tabPoints[2].x
|
|
|
|
# According to the equation y = ax + b => x = (y-b)/a
|
|
# We know the first 2 points
|
|
|
|
x1, x2, y1, y2 = list(map(float, (x1, x2, y1, y2)))
|
|
|
|
if abs(x2 - x1) < 1e-6:
|
|
return x2
|
|
else:
|
|
a = (y2 - y1)/(x2 - x1)
|
|
|
|
b = y1 - ((y2 - y1)/(x2 - x1))*x1
|
|
|
|
if a == 0:
|
|
return int(x1)
|
|
|
|
x = (y - b)/a
|
|
|
|
return int(x)
|
|
|
|
|
|
def GetEndX(self, tabPoints, y, style):
|
|
"""
|
|
Returns the `x` end position of a tab.
|
|
|
|
:param `tabPoints`: a Python list of :class:`Point` representing the tab outline;
|
|
:param `y`: the y end position of the tab;
|
|
:param `style`: can be ``FNB_BOTTOM`` or the default (tabs at top).
|
|
"""
|
|
|
|
x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0
|
|
|
|
# We check the 3 points to the left
|
|
bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0]
|
|
match = False
|
|
|
|
if bBottomStyle:
|
|
|
|
for i in range(7, 3, -1):
|
|
|
|
if y >= tabPoints[i].y and y < tabPoints[i-1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i-1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i-1].y
|
|
match = True
|
|
break
|
|
|
|
else:
|
|
|
|
for i in range(7, 3, -1):
|
|
|
|
if y <= tabPoints[i].y and y > tabPoints[i-1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i-1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i-1].y
|
|
match = True
|
|
break
|
|
|
|
if not match:
|
|
return tabPoints[3].x
|
|
|
|
# According to the equation y = ax + b => x = (y-b)/a
|
|
# We know the first 2 points
|
|
|
|
# Vertical line
|
|
if x1 == x2:
|
|
return int(x1)
|
|
|
|
a = (y2 - y1)/(x2 - x1)
|
|
b = y1 - ((y2 - y1)/(x2 - x1))*x1
|
|
|
|
if a == 0:
|
|
return int(x1)
|
|
|
|
x = (y - b)/a
|
|
|
|
return int(x)
|
|
|
|
|
|
def NumberTabsCanFit(self, pageContainer, fr=-1):
|
|
"""
|
|
Calculates the number of tabs that can fit on the available space on screen.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `fr`: the current first visible tab.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
# Empty results
|
|
vTabInfo = []
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
|
|
# The drawing starts from posx
|
|
posx = pc._pParent.GetPadding()
|
|
|
|
if fr < 0:
|
|
fr = pc._nFrom
|
|
|
|
for i in range(fr, len(pc._pagesInfoVec)):
|
|
|
|
vc8glitch = tabHeight + FNB_HEIGHT_SPACER
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
|
|
if posx + tabWidth + vc8glitch + self.GetButtonsAreaLength(pc) >= clientWidth:
|
|
break
|
|
|
|
# Add a result to the returned vector
|
|
tabRect = wx.Rect(posx, VERTICAL_BORDER_PADDING, tabWidth, tabHeight)
|
|
vTabInfo.append(tabRect)
|
|
|
|
# Advance posx
|
|
posx += tabWidth + FNB_HEIGHT_SPACER
|
|
|
|
return vTabInfo
|
|
|
|
#------------------------------------------------------------------
|
|
# Ribbon Tabs style
|
|
#------------------------------------------------------------------
|
|
class FNBRendererRibbonTabs(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the `Ribbon Tabs` renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
self._first = True
|
|
self._factor = 1
|
|
|
|
# definte this because we don't want to use the bold font
|
|
def CalcTabWidth(self, pageContainer, tabIdx, tabHeight):
|
|
"""
|
|
Calculates the width of the input tab.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `tabIdx`: the index of the input tab;
|
|
:param `tabHeight`: the height of the tab.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
dc = wx.MemoryDC()
|
|
dc.SelectObject(wx.Bitmap(1,1))
|
|
|
|
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
|
|
if pc.IsDefaultTabs():
|
|
shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
|
|
dc.SetFont(font)
|
|
width, pom = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
|
|
# Set a minimum size to a tab
|
|
if width < 20:
|
|
width = 20
|
|
|
|
tabWidth = 2*pc._pParent.GetPadding() + width
|
|
|
|
# Style to add a small 'x' button on the top right
|
|
# of the tab
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
# The xpm image that contains the 'x' button is 9 pixels
|
|
spacer = 9
|
|
if pc.HasAGWFlag(FNB_VC8):
|
|
spacer = 4
|
|
|
|
tabWidth += pc._pParent.GetPadding() + spacer
|
|
|
|
if pc.IsDefaultTabs():
|
|
# Default style
|
|
tabWidth += 2*shapePoints
|
|
|
|
hasImage = pc._ImageList != None and pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
|
|
# For VC71 style, we only add the icon size (16 pixels)
|
|
if hasImage:
|
|
|
|
if not pc.IsDefaultTabs():
|
|
tabWidth += 16 + pc._pParent.GetPadding()
|
|
else:
|
|
# Default style
|
|
tabWidth += 16 + pc._pParent.GetPadding() + shapePoints/2
|
|
|
|
return tabWidth
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
"""
|
|
Draws a tab using the `Ribbon Tabs` style.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`;
|
|
:param `posx`: the x position of the tab;
|
|
:param `tabIdx`: the index of the tab;
|
|
:param `tabWidth`: the tab's width;
|
|
:param `tabHeight`: the tab's height;
|
|
:param `btnStatus`: the status of the 'X' button inside this tab.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
|
|
gc = wx.GraphicsContext.Create(dc)
|
|
gc.SetPen(dc.GetPen())
|
|
gc.SetBrush(dc.GetBrush())
|
|
|
|
spacer = math.ceil(float(FNB_HEIGHT_SPACER)/2/2)
|
|
gc.DrawRoundedRectangle(posx+1,spacer,tabWidth-1,tabHeight-spacer*2,5)
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
pass
|
|
else:
|
|
if tabIdx != pc.GetSelection() - 1:
|
|
pass
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = FNB_HEIGHT_SPACER/2
|
|
|
|
if hasImage:
|
|
textOffset = 2*pc._pParent._nPadding + 16
|
|
else:
|
|
textOffset = pc._pParent._nPadding
|
|
|
|
textOffset += 2
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
pageTextColour = pc._pParent.GetPageTextColour(tabIdx)
|
|
if pageTextColour is not None:
|
|
dc.SetTextForeground(pageTextColour)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasAGWFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
def DrawTabs(self, pageContainer, dc):
|
|
"""
|
|
Actually draws the tabs in :class:`FlatNotebook`.
|
|
|
|
:param `pageContainer`: an instance of :class:`FlatNotebook`;
|
|
:param `dc`: an instance of :class:`DC`.
|
|
"""
|
|
|
|
pc = pageContainer
|
|
#style = pc.GetParent().GetWindowStyleFlag()
|
|
|
|
if "__WXMAC__" in wx.PlatformInfo:
|
|
# Works well on MSW & GTK, however this lines should be skipped on MAC
|
|
if not pc._pagesInfoVec or pc._nFrom >= len(pc._pagesInfoVec):
|
|
pc.Hide()
|
|
return
|
|
|
|
# Get the text height
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
|
|
# Calculate the number of rows required for drawing the tabs
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
# Set the maximum client size
|
|
pc.SetSizeHints(self.GetButtonsAreaLength(pc), tabHeight)
|
|
|
|
size = pc.GetSize()
|
|
|
|
# Background
|
|
dc.SetTextBackground(pc.GetBackgroundColour())
|
|
dc.SetTextForeground(pc._activeTextColour)
|
|
|
|
borderPen = wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
backBrush = wx.Brush(pc._tabAreaColour)
|
|
|
|
# If border style is set, set the pen to be border pen
|
|
if pc.HasAGWFlag(FNB_TABS_BORDER_SIMPLE):
|
|
dc.SetPen(borderPen)
|
|
else:
|
|
dc.SetPen(wx.Pen(pc._tabAreaColour))
|
|
|
|
dc.SetBrush(backBrush)
|
|
dc.DrawRectangle(0, 0, size.x, size.y)
|
|
|
|
# Draw labels
|
|
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
dc.SetFont(font)
|
|
|
|
posx = pc._pParent.GetPadding()
|
|
|
|
# Update all the tabs from 0 to 'pc._nFrom' to be non visible
|
|
for i in range(pc._nFrom):
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
|
|
count = pc._nFrom
|
|
|
|
#----------------------------------------------------------
|
|
# Go over and draw the visible tabs
|
|
#----------------------------------------------------------
|
|
selPen = wx.Pen(adjust_colour(pc._tabAreaColour, -20))
|
|
noselPen = wx.Pen(pc._tabAreaColour)
|
|
noselBrush = wx.Brush(pc._tabAreaColour)
|
|
selBrush = wx.Brush(LightColour(pc._tabAreaColour,60))
|
|
|
|
for i in range(pc._nFrom, len(pc._pagesInfoVec)):
|
|
|
|
# This style highlights the selected tab and the tab the mouse is over
|
|
highlight = (i==pc.GetSelection()) or pc.IsMouseHovering(i)
|
|
dc.SetPen((highlight and [selPen] or [noselPen])[0])
|
|
dc.SetBrush((highlight and [selBrush] or [noselBrush])[0])
|
|
|
|
# Add the padding to the tab width
|
|
# Tab width:
|
|
# +-----------------------------------------------------------+
|
|
# | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
|
|
# +-----------------------------------------------------------+
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
|
|
# Check if we can draw more
|
|
if posx + tabWidth + self.GetButtonsAreaLength(pc) >= clientWidth:
|
|
break
|
|
|
|
count = count + 1
|
|
|
|
# By default we clean the tab region
|
|
#pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Clean the 'x' buttn on the tab.
|
|
# A 'Clean' rectangle, is a rectangle with width or height
|
|
# with values lower than or equal to 0
|
|
pc._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1))
|
|
|
|
# Draw the tab (border, text, image & 'x' on tab)
|
|
self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus)
|
|
|
|
# Restore the text forground
|
|
dc.SetTextForeground(pc._activeTextColour)
|
|
|
|
# Update the tab position & size
|
|
posy = (pc.HasAGWFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(posx, posy))
|
|
pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight))
|
|
|
|
posx += tabWidth
|
|
|
|
# Update all tabs that can not fit into the screen as non-visible
|
|
for i in range(count, len(pc._pagesInfoVec)):
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Draw the left/right/close buttons
|
|
# Left arrow
|
|
self.DrawLeftArrow(pc, dc)
|
|
self.DrawRightArrow(pc, dc)
|
|
self.DrawX(pc, dc)
|
|
self.DrawDropDownArrow(pc, dc)
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FlatNotebook
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FlatNotebook(wx.Panel):
|
|
"""
|
|
The :class:`FlatNotebook` is a full implementation of the :class:`Notebook`, and designed to be
|
|
a drop-in replacement for :class:`Notebook`. The API functions are similar so one can
|
|
expect the function to behave in the same way.
|
|
"""
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
|
style=0, agwStyle=0, name="FlatNotebook"):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `parent`: the :class:`FlatNotebook` parent;
|
|
:param `id`: an identifier for the control: a value of -1 is taken to mean a default;
|
|
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `size`: the control size. A value of (-1, -1) indicates a default size,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `style`: the underlying :class:`Panel` window style;
|
|
:param `agwStyle`: the AGW-specific window style. This can be a combination of the
|
|
following bits:
|
|
|
|
================================ =========== ==================================================
|
|
Window Styles Hex Value Description
|
|
================================ =========== ==================================================
|
|
``FNB_VC71`` 0x1 Use Visual Studio 2003 (VC7.1) style for tabs.
|
|
``FNB_FANCY_TABS`` 0x2 Use fancy style - square tabs filled with gradient colouring.
|
|
``FNB_TABS_BORDER_SIMPLE`` 0x4 Draw thin border around the page.
|
|
``FNB_NO_X_BUTTON`` 0x8 Do not display the 'X' button.
|
|
``FNB_NO_NAV_BUTTONS`` 0x10 Do not display the right/left arrows.
|
|
``FNB_MOUSE_MIDDLE_CLOSES_TABS`` 0x20 Use the mouse middle button for cloing tabs.
|
|
``FNB_BOTTOM`` 0x40 Place tabs at bottom - the default is to place them at top.
|
|
``FNB_NODRAG`` 0x80 Disable dragging of tabs.
|
|
``FNB_VC8`` 0x100 Use Visual Studio 2005 (VC8) style for tabs.
|
|
``FNB_X_ON_TAB`` 0x200 Place 'X' close button on the active tab.
|
|
``FNB_BACKGROUND_GRADIENT`` 0x400 Use gradients to paint the tabs background.
|
|
``FNB_COLOURFUL_TABS`` 0x800 Use colourful tabs (VC8 style only).
|
|
``FNB_DCLICK_CLOSES_TABS`` 0x1000 Style to close tab using double click.
|
|
``FNB_SMART_TABS`` 0x2000 Use `Smart Tabbing`, like ``Alt`` + ``Tab`` on Windows.
|
|
``FNB_DROPDOWN_TABS_LIST`` 0x4000 Use a dropdown menu on the left in place of the arrows.
|
|
``FNB_ALLOW_FOREIGN_DND`` 0x8000 Allows drag 'n' drop operations between different :class:`FlatNotebook`.
|
|
``FNB_HIDE_ON_SINGLE_TAB`` 0x10000 Hides the Page Container when there is one or fewer tabs.
|
|
``FNB_DEFAULT_STYLE`` 0x10020 :class:`FlatNotebook` default style.
|
|
``FNB_FF2`` 0x20000 Use Firefox 2 style for tabs.
|
|
``FNB_NO_TAB_FOCUS`` 0x40000 Does not allow tabs to have focus.
|
|
``FNB_RIBBON_TABS`` 0x80000 Use the Ribbon Tabs style.
|
|
``FNB_HIDE_TABS`` 0x100000 Hides the Page Container allowing only keyboard navigation
|
|
``FNB_NAV_BUTTONS_WHEN_NEEDED`` 0x200000 Hides the navigation left/right arrows if all tabs fit
|
|
================================ =========== ==================================================
|
|
|
|
:param `name`: the window name.
|
|
"""
|
|
|
|
self._bForceSelection = False
|
|
self._nPadding = 6
|
|
self._nFrom = 0
|
|
style |= wx.TAB_TRAVERSAL
|
|
self._pages = None
|
|
self._windows = []
|
|
self._popupWin = None
|
|
self._naviIcon = None
|
|
self._agwStyle = agwStyle
|
|
self._orientation = None
|
|
self._customPanel = None
|
|
|
|
wx.Panel.__init__(self, parent, id, pos, size, style)
|
|
attr = self.GetDefaultAttributes()
|
|
self.SetOwnForegroundColour(attr.colFg)
|
|
self.SetOwnBackgroundColour(attr.colBg)
|
|
|
|
self._pages = PageContainer(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, style)
|
|
|
|
self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
|
|
|
|
self.Init()
|
|
|
|
|
|
def Init(self):
|
|
""" Initializes all the class attributes. """
|
|
|
|
self._pages._colourBorder = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW)
|
|
|
|
self._mainSizer = wx.BoxSizer(wx.VERTICAL)
|
|
self.SetSizer(self._mainSizer)
|
|
|
|
# The child panels will inherit this bg colour, so leave it at the default value
|
|
#self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_APPWORKSPACE))
|
|
|
|
# Set default page height
|
|
dc = wx.ClientDC(self)
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
# For GTK it seems that we must do this steps in order
|
|
# for the tabs will get the proper height on initialization
|
|
# on MSW, preforming these steps yields wierd results
|
|
boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
dc.SetFont(boldFont)
|
|
|
|
height = dc.GetCharHeight()
|
|
|
|
tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
tabHeight += 6
|
|
|
|
self._pages.SetSizeHints(-1, tabHeight)
|
|
# Add the tab container to the sizer
|
|
self._mainSizer.Insert(0, self._pages, 0, wx.EXPAND)
|
|
self._mainSizer.Layout()
|
|
|
|
self._pages._nFrom = self._nFrom
|
|
self._pDropTarget = FNBDropTarget(self)
|
|
self.SetDropTarget(self._pDropTarget)
|
|
|
|
|
|
def DoGetBestSize(self):
|
|
"""
|
|
Gets the size which best suits the window: for a control, it would be the
|
|
minimal size which doesn't truncate the control, for a panel - the same
|
|
size as it would have after a call to `Fit()`.
|
|
|
|
:note: Overridden from :class:`Panel`.
|
|
"""
|
|
|
|
if not self._windows:
|
|
# Something is better than nothing... no pages!
|
|
return wx.Size(20, 20)
|
|
|
|
maxWidth = maxHeight = 0
|
|
tabHeight = self.GetPageBestSize().height
|
|
|
|
for win in self._windows:
|
|
# Loop over all the windows to get their best size
|
|
width, height = win.GetBestSize()
|
|
maxWidth, maxHeight = max(maxWidth, width), max(maxHeight, height)
|
|
|
|
return wx.Size(maxWidth, maxHeight+tabHeight)
|
|
|
|
|
|
def Tile(self, orient=None):
|
|
"""
|
|
Shows pages in column/row mode (one panel after the other in columns/rows).
|
|
|
|
:param `orient`: this parameter represents the orientation of the stacked
|
|
panels. Pass ``wx.VERTICAL`` to get vertically stacked panels, ``wx.HORIZONTAL``
|
|
to get horizontally stacked panels or ``None`` to return to the default
|
|
:class:`FlatNotebook` behaviour with tabs.
|
|
"""
|
|
|
|
self.Freeze()
|
|
self._orientation = orient
|
|
|
|
self._mainSizer.Detach(self._pages)
|
|
for win in self._windows:
|
|
self._mainSizer.Detach(win)
|
|
|
|
## self._mainSizer.Destroy()
|
|
|
|
if orient is not None:
|
|
self._mainSizer = wx.BoxSizer(orient)
|
|
self._mainSizer.Add(self._pages)
|
|
|
|
for win in self._windows:
|
|
self._mainSizer.Add(win, 1, wx.EXPAND | wx.ALL, 2)
|
|
win.Show()
|
|
|
|
else:
|
|
|
|
self._mainSizer = wx.BoxSizer(wx.VERTICAL)
|
|
# Add the tab container to the sizer
|
|
self._mainSizer.Insert(0, self._pages, 0, wx.EXPAND)
|
|
|
|
curr = self._pages.GetSelection()
|
|
for indx, win in enumerate(self._windows):
|
|
if indx == curr:
|
|
if self.GetAGWWindowStyleFlag() & FNB_BOTTOM:
|
|
self._mainSizer.Insert(0, win, 1, wx.EXPAND)
|
|
else:
|
|
# We leave a space of 1 pixel around the window
|
|
self._mainSizer.Add(win, 1, wx.EXPAND)
|
|
win.Show()
|
|
|
|
else:
|
|
win.Hide()
|
|
|
|
self.SetSizer(self._mainSizer)
|
|
|
|
self._mainSizer.Layout()
|
|
self.Layout()
|
|
|
|
if orient is None:
|
|
self.SetSelection(self._pages._iActivePage)
|
|
self._pages._ReShow()
|
|
|
|
self.Thaw()
|
|
|
|
|
|
def GetTileOrientation(self):
|
|
"""
|
|
Returns the orientation when on tiling mode. This method can return
|
|
``wx.VERTICAL`` when the panels are vertically stacked, ``wx.HORIZONTAL``
|
|
when they are horizontally stacked panels or ``None`` when there is no
|
|
stacking and :class:`FlatNotebook` behaves like a normal notebook.
|
|
"""
|
|
|
|
return self._orientation
|
|
|
|
|
|
def SetCustomPage(self, panel):
|
|
"""
|
|
Sets a custom panel to show when there are no pages left in :class:`FlatNotebook`.
|
|
|
|
:param `panel`: any subclass of :class:`Window` will do, as long as it is suitable
|
|
to be used as a notebook page. Examples include :class:`Panel`, :class:`ScrolledWindow`,
|
|
and so on.
|
|
"""
|
|
|
|
self.Freeze()
|
|
|
|
if panel is None:
|
|
if self._customPanel is not None:
|
|
self._mainSizer.Detach(self._customPanel)
|
|
self._customPanel.Show(False)
|
|
|
|
else:
|
|
if self._customPanel is not None:
|
|
self._mainSizer.Detach(self._customPanel)
|
|
self._customPanel.Show(False)
|
|
self._customPanel.Destroy()
|
|
|
|
self._customPanel = panel
|
|
self.ShowCustomPage(True)
|
|
|
|
self._pages._ReShow()
|
|
self._mainSizer.Layout()
|
|
self.Thaw()
|
|
|
|
|
|
def GetCustomPage(self):
|
|
""" Returns a custom panel to show when there are no pages left in :class:`FlatNotebook`. """
|
|
|
|
return self._customPanel
|
|
|
|
|
|
def ShowCustomPage(self, show=True):
|
|
""" Hides the custom panel which is shown when there are no pages left in :class:`FlatNotebook`. """
|
|
|
|
if self._customPanel is None:
|
|
return
|
|
|
|
self.Freeze()
|
|
self._customPanel.Show(False)
|
|
|
|
if show:
|
|
if self.GetPageCount() == 0:
|
|
self._mainSizer.Add(self._customPanel, 1, wx.EXPAND | wx.ALL, 2)
|
|
self._customPanel.Show(True)
|
|
else:
|
|
if self._customPanel.GetContainingSizer() is not None:
|
|
self._mainSizer.Detach(self._customPanel)
|
|
|
|
self._mainSizer.Layout()
|
|
self.Thaw()
|
|
|
|
|
|
def SetActiveTabTextColour(self, textColour):
|
|
"""
|
|
Sets the text colour for the active tab.
|
|
|
|
:param `textColour`: a valid :class:`Colour` object or any typemap supported by wxWidgets/wxPython
|
|
to generate a colour (i.e., a hex string, a colour name, a 3 or 4 integer tuple).
|
|
"""
|
|
|
|
self._pages._activeTextColour = FormatColour(textColour)
|
|
|
|
|
|
def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer):
|
|
"""
|
|
Handles the drop action from a drag and drop operation.
|
|
|
|
:param `x`: the x position of the drop action;
|
|
:param `y`: the y position of the drop action;
|
|
:param `nTabPage`: the index of the tab being dropped;
|
|
:param `wnd_oldContainer`: the :class:`FlatNotebook` to which the dropped tab previously
|
|
belonged to.
|
|
"""
|
|
|
|
return self._pages.OnDropTarget(x, y, nTabPage, wnd_oldContainer)
|
|
|
|
|
|
def GetPreviousSelection(self):
|
|
""" Returns the previous selection. """
|
|
|
|
return self._pages._iPreviousActivePage
|
|
|
|
|
|
def AddPage(self, page, text, select=False, imageId=-1):
|
|
"""
|
|
Adds a page to the :class:`FlatNotebook`.
|
|
|
|
:param `page`: specifies the new page;
|
|
:param `text`: specifies the text for the new page;
|
|
:param `select`: specifies whether the page should be selected;
|
|
:param `imageId`: specifies the optional image index for the new page.
|
|
|
|
:return: ``True`` if successful, ``False`` otherwise.
|
|
"""
|
|
|
|
# sanity check
|
|
if not page:
|
|
return False
|
|
|
|
min_size = page.GetMinSize()
|
|
if not min_size.IsFullySpecified():
|
|
page.SetMinSize((1, 1))
|
|
|
|
self.ShowCustomPage(False)
|
|
|
|
# reparent the window to us
|
|
page.Reparent(self)
|
|
|
|
# Add tab
|
|
bSelected = select or len(self._windows) == 0
|
|
|
|
if bSelected:
|
|
|
|
bSelected = False
|
|
|
|
# Check for selection and send events
|
|
oldSelection = self._pages._iActivePage
|
|
tabIdx = len(self._windows)
|
|
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetId())
|
|
event.SetSelection(tabIdx)
|
|
event.SetOldSelection(oldSelection)
|
|
event.SetEventObject(self)
|
|
|
|
if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0:
|
|
bSelected = True
|
|
|
|
curSel = self._pages.GetSelection()
|
|
|
|
if not self._pages.IsShown():
|
|
self._pages.Show()
|
|
|
|
self._pages.AddPage(text, bSelected, imageId)
|
|
self._windows.append(page)
|
|
|
|
self.Freeze()
|
|
|
|
# Check if a new selection was made
|
|
if bSelected:
|
|
|
|
if curSel >= 0:
|
|
|
|
# Remove the window from the main sizer
|
|
self._mainSizer.Detach(self._windows[curSel])
|
|
self._windows[curSel].Hide()
|
|
|
|
if self.GetAGWWindowStyleFlag() & FNB_BOTTOM:
|
|
|
|
self._mainSizer.Insert(0, page, 1, wx.EXPAND)
|
|
|
|
else:
|
|
|
|
# We leave a space of 1 pixel around the window
|
|
self._mainSizer.Add(page, 1, wx.EXPAND)
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(oldSelection)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
else:
|
|
|
|
# Hide the page
|
|
page.Hide()
|
|
|
|
self.Tile(self._orientation)
|
|
self.Thaw()
|
|
self._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
return True
|
|
|
|
|
|
def SetImageList(self, imageList):
|
|
"""
|
|
Sets the image list for the page control.
|
|
|
|
:param `imageList`: an instance of :class:`ImageList`.
|
|
"""
|
|
|
|
self._pages.SetImageList(imageList)
|
|
|
|
|
|
def AssignImageList(self, imageList):
|
|
"""
|
|
Assigns the image list for the page control.
|
|
|
|
:param `imageList`: an instance of :class:`ImageList`.
|
|
"""
|
|
|
|
self._pages.AssignImageList(imageList)
|
|
|
|
|
|
def GetImageList(self):
|
|
""" Returns the associated image list. """
|
|
|
|
return self._pages.GetImageList()
|
|
|
|
|
|
def InsertPage(self, indx, page, text, select=True, imageId=-1):
|
|
"""
|
|
Inserts a new page at the specified position.
|
|
|
|
:param `indx`: specifies the position of the new page;
|
|
:param `page`: specifies the new page;
|
|
:param `text`: specifies the text for the new page;
|
|
:param `select`: specifies whether the page should be selected;
|
|
:param `imageId`: specifies the optional image index for the new page.
|
|
|
|
:return: ``True`` if successful, ``False`` otherwise.
|
|
"""
|
|
|
|
# sanity check
|
|
if not page:
|
|
return False
|
|
|
|
self.ShowCustomPage(False)
|
|
|
|
# reparent the window to us
|
|
page.Reparent(self)
|
|
|
|
if not self._windows:
|
|
|
|
self.AddPage(page, text, select, imageId)
|
|
return True
|
|
|
|
# Insert tab
|
|
bSelected = select or not self._windows
|
|
curSel = self._pages.GetSelection()
|
|
|
|
indx = max(0, min(indx, len(self._windows)))
|
|
|
|
if indx <= len(self._windows):
|
|
|
|
self._windows.insert(indx, page)
|
|
|
|
else:
|
|
|
|
self._windows.append(page)
|
|
|
|
if bSelected:
|
|
|
|
bSelected = False
|
|
|
|
# Check for selection and send events
|
|
oldSelection = self._pages._iActivePage
|
|
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetId())
|
|
event.SetSelection(indx)
|
|
event.SetOldSelection(oldSelection)
|
|
event.SetEventObject(self)
|
|
|
|
if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0:
|
|
bSelected = True
|
|
|
|
self._pages.InsertPage(indx, text, bSelected, imageId)
|
|
|
|
if indx <= curSel:
|
|
curSel = curSel + 1
|
|
|
|
self.Freeze()
|
|
|
|
# Check if a new selection was made
|
|
if bSelected:
|
|
|
|
if curSel >= 0:
|
|
|
|
# Remove the window from the main sizer
|
|
self._mainSizer.Detach(self._windows[curSel])
|
|
self._windows[curSel].Hide()
|
|
|
|
self._pages.SetSelection(indx)
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(oldSelection)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
else:
|
|
|
|
# Hide the page
|
|
page.Hide()
|
|
|
|
self.Tile(self._orientation)
|
|
self.Thaw()
|
|
self._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
return True
|
|
|
|
|
|
def SetSelection(self, page):
|
|
"""
|
|
Sets the selection for the given page.
|
|
|
|
:param `page`: an integer specifying the new selected page.
|
|
|
|
:note: The call to this function **does not** generate the page changing events.
|
|
"""
|
|
|
|
if page >= len(self._windows) or not self._windows:
|
|
return
|
|
|
|
# Support for disabed tabs
|
|
if not self._pages.GetEnabled(page) and len(self._windows) > 1 and not self._bForceSelection:
|
|
return
|
|
|
|
if self._orientation is not None:
|
|
return
|
|
|
|
curSel = self._pages.GetSelection()
|
|
|
|
# program allows the page change
|
|
self.Freeze()
|
|
if curSel >= 0:
|
|
|
|
# Remove the window from the main sizer
|
|
self._mainSizer.Detach(self._windows[curSel])
|
|
self._windows[curSel].Hide()
|
|
|
|
if self.GetAGWWindowStyleFlag() & FNB_BOTTOM:
|
|
|
|
self._mainSizer.Insert(0, self._windows[page], 1, wx.EXPAND)
|
|
|
|
else:
|
|
|
|
# We leave a space of 1 pixel around the window
|
|
self._mainSizer.Add(self._windows[page], 1, wx.EXPAND)
|
|
|
|
self._windows[page].Show()
|
|
self.Thaw()
|
|
|
|
self._mainSizer.Layout()
|
|
|
|
if page != self._pages._iActivePage:
|
|
# there is a real page changing
|
|
self._pages._iPreviousActivePage = self._pages._iActivePage
|
|
|
|
self._pages._iActivePage = page
|
|
self._pages.DoSetSelection(page)
|
|
|
|
|
|
def DeletePage(self, page):
|
|
"""
|
|
Deletes the specified page, and the associated window.
|
|
|
|
:param `page`: an integer specifying the new selected page.
|
|
|
|
:note: The call to this function generates the page changing events.
|
|
"""
|
|
|
|
if page >= len(self._windows) or page < 0:
|
|
return
|
|
|
|
# Fire a closing event
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId())
|
|
event.SetSelection(page)
|
|
event.SetEventObject(self)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
# The event handler allows it?
|
|
if not event.IsAllowed():
|
|
return
|
|
|
|
self.Freeze()
|
|
|
|
# Delete the requested page
|
|
pageRemoved = self._windows[page]
|
|
|
|
# If the page is the current window, remove it from the sizer
|
|
# as well
|
|
if page == self._pages.GetSelection():
|
|
self._mainSizer.Detach(pageRemoved)
|
|
|
|
# Remove it from the array as well
|
|
self._windows.pop(page)
|
|
|
|
# Now we can destroy it in wxWidgets use Destroy instead of delete
|
|
pageRemoved.Destroy()
|
|
|
|
self.Thaw()
|
|
|
|
self._pages.DoDeletePage(page)
|
|
|
|
self.Tile(self._orientation)
|
|
self.ShowCustomPage(True)
|
|
|
|
self.Refresh()
|
|
self.Update()
|
|
|
|
# Fire a closed event
|
|
closedEvent = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, self.GetId())
|
|
closedEvent.SetSelection(page)
|
|
closedEvent.SetEventObject(self)
|
|
self.GetEventHandler().ProcessEvent(closedEvent)
|
|
|
|
|
|
def DeleteAllPages(self):
|
|
""" Deletes all the pages in the :class:`FlatNotebook`. """
|
|
|
|
if not self._windows:
|
|
return False
|
|
|
|
self.Freeze()
|
|
|
|
for page in self._windows:
|
|
page.Destroy()
|
|
|
|
self._windows = []
|
|
self.Thaw()
|
|
|
|
# Clear the container of the tabs as well
|
|
self._pages.DeleteAllPages()
|
|
self.ShowCustomPage(True)
|
|
|
|
return True
|
|
|
|
|
|
def GetCurrentPage(self):
|
|
""" Returns the currently selected notebook page or ``None`` if none is selected. """
|
|
|
|
sel = self._pages.GetSelection()
|
|
if sel < 0 or sel >= len(self._windows):
|
|
return None
|
|
|
|
return self._windows[sel]
|
|
|
|
|
|
def GetPage(self, page):
|
|
""" Returns the window at the given page position, or ``None``. """
|
|
|
|
if page >= len(self._windows):
|
|
return None
|
|
|
|
return self._windows[page]
|
|
|
|
|
|
def GetPageIndex(self, win):
|
|
"""
|
|
Returns the index at which the window is found.
|
|
|
|
:param `win`: an instance of :class:`Window`.
|
|
"""
|
|
|
|
try:
|
|
return self._windows.index(win)
|
|
except:
|
|
return -1
|
|
|
|
|
|
def GetSelection(self):
|
|
""" Returns the currently selected page, or -1 if none was selected. """
|
|
|
|
return self._pages.GetSelection()
|
|
|
|
|
|
def AdvanceSelection(self, forward=True):
|
|
"""
|
|
Cycles through the tabs.
|
|
|
|
:param `forward`: if ``True``, the selection is advanced in ascending order
|
|
(to the right), otherwise the selection is advanced in descending order.
|
|
|
|
:note: The call to this function generates the page changing events.
|
|
"""
|
|
|
|
self._pages.AdvanceSelection(forward)
|
|
|
|
|
|
def GetPageCount(self):
|
|
""" Returns the number of pages in the :class:`FlatNotebook` control. """
|
|
|
|
return self._pages.GetPageCount()
|
|
|
|
|
|
def SetNavigatorIcon(self, bmp):
|
|
"""
|
|
Set the icon used by the :class:`TabNavigatorWindow`.
|
|
|
|
:param `bmp`: a valid :class:`Bitmap` object.
|
|
"""
|
|
|
|
if isinstance(bmp, wx.Bitmap) and bmp.IsOk():
|
|
# Make sure image is proper size
|
|
if bmp.GetSize() != (16, 16):
|
|
img = bmp.ConvertToImage()
|
|
img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
|
|
bmp = wx.Bitmap(img)
|
|
self._naviIcon = bmp
|
|
else:
|
|
raise TypeError("SetNavigatorIcon requires a valid bitmap")
|
|
|
|
|
|
def OnNavigationKey(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_NAVIGATION_KEY`` event for :class:`FlatNotebook`.
|
|
|
|
:param `event`: a :class:`NavigationKeyEvent` event to be processed.
|
|
"""
|
|
|
|
if event.IsWindowChange():
|
|
if len(self._windows) == 0:
|
|
return
|
|
# change pages
|
|
if self.HasAGWFlag(FNB_SMART_TABS):
|
|
if not self._popupWin:
|
|
self._popupWin = TabNavigatorWindow(self, self._naviIcon)
|
|
self._popupWin.SetReturnCode(wx.ID_OK)
|
|
self._popupWin.ShowModal()
|
|
self._popupWin.Destroy()
|
|
self._popupWin = None
|
|
else:
|
|
# a dialog is already opened
|
|
self._popupWin.OnNavigationKey(event)
|
|
return
|
|
else:
|
|
# change pages
|
|
self.AdvanceSelection(event.GetDirection())
|
|
|
|
else:
|
|
event.Skip()
|
|
|
|
|
|
def GetPageShapeAngle(self, page_index):
|
|
"""
|
|
Returns the angle associated to a tab.
|
|
|
|
:param `page_index`: the index of the tab for which we wish to get the shape angle.
|
|
"""
|
|
|
|
if page_index < 0 or page_index >= len(self._pages._pagesInfoVec):
|
|
return None, False
|
|
|
|
result = self._pages._pagesInfoVec[page_index].GetTabAngle()
|
|
return result, True
|
|
|
|
|
|
def SetPageShapeAngle(self, page_index, angle):
|
|
"""
|
|
Sets the angle associated to a tab.
|
|
|
|
:param `page_index`: the index of the tab for which we wish to get the shape angle;
|
|
:param `angle`: the new shape angle for the tab (must be less than 15 degrees).
|
|
"""
|
|
|
|
if page_index < 0 or page_index >= len(self._pages._pagesInfoVec):
|
|
return
|
|
|
|
if angle > 15:
|
|
return
|
|
|
|
self._pages._pagesInfoVec[page_index].SetTabAngle(angle)
|
|
|
|
|
|
def SetAllPagesShapeAngle(self, angle):
|
|
"""
|
|
Sets the angle associated to all the tab.
|
|
|
|
:param `angle`: the new shape angle for the tab (must be less than 15 degrees).
|
|
"""
|
|
|
|
if angle > 15:
|
|
return
|
|
|
|
for ii in range(len(self._pages._pagesInfoVec)):
|
|
self._pages._pagesInfoVec[ii].SetTabAngle(angle)
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def GetPageBestSize(self):
|
|
""" Return the page best size. """
|
|
|
|
return self._pages.GetClientSize()
|
|
|
|
|
|
def SetPageText(self, page, text):
|
|
"""
|
|
Sets the text for the given page.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `text`: the new tab label.
|
|
"""
|
|
|
|
bVal = self._pages.SetPageText(page, text)
|
|
self._pages.Refresh()
|
|
|
|
return bVal
|
|
|
|
|
|
def SetPadding(self, padding):
|
|
"""
|
|
Sets the amount of space around each page's icon and label, in pixels.
|
|
|
|
:param `padding`: the amount of space around each page's icon and label,
|
|
in pixels.
|
|
|
|
:note: Only the horizontal padding is considered.
|
|
"""
|
|
|
|
self._nPadding = padding.GetWidth()
|
|
|
|
|
|
def GetTabArea(self):
|
|
""" Returns the associated page. """
|
|
|
|
return self._pages
|
|
|
|
|
|
def GetPadding(self):
|
|
""" Returns the amount of space around each page's icon and label, in pixels. """
|
|
|
|
return self._nPadding
|
|
|
|
|
|
def SetAGWWindowStyleFlag(self, agwStyle):
|
|
"""
|
|
Sets the :class:`FlatNotebook` window style flags.
|
|
|
|
:param `agwStyle`: the AGW-specific window style. This can be a combination of the
|
|
following bits:
|
|
|
|
================================ =========== ==================================================
|
|
Window Styles Hex Value Description
|
|
================================ =========== ==================================================
|
|
``FNB_VC71`` 0x1 Use Visual Studio 2003 (VC7.1) style for tabs.
|
|
``FNB_FANCY_TABS`` 0x2 Use fancy style - square tabs filled with gradient colouring.
|
|
``FNB_TABS_BORDER_SIMPLE`` 0x4 Draw thin border around the page.
|
|
``FNB_NO_X_BUTTON`` 0x8 Do not display the 'X' button.
|
|
``FNB_NO_NAV_BUTTONS`` 0x10 Do not display the right/left arrows.
|
|
``FNB_MOUSE_MIDDLE_CLOSES_TABS`` 0x20 Use the mouse middle button for cloing tabs.
|
|
``FNB_BOTTOM`` 0x40 Place tabs at bottom - the default is to place them at top.
|
|
``FNB_NODRAG`` 0x80 Disable dragging of tabs.
|
|
``FNB_VC8`` 0x100 Use Visual Studio 2005 (VC8) style for tabs.
|
|
``FNB_X_ON_TAB`` 0x200 Place 'X' close button on the active tab.
|
|
``FNB_BACKGROUND_GRADIENT`` 0x400 Use gradients to paint the tabs background.
|
|
``FNB_COLOURFUL_TABS`` 0x800 Use colourful tabs (VC8 style only).
|
|
``FNB_DCLICK_CLOSES_TABS`` 0x1000 Style to close tab using double click.
|
|
``FNB_SMART_TABS`` 0x2000 Use `Smart Tabbing`, like ``Alt`` + ``Tab`` on Windows.
|
|
``FNB_DROPDOWN_TABS_LIST`` 0x4000 Use a dropdown menu on the left in place of the arrows.
|
|
``FNB_ALLOW_FOREIGN_DND`` 0x8000 Allows drag 'n' drop operations between different :class:`FlatNotebook`.
|
|
``FNB_HIDE_ON_SINGLE_TAB`` 0x10000 Hides the Page Container when there is one or fewer tabs.
|
|
``FNB_DEFAULT_STYLE`` 0x10020 :class:`FlatNotebook` default style.
|
|
``FNB_FF2`` 0x20000 Use Firefox 2 style for tabs.
|
|
``FNB_NO_TAB_FOCUS`` 0x40000 Does not allow tabs to have focus.
|
|
``FNB_RIBBON_TABS`` 0x80000 Use the Ribbon Tabs style.
|
|
``FNB_HIDE_TABS`` 0x100000 Hides the Page Container allowing only keyboard navigation
|
|
``FNB_NAV_BUTTONS_WHEN_NEEDED`` 0x200000 Hides the navigation left/right arrows if all tabs fit
|
|
================================ =========== ==================================================
|
|
|
|
"""
|
|
|
|
oldStyle = self._agwStyle
|
|
self._agwStyle = agwStyle
|
|
renderer = self._pages._mgr.GetRenderer(agwStyle)
|
|
renderer._tabHeight = None
|
|
|
|
if self._pages:
|
|
# For changing the tab position (i.e. placing them top/bottom)
|
|
# refreshing the tab container is not enough
|
|
self.SetSelection(self._pages._iActivePage)
|
|
|
|
# If we just hid the tabs we must Refresh()
|
|
if (not (oldStyle & FNB_HIDE_TABS) and agwStyle & FNB_HIDE_TABS) or \
|
|
(not (oldStyle & FNB_HIDE_ON_SINGLE_TAB) and agwStyle & FNB_HIDE_ON_SINGLE_TAB):
|
|
self.Refresh()
|
|
|
|
if (oldStyle & FNB_HIDE_TABS and not (agwStyle & FNB_HIDE_TABS)) or \
|
|
(oldStyle & FNB_HIDE_ON_SINGLE_TAB and not self.HasAGWFlag(FNB_HIDE_ON_SINGLE_TAB)):
|
|
#For Redrawing the Tabs once you remove the Hide tyle
|
|
self._pages._ReShow()
|
|
|
|
|
|
def GetAGWWindowStyleFlag(self):
|
|
"""
|
|
Returns the :class:`FlatNotebook` window style.
|
|
|
|
:see: :meth:`~FlatNotebook.SetAGWWindowStyleFlag` for a list of valid window styles.
|
|
"""
|
|
|
|
return self._agwStyle
|
|
|
|
|
|
def HasAGWFlag(self, flag):
|
|
"""
|
|
Returns whether a flag is present in the :class:`FlatNotebook` style.
|
|
|
|
:param `flag`: one of the possible :class:`FlatNotebook` window styles.
|
|
|
|
:see: :meth:`~FlatNotebook.SetAGWWindowStyleFlag` for a list of possible window style flags.
|
|
"""
|
|
|
|
agwStyle = self.GetAGWWindowStyleFlag()
|
|
res = (agwStyle & flag and [True] or [False])[0]
|
|
return res
|
|
|
|
|
|
def HideTabs(self):
|
|
""" Hides the tabs. """
|
|
|
|
agwStyle = self.GetAGWWindowStyleFlag()
|
|
agwStyle |= FNB_HIDE_TABS
|
|
self.SetAGWWindowStyleFlag(agwStyle)
|
|
|
|
|
|
def ShowTabs(self):
|
|
""" Shows the tabs if hidden previously. """
|
|
|
|
agwStyle = self.GetAGWWindowStyleFlag()
|
|
agwStyle &= ~FNB_HIDE_TABS
|
|
self.SetAGWWindowStyleFlag(agwStyle)
|
|
|
|
|
|
def RemovePage(self, page):
|
|
"""
|
|
Deletes the specified page, without deleting the associated window.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
if page >= len(self._windows):
|
|
return False
|
|
|
|
# Fire a closing event
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId())
|
|
event.SetSelection(page)
|
|
event.SetEventObject(self)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
# The event handler allows it?
|
|
if not event.IsAllowed():
|
|
return False
|
|
|
|
self.Freeze()
|
|
|
|
# Remove the requested page
|
|
pageRemoved = self._windows[page]
|
|
|
|
# If the page is the current window, remove it from the sizer
|
|
# as well
|
|
if page == self._pages.GetSelection():
|
|
self._mainSizer.Detach(pageRemoved)
|
|
|
|
# Remove it from the array as well
|
|
self._windows.pop(page)
|
|
|
|
self.Tile(self._orientation)
|
|
self.ShowCustomPage(True)
|
|
|
|
self.Thaw()
|
|
|
|
self._pages.DoDeletePage(page)
|
|
|
|
return True
|
|
|
|
|
|
def SetRightClickMenu(self, menu):
|
|
"""
|
|
Sets the popup menu associated to a right click on a tab.
|
|
|
|
:param `menu`: an instance of :class:`Menu`.
|
|
"""
|
|
|
|
self._pages._pRightClickMenu = menu
|
|
|
|
|
|
def GetPageText(self, page):
|
|
"""
|
|
Returns the string for the given page.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
return self._pages.GetPageText(page)
|
|
|
|
|
|
def SetGradientColours(self, fr, to, border):
|
|
"""
|
|
Sets the gradient colours for the tab.
|
|
|
|
:param `fr`: the first gradient colour, an instance of :class:`Colour`;
|
|
:param `to`: the second gradient colour, an instance of :class:`Colour`;
|
|
:param `border`: the border colour, an instance of :class:`Colour`.
|
|
"""
|
|
|
|
self._pages._colourFrom = fr
|
|
self._pages._colourTo = to
|
|
self._pages._colourBorder = border
|
|
|
|
|
|
def SetGradientColourFrom(self, fr):
|
|
"""
|
|
Sets the starting colour for the gradient.
|
|
|
|
:param `fr`: the first gradient colour, an instance of :class:`Colour`.
|
|
"""
|
|
|
|
self._pages._colourFrom = fr
|
|
|
|
|
|
def SetGradientColourTo(self, to):
|
|
"""
|
|
Sets the ending colour for the gradient.
|
|
|
|
:param `to`: the second gradient colour, an instance of :class:`Colour`;
|
|
"""
|
|
|
|
self._pages._colourTo = to
|
|
|
|
|
|
def SetGradientColourBorder(self, border):
|
|
"""
|
|
Sets the tab border colour.
|
|
|
|
:param `border`: the border colour, an instance of :class:`Colour`.
|
|
"""
|
|
|
|
self._pages._colourBorder = border
|
|
|
|
|
|
def GetGradientColourFrom(self):
|
|
""" Gets first gradient colour. """
|
|
|
|
return self._pages._colourFrom
|
|
|
|
|
|
def GetGradientColourTo(self):
|
|
""" Gets second gradient colour. """
|
|
|
|
return self._pages._colourTo
|
|
|
|
|
|
def GetGradientColourBorder(self):
|
|
""" Gets the tab border colour. """
|
|
|
|
return self._pages._colourBorder
|
|
|
|
|
|
def GetBorderColour(self):
|
|
""" Returns the border colour. """
|
|
|
|
return self._pages._colourBorder
|
|
|
|
|
|
def GetActiveTabTextColour(self):
|
|
""" Get the active tab text colour. """
|
|
|
|
return self._pages._activeTextColour
|
|
|
|
|
|
def SetPageImage(self, page, image):
|
|
"""
|
|
Sets the image index for the given page.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `image`: an index into the image list which was set with :meth:`~FlatNotebook.SetImageList`.
|
|
"""
|
|
|
|
self._pages.SetPageImage(page, image)
|
|
|
|
|
|
def GetPageImage(self, page):
|
|
"""
|
|
Returns the image index for the given page.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
return self._pages.GetPageImage(page)
|
|
|
|
|
|
def GetEnabled(self, page):
|
|
"""
|
|
Returns whether a tab is enabled or not.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
return self._pages.GetEnabled(page)
|
|
|
|
|
|
def EnableTab(self, page, enabled=True):
|
|
"""
|
|
Enables or disables a tab.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `enabled`: ``True`` to enable a tab, ``False`` to disable it.
|
|
"""
|
|
|
|
if page >= len(self._windows):
|
|
return
|
|
|
|
self._windows[page].Enable(enabled)
|
|
self._pages.EnableTab(page, enabled)
|
|
|
|
|
|
def GetNonActiveTabTextColour(self):
|
|
""" Returns the non active tabs text colour. """
|
|
|
|
return self._pages._nonActiveTextColour
|
|
|
|
|
|
def SetNonActiveTabTextColour(self, colour):
|
|
"""
|
|
Sets the non active tabs text colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object or any typemap supported by wxWidgets/wxPython
|
|
to generate a colour (i.e., a hex string, a colour name, a 3 or 4 integer tuple).
|
|
"""
|
|
|
|
self._pages._nonActiveTextColour = FormatColour(colour)
|
|
|
|
|
|
def GetPageTextColour(self, page):
|
|
"""
|
|
Returns the tab text colour if it has been set previously, or ``None`` otherwise.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
return self._pages.GetPageTextColour(page)
|
|
|
|
|
|
def SetPageTextColour(self, page, colour):
|
|
"""
|
|
Sets the tab text colour individually.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `colour`: a valid :class:`Colour` object or any typemap supported by wxWidgets/wxPython
|
|
to generate a colour (i.e., a hex string, a colour name, a 3 or 4 integer tuple). You can
|
|
pass ``None`` or :class:`NullColour` to return to the default page text colour.
|
|
"""
|
|
|
|
self._pages.SetPageTextColour(page, colour)
|
|
|
|
|
|
def SetTabAreaColour(self, colour):
|
|
"""
|
|
Sets the area behind the tabs colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object or any typemap supported by wxWidgets/wxPython
|
|
to generate a colour (i.e., a hex string, a colour name, a 3 or 4 integer tuple).
|
|
"""
|
|
|
|
self._pages._tabAreaColour = FormatColour(colour)
|
|
|
|
|
|
def GetTabAreaColour(self):
|
|
""" Returns the area behind the tabs colour. """
|
|
|
|
return self._pages._tabAreaColour
|
|
|
|
|
|
def SetActiveTabColour(self, colour):
|
|
"""
|
|
Sets the active tab colour.
|
|
|
|
:param `colour`: a valid :class:`Colour` object or any typemap supported by wxWidgets/wxPython
|
|
to generate a colour (i.e., a hex string, a colour name, a 3 or 4 integer tuple).
|
|
"""
|
|
|
|
self._pages._activeTabColour = FormatColour(colour)
|
|
|
|
|
|
def GetActiveTabColour(self):
|
|
""" Returns the active tab colour. """
|
|
|
|
return self._pages._activeTabColour
|
|
|
|
|
|
def EnsureVisible(self, page):
|
|
"""
|
|
Ensures that a tab is visible.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
self._pages.DoSetSelection(page)
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class PageContainer
|
|
# Acts as a container for the pages you add to FlatNotebook
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class PageContainer(wx.Panel):
|
|
"""
|
|
This class acts as a container for the pages you add to :class:`FlatNotebook`.
|
|
"""
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
|
|
size=wx.DefaultSize, style=0):
|
|
"""
|
|
Default class constructor.
|
|
|
|
Used internally, do not call it in your code!
|
|
|
|
:param `parent`: the :class:`PageContainer` parent;
|
|
:param `id`: an identifier for the control: a value of -1 is taken to mean a default;
|
|
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `size`: the control size. A value of (-1, -1) indicates a default size,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `style`: the window style.
|
|
"""
|
|
|
|
self._ImageList = None
|
|
self._iActivePage = -1
|
|
self._pDropTarget = None
|
|
self._nLeftClickZone = FNB_NOWHERE
|
|
self._iPreviousActivePage = -1
|
|
|
|
self._pRightClickMenu = None
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
self._pParent = parent
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
|
|
self._nHoveringOverTabIndex = -1
|
|
self._nHoveringOverLastTabIndex = -1
|
|
|
|
self._setCursor = False
|
|
|
|
self._pagesInfoVec = []
|
|
|
|
self._colourTo = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
|
|
self._colourFrom = wx.WHITE
|
|
self._activeTabColour = wx.WHITE
|
|
self._activeTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT)
|
|
self._nonActiveTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT)
|
|
self._tabAreaColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)
|
|
|
|
self._nFrom = 0
|
|
self._isdragging = False
|
|
|
|
# Set default page height, this is done according to the system font
|
|
memDc = wx.MemoryDC()
|
|
memDc.SelectObject(wx.Bitmap(1,1))
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.BOLD)
|
|
memDc.SetFont(boldFont)
|
|
|
|
height = memDc.GetCharHeight()
|
|
tabHeight = height + FNB_HEIGHT_SPACER # We use 10 pixels as padding
|
|
|
|
wx.Panel.__init__(self, parent, id, pos, wx.Size(size.x, tabHeight),
|
|
style|wx.NO_BORDER|wx.NO_FULL_REPAINT_ON_RESIZE|wx.WANTS_CHARS)
|
|
|
|
attr = self.GetDefaultAttributes()
|
|
self.SetOwnForegroundColour(attr.colFg)
|
|
self.SetOwnBackgroundColour(attr.colBg)
|
|
|
|
self._pDropTarget = FNBDropTarget(self)
|
|
self.SetDropTarget(self._pDropTarget)
|
|
self._mgr = FNBRendererMgr()
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_SIZE, self.OnSize)
|
|
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
|
|
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
|
|
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
|
|
self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
|
|
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
|
|
self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
|
|
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
|
|
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow)
|
|
self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
|
|
self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
|
|
self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
|
|
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
|
|
|
|
|
|
def OnEraseBackground(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ERASE_BACKGROUND`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`EraseEvent` event to be processed.
|
|
|
|
:note: This method is intentionally empty to reduce flicker.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
def _ReShow(self):
|
|
"""
|
|
Handles the redraw of the tabs when the ``FNB_HIDE_ON_SINGLE_TAB`` has been removed.
|
|
"""
|
|
|
|
self.Show()
|
|
self.GetParent()._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
|
|
def OnPaint(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_PAINT`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`PaintEvent` event to be processed.
|
|
"""
|
|
|
|
dc = wx.BufferedPaintDC(self)
|
|
parent = self.GetParent()
|
|
|
|
renderer = self._mgr.GetRenderer(parent.GetAGWWindowStyleFlag())
|
|
renderer.DrawTabs(self, dc)
|
|
|
|
if self.HasAGWFlag(FNB_HIDE_ON_SINGLE_TAB) and len(self._pagesInfoVec) <= 1 or \
|
|
self.HasAGWFlag(FNB_HIDE_TABS) or parent._orientation or \
|
|
(parent._customPanel and len(self._pagesInfoVec) == 0):
|
|
self.Hide()
|
|
self.GetParent()._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
|
|
def AddPage(self, caption, selected=False, imgindex=-1):
|
|
"""
|
|
Adds a page to the :class:`PageContainer`.
|
|
|
|
:param `page`: specifies the new page;
|
|
:param `text`: specifies the text for the new page;
|
|
:param `select`: specifies whether the page should be selected;
|
|
:param `imageId`: specifies the optional image index for the new page.
|
|
"""
|
|
|
|
if selected:
|
|
|
|
self._iPreviousActivePage = self._iActivePage
|
|
self._iActivePage = len(self._pagesInfoVec)
|
|
|
|
# Create page info and add it to the vector
|
|
pageInfo = PageInfo(caption, imgindex)
|
|
self._pagesInfoVec.append(pageInfo)
|
|
self.Refresh()
|
|
|
|
|
|
def InsertPage(self, indx, text, selected=True, imgindex=-1):
|
|
"""
|
|
Inserts a new page at the specified position.
|
|
|
|
:param `indx`: specifies the position of the new page;
|
|
:param `page`: specifies the new page;
|
|
:param `text`: specifies the text for the new page;
|
|
:param `select`: specifies whether the page should be selected;
|
|
:param `imageId`: specifies the optional image index for the new page.
|
|
"""
|
|
|
|
if selected:
|
|
|
|
self._iPreviousActivePage = self._iActivePage
|
|
self._iActivePage = len(self._pagesInfoVec)
|
|
|
|
self._pagesInfoVec.insert(indx, PageInfo(text, imgindex))
|
|
|
|
self.Refresh()
|
|
return True
|
|
|
|
|
|
def OnSize(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SIZE`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`SizeEvent` event to be processed.
|
|
"""
|
|
|
|
# When resizing the control, try to fit to screen as many tabs as we can
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
renderer = self._mgr.GetRenderer(agwStyle)
|
|
|
|
fr = 0
|
|
page = self.GetSelection()
|
|
|
|
for fr in range(self._nFrom):
|
|
vTabInfo = renderer.NumberTabsCanFit(self, fr)
|
|
if page - fr >= len(vTabInfo):
|
|
continue
|
|
break
|
|
|
|
self._nFrom = fr
|
|
|
|
self.Refresh() # Call on paint
|
|
event.Skip()
|
|
|
|
|
|
def OnMiddleDown(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_MIDDLE_DOWN`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
# Test if this style is enabled
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
|
|
if not agwStyle & FNB_MOUSE_MIDDLE_CLOSES_TABS:
|
|
return
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where == FNB_TAB:
|
|
self.DeletePage(tabIdx)
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnMouseWheel(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_MOUSEWHEEL`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
rotation = event.GetWheelRotation()
|
|
delta = event.GetWheelDelta()
|
|
steps = int(rotation/delta)
|
|
|
|
for tab in range(abs(steps)):
|
|
if steps > 0:
|
|
before = self._nLeftButtonStatus
|
|
self._nLeftButtonStatus = FNB_BTN_PRESSED
|
|
self.RotateLeft()
|
|
self._nLeftButtonStatus = before
|
|
else:
|
|
before = self._nRightButtonStatus
|
|
self._nRightButtonStatus = FNB_BTN_PRESSED
|
|
self.RotateRight()
|
|
self._nRightButtonStatus = before
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnRightDown(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_RIGHT_DOWN`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where in [FNB_TAB, FNB_TAB_X]:
|
|
|
|
if self._pagesInfoVec[tabIdx].GetEnabled():
|
|
# This shouldn't really change the selection, so it's commented out
|
|
# Fire events and eventually (if allowed) change selection
|
|
# self.FireEvent(tabIdx)
|
|
|
|
# send a message to popup a custom menu
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, self.GetParent().GetId())
|
|
event.SetSelection(tabIdx)
|
|
event.SetOldSelection(self._iActivePage)
|
|
event.SetEventObject(self.GetParent())
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
if self._pRightClickMenu:
|
|
self.PopupMenu(self._pRightClickMenu)
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnLeftDown(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_LEFT_DOWN`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
# Reset buttons status
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
self._nLeftClickZone, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if self._nLeftClickZone == FNB_DROP_DOWN_ARROW:
|
|
self._nArrowDownButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_LEFT_ARROW:
|
|
self._nLeftButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_RIGHT_ARROW:
|
|
self._nRightButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_X:
|
|
self._nXButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_TAB_X:
|
|
self._nTabXButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
|
|
elif self._nLeftClickZone == FNB_TAB:
|
|
|
|
if self._iActivePage != tabIdx:
|
|
|
|
# In case the tab is disabled, we dont allow to choose it
|
|
if len(self._pagesInfoVec) > tabIdx and \
|
|
self._pagesInfoVec[tabIdx].GetEnabled():
|
|
self.FireEvent(tabIdx)
|
|
|
|
|
|
def RotateLeft(self):
|
|
""" Scrolls tabs to the left by bulk of 5 tabs. """
|
|
|
|
if self._nFrom == 0:
|
|
return
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nLeftButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_HOVER
|
|
|
|
# We scroll left with bulks of 5
|
|
scrollLeft = self.GetNumTabsCanScrollLeft()
|
|
|
|
self._nFrom -= scrollLeft
|
|
if self._nFrom < 0:
|
|
self._nFrom = 0
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def RotateRight(self):
|
|
""" Scrolls tabs to the right by bulk of 5 tabs. """
|
|
|
|
if self._nFrom >= len(self._pagesInfoVec) - 1:
|
|
return
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nRightButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nRightButtonStatus = FNB_BTN_HOVER
|
|
|
|
# Check if the right most tab is visible, if it is
|
|
# don't rotate right anymore
|
|
if self._pagesInfoVec[len(self._pagesInfoVec)-1].GetPosition() != wx.Point(-1, -1):
|
|
return
|
|
|
|
self._nFrom += 1
|
|
self.Refresh()
|
|
|
|
|
|
def OnLeftUp(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_LEFT_UP`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
# forget the zone that was initially clicked
|
|
self._nLeftClickZone = FNB_NOWHERE
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if not self.HasAGWFlag(FNB_NO_TAB_FOCUS):
|
|
# Make sure selected tab has focus
|
|
self.SetFocus()
|
|
|
|
if where == FNB_LEFT_ARROW:
|
|
self.RotateLeft()
|
|
|
|
elif where == FNB_RIGHT_ARROW:
|
|
self.RotateRight()
|
|
|
|
elif where == FNB_X:
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nXButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nXButtonStatus = FNB_BTN_HOVER
|
|
|
|
self.DeletePage(self._iActivePage)
|
|
|
|
elif where == FNB_TAB_X:
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nTabXButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nTabXButtonStatus = FNB_BTN_HOVER
|
|
|
|
self.DeletePage(self._iActivePage)
|
|
|
|
elif where == FNB_DROP_DOWN_ARROW:
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nArrowDownButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
# Refresh the button status
|
|
renderer = self._mgr.GetRenderer(self.GetParent().GetAGWWindowStyleFlag())
|
|
dc = wx.ClientDC(self)
|
|
renderer.DrawDropDownArrow(self, dc)
|
|
|
|
self.PopupTabsMenu()
|
|
|
|
event.Skip()
|
|
|
|
|
|
def HitTest(self, pt):
|
|
"""
|
|
HitTest method for :class:`PageContainer`.
|
|
|
|
:param `pt`: an instance of :class:`Point`, to test for hits.
|
|
|
|
:return: The hit test flag (if any) and the hit page index (if any). The return
|
|
value can be one of the following bits:
|
|
|
|
========================= ======= =================================
|
|
HitTest Flag Value Description
|
|
========================= ======= =================================
|
|
``FNB_NOWHERE`` 0 Indicates mouse coordinates not on any tab of the notebook
|
|
``FNB_TAB`` 1 Indicates mouse coordinates inside a tab
|
|
``FNB_X`` 2 Indicates mouse coordinates inside the 'X' button region
|
|
``FNB_TAB_X`` 3 Indicates mouse coordinates inside the 'X' region in a tab
|
|
``FNB_LEFT_ARROW`` 4 Indicates mouse coordinates inside the left arrow region
|
|
``FNB_RIGHT_ARROW`` 5 Indicates mouse coordinates inside the right arrow region
|
|
``FNB_DROP_DOWN_ARROW`` 6 Indicates mouse coordinates inside the drop down arrow region
|
|
========================= ======= =================================
|
|
|
|
"""
|
|
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
render = self._mgr.GetRenderer(agwStyle)
|
|
|
|
fullrect = self.GetClientRect()
|
|
btnLeftPos = render.GetLeftButtonPos(self)
|
|
btnRightPos = render.GetRightButtonPos(self)
|
|
btnXPos = render.GetXPos(self)
|
|
|
|
tabIdx = -1
|
|
|
|
if len(self._pagesInfoVec) == 0:
|
|
return FNB_NOWHERE, tabIdx
|
|
|
|
rect = wx.Rect(btnXPos, 8, 16, 16)
|
|
if rect.Contains(pt):
|
|
return (agwStyle & FNB_NO_X_BUTTON and [FNB_NOWHERE] or [FNB_X])[0], tabIdx
|
|
|
|
rect = wx.Rect(btnRightPos, 8, 16, 16)
|
|
if agwStyle & FNB_DROPDOWN_TABS_LIST:
|
|
rect = wx.Rect(render.GetDropArrowButtonPos(self), 8, 16, 16)
|
|
if rect.Contains(pt):
|
|
return FNB_DROP_DOWN_ARROW, tabIdx
|
|
|
|
if rect.Contains(pt):
|
|
return (agwStyle & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_RIGHT_ARROW])[0], tabIdx
|
|
|
|
rect = wx.Rect(btnLeftPos, 8, 16, 16)
|
|
if rect.Contains(pt):
|
|
return (agwStyle & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_LEFT_ARROW])[0], tabIdx
|
|
|
|
# Test whether a left click was made on a tab
|
|
bFoundMatch = False
|
|
|
|
for cur in range(self._nFrom, len(self._pagesInfoVec)):
|
|
|
|
pgInfo = self._pagesInfoVec[cur]
|
|
|
|
if pgInfo.GetPosition() == wx.Point(-1, -1):
|
|
continue
|
|
|
|
if agwStyle & FNB_X_ON_TAB and cur == self.GetSelection():
|
|
# 'x' button exists on a tab
|
|
if self._pagesInfoVec[cur].GetXRect().Contains(pt):
|
|
return FNB_TAB_X, cur
|
|
|
|
if agwStyle & FNB_VC8:
|
|
|
|
if self._pagesInfoVec[cur].GetRegion().Contains(pt.x, pt.y):
|
|
if bFoundMatch or cur == self.GetSelection():
|
|
return FNB_TAB, cur
|
|
|
|
tabIdx = cur
|
|
bFoundMatch = True
|
|
|
|
else:
|
|
|
|
tabRect = wx.Rect(pgInfo.GetPosition().x, pgInfo.GetPosition().y,
|
|
pgInfo.GetSize().x, pgInfo.GetSize().y)
|
|
|
|
if tabRect.Contains(pt):
|
|
# We have a match
|
|
return FNB_TAB, cur
|
|
|
|
if bFoundMatch:
|
|
return FNB_TAB, tabIdx
|
|
|
|
if self._isdragging:
|
|
# We are doing DND, so check also the region outside the tabs
|
|
# try before the first tab
|
|
pgInfo = self._pagesInfoVec[0]
|
|
tabRect = wx.Rect(0, pgInfo.GetPosition().y, pgInfo.GetPosition().x, self.GetParent().GetSize().y)
|
|
if tabRect.Contains(pt):
|
|
return FNB_TAB, 0
|
|
|
|
# try after the last tab
|
|
pgInfo = self._pagesInfoVec[-1]
|
|
startpos = pgInfo.GetPosition().x+pgInfo.GetSize().x
|
|
tabRect = wx.Rect(startpos, pgInfo.GetPosition().y, fullrect.width-startpos, self.GetParent().GetSize().y)
|
|
|
|
if tabRect.Contains(pt):
|
|
return FNB_TAB, len(self._pagesInfoVec)
|
|
|
|
# Default
|
|
return FNB_NOWHERE, -1
|
|
|
|
|
|
def SetSelection(self, page):
|
|
"""
|
|
Sets the selected page.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
book = self.GetParent()
|
|
FlatNotebook.SetSelection(book, page)
|
|
|
|
## self.DoSetSelection(page)
|
|
|
|
|
|
def DoSetSelection(self, page):
|
|
"""
|
|
Does the actual selection of a page.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
#! fix for tabfocus
|
|
da_page = self._pParent.GetPage(page)
|
|
|
|
if da_page != None:
|
|
da_page.SetFocus()
|
|
|
|
if not self.IsTabVisible(page):
|
|
# Try to remove one tab from start and try again
|
|
|
|
if not self.CanFitToScreen(page):
|
|
|
|
if self._nFrom > page:
|
|
self._nFrom = page
|
|
else:
|
|
while self._nFrom < page:
|
|
self._nFrom += 1
|
|
if self.CanFitToScreen(page):
|
|
break
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def DeletePage(self, page):
|
|
"""
|
|
Delete the specified page from :class:`PageContainer`.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
book = self.GetParent()
|
|
book.DeletePage(page)
|
|
book.Refresh()
|
|
|
|
|
|
def IsMouseHovering(self, page):
|
|
"""
|
|
Returns whether or not the mouse is hovering over this page's tab
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
return page == self._nHoveringOverTabIndex
|
|
|
|
|
|
def IsTabVisible(self, page):
|
|
"""
|
|
Returns whether a tab is visible or not.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
iLastVisiblePage = self.GetLastVisibleTab()
|
|
return page <= iLastVisiblePage and page >= self._nFrom
|
|
|
|
|
|
def DoDeletePage(self, page):
|
|
"""
|
|
Does the actual page deletion.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
# Remove the page from the vector
|
|
book = self.GetParent()
|
|
self._pagesInfoVec.pop(page)
|
|
|
|
# Thanks to Yiaanis AKA Mandrav
|
|
if self._iActivePage >= page:
|
|
self._iActivePage = self._iActivePage - 1
|
|
self._iPreviousActivePage = -1
|
|
|
|
# The delete page was the last first on the array,
|
|
# but the book still has more pages, so we set the
|
|
# active page to be the first one (0)
|
|
if self._iActivePage < 0 and len(self._pagesInfoVec) > 0:
|
|
self._iActivePage = 0
|
|
self._iPreviousActivePage = -1
|
|
|
|
# Refresh the tabs
|
|
if self._iActivePage >= 0:
|
|
|
|
book._bForceSelection = True
|
|
|
|
# Check for selection and send event
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId())
|
|
event.SetSelection(self._iActivePage)
|
|
event.SetOldSelection(self._iPreviousActivePage)
|
|
event.SetEventObject(self.GetParent())
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
book.SetSelection(self._iActivePage)
|
|
book._bForceSelection = False
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(self._iPreviousActivePage)
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
#if not self._pagesInfoVec:
|
|
# # Erase the page container drawings
|
|
# dc = wx.ClientDC(self)
|
|
# dc.Clear()
|
|
|
|
|
|
def DeleteAllPages(self):
|
|
""" Deletes all the pages in the :class:`PageContainer`. """
|
|
|
|
self._iActivePage = -1
|
|
self._iPreviousActivePage = -1
|
|
self._nFrom = 0
|
|
self._pagesInfoVec = []
|
|
|
|
# Erase the page container drawings
|
|
dc = wx.ClientDC(self)
|
|
dc.Clear()
|
|
|
|
|
|
def OnMouseMove(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_MOTION`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
if self._pagesInfoVec and self.IsShown():
|
|
|
|
xButtonStatus = self._nXButtonStatus
|
|
xTabButtonStatus = self._nTabXButtonStatus
|
|
rightButtonStatus = self._nRightButtonStatus
|
|
leftButtonStatus = self._nLeftButtonStatus
|
|
dropDownButtonStatus = self._nArrowDownButtonStatus
|
|
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
bRedrawTabs = False
|
|
self._nHoveringOverTabIndex = -1
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where == FNB_X:
|
|
if event.LeftIsDown():
|
|
|
|
self._nXButtonStatus = (self._nLeftClickZone==FNB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nXButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_DROP_DOWN_ARROW:
|
|
if event.LeftIsDown():
|
|
|
|
self._nArrowDownButtonStatus = (self._nLeftClickZone==FNB_DROP_DOWN_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nArrowDownButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_TAB_X:
|
|
if event.LeftIsDown():
|
|
|
|
self._nTabXButtonStatus = (self._nLeftClickZone==FNB_TAB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nTabXButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_RIGHT_ARROW:
|
|
if event.LeftIsDown():
|
|
|
|
self._nRightButtonStatus = (self._nLeftClickZone==FNB_RIGHT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nRightButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_LEFT_ARROW:
|
|
if event.LeftIsDown():
|
|
|
|
self._nLeftButtonStatus = (self._nLeftClickZone==FNB_LEFT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_TAB:
|
|
# Call virtual method for showing tooltip
|
|
self.ShowTabTooltip(tabIdx)
|
|
|
|
if not self.GetEnabled(tabIdx):
|
|
# Set the cursor to be 'No-entry'
|
|
self.SetCursor(wx.Cursor(wx.CURSOR_NO_ENTRY))
|
|
self._setCursor = True
|
|
else:
|
|
self._nHoveringOverTabIndex = tabIdx
|
|
if self._setCursor:
|
|
self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
|
|
self._setCursor = False
|
|
|
|
# Support for drag and drop
|
|
if event.Dragging() and not (agwStyle & FNB_NODRAG):
|
|
|
|
self._isdragging = True
|
|
draginfo = FNBDragInfo(self, tabIdx)
|
|
drginfo = pickle.dumps(draginfo)
|
|
dataobject = wx.CustomDataObject(wx.DataFormat(six.u('FlatNotebook')))
|
|
dataobject.SetData(drginfo)
|
|
dragSource = FNBDropSource(self)
|
|
dragSource.SetData(dataobject)
|
|
dragSource.DoDragDrop(wx.Drag_DefaultMove)
|
|
|
|
if self._nHoveringOverTabIndex != self._nHoveringOverLastTabIndex:
|
|
self._nHoveringOverLastTabIndex = self._nHoveringOverTabIndex
|
|
if self._nHoveringOverTabIndex >= 0:
|
|
bRedrawTabs = True
|
|
|
|
bRedrawX = self._nXButtonStatus != xButtonStatus
|
|
bRedrawRight = self._nRightButtonStatus != rightButtonStatus
|
|
bRedrawLeft = self._nLeftButtonStatus != leftButtonStatus
|
|
bRedrawTabX = self._nTabXButtonStatus != xTabButtonStatus
|
|
bRedrawDropArrow = self._nArrowDownButtonStatus != dropDownButtonStatus
|
|
|
|
render = self._mgr.GetRenderer(agwStyle)
|
|
|
|
if (bRedrawX or bRedrawRight or bRedrawLeft or bRedrawTabX or bRedrawDropArrow or bRedrawTabs):
|
|
|
|
dc = wx.ClientDC(self)
|
|
|
|
if bRedrawX:
|
|
|
|
render.DrawX(self, dc)
|
|
|
|
if bRedrawLeft:
|
|
|
|
render.DrawLeftArrow(self, dc)
|
|
|
|
if bRedrawRight:
|
|
|
|
render.DrawRightArrow(self, dc)
|
|
|
|
if bRedrawTabX or bRedrawTabs:
|
|
|
|
self.Refresh()
|
|
|
|
if bRedrawDropArrow:
|
|
|
|
render.DrawDropDownArrow(self, dc)
|
|
|
|
event.Skip()
|
|
|
|
|
|
def GetLastVisibleTab(self):
|
|
""" Returns the last visible tab in the tab area. """
|
|
|
|
if self._nFrom < 0:
|
|
return -1
|
|
|
|
ii = 0
|
|
|
|
for ii in range(self._nFrom, len(self._pagesInfoVec)):
|
|
|
|
if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1):
|
|
break
|
|
|
|
return ii-1
|
|
|
|
|
|
def GetNumTabsCanScrollLeft(self):
|
|
""" Returns the number of tabs than can be scrolled left. """
|
|
|
|
if self._nFrom - 1 >= 0:
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
def IsDefaultTabs(self):
|
|
""" Returns whether a tab has a default style. """
|
|
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
res = (agwStyle & FNB_VC71) or (agwStyle & FNB_FANCY_TABS) or (agwStyle & FNB_VC8)
|
|
return not res
|
|
|
|
|
|
def AdvanceSelection(self, forward=True):
|
|
"""
|
|
Cycles through the tabs.
|
|
|
|
:param `forward`: if ``True``, the selection is advanced in ascending order
|
|
(to the right), otherwise the selection is advanced in descending order.
|
|
|
|
:note: The call to this function generates the page changing events.
|
|
"""
|
|
|
|
nSel = self.GetSelection()
|
|
|
|
if nSel < 0:
|
|
return
|
|
|
|
nMax = self.GetPageCount() - 1
|
|
|
|
if forward:
|
|
newSelection = (nSel == nMax and [0] or [nSel + 1])[0]
|
|
else:
|
|
newSelection = (nSel == 0 and [nMax] or [nSel - 1])[0]
|
|
|
|
if not self._pagesInfoVec[newSelection].GetEnabled():
|
|
return
|
|
|
|
self.FireEvent(newSelection)
|
|
|
|
|
|
def OnMouseLeave(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_LEAVE_WINDOW`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
self._nHoveringOverTabIndex = -1
|
|
self._nHoveringOverLastTabIndex = -1
|
|
|
|
self.Refresh()
|
|
selection = self.GetSelection()
|
|
|
|
if selection == -1:
|
|
event.Skip()
|
|
return
|
|
|
|
if not self.IsTabVisible(selection):
|
|
if selection == len(self._pagesInfoVec) - 1:
|
|
if not self.CanFitToScreen(selection):
|
|
event.Skip()
|
|
return
|
|
else:
|
|
event.Skip()
|
|
return
|
|
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
render = self._mgr.GetRenderer(agwStyle)
|
|
dc = wx.ClientDC(self)
|
|
render.DrawTabX(self, dc, self._pagesInfoVec[selection].GetXRect(), selection, self._nTabXButtonStatus)
|
|
if not agwStyle & FNB_RIBBON_TABS:
|
|
render.DrawFocusRectangle(dc, self, self._pagesInfoVec[selection])
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnMouseEnterWindow(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_ENTER_WINDOW`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nLeftClickZone = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
event.Skip()
|
|
|
|
|
|
def ShowTabTooltip(self, tabIdx):
|
|
"""
|
|
Shows a tab tooltip.
|
|
|
|
:param `tabIdx`: an integer specifying the page index.
|
|
"""
|
|
|
|
pWindow = self._pParent.GetPage(tabIdx)
|
|
|
|
if pWindow:
|
|
pToolTip = pWindow.GetToolTip()
|
|
if pToolTip and pToolTip.GetWindow() == pWindow:
|
|
self.SetToolTip(pToolTip.GetTip())
|
|
|
|
|
|
def SetPageImage(self, page, image):
|
|
"""
|
|
Sets the image index for the given page.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `image`: an index into the image list which was set with :meth:`~PageContainer.SetImageList`.
|
|
"""
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
self._pagesInfoVec[page].SetImageIndex(image)
|
|
self.Refresh()
|
|
|
|
|
|
def GetPageImage(self, page):
|
|
"""
|
|
Returns the image index associated to a page.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
return self._pagesInfoVec[page].GetImageIndex()
|
|
|
|
return -1
|
|
|
|
|
|
def GetPageTextColour(self, page):
|
|
"""
|
|
Returns the tab text colour if it has been set previously, or ``None`` otherwise.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
return self._pagesInfoVec[page].GetPageTextColour()
|
|
|
|
return None
|
|
|
|
|
|
def SetPageTextColour(self, page, colour):
|
|
"""
|
|
Sets the tab text colour individually.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `colour`: a valid :class:`Colour` object or any typemap supported by wxWidgets/wxPython
|
|
to generate a colour (i.e., a hex string, a colour name, a 3 or 4 integer tuple). You can
|
|
pass ``None`` or :class:`NullColour` to return to the default page text colour.
|
|
"""
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
self._pagesInfoVec[page].SetPageTextColour(colour)
|
|
self.Refresh()
|
|
|
|
|
|
def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer):
|
|
"""
|
|
Handles the drop action from a drag and drop operation.
|
|
|
|
:param `x`: the x position of the drop action;
|
|
:param `y`: the y position of the drop action;
|
|
:param `nTabPage`: the index of the tab being dropped;
|
|
:param `wnd_oldContainer`: the :class:`FlatNotebook` to which the dropped tab previously
|
|
belonged to.
|
|
"""
|
|
|
|
# Disable drag'n'drop for disabled tab
|
|
if len(wnd_oldContainer._pagesInfoVec) > nTabPage and \
|
|
not wnd_oldContainer._pagesInfoVec[nTabPage].GetEnabled():
|
|
return wx.DragCancel
|
|
|
|
self._isdragging = True
|
|
oldContainer = wnd_oldContainer
|
|
nIndex = -1
|
|
|
|
where, nIndex = self.HitTest(wx.Point(x, y))
|
|
|
|
oldNotebook = oldContainer.GetParent()
|
|
newNotebook = self.GetParent()
|
|
|
|
if oldNotebook == newNotebook:
|
|
|
|
if nTabPage >= 0:
|
|
|
|
if where == FNB_TAB:
|
|
self.MoveTabPage(nTabPage, nIndex)
|
|
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_DROPPED, self.GetParent().GetId())
|
|
event.SetSelection(nIndex)
|
|
event.SetOldSelection(nTabPage)
|
|
event.SetEventObject(self.GetParent())
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
elif self.GetParent().GetAGWWindowStyleFlag() & FNB_ALLOW_FOREIGN_DND:
|
|
|
|
if wx.Platform in ["__WXMSW__", "__WXGTK__", "__WXMAC__"]:
|
|
if nTabPage >= 0:
|
|
|
|
window = oldNotebook.GetPage(nTabPage)
|
|
|
|
if window:
|
|
where, nIndex = newNotebook._pages.HitTest(wx.Point(x, y))
|
|
caption = oldContainer.GetPageText(nTabPage)
|
|
imageindex = oldContainer.GetPageImage(nTabPage)
|
|
oldNotebook.RemovePage(nTabPage)
|
|
window.Reparent(newNotebook)
|
|
|
|
if imageindex >= 0:
|
|
|
|
bmp = oldNotebook.GetImageList().GetBitmap(imageindex)
|
|
newImageList = newNotebook.GetImageList()
|
|
|
|
if not newImageList:
|
|
xbmp, ybmp = bmp.GetWidth(), bmp.GetHeight()
|
|
newImageList = wx.ImageList(xbmp, ybmp)
|
|
imageindex = 0
|
|
else:
|
|
imageindex = newImageList.GetImageCount()
|
|
|
|
newImageList.Add(bmp)
|
|
newNotebook.SetImageList(newImageList)
|
|
|
|
newNotebook.InsertPage(nIndex, window, caption, True, imageindex)
|
|
|
|
event = FlatNotebookDragEvent(wxEVT_FLATNOTEBOOK_PAGE_DROPPED_FOREIGN, self.GetParent().GetId())
|
|
event.SetSelection(nIndex)
|
|
event.SetOldSelection(nTabPage)
|
|
event.SetNotebook(newNotebook)
|
|
event.SetOldNotebook(oldNotebook)
|
|
event.SetEventObject(self.GetParent())
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
self._isdragging = False
|
|
|
|
return wx.DragMove
|
|
|
|
|
|
def MoveTabPage(self, nMove, nMoveTo):
|
|
"""
|
|
Moves a tab inside the same :class:`FlatNotebook`.
|
|
|
|
:param `nMove`: the start index of the moved tab;
|
|
:param `nMoveTo`: the destination index of the moved tab.
|
|
"""
|
|
|
|
if nMove == nMoveTo:
|
|
return
|
|
|
|
elif nMoveTo < len(self._pParent._windows):
|
|
nMoveTo = nMoveTo + 1
|
|
|
|
self._pParent.Freeze()
|
|
|
|
# Remove the window from the main sizer
|
|
nCurSel = self._pParent._pages.GetSelection()
|
|
self._pParent._mainSizer.Detach(self._pParent._windows[nCurSel])
|
|
self._pParent._windows[nCurSel].Hide()
|
|
|
|
pWindow = self._pParent._windows[nMove]
|
|
self._pParent._windows.pop(nMove)
|
|
self._pParent._windows.insert(nMoveTo-1, pWindow)
|
|
|
|
pgInfo = self._pagesInfoVec[nMove]
|
|
|
|
self._pagesInfoVec.pop(nMove)
|
|
self._pagesInfoVec.insert(nMoveTo - 1, pgInfo)
|
|
|
|
# Add the page according to the style
|
|
pSizer = self._pParent._mainSizer
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
|
|
if agwStyle & FNB_BOTTOM:
|
|
|
|
pSizer.Insert(0, pWindow, 1, wx.EXPAND)
|
|
|
|
else:
|
|
|
|
# We leave a space of 1 pixel around the window
|
|
pSizer.Add(pWindow, 1, wx.EXPAND)
|
|
|
|
pWindow.Show()
|
|
|
|
pSizer.Layout()
|
|
self._iActivePage = nMoveTo - 1
|
|
self._iPreviousActivePage = -1
|
|
self.DoSetSelection(self._iActivePage)
|
|
self.Refresh()
|
|
self._pParent.Thaw()
|
|
|
|
|
|
def CanFitToScreen(self, page):
|
|
"""
|
|
Returns wheter a tab can fit in the left space in the screen or not.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
# Incase the from is greater than page,
|
|
# we need to reset the self._nFrom, so in order
|
|
# to force the caller to do so, we return false
|
|
if self._nFrom > page:
|
|
return False
|
|
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
render = self._mgr.GetRenderer(agwStyle)
|
|
|
|
vTabInfo = render.NumberTabsCanFit(self)
|
|
|
|
if page - self._nFrom >= len(vTabInfo):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def GetNumOfVisibleTabs(self):
|
|
""" Returns the number of visible tabs. """
|
|
|
|
count = 0
|
|
for ii in range(self._nFrom, len(self._pagesInfoVec)):
|
|
if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1):
|
|
break
|
|
count = count + 1
|
|
|
|
return count
|
|
|
|
|
|
def GetEnabled(self, page):
|
|
"""
|
|
Returns whether a tab is enabled or not.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
if page >= len(self._pagesInfoVec):
|
|
return True # Seems strange, but this is the default
|
|
|
|
return self._pagesInfoVec[page].GetEnabled()
|
|
|
|
|
|
def EnableTab(self, page, enabled=True):
|
|
"""
|
|
Enables or disables a tab.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `enabled`: ``True`` to enable a tab, ``False`` to disable it.
|
|
"""
|
|
|
|
if page >= len(self._pagesInfoVec):
|
|
return
|
|
|
|
self._pagesInfoVec[page].EnableTab(enabled)
|
|
|
|
|
|
def GetSingleLineBorderColour(self):
|
|
""" Returns the colour for the single line border. """
|
|
|
|
if self.HasAGWFlag(FNB_FANCY_TABS):
|
|
return self._colourFrom
|
|
|
|
return wx.WHITE
|
|
|
|
|
|
def HasAGWFlag(self, flag):
|
|
"""
|
|
Returns whether a flag is present in the :class:`FlatNotebook` style.
|
|
|
|
:param `flag`: one of the possible :class:`FlatNotebook` window styles.
|
|
|
|
:see: :meth:`FlatNotebook.SetAGWWindowStyleFlag() <FlatNotebook.SetAGWWindowStyleFlag>` for a list of possible window
|
|
style flags.
|
|
"""
|
|
|
|
agwStyle = self.GetParent().GetAGWWindowStyleFlag()
|
|
res = (agwStyle & flag and [True] or [False])[0]
|
|
return res
|
|
|
|
|
|
def ClearAGWFlag(self, flag):
|
|
"""
|
|
Deletes a flag from the :class:`FlatNotebook` style.
|
|
|
|
:param `flag`: one of the possible :class:`FlatNotebook` window styles.
|
|
|
|
:see: :meth:`FlatNotebook.SetAGWWindowStyleFlag() <FlatNotebook.SetAGWWindowStyleFlag>` for a list of possible window
|
|
style flags.
|
|
"""
|
|
|
|
parent = self.GetParent()
|
|
agwStyle = parent.GetAGWWindowStyleFlag()
|
|
agwStyle &= ~flag
|
|
parent.SetAGWWindowStyleFlag(agwStyle)
|
|
|
|
|
|
def SetAGWWindowStyleFlag(self, agwStyle):
|
|
"""
|
|
Sets the :class:`FlatNotebook` window style.
|
|
|
|
:param `agwStyle`: the new :class:`FlatNotebook` window style.
|
|
|
|
:see: The :meth:`FlatNotebook.__init__() <FlatNotebook.__init__>` method for the `agwStyle` parameter description.
|
|
"""
|
|
|
|
self.GetParent().SetAGWWindowStyleFlag(agwStyle)
|
|
|
|
|
|
def GetAGWWindowStyleFlag(self):
|
|
"""
|
|
Returns the :class:`FlatNotebook` window style.
|
|
|
|
:see: The :meth:`FlatNotebook.__init__() <FlatNotebook.__init__>` method for the `agwStyle` parameter description.
|
|
"""
|
|
|
|
return self.GetParent().GetAGWWindowStyleFlag()
|
|
|
|
|
|
def TabHasImage(self, tabIdx):
|
|
"""
|
|
Returns whether a tab has an associated image index or not.
|
|
|
|
:param `tabIdx`: an integer specifying the page index.
|
|
"""
|
|
|
|
if self._ImageList:
|
|
return self._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
|
|
return False
|
|
|
|
|
|
def OnLeftDClick(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_LEFT_DCLICK`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MouseEvent` event to be processed.
|
|
"""
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where == FNB_RIGHT_ARROW:
|
|
self._nRightButtonStatus = FNB_BTN_PRESSED
|
|
self.RotateRight()
|
|
|
|
elif where == FNB_LEFT_ARROW:
|
|
self._nLeftButtonStatus = FNB_BTN_PRESSED
|
|
self.RotateLeft()
|
|
|
|
elif self.HasAGWFlag(FNB_DCLICK_CLOSES_TABS):
|
|
|
|
if where == FNB_TAB:
|
|
self.DeletePage(tabIdx)
|
|
|
|
else:
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnSetFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_SET_FOCUS`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`FocusEvent` event to be processed.
|
|
"""
|
|
|
|
if self._iActivePage < 0:
|
|
event.Skip()
|
|
return
|
|
|
|
self.SetFocusedPage(self._iActivePage)
|
|
|
|
|
|
def OnKillFocus(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KILL_FOCUS`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`FocusEvent` event to be processed.
|
|
"""
|
|
|
|
self.SetFocusedPage()
|
|
|
|
|
|
def OnKeyDown(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_KEY_DOWN`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`KeyEvent` event to be processed.
|
|
|
|
:note: When the :class:`PageContainer` has the focus tabs can be changed with
|
|
the left/right arrow keys.
|
|
"""
|
|
|
|
key = event.GetKeyCode()
|
|
if key == wx.WXK_LEFT:
|
|
self.GetParent().AdvanceSelection(False)
|
|
self.SetFocus()
|
|
elif key == wx.WXK_RIGHT:
|
|
self.GetParent().AdvanceSelection(True)
|
|
self.SetFocus()
|
|
elif key == wx.WXK_TAB and not event.ControlDown():
|
|
flags = 0
|
|
if not event.ShiftDown(): flags |= wx.NavigationKeyEvent.IsForward
|
|
if event.CmdDown(): flags |= wx.NavigationKeyEvent.WinChange
|
|
self.Navigate(flags)
|
|
else:
|
|
event.Skip()
|
|
|
|
|
|
def SetFocusedPage(self, pageIndex=-1):
|
|
"""
|
|
Sets/Unsets the focus on the appropriate page.
|
|
|
|
:param `pageIndex`: an integer specifying the page index. If `pageIndex`
|
|
is defaulted to -1, we have lost focus and no focus indicator is drawn.
|
|
"""
|
|
|
|
for indx, page in enumerate(self._pagesInfoVec):
|
|
if indx == pageIndex:
|
|
page._hasFocus = True
|
|
else:
|
|
page._hasFocus = False
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def PopupTabsMenu(self):
|
|
""" Pops up the menu activated with the drop down arrow in the navigation area. """
|
|
|
|
popupMenu = wx.Menu()
|
|
|
|
for i in range(len(self._pagesInfoVec)):
|
|
pi = self._pagesInfoVec[i]
|
|
item = wx.MenuItem(popupMenu, i+1, pi.GetCaption(), pi.GetCaption(), wx.ITEM_NORMAL)
|
|
self.Bind(wx.EVT_MENU, self.OnTabMenuSelection, item)
|
|
|
|
# There is an alignment problem with wx2.6.3 & Menus so only use
|
|
# images for versions above 2.6.3
|
|
if wx.VERSION > (2, 6, 3, 0) and self.TabHasImage(i):
|
|
item.SetBitmap(self.GetImageList().GetBitmap(pi.GetImageIndex()))
|
|
|
|
popupMenu.AppendItem(item)
|
|
item.Enable(pi.GetEnabled())
|
|
|
|
self.PopupMenu(popupMenu)
|
|
|
|
|
|
def OnTabMenuSelection(self, event):
|
|
"""
|
|
Handles the ``wx.EVT_MENU`` event for :class:`PageContainer`.
|
|
|
|
:param `event`: a :class:`MenuEvent` event to be processed.
|
|
"""
|
|
|
|
selection = event.GetId() - 1
|
|
self.FireEvent(selection)
|
|
|
|
|
|
def FireEvent(self, selection):
|
|
"""
|
|
Fires the ``EVT_FLATNOTEBOOK_PAGE_CHANGING`` and ``EVT_FLATNOTEBOOK_PAGE_CHANGED``
|
|
events called from other methods (from menu selection or `Smart Tabbing`).
|
|
|
|
This is an utility function.
|
|
|
|
:param `selection`: the new selection inside :class:`FlatNotebook`.
|
|
"""
|
|
|
|
if selection == self._iActivePage:
|
|
# No events for the same selection
|
|
return
|
|
|
|
oldSelection = self._iActivePage
|
|
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId())
|
|
event.SetSelection(selection)
|
|
event.SetOldSelection(oldSelection)
|
|
event.SetEventObject(self.GetParent())
|
|
|
|
if not self.GetParent().GetEventHandler().ProcessEvent(event) or event.IsAllowed():
|
|
|
|
self.SetSelection(selection)
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(oldSelection)
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
if not self.HasAGWFlag(FNB_NO_TAB_FOCUS):
|
|
self.SetFocus()
|
|
|
|
|
|
def SetImageList(self, imglist):
|
|
"""
|
|
Sets the image list for the :class:`PageContainer`.
|
|
|
|
:param `imageList`: an instance of :class:`ImageList`.
|
|
"""
|
|
|
|
self._ImageList = imglist
|
|
|
|
|
|
def AssignImageList(self, imglist):
|
|
"""
|
|
Assigns the image list for the :class:`PageContainer`.
|
|
|
|
:param `imageList`: an instance of :class:`ImageList`.
|
|
"""
|
|
|
|
self._ImageList = imglist
|
|
|
|
|
|
def GetImageList(self):
|
|
""" Returns the image list for the page control. """
|
|
|
|
return self._ImageList
|
|
|
|
|
|
def GetSelection(self):
|
|
""" Returns the current selected page. """
|
|
|
|
return self._iActivePage
|
|
|
|
|
|
def GetPageCount(self):
|
|
""" Returns the number of tabs in the :class:`FlatNotebook` control. """
|
|
|
|
return len(self._pagesInfoVec)
|
|
|
|
|
|
def GetPageText(self, page):
|
|
"""
|
|
Returns the tab caption of the page.
|
|
|
|
:param `page`: an integer specifying the page index.
|
|
"""
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
return self._pagesInfoVec[page].GetCaption()
|
|
else:
|
|
return six.u('')
|
|
|
|
|
|
def SetPageText(self, page, text):
|
|
"""
|
|
Sets the tab caption of the page.
|
|
|
|
:param `page`: an integer specifying the page index;
|
|
:param `text`: the new tab label.
|
|
"""
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
self._pagesInfoVec[page].SetCaption(text)
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def DrawDragHint(self):
|
|
""" Draws small arrow at the place that the tab will be placed. """
|
|
|
|
# get the index of tab that will be replaced with the dragged tab
|
|
pt = wx.GetMousePosition()
|
|
client_pt = self.ScreenToClient(pt)
|
|
where, tabIdx = self.HitTest(client_pt)
|
|
self._mgr.GetRenderer(self.GetParent().GetAGWWindowStyleFlag()).DrawDragHint(self, tabIdx)
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FlatNotebookCompatible
|
|
# This class is more compatible with the wx.Notebook API.
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FlatNotebookCompatible(FlatNotebook):
|
|
"""
|
|
This class is more compatible with the :class:`Notebook` API, especially regarding
|
|
page changing events. Use the :meth:`FlatNotebookCompatible.SetSelection() <FlatNotebookCompatible.SetSelection>` method if you wish to send page
|
|
changing events, or :meth:`FlatNotebookCompatible.ChangeSelection() <FlatNotebookCompatible.ChangeSelection>` otherwise.
|
|
"""
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
|
style=0, agwStyle=0, name="FlatNotebook"):
|
|
"""
|
|
Default class constructor.
|
|
|
|
:param `parent`: the :class:`FlatNotebook` parent;
|
|
:param `id`: an identifier for the control: a value of -1 is taken to mean a default;
|
|
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `size`: the control size. A value of (-1, -1) indicates a default size,
|
|
chosen by either the windowing system or wxPython, depending on platform;
|
|
:param `style`: the underlying :class:`Panel` window style;
|
|
:param `agwStyle`: the AGW-specific window style. This can be a combination of the
|
|
following bits:
|
|
|
|
================================ =========== ==================================================
|
|
Window Styles Hex Value Description
|
|
================================ =========== ==================================================
|
|
``FNB_VC71`` 0x1 Use Visual Studio 2003 (VC7.1) style for tabs.
|
|
``FNB_FANCY_TABS`` 0x2 Use fancy style - square tabs filled with gradient colouring.
|
|
``FNB_TABS_BORDER_SIMPLE`` 0x4 Draw thin border around the page.
|
|
``FNB_NO_X_BUTTON`` 0x8 Do not display the 'X' button.
|
|
``FNB_NO_NAV_BUTTONS`` 0x10 Do not display the right/left arrows.
|
|
``FNB_MOUSE_MIDDLE_CLOSES_TABS`` 0x20 Use the mouse middle button for cloing tabs.
|
|
``FNB_BOTTOM`` 0x40 Place tabs at bottom - the default is to place them at top.
|
|
``FNB_NODRAG`` 0x80 Disable dragging of tabs.
|
|
``FNB_VC8`` 0x100 Use Visual Studio 2005 (VC8) style for tabs.
|
|
``FNB_X_ON_TAB`` 0x200 Place 'X' close button on the active tab.
|
|
``FNB_BACKGROUND_GRADIENT`` 0x400 Use gradients to paint the tabs background.
|
|
``FNB_COLOURFUL_TABS`` 0x800 Use colourful tabs (VC8 style only).
|
|
``FNB_DCLICK_CLOSES_TABS`` 0x1000 Style to close tab using double click.
|
|
``FNB_SMART_TABS`` 0x2000 Use `Smart Tabbing`, like ``Alt`` + ``Tab`` on Windows.
|
|
``FNB_DROPDOWN_TABS_LIST`` 0x4000 Use a dropdown menu on the left in place of the arrows.
|
|
``FNB_ALLOW_FOREIGN_DND`` 0x8000 Allows drag 'n' drop operations between different :class:`FlatNotebook`.
|
|
``FNB_HIDE_ON_SINGLE_TAB`` 0x10000 Hides the Page Container when there is one or fewer tabs.
|
|
``FNB_DEFAULT_STYLE`` 0x10020 :class:`FlatNotebook` default style.
|
|
``FNB_FF2`` 0x20000 Use Firefox 2 style for tabs.
|
|
``FNB_NO_TAB_FOCUS`` 0x40000 Does not allow tabs to have focus.
|
|
``FNB_RIBBON_TABS`` 0x80000 Use the Ribbon Tabs style.
|
|
``FNB_HIDE_TABS`` 0x100000 Hides the Page Container allowing only keyboard navigation
|
|
``FNB_NAV_BUTTONS_WHEN_NEEDED`` 0x200000 Hides the navigation left/right arrows if all tabs fit
|
|
================================ =========== ==================================================
|
|
|
|
:param `name`: the window name.
|
|
"""
|
|
|
|
FlatNotebook.__init__(self, parent, id, pos, size, style, agwStyle, name)
|
|
|
|
|
|
def SetSelection(self, page):
|
|
"""
|
|
Sets the selection for the given page.
|
|
|
|
:param `page`: an integer specifying the new selected page.
|
|
|
|
:note: The call to this function **generates** the page changing events.
|
|
"""
|
|
|
|
if page >= len(self._windows) or not self._windows:
|
|
return
|
|
|
|
# Support for disabed tabs
|
|
if not self._pages.GetEnabled(page) and len(self._windows) > 1 and not self._bForceSelection:
|
|
return
|
|
|
|
self._pages.FireEvent(page)
|
|
|
|
|
|
def ChangeSelection(self, page):
|
|
"""
|
|
Sets the selection for the given page.
|
|
|
|
:param `page`: an integer specifying the new selected page.
|
|
|
|
:note: The call to this function **does not** generate the page changing events.
|
|
"""
|
|
|
|
FlatNotebook.SetSelection(self, page)
|
|
|