• No results found

About the Authors

C ONTAINER W INDOWS

Container windows are designed to hold further visual elements, either child windows or graphics drawn on the window.

wxPanel

wxPanelis essentially a wxWindowwith some dialog-like properties. This is usu- ally the class to use when you want to arrange controls on a window that’s not a wxDialog, such as a wxFrame. It’s often used for pages of a wxNotebook.wxPanel normally takes the system dialog color.

As with wxDialog, you can use InitDialog to send a wxInitDialogEventto the panel, transferring data to the panel via validators or other means. wxPanelalso handles navigation keys such as the Tab key to provide automatic traversal between controls, if the wxTAB_TRAVERSALstyle is provided.

wxPanel has the following constructor in addition to the default constructor:

wxPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL|wxNO_BORDER, const wxString& name = wxT(“panel”));

For example:

wxPanel* panel = new wxPanel(frame, wxID_ANY, wxDefaultPosition, (500, 300));

wxPanelStyles

There are no specific styles for wxPanel, but see the styles for wxWindow. wxPanelMember Functions

There are no distinct wxPanelfunctions; please refer to the wxWindowmember functions, inherited by wxPanel.

wxNotebook

This class represents a control with several pages, switched by clicking on tabs along an edge of the control. A page is normally a wxPanelor a class derived from it, although you may use other window classes.

Notebook tabs may have images as well as, or instead of, text labels. The images are supplied via a wxImageList(see Chapter 10), and specified by posi- tion in the list.

To use a notebook, create a wxNotebook object and call AddPage or InsertPage, passing a window to be used as the page. Do not explicitly destroy the window for a page that is currently managed by wxNotebook; use DeletePage instead, or let the notebook destroy the pages when it is itself destroyed.

Here’s an example of creating a notebook with three panels, and a text label and icon for each tab:

#include “wx/notebook.h” #include “copy.xpm” #include “cut.xpm” #include “paste.xpm” // Create the notebook

wxNotebook* notebook = new wxNotebook(parent, wxID_ANY, wxDefaultPosition, wxSize(300, 200));

// Create the image list

wxImageList* imageList = new wxImageList(16, 16, true, 3); imageList->Add(wxIcon(copy_xpm));

imageList->Add(wxIcon(paste_xpm)); imageList->Add(wxIcon(cut_xpm)); // Create and add the pages

wxPanel1* window1 = new wxPanel(notebook, wxID_ANY); wxPanel2* window2 = new wxPanel(notebook, wxID_ANY); wxPanel3* window3 = new wxPanel(notebook, wxID_ANY); notebook->AddPage(window1, wxT(“Tab one”), true, 0); notebook->AddPage(window2, wxT(“Tab two”), false, 1); notebook->AddPage(window3, wxT(“Tab three”), false 2);

Figure 4-9 shows the result on Windows.

Container Windows 73

On most platforms, there are scroll buttons to view tabs that cannot all be displayed on the window at once. However, on Mac OS, the tabs do not scroll, so the number you can display is limited by the window and tab label size.

If you use sizers to lay out controls on individual pages, and pass wxDefaultSizeto the notebook constructor,wxNotebookwill adjust its size to fit the sizes of its pages.

Notebook Theme Management

On Windows XP, the default theme paints a gradient on the notebook’s pages. Although this is the expected native behavior, it can slow down performance, and you may prefer a solid background for aesthetic reasons, especially when the notebook is not being used in a dialog. If you want to suppress themed drawing, there are three ways of doing it. You can use the wxNB_NOPAGETHEME style to disable themed drawing for a particular notebook, you can call wxSystemOptions::SetOptionto disable it for the whole application, or you can disable it for individual pages by using SetBackgroundColour. To disable themed pages globally, do this:

wxSystemOptions::SetOption(wxT(“msw.notebook.themed-background”), 0);

Set the value to 1to enable it again. To give a single page a solid background that matches the current theme, use

wxColour col = notebook->GetThemeBackgroundColour(); if (col.Ok())

{

page->SetBackgroundColour(col); }

On platforms other than Windows, or if the application is not using Windows themes,GetThemeBackgroundColourwill return an uninitialized color object, and this code will therefore work on all platforms. Please note that this syntax and behavior is subject to change, so refer to the wxNotebookdocumentation in your wxWidgets distribution for the latest information.

wxNotebookStyles

wxNotebookcan have the window styles listed in Table 4-11, in addition to those described for wxWindow.

Table 4-11 wxNotebookStyles

wxNB_TOP Place tabs on the top side.

wxNB_LEFT Place tabs on the left side. Not supported under Windows XP for all themes.

wxNB_RIGHT Place tabs on the right side. Not supported under Windows XP for all themes.

wxNB_BOTTOM Place tabs under instead of above the notebook pages. Not supported under Windows XP for all themes.

wxNB_FIXEDWIDTH All tabs will have same width. Windows only. wxNB_MULTILINE There can be several rows of tabs. Windows only.

wxNB_NOPAGETHEME On Windows, suppresses the textured theme painting for the notebook’s pages, drawing a solid color to match the current theme instead. This can improve performance in addition to giving an aesthetic choice.

wxNotebookEvents

wxNotebook generates wxNotebookEvent propagating events (events that can be handled by the notebook or its ancestors) specified in Table 4-12.

Table 4-12 wxNotebookEvents

EVT_NOTEBOOK_PAGE_CHANGED(id, func) The page selection has changed. EVT_NOTEBOOK_PAGE_CHANGING(id, func) The page selection is about to change.

You can veto the selection change with Veto.

wxNotebookMember Functions

These are the major wxNotebookfunctions.

AddPageadds a page, and InsertPageinserts a page at the given position. You can use a text label for the tab, or an image (specified by index into an image list), or both. For example:

// Adds an unselected page with a label and an image // (index 2 in the associated image list).

notebook->AddPage(page, wxT(“My tab”), false, 2);

DeletePage removes and destroys the specified page, while RemovePage just removes the page without deleting the page. Call DeleteAllPagesto delete all the pages. When the wxNotebookis deleted, it will delete all its pages.

AdvanceSelection cycles through the pages, and SetSelection sets the specified page by zero-based index. Use GetSelection to get the index of the selected page, or wxNOT_FOUNDif none was selected.

SetImageListsets a wxImageList to be used by the notebook but does not take ownership of it. Call AssignImageList if you want the notebook to delete the image list when it is destroyed.GetImageListreturns the associated image list. An image list stores images to be shown on each page tab, if required. wxImageListis described in Chapter 10.

Use GetPage to return the page window for a given index, and use GetPageCountto return the number of pages in the notebook.

SetPageText and GetPageText are accessors for the label for a given page (by index).

SetPageImage and GetPageImage are accessors for the index of a page’s image index in the notebook’s image list.

Alternatives to wxNotebook

wxNotebook is derived from a base class wxBookCtrlBase, which abstracts the concept of a control that manages pages. There are two API-compatible varia- tions of the wxNotebook concept, wxListbook and wxChoicebook, and you can implement your own, such as wxTreebook.

wxListbook uses a wxListCtrl to change pages; the list control displays icons with text labels underneath them, and can be on any of the four sides, defaulting to the left side. This is an attractive alternative to wxNotebook, and it has the advantage of being able to cope with an arbitrary number of pages even on Mac OS X because the list control can scroll.

wxChoicebookuses a choice control (a drop-down list) and is particularly handy for small devices with restricted screen space, such as smartphones. It does not display images, and by default, it will display the choice control at the top.

The include files for these classes are wx/listbook.h and wx/choicebk.h. Event handlers for these two classes take a wxListbookEvent or wxChoicebookEvent argument, respectively, and you can use the event macros EVT_XXX_PAGE_CHANGED(id, func) and EVT_XXX_PAGE_CHANGING(id, func) where XXXis LISTBOOKor CHOICEBOOK.

You can use the same window styles as wxNotebook, or you can use the equivalents, such as wxCHB_TOPor wxLB_TOPinstead of wxNB_TOP.

wxScrolledWindow

All windows can have scrollbars, but extra code is required to make scrolling work. This gives the flexibility to define appropriate scrolling behaviors for dif- ferent kinds of windows. wxScrolledWindow implements commonly required scrolling behavior by assuming that scrolling happens in consistent units, not different-sized jumps, and that page size (the amount scrolled when “paging”

up, down, left, or right) is represented by the visible portion of the window. It is suited to drawing applications but is not so suitable for a sophisticated editor in which the amount scrolled may vary according to the size of text on a given line. For this, you would derive from wxWindowand implement scrolling your- self.wxGridis an example of a class that implements its own scrolling, largely because columns and rows can vary in size.

To use a scrolled window, you need to define the number of pixels per log- ical scroll unit (how much the window is scrolled for a line up or down scroll event) and provide the virtual size in logical units.wxScrolledWindowwill then take care of showing the scrollbars with appropriately sized “thumbs” (the parts you can drag) and will show or hide scrollbars as appropriate, according to the actual size of the window.

The following fragment shows how to create a scrolled window: #include “wx/scrolwin.h”

wxScrolledWindow* scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxPoint(0, 0), wxSize(400, 400), wxVSCROLL|wxHSCROLL);

// Set up virtual window dimensions. It will be 1000x1000 // and will scroll 10 pixels at a time

int pixelsPerUnixX = 10; int pixelsPerUnixY = 10; int noUnitsX = 1000; int noUnitsY = 1000; scrolledWindow->SetScrollbars(pixelsPerUnitX, pixelsPerUnitY, noUnitsX, noUnitsY);

A second way to specify the virtual size is to use SetVirtualSize, which takes the virtual size in pixels, plus a call to SetScrollRateto set the horizontal and vertical scrolling increments. A third way is to set a sizer for the window, and the scrolled window will calculate the required scrollbar dimensions from the space taken up by the child windows. You will still need to call SetScrollRateto specify the scrolling increments.

You can provide a paint event handler as normal, but in order to draw the graphics at the appropriate position for the current positions of the scroll- bars, call DoPrepareDC before drawing. This sets the device context’s device origin. For example:

void MyScrolledWindow::OnPaint(wxPaintEvent& event) { wxPaintDC dc(this); DoPrepareDC(dc); dc.SetPen(*wxBLACK_PEN); dc.DrawLine(0, 0, 100, 100); } Container Windows 77

Alternatively, you can override the OnDraw virtual function;wxScrolledWindow creates a paint device context and calls DoPrepareDCfor you before calling your OnDrawfunction, so the code simplifies to the following:

void MyScrolledWindow::OnDraw(wxDC& dc) {

dc.SetPen(*wxBLACK_PEN); dc.DrawLine(0, 0, 100, 100); }

Note that you will need to call DoPrepareDC if you draw on the window from outside the paint event, such as within a mouse event handler.

You can provide your own DoPrepareDCfunction. The default function sim- ply shifts the device origin according to the current scroll positions so that subsequent drawing will appear at the right place:

void wxScrolledWindow::DoPrepareDC(wxDC& dc) {

int ppuX, ppuY, startX, startY;

GetScrollPixelsPerUnit(& ppuX, & ppuY); GetViewStart(& startX, & startY);

dc.SetDeviceOrigin( - startX * ppuX, - startY * ppuY ); }

For more on painting on a wxScrolledWindow, including using buffered drawing, please see the section on wxPaintDC in Chapter 5, “Drawing and Printing.”

wxScrolledWindowStyles

There are no special styles for wxScrolledWindow, but usually you will supply wxVSCROLL|wxHSCROLL (the default style for wxScrolledWindow). On some plat- forms, if these styles are not present, no scrollbars will be provided for effi- ciency reasons.

wxScrolledWindowEvents

wxScrolledWindow generates wxScrollWinEvent events (see Table 4-13). These events do not propagate up the window parent-child hierarchy, so if you want to intercept these events, you must derive a new class or plug an event han- dler object into the window object. Normally you will not need to override the existing handlers for these events.

Table 4-13 wxScrolledWindowEvents

EVT_SCROLLWIN(func) Handles all scroll events.

EVT_SCROLLWIN_TOP(func) Handles wxEVT_SCROLLWIN_TOPscroll-to-top events.

EVT_SCROLLWIN_BOTTOM(func) Handles wxEVT_SCROLLWIN_TOPscroll-to- bottom events.

EVT_SCROLLWIN_LINEUP(func) Handles wxEVT_SCROLLWIN_LINEUPline up events.

EVT_SCROLLWIN_LINEDOWN(func) Handles wxEVT_SCROLLWIN_LINEDOWNline down events.

EVT_SCROLLWIN_PAGEUP(func) Handles wxEVT_SCROLLWIN_PAGEUPpage up events.

EVT_SCROLLWIN_PAGEDOWN(func) Handles wxEVT_SCROLLWIN_PAGEDOWNpage down events.

wxScrolledWindowMember Functions

These are the major wxScrolledWindowfunctions.

CalcScrolledPosition and CalcUnscrolledPosition both take four argu- ments: two integers for the position input in pixels, and two pointers to inte- gers for the transformed position output, also in pixels.CalcScrolledPosition calculates the device position from the logical position. For example, if the window is scrolled 10 pixels down from the top, the logicalfirst visible position is 0, but the deviceposition is -10.CalcUnscrolledPositiondoes the inverse, cal- culating the logical position from the device position.

EnableScrolling enables or disables physical scrolling in horizontal and vertical directions independently. Physical scrolling is the physical transfer of bits up or down the screen when a scroll event occurs. If the application scrolls by a variable amount (for example, if there are different font sizes), then phys- ical scrolling will not work, and you should switch it off. If physical scrolling is disabled, you will have to reposition child windows yourself. Physical scrolling may not be available on all platforms, but it is enabled by default where it is available.

GetScrollPixelsPerUnit returns the horizontal and vertical scroll unit sizes in two pointers to integers. A value of zero indicates that there is no scrolling in that direction.

GetViewStartreturns the position of the first visible position on the win- dow, in logical units. Pass two pointers to integers to receive the values. You will need to multiply by the values returned by GetScrollPixelsPerUnitto get pixel values.

GetVirtualSize returns the size in device units (pixels) of the scrollable window area. Pass two pointers to integers to receive the virtual width and height.

DoPrepareDC prepares the device context by setting the device origin according to the current scrollbar positions.

Scroll scrolls a window so that the view is at the given point in scroll units (not pixels), passed as two integers. If either parameter is -1, that posi- tion will be unchanged.

SetScrollbars sets the pixels per unit in each direction, the number of units for the virtual window in each direction, the horizontal and vertical posi- tion to scroll to (optional), and a boolean to indicate whether the window should be refreshed (falseby default).

SetScrollRatesets the horizontal and increment scroll rate (the same as the pixels per unit parameters in SetScrollbars).

SetTargetWindow can be used to scroll a window other than the wxScrolledWindow.

Scrolling Without Using wxScrolledWindow

If you want to implement your own scrolling behavior, you can derive a class from wxWindowand use wxWindow::SetScrollbarto set scrollbar properties.

SetScrollbartakes the arguments listed in Table 4-14. Table 4-14 SetScrollbarArguments

int orientation The scrollbar to set:wxVERTICALor wxHORIZONTAL. int position The position of the scrollbar “thumb” in scroll units.

int visible The size of the visible portion of the scrollbar, in scroll units. Normally, a scrollbar is capable of indicating the visible por- tion visually by showing a different length of thumb. int range The maximum value of the scrollbar, where zero is the start

position. You choose the units that suit you, so if you wanted to display text that has 100 lines, you would set this to 100. Note that this doesn’t have to correspond to the number of pixels scrolled—it is up to you how you actually show the contents of the window.

bool refresh trueif the scrollbar should be repainted immediately.

Let’s say you want to display 50 lines of text, using the same font. The window is sized so that you can only see 16 lines at a time.

You would use

SetScrollbar(wxVERTICAL, 0, 16, 50)

Note that with the window at this size, the thumb position can never go above 50 minus 16, or 34.

You can determine how many lines are currently visible by dividing the current view size by the character height in pixels.

When defining your own scrollbar behavior, you will always need to recalculate the scrollbar settings when the window size changes. You could therefore introduce a new function AdjustScrollbarsinto which you place your scrollbar calculations and SetScrollbarcall.AdjustScrollbarscan be called ini- tially, and also from your wxSizeEventhandler function.

It’s instructive to look at the implementations of wxScrolledWindow and wxGridif you’re thinking of implementing your own scrolling behavior.

You may want to look at wxVScrolledWindow in the wxWidgets reference manual; this can be used to build a scrolled window class that can scroll by lines of unequal height in the vertical direction.

wxSplitterWindow

This class manages up to two subwindows (use nested splitter windows if you need more splits). The current view can be split into two by the application, for example, from a menu command. It can be unsplit either by the application or via the splitter window user interface by double-clicking on the sash or drag- ging the sash until one of the panes has zero size (override the latter behavior with SetMinimumPaneSize).

On most platforms, when the sash is dragged, a reverse-video line will be drawn to show where the sash will end up. You can pass wxSP_LIVE_UPDATEto let the sash move in “real time” instead, resizing the child windows. This is the default (and only) mode on Mac OS X.

The following fragment shows how to create a splitter window, creating two subwindows and hiding one of them.

#include “wx/splitter.h”

wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY, wxPoint(0, 0), wxSize(400, 400), wxSP_3D);

leftWindow = new MyWindow(splitter); leftWindow->SetScrollbars(20, 20, 50, 50); rightWindow = new MyWindow(splitter);

rightWindow->SetScrollbars(20, 20, 50, 50); rightWindow->Show(false);

splitter->Initialize(leftWindow);

// Unncomment this to prevent unsplitting // splitter->SetMinimumPaneSize(20);

This fragment shows how the splitter window can be manipulated after creation:

void MyFrame::OnSplitVertical(wxCommandEvent& event) {

if ( splitter->IsSplit() ) splitter->Unsplit(); leftWindow->Show(true); rightWindow->Show(true);

splitter->SplitVertically( leftWindow, rightWindow ); }

void MyFrame::OnSplitHorizontal(wxCommandEvent& event) {

if ( splitter->IsSplit() ) splitter->Unsplit(); leftWindow->Show(true); rightWindow->Show(true);

splitter->SplitHorizontally( leftWindow, rightWindow ); }

void MyFrame::OnUnsplit(wxCommandEvent& event) {

if ( splitter->IsSplit() ) splitter->Unsplit(); }

Figure 4-10 shows how the wxWidgets splitter sample looks on Windows with- out the wxSP_NO_XP_THEMEstyle. If you use this style, the splitter will take on a more traditional look with a sunken border and 3D sash.

82 Window Basics Chapter 4

wxSplitterWindowStyles

wxSplitterWindowcan have the window styles shown in Table 4-15 in addition to those described for wxWindow.