Add support for stretchable spaces to wxToolBar.

Stretchable spaces consume all extra toolbar space not allocated to the fixed
size items. They can in particular be used to right-align (some) toolbar tools.

Add and document the new API, change the sample to show it and implement it
for MSW, GTK and OS X/Cocoa.

Also refactor MSW background erasing/repainting code to avoid duplicated calls
to DrawThemeBackground(), call it from a new helper MSWEraseRect() function.

Note that we may want to add support for "invisible" separators, IOW
non-stretchable spaces. This could be easily done for MSW after the changes in
this commit and is supported natively by GTK+ and Cocoa so implementing this
would be trivial if there is any interest.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62850 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2009-12-10 03:04:19 +00:00
parent 0b83552acf
commit cc26010927
9 changed files with 374 additions and 185 deletions

View File

@@ -445,6 +445,7 @@ All (GUI):
- Added support for showing bitmaps in wxButton. - Added support for showing bitmaps in wxButton.
- Added wxInfoBar. - Added wxInfoBar.
- Added stretchable spaces support to wxToolBar.
- Added support for corner, row and column headers renderers to wxGrid. - Added support for corner, row and column headers renderers to wxGrid.
- wxWindow::SetAutoLayout() now works for all windows, not just panels. - wxWindow::SetAutoLayout() now works for all windows, not just panels.
- Support wxListCtrl columns, items and image lists in XRC (Kinaou Hervé). - Support wxListCtrl columns, items and image lists in XRC (Kinaou Hervé).

View File

@@ -142,6 +142,10 @@ protected:
// the total number of toolbar elements // the total number of toolbar elements
size_t m_nButtons; size_t m_nButtons;
// the sum of the sizes of the fixed items (i.e. excluding stretchable
// spaces) in the toolbar direction
int m_totalFixedSize;
// the tool the cursor is in // the tool the cursor is in
wxToolBarToolBase *m_pInTool; wxToolBarToolBase *m_pInTool;
@@ -149,6 +153,17 @@ private:
// makes sure tool bitmap size is sufficient for all tools // makes sure tool bitmap size is sufficient for all tools
void AdjustToolBitmapSize(); void AdjustToolBitmapSize();
// update the sizes of stretchable spacers to consume all extra space we
// have
void UpdateStretchableSpacersSize();
// redraw the background of the given part of the window (or entire window
// if the parameter is NULL) to erase separator drawn in it
//
// return true if the background was erased using DrawThemeBackground()
bool MSWEraseRect(wxDC& dc, const wxRect *rectItem = NULL);
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
DECLARE_DYNAMIC_CLASS(wxToolBar) DECLARE_DYNAMIC_CLASS(wxToolBar)
wxDECLARE_NO_COPY_CLASS(wxToolBar); wxDECLARE_NO_COPY_CLASS(wxToolBar);

View File

@@ -101,11 +101,6 @@ public:
m_control = control; m_control = control;
} }
m_toolStyle = wxTOOL_STYLE_CONTROL;
m_dropdownMenu = 0;
}
virtual ~wxToolBarToolBase(); virtual ~wxToolBarToolBase();
// accessors // accessors
@@ -123,10 +118,12 @@ public:
wxToolBarBase *GetToolBar() const { return m_tbar; } wxToolBarBase *GetToolBar() const { return m_tbar; }
// style // style/kind
bool IsStretchable() const { return m_stretchable; }
bool IsButton() const { return m_toolStyle == wxTOOL_STYLE_BUTTON; } bool IsButton() const { return m_toolStyle == wxTOOL_STYLE_BUTTON; }
bool IsControl() const { return m_toolStyle == wxTOOL_STYLE_CONTROL; } bool IsControl() const { return m_toolStyle == wxTOOL_STYLE_CONTROL; }
bool IsSeparator() const { return m_toolStyle == wxTOOL_STYLE_SEPARATOR; } bool IsSeparator() const { return m_toolStyle == wxTOOL_STYLE_SEPARATOR; }
bool IsStretchableSpace() const { return IsSeparator() && IsStretchable(); }
int GetStyle() const { return m_toolStyle; } int GetStyle() const { return m_toolStyle; }
wxItemKind GetKind() const wxItemKind GetKind() const
{ {
@@ -135,6 +132,13 @@ public:
return m_kind; return m_kind;
} }
void MakeStretchable()
{
wxASSERT_MSG( IsSeparator(), "only separators can be stretchable" );
m_stretchable = true;
}
// state // state
bool IsEnabled() const { return m_enabled; } bool IsEnabled() const { return m_enabled; }
bool IsToggled() const { return m_toggled; } bool IsToggled() const { return m_toggled; }
@@ -214,6 +218,7 @@ protected:
m_clientData = NULL; m_clientData = NULL;
m_stretchable = false;
m_toggled = false; m_toggled = false;
m_enabled = true; m_enabled = true;
@@ -223,7 +228,7 @@ protected:
wxToolBarBase *m_tbar; // the toolbar to which we belong (may be NULL) wxToolBarBase *m_tbar; // the toolbar to which we belong (may be NULL)
// tool parameters // tool parameters
int m_toolStyle; // see enum wxToolBarToolStyle wxToolBarToolStyle m_toolStyle;
wxWindowIDRef m_id; // the tool id, wxID_SEPARATOR for separator wxWindowIDRef m_id; // the tool id, wxID_SEPARATOR for separator
wxItemKind m_kind; // for normal buttons may be wxITEM_NORMAL/CHECK/RADIO wxItemKind m_kind; // for normal buttons may be wxITEM_NORMAL/CHECK/RADIO
@@ -234,6 +239,9 @@ protected:
wxControl *m_control; wxControl *m_control;
}; };
// true if this tool is stretchable: currently is only value for separators
bool m_stretchable;
// tool state // tool state
bool m_toggled; bool m_toggled;
bool m_enabled; bool m_enabled;
@@ -362,6 +370,12 @@ public:
virtual wxToolBarToolBase *AddSeparator(); virtual wxToolBarToolBase *AddSeparator();
virtual wxToolBarToolBase *InsertSeparator(size_t pos); virtual wxToolBarToolBase *InsertSeparator(size_t pos);
// add a stretchable space to the toolbar: this is similar to a separator
// except that it's always blank and that all the extra space the toolbar
// has is [equally] distributed among the stretchable spaces in it
virtual wxToolBarToolBase *AddStretchableSpace();
virtual wxToolBarToolBase *InsertStretchableSpace(size_t pos);
// remove the tool from the toolbar: the caller is responsible for actually // remove the tool from the toolbar: the caller is responsible for actually
// deleting the pointer // deleting the pointer
virtual wxToolBarToolBase *RemoveTool(int toolid); virtual wxToolBarToolBase *RemoveTool(int toolid);
@@ -606,6 +620,17 @@ protected:
virtual wxToolBarToolBase *CreateTool(wxControl *control, virtual wxToolBarToolBase *CreateTool(wxControl *control,
const wxString& label) = 0; const wxString& label) = 0;
// this one is not virtual but just a simple helper/wrapper around
// CreateTool() for separators
wxToolBarToolBase *CreateSeparator()
{
return CreateTool(wxID_SEPARATOR,
wxEmptyString,
wxNullBitmap, wxNullBitmap,
wxITEM_SEPARATOR, NULL,
wxEmptyString, wxEmptyString);
}
// helper functions // helper functions
// ---------------- // ----------------

View File

@@ -228,10 +228,27 @@ public:
platform so it can be a vertical line (MSW, some versions of GTK) or platform so it can be a vertical line (MSW, some versions of GTK) or
just an empty space or something else. just an empty space or something else.
@see AddTool(), SetToolSeparation() @see AddTool(), SetToolSeparation(), AddStretchableSpace()
*/ */
virtual wxToolBarToolBase* AddSeparator(); virtual wxToolBarToolBase* AddSeparator();
/**
Adds a stretchable space to the toolbar.
Any space not taken up by the fixed items (all items except for
stretchable spaces) is distributed in equal measure between the
stretchable spaces in the toolbar. The most common use for this method
is to add a single stretchable space before the items which should be
right-aligned in the toolbar, but more exotic possibilities are
possible, e.g. a stretchable space may be added in the beginning and
the end of the toolbar to centre all toolbar items.
@see AddTool(), AddSeparator(), InsertStretchableSpace()
@since 2.9.1
*/
wxToolBarToolBase *AddStretchableSpace();
//@{ //@{
/** /**
Adds a tool to the toolbar. Adds a tool to the toolbar.
@@ -525,6 +542,17 @@ public:
*/ */
virtual wxToolBarToolBase* InsertSeparator(size_t pos); virtual wxToolBarToolBase* InsertSeparator(size_t pos);
/**
Inserts a stretchable space at the given position.
See AddStretchableSpace() for details about stretchable spaces.
@see InsertTool(), InsertSeparator()
@since 2.9.1
*/
wxToolBarToolBase *InsertStretchableSpace(size_t pos);
//@{ //@{
/** /**
Inserts the tool with the specified attributes into the toolbar at the Inserts the tool with the specified attributes into the toolbar at the

View File

@@ -310,7 +310,7 @@ bool MyApp::OnInit()
// Create the main frame window // Create the main frame window
MyFrame* frame = new MyFrame((wxFrame *) NULL, wxID_ANY, MyFrame* frame = new MyFrame((wxFrame *) NULL, wxID_ANY,
wxT("wxToolBar Sample"), wxT("wxToolBar Sample"),
wxPoint(100, 100), wxSize(550, 300)); wxPoint(100, 100), wxSize(650, 300));
frame->Show(true); frame->Show(true);
@@ -474,9 +474,12 @@ void MyFrame::PopulateToolbar(wxToolBarBase* toolBar)
#endif // USE_CONTROLS_IN_TOOLBAR #endif // USE_CONTROLS_IN_TOOLBAR
toolBar->AddTool(wxID_SAVE, wxT("Save"), toolBarBitmaps[Tool_save], wxT("Toggle button 1"), wxITEM_CHECK); toolBar->AddTool(wxID_SAVE, wxT("Save"), toolBarBitmaps[Tool_save], wxT("Toggle button 1"), wxITEM_CHECK);
toolBar->AddSeparator();
toolBar->AddTool(wxID_COPY, wxT("Copy"), toolBarBitmaps[Tool_copy], wxT("Toggle button 2"), wxITEM_CHECK); toolBar->AddTool(wxID_COPY, wxT("Copy"), toolBarBitmaps[Tool_copy], wxT("Toggle button 2"), wxITEM_CHECK);
toolBar->AddTool(wxID_CUT, wxT("Cut"), toolBarBitmaps[Tool_cut], wxT("Toggle/Untoggle help button")); toolBar->AddTool(wxID_CUT, wxT("Cut"), toolBarBitmaps[Tool_cut], wxT("Toggle/Untoggle help button"));
toolBar->AddTool(wxID_PASTE, wxT("Paste"), toolBarBitmaps[Tool_paste], wxT("Paste")); toolBar->AddTool(wxID_PASTE, wxT("Paste"), toolBarBitmaps[Tool_paste], wxT("Paste"));
toolBar->AddSeparator();
if ( m_useCustomDisabled ) if ( m_useCustomDisabled )
{ {
@@ -500,7 +503,9 @@ void MyFrame::PopulateToolbar(wxToolBarBase* toolBar)
wxT("Delete this tool. This is a very long tooltip to test whether it does the right thing when the tooltip is more than Windows can cope with.")); wxT("Delete this tool. This is a very long tooltip to test whether it does the right thing when the tooltip is more than Windows can cope with."));
} }
toolBar->AddSeparator(); // add a stretchable space before the "Help" button to make it
// right-aligned
toolBar->AddStretchableSpace();
toolBar->AddTool(wxID_HELP, wxT("Help"), toolBarBitmaps[Tool_help], wxT("Help button"), wxITEM_CHECK); toolBar->AddTool(wxID_HELP, wxT("Help"), toolBarBitmaps[Tool_help], wxT("Help button"), wxITEM_CHECK);
if ( !m_pathBmp.empty() ) if ( !m_pathBmp.empty() )
@@ -899,14 +904,12 @@ void MyFrame::DoToggleHelp()
void MyFrame::OnToggleSearch(wxCommandEvent& WXUNUSED(event)) void MyFrame::OnToggleSearch(wxCommandEvent& WXUNUSED(event))
{ {
static const int searchPos = 3;
wxToolBarBase * const tb = GetToolBar(); wxToolBarBase * const tb = GetToolBar();
if ( !m_searchTool ) if ( !m_searchTool )
{ {
wxSearchCtrl * const srch = new wxSearchCtrl(tb, wxID_ANY, "needle"); wxSearchCtrl * const srch = new wxSearchCtrl(tb, wxID_ANY, "needle");
srch->SetMinSize(wxSize(80, -1)); srch->SetMinSize(wxSize(80, -1));
m_searchTool = tb->InsertControl(searchPos, srch); m_searchTool = tb->AddControl(srch);
} }
else // tool already exists else // tool already exists
{ {
@@ -919,7 +922,7 @@ void MyFrame::OnToggleSearch(wxCommandEvent& WXUNUSED(event))
} }
else // tool exists in detached state, attach it back else // tool exists in detached state, attach it back
{ {
tb->InsertTool(searchPos, m_searchTool); tb->AddTool(m_searchTool);
win->Show(); win->Show();
} }
} }

View File

@@ -259,11 +259,29 @@ wxToolBarToolBase *wxToolBarBase::AddSeparator()
wxToolBarToolBase *wxToolBarBase::InsertSeparator(size_t pos) wxToolBarToolBase *wxToolBarBase::InsertSeparator(size_t pos)
{ {
return DoInsertNewTool(pos, CreateTool(wxID_SEPARATOR, return DoInsertNewTool(pos, CreateSeparator());
wxEmptyString, }
wxNullBitmap, wxNullBitmap,
wxITEM_SEPARATOR, NULL, wxToolBarToolBase *wxToolBarBase::AddStretchableSpace()
wxEmptyString, wxEmptyString)); {
return InsertStretchableSpace(GetToolsCount());
}
wxToolBarToolBase *wxToolBarBase::InsertStretchableSpace(size_t pos)
{
wxToolBarToolBase * const tool = CreateSeparator();
if ( tool )
{
// this is a hack but we know that all the current implementations
// don't really use the tool when it's created, they will do it
// InsertTool() at earliest and maybe even in Realize() much later
//
// so we can create the tool as a plain separator and mark it as being
// a stretchable space later
tool->MakeStretchable();
}
return DoInsertNewTool(pos, tool);
} }
wxToolBarToolBase *wxToolBarBase::RemoveTool(int id) wxToolBarToolBase *wxToolBarBase::RemoveTool(int id)

View File

@@ -519,6 +519,15 @@ bool wxToolBar::DoInsertTool(size_t pos, wxToolBarToolBase *toolBase)
case wxTOOL_STYLE_SEPARATOR: case wxTOOL_STYLE_SEPARATOR:
tool->m_item = gtk_separator_tool_item_new(); tool->m_item = gtk_separator_tool_item_new();
if ( tool->IsStretchable() )
{
gtk_separator_tool_item_set_draw
(
GTK_SEPARATOR_TOOL_ITEM(tool->m_item),
FALSE
);
gtk_tool_item_set_expand(tool->m_item, TRUE);
}
gtk_toolbar_insert(m_toolbar, tool->m_item, int(pos)); gtk_toolbar_insert(m_toolbar, tool->m_item, int(pos));
break; break;

View File

@@ -149,7 +149,7 @@ public:
clientData, shortHelp, longHelp) clientData, shortHelp, longHelp)
{ {
m_nSepCount = 0; m_nSepCount = 0;
m_staticText = 0; m_staticText = NULL;
} }
wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label) wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label)
@@ -210,6 +210,29 @@ public:
void SetSeparatorsCount(size_t count) { m_nSepCount = count; } void SetSeparatorsCount(size_t count) { m_nSepCount = count; }
size_t GetSeparatorsCount() const { return m_nSepCount; } size_t GetSeparatorsCount() const { return m_nSepCount; }
// we need ids for the spacers which we want to modify later on, this
// function will allocate a valid/unique id for a spacer if not done yet
void AllocSpacerId()
{
if ( m_id == wxID_SEPARATOR )
m_id = wxWindow::NewControlId();
}
// this method is used for controls only and offsets the control by the
// given amount (in pixels) in horizontal direction
void MoveBy(int offset)
{
wxControl * const control = GetControl();
control->Move(control->GetPosition().x + offset, wxDefaultCoord);
if ( m_staticText )
{
m_staticText->Move(m_staticText->GetPosition().x + offset,
wxDefaultCoord);
}
}
private: private:
size_t m_nSepCount; size_t m_nSepCount;
wxStaticText *m_staticText; wxStaticText *m_staticText;
@@ -281,6 +304,7 @@ void wxToolBar::Init()
m_disabledImgList = NULL; m_disabledImgList = NULL;
m_nButtons = 0; m_nButtons = 0;
m_totalFixedSize = 0;
// even though modern Windows applications typically use 24*24 (or even // even though modern Windows applications typically use 24*24 (or even
// 32*32) size for their bitmaps, the native control itself still uses the // 32*32) size for their bitmaps, the native control itself still uses the
@@ -582,15 +606,7 @@ bool wxToolBar::DoDeleteTool(size_t pos, wxToolBarToolBase *tool)
wxToolBarTool *tool2 = (wxToolBarTool*)node->GetData(); wxToolBarTool *tool2 = (wxToolBarTool*)node->GetData();
if ( tool2->IsControl() ) if ( tool2->IsControl() )
{ {
wxControl * const control = tool2->GetControl(); tool2->MoveBy(-width);
int x;
control->GetPosition(&x, NULL);
control->Move(x - width, wxDefaultCoord);
wxStaticText * const staticText = tool2->GetStaticText();
if ( staticText )
staticText->Move(x - width, wxDefaultCoord);
} }
} }
@@ -900,7 +916,7 @@ bool wxToolBar::Realize()
int i = 0; int i = 0;
for ( node = m_tools.GetFirst(); node; node = node->GetNext() ) for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
{ {
wxToolBarToolBase *tool = node->GetData(); wxToolBarTool *tool = static_cast<wxToolBarTool *>(node->GetData());
// don't add separators to the vertical toolbar with old comctl32.dll // don't add separators to the vertical toolbar with old comctl32.dll
// versions as they didn't handle this properly // versions as they didn't handle this properly
@@ -918,10 +934,16 @@ bool wxToolBar::Realize()
switch ( tool->GetStyle() ) switch ( tool->GetStyle() )
{ {
case wxTOOL_STYLE_CONTROL: case wxTOOL_STYLE_CONTROL:
button.idCommand = tool->GetId();
// fall through: create just a separator too
case wxTOOL_STYLE_SEPARATOR: case wxTOOL_STYLE_SEPARATOR:
if ( tool->IsStretchableSpace() )
{
// we're going to modify the size of this button later and
// so we need a valid id for it and not wxID_SEPARATOR
// which is used by spacers by default
tool->AllocSpacerId();
}
button.idCommand = tool->GetId();
button.fsState = TBSTATE_ENABLED; button.fsState = TBSTATE_ENABLED;
button.fsStyle = TBSTYLE_SEP; button.fsStyle = TBSTYLE_SEP;
break; break;
@@ -1017,33 +1039,38 @@ bool wxToolBar::Realize()
} }
// Deal with the controls finally // Adjust controls and stretchable spaces
// ------------------------------ // --------------------------------------
// adjust the controls size to fit nicely in the toolbar // adjust the controls size to fit nicely in the toolbar and compute its
int y = 0; // total size while doing it
size_t index = 0; m_totalFixedSize = 0;
for ( node = m_tools.GetFirst(); node; node = node->GetNext(), index++ ) int toolIndex = 0;
for ( node = m_tools.GetFirst(); node; node = node->GetNext(), toolIndex++ )
{ {
wxToolBarTool *tool = (wxToolBarTool*)node->GetData(); wxToolBarTool * const tool = (wxToolBarTool*)node->GetData();
// we calculate the running y coord for vertical toolbars so we need to const RECT r = wxGetTBItemRect(GetHwnd(), toolIndex);
// get the items size for all items but for the horizontal ones we
// don't need to deal with the non controls
bool isControl = tool->IsControl();
if ( !isControl && !IsVertical() )
continue;
const RECT r = wxGetTBItemRect(GetHwnd(), index); if ( !tool->IsControl() )
if ( !isControl )
{ {
// can only be control if isVertical if ( IsVertical() )
y += r.bottom - r.top; m_totalFixedSize += r.bottom - r.top;
else
m_totalFixedSize += r.right - r.left;
continue; continue;
} }
wxControl *control = tool->GetControl(); if ( IsVertical() )
{
// don't embed controls in the vertical toolbar, this doesn't look
// good and wxGTK doesn't do it neither (and the code below can't
// deal with this case)
continue;
}
wxControl * const control = tool->GetControl();
wxStaticText * const staticText = tool->GetStaticText(); wxStaticText * const staticText = tool->GetStaticText();
wxSize size = control->GetSize(); wxSize size = control->GetSize();
@@ -1054,9 +1081,6 @@ bool wxToolBar::Realize()
staticTextSize.y += 3; // margin between control and its label staticTextSize.y += 3; // margin between control and its label
} }
// the position of the leftmost controls corner
int left = wxDefaultCoord;
// TB_SETBUTTONINFO message is only supported by comctl32.dll 4.71+ // TB_SETBUTTONINFO message is only supported by comctl32.dll 4.71+
#ifdef TB_SETBUTTONINFO #ifdef TB_SETBUTTONINFO
// available in headers, now check whether it is available now // available in headers, now check whether it is available now
@@ -1082,7 +1106,6 @@ bool wxToolBar::Realize()
{ {
// try adding several separators to fit the controls width // try adding several separators to fit the controls width
int widthSep = r.right - r.left; int widthSep = r.right - r.left;
left = r.left;
TBBUTTON tbb; TBBUTTON tbb;
wxZeroMemory(tbb); wxZeroMemory(tbb);
@@ -1094,17 +1117,17 @@ bool wxToolBar::Realize()
for ( size_t nSep = 0; nSep < nSeparators; nSep++ ) for ( size_t nSep = 0; nSep < nSeparators; nSep++ )
{ {
if ( !::SendMessage(GetHwnd(), TB_INSERTBUTTON, if ( !::SendMessage(GetHwnd(), TB_INSERTBUTTON,
index, (LPARAM)&tbb) ) toolIndex, (LPARAM)&tbb) )
{ {
wxLogLastError(wxT("TB_INSERTBUTTON")); wxLogLastError(wxT("TB_INSERTBUTTON"));
} }
index++; toolIndex++;
} }
// remember the number of separators we used - we'd have to // remember the number of separators we used - we'd have to
// delete all of them later // delete all of them later
((wxToolBarTool *)tool)->SetSeparatorsCount(nSeparators); tool->SetSeparatorsCount(nSeparators);
// adjust the controls width to exactly cover the separators // adjust the controls width to exactly cover the separators
size.x = (nSeparators + 1)*widthSep; size.x = (nSeparators + 1)*widthSep;
@@ -1139,33 +1162,19 @@ bool wxToolBar::Realize()
staticText->Show(); staticText->Show();
} }
int top; control->Move(r.left, r.top + (diff + 1) / 2);
if ( IsVertical() )
{
left = 0;
top = y;
y += height + 2 * GetMargins().y;
}
else // horizontal toolbar
{
if ( left == wxDefaultCoord )
left = r.left;
top = r.top;
}
control->Move(left, top + (diff + 1) / 2);
if ( staticText ) if ( staticText )
{ {
staticText->Move(left + (size.x - staticTextSize.x)/2, staticText->Move(r.left + (size.x - staticTextSize.x)/2,
r.bottom - staticTextSize.y); r.bottom - staticTextSize.y);
} }
m_totalFixedSize += size.x;
} }
// the max index is the "real" number of buttons - i.e. counting even the // the max index is the "real" number of buttons - i.e. counting even the
// separators which we added just for aligning the controls // separators which we added just for aligning the controls
m_nButtons = index; m_nButtons = toolIndex;
if ( !IsVertical() ) if ( !IsVertical() )
{ {
@@ -1186,6 +1195,77 @@ bool wxToolBar::Realize()
return true; return true;
} }
void wxToolBar::UpdateStretchableSpacersSize()
{
// we can't resize the spacers if TB_SETBUTTONINFO is not supported
if ( wxApp::GetComCtl32Version() < 471 )
return;
// check if we have any stretchable spacers in the first place
unsigned numSpaces = 0;
wxToolBarToolsList::compatibility_iterator node;
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
{
wxToolBarTool * const tool = (wxToolBarTool*)node->GetData();
if ( tool->IsStretchableSpace() )
numSpaces++;
}
if ( !numSpaces )
return;
// we do, adjust their size: either distribute the extra size among them or
// reduce their size if there is not enough place for all tools
const int totalSize = IsVertical() ? GetClientSize().y : GetClientSize().x;
const int extraSize = totalSize - m_totalFixedSize;
const int sizeSpacer = extraSize > 0 ? extraSize / numSpaces : 0;
// the last spacer should consume all remaining space if we have too much
// of it (which can be greater than sizeSpacer because of the rounding)
const int sizeLastSpacer = extraSize > 0
? extraSize - (numSpaces - 1)*sizeSpacer
: 0;
// cumulated offset by which we need to move all the following controls to
// the right: while the toolbar takes care of the normal items, we must
// move the controls manually ourselves to ensure they remain at the
// correct place
int offset = 0;
int toolIndex = 0;
for ( node = m_tools.GetFirst(); node; node = node->GetNext(), toolIndex++ )
{
wxToolBarTool * const tool = (wxToolBarTool*)node->GetData();
if ( tool->IsControl() && offset )
{
tool->MoveBy(offset);
continue;
}
if ( !tool->IsStretchableSpace() )
continue;
const RECT rcOld = wxGetTBItemRect(GetHwnd(), toolIndex);
WinStruct<TBBUTTONINFO> tbbi;
tbbi.dwMask = TBIF_SIZE;
tbbi.cx = --numSpaces ? sizeSpacer : sizeLastSpacer;
if ( !::SendMessage(GetHwnd(), TB_SETBUTTONINFO,
tool->GetId(), (LPARAM)&tbbi) )
{
wxLogLastError(wxT("TB_SETBUTTONINFO"));
}
else
{
// we successfully resized this one, move all the controls after it
// by the corresponding amount (may be positive or negative)
offset += tbbi.cx - (rcOld.right - rcOld.left);
}
}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// message handlers // message handlers
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -1576,12 +1656,7 @@ void wxToolBar::OnEraseBackground(wxEraseEvent& event)
RECT rect = wxGetClientRect(GetHwnd()); RECT rect = wxGetClientRect(GetHwnd());
wxDC *dc = event.GetDC(); wxDC *dc = event.GetDC();
if (!dc) return; HDC hdc = GetHdcOf(*dc);
wxMSWDCImpl *impl = (wxMSWDCImpl*) dc->GetImpl();
HDC hdc = GetHdcOf(*impl);
int majorVersion, minorVersion;
wxGetOsVersion(& majorVersion, & minorVersion);
#if wxUSE_UXTHEME #if wxUSE_UXTHEME
// we may need to draw themed colour so that we appear correctly on // we may need to draw themed colour so that we appear correctly on
@@ -1606,36 +1681,13 @@ void wxToolBar::OnEraseBackground(wxEraseEvent& event)
} }
} }
// Only draw a rebar theme on Vista, since it doesn't jive so well with XP if ( MSWEraseRect(*dc) )
if ( !UseBgCol() && majorVersion >= 6 ) return;
{
wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive();
if ( theme )
{
wxUxThemeHandle hTheme(this, L"REBAR");
RECT r;
wxRect rect = GetClientRect();
wxCopyRectToRECT(rect, r);
HRESULT hr = theme->DrawThemeBackground(hTheme, hdc, 0, 0, & r, NULL);
if ( hr == S_OK )
return;
// it can also return S_FALSE which seems to simply say that it
// didn't draw anything but no error really occurred
if ( FAILED(hr) )
{
wxLogApiError(wxT("DrawThemeParentBackground(toolbar)"), hr);
}
}
}
#endif // wxUSE_UXTHEME #endif // wxUSE_UXTHEME
// we need to always draw our background under XP, as otherwise it doesn't // we need to always draw our background under XP, as otherwise it doesn't
// appear correctly with some themes (e.g. Zune one) // appear correctly with some themes (e.g. Zune one)
if ( majorVersion == 5 || if ( wxGetWinVersion() == wxWinVersion_XP ||
UseBgCol() || (GetMSWToolbarStyle() & TBSTYLE_TRANSPARENT) ) UseBgCol() || (GetMSWToolbarStyle() & TBSTYLE_TRANSPARENT) )
{ {
// do draw our background // do draw our background
@@ -1648,7 +1700,7 @@ void wxToolBar::OnEraseBackground(wxEraseEvent& event)
wxCHANGE_HDC_MAP_MODE(hdc, MM_TEXT); wxCHANGE_HDC_MAP_MODE(hdc, MM_TEXT);
::FillRect(hdc, &rect, hBrush); ::FillRect(hdc, &rect, hBrush);
} }
else // we have no non default background colour else // we have no non-default background colour
{ {
// let the system do it for us // let the system do it for us
event.Skip(); event.Skip();
@@ -1699,45 +1751,100 @@ bool wxToolBar::HandleSize(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
SetSize(w, h); SetSize(w, h);
} }
UpdateStretchableSpacersSize();
// message processed // message processed
return true; return true;
} }
#ifndef __WXWINCE__ #ifndef __WXWINCE__
bool wxToolBar::MSWEraseRect(wxDC& dc, const wxRect *rectItem)
{
// erase the given rectangle to hide the separator
#if wxUSE_UXTHEME
// themed background doesn't look well under XP so only draw it for Vista
// and later
if ( !UseBgCol() && wxGetWinVersion() >= wxWinVersion_Vista )
{
wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive();
if ( theme )
{
wxUxThemeHandle hTheme(this, L"REBAR");
// Draw the whole background since the pattern may be position
// sensitive; but clip it to the area of interest.
RECT rcTotal;
wxCopyRectToRECT(GetClientSize(), rcTotal);
RECT rcItem;
if ( rectItem )
wxCopyRectToRECT(*rectItem, rcItem);
HRESULT hr = theme->DrawThemeBackground
(
hTheme,
GetHdcOf(dc),
0, 0,
&rcTotal,
rectItem ? &rcItem : NULL
);
if ( hr == S_OK )
return true;
// it can also return S_FALSE which seems to simply say that it
// didn't draw anything but no error really occurred
if ( FAILED(hr) )
{
wxLogApiError(wxT("DrawThemeBackground(toolbar)"), hr);
}
}
}
#endif // wxUSE_UXTHEME
// this is a bit peculiar but we may simply do nothing here if no rectItem
// is specified (and hence we need to erase everything) as this only
// happens when we're called from OnEraseBackground() and in this case we
// may simply return false to let the systems default background erasing to
// take place
if ( rectItem )
dc.DrawRectangle(*rectItem);
return false;
}
bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam) bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam)
{ {
// erase any dummy separators which were used // erase any dummy separators which were used only for reserving space in
// for aligning the controls if any here // the toolbar (either for a control or just for a stretchable space)
// first of all, are there any controls at all? // first of all, are there any controls at all?
wxToolBarToolsList::compatibility_iterator node; wxToolBarToolsList::compatibility_iterator node;
for ( node = m_tools.GetFirst(); node; node = node->GetNext() ) for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
{ {
if ( node->GetData()->IsControl() ) wxToolBarToolBase * const tool = node->GetData();
if ( tool->IsControl() || tool->IsStretchableSpace() )
break; break;
} }
if ( !node ) if ( !node )
{
// no controls, nothing to erase // no controls, nothing to erase
return false; return false;
}
wxSize clientSize = GetClientSize();
int majorVersion, minorVersion;
wxGetOsVersion(& majorVersion, & minorVersion);
// prepare the DC on which we'll be drawing // prepare the DC on which we'll be drawing
wxClientDC dc(this); wxClientDC dc(this);
dc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
dc.SetPen(*wxTRANSPARENT_PEN); dc.SetPen(*wxTRANSPARENT_PEN);
RECT r; RECT rcUpdate;
if ( !::GetUpdateRect(GetHwnd(), &r, FALSE) ) if ( !::GetUpdateRect(GetHwnd(), &rcUpdate, FALSE) )
{
// nothing to redraw anyhow // nothing to redraw anyhow
return false; return false;
}
wxRect rectUpdate; const wxRect rectUpdate = wxRectFromRECT(rcUpdate);
wxCopyRECTToRect(r, rectUpdate);
dc.SetClippingRegion(rectUpdate); dc.SetClippingRegion(rectUpdate);
// draw the toolbar tools, separators &c normally // draw the toolbar tools, separators &c normally
@@ -1749,7 +1856,8 @@ bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam)
// NB: this is really the only way to do it as we don't know if a separator // NB: this is really the only way to do it as we don't know if a separator
// corresponds to a control (i.e. is a dummy one) or a real one // corresponds to a control (i.e. is a dummy one) or a real one
// otherwise // otherwise
for ( node = m_tools.GetFirst(); node; node = node->GetNext() ) int toolIndex = 0;
for ( node = m_tools.GetFirst(); node; node = node->GetNext(), toolIndex++ )
{ {
wxToolBarTool *tool = (wxToolBarTool*)node->GetData(); wxToolBarTool *tool = (wxToolBarTool*)node->GetData();
if ( tool->IsControl() ) if ( tool->IsControl() )
@@ -1758,13 +1866,16 @@ bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam)
wxControl *control = tool->GetControl(); wxControl *control = tool->GetControl();
wxStaticText *staticText = tool->GetStaticText(); wxStaticText *staticText = tool->GetStaticText();
wxRect rectCtrl = control->GetRect(); wxRect rectCtrl = control->GetRect();
wxRect rectStaticText(0,0,0,0); wxRect rectStaticText;
if ( staticText ) if ( staticText )
{
rectStaticText = staticText->GetRect(); rectStaticText = staticText->GetRect();
}
// iterate over all buttons if ( !rectCtrl.Intersects(rectUpdate) &&
(!staticText || !rectStaticText.Intersects(rectUpdate)) )
continue;
// iterate over all buttons to find all separators intersecting
// this control
TBBUTTON tbb; TBBUTTON tbb;
int count = ::SendMessage(GetHwnd(), TB_BUTTONCOUNT, 0, 0); int count = ::SendMessage(GetHwnd(), TB_BUTTONCOUNT, 0, 0);
for ( int n = 0; n < count; n++ ) for ( int n = 0; n < count; n++ )
@@ -1786,63 +1897,34 @@ bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam)
if ( !r.right ) if ( !r.right )
continue; continue;
// does it intersect the control? const wxRect rectItem = wxRectFromRECT(r);
wxRect rectItem;
wxCopyRECTToRect(r, rectItem);
if ( rectCtrl.Intersects(rectItem) || (staticText && rectStaticText.Intersects(rectItem)))
{
// yes, do erase it!
bool haveRefreshed = false; // does it intersect the update region at all?
if ( !rectUpdate.Intersects(rectItem) )
#if wxUSE_UXTHEME continue;
if ( !UseBgCol() && !GetParent()->UseBgCol() )
{
// Don't use DrawThemeBackground
}
else if ( !UseBgCol() && majorVersion >= 6 )
{
wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive();
if ( theme )
{
wxUxThemeHandle hTheme(this, L"REBAR");
RECT clipRect = r;
// Draw the whole background since the pattern may be position sensitive;
// but clip it to the area of interest.
r.left = 0;
r.right = clientSize.x;
r.top = 0;
r.bottom = clientSize.y;
wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
HRESULT hr = theme->DrawThemeBackground(hTheme, GetHdcOf(*impl), 0, 0, & r, & clipRect);
if ( hr == S_OK )
haveRefreshed = true;
}
}
#endif // wxUSE_UXTHEME
if (!haveRefreshed)
dc.DrawRectangle(rectItem);
}
// does it intersect the control itself or its label?
//
// if it does, refresh it so it's redrawn on top of the
// background
if ( rectCtrl.Intersects(rectItem) ) if ( rectCtrl.Intersects(rectItem) )
{
// Necessary in case we use a no-paint-on-size
// style in the parent: the controls can disappear
control->Refresh(false); control->Refresh(false);
} else if ( staticText && rectStaticText.Intersects(rectItem) )
if ( staticText && rectStaticText.Intersects(rectItem) )
{
// Necessary in case we use a no-paint-on-size
// style in the parent: the controls can disappear
staticText->Refresh(false); staticText->Refresh(false);
} else
continue;
MSWEraseRect(dc, &rectItem);
} }
} }
else if ( tool->IsStretchableSpace() )
{
const wxRect
rectItem = wxRectFromRECT(wxGetTBItemRect(GetHwnd(), toolIndex));
if ( rectUpdate.Intersects(rectItem) )
MSWEraseRect(dc, &rectItem);
}
} }
return true; return true;

View File

@@ -960,16 +960,21 @@ bool wxToolBar::Realize()
} }
} }
} }
wxCFStringRef cfidentifier;
const NSString *nsItemId;
if (tool->GetStyle() == wxTOOL_STYLE_SEPARATOR) if (tool->GetStyle() == wxTOOL_STYLE_SEPARATOR)
[refTB insertItemWithItemIdentifier:NSToolbarSeparatorItemIdentifier atIndex:currentPosition]; {
nsItemId = tool->IsStretchable() ? NSToolbarFlexibleSpaceItemIdentifier
: NSToolbarSeparatorItemIdentifier;
}
else else
{ {
cfidentifier = wxCFStringRef(wxString::Format("%ld", (long)tool));
wxString identifier = wxString::Format( wxT("%ld"), (long) tool ); nsItemId = cfidentifier.AsNSString();
wxCFStringRef cfidentifier(identifier);
[refTB insertItemWithItemIdentifier:cfidentifier.AsNSString() atIndex:currentPosition];
} }
[refTB insertItemWithItemIdentifier:nsItemId atIndex:currentPosition];
tool->SetIndex( currentPosition ); tool->SetIndex( currentPosition );
} }
@@ -1220,7 +1225,10 @@ bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos), wxToolBarToolBase *toolBase)
#if wxOSX_USE_NATIVE_TOOLBAR #if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != NULL) if (m_macToolbar != NULL)
{ {
NSToolbarItem* item = [[NSToolbarItem alloc] initWithItemIdentifier:NSToolbarSeparatorItemIdentifier]; const NSString * const
nsItemId = tool->IsStretchable() ? NSToolbarFlexibleSpaceItemIdentifier
: NSToolbarSeparatorItemIdentifier;
NSToolbarItem* item = [[NSToolbarItem alloc] initWithItemIdentifier:nsItemId];
tool->SetToolbarItemRef( item ); tool->SetToolbarItemRef( item );
} }
#endif // wxOSX_USE_NATIVE_TOOLBAR #endif // wxOSX_USE_NATIVE_TOOLBAR