Improve support for ribbon panel sizers: panels with sizers should now automatically minimise at small sizes, and behave properly when popping up from a minimised state.

Patch by johnr in trac issue #12580.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65852 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Peter Cawley
2010-10-20 17:49:42 +00:00
parent ab3332517f
commit 140091e55c
5 changed files with 238 additions and 91 deletions

View File

@@ -76,6 +76,8 @@ public:
protected:
virtual wxSize DoGetBestSize() const;
virtual wxSize GetPanelSizerBestSize() const;
wxSize GetPanelSizerMinSize() const;
wxBorder GetDefaultBorder() const { return wxBORDER_NONE; }
wxSize GetMinNotMinimisedSize() const;

View File

@@ -16,14 +16,11 @@
A panel adds a border and label to a group of controls, and can be
minimised (either automatically to conserve space, or manually by the user).
Non ribbon controls can be placed on a panel using wxSizers to manage
layout. wxWrapSizer and AddStretchSpacer() are useful for proportional
vertical and horizontal positioning. Note that layout is done within the
constraints of the panel's client area and this is dictated by
wxRibbonArtProvider.
Mixing ribbon and non-ribbon controls in a RibbonPanel is not supported at
present.
Non ribbon controls can be placed on a panel using wxSizers to manage
layout. Panel size is governed by the sizer's minimum calculated size and
the parent wxRibbonPage's dimensions. For functional and aesthetic reasons
it is recommended that ribbon and non ribbon controls are not mixed in one
panel.
@sa wxRibbonPage

View File

@@ -212,7 +212,9 @@ MyFrame::MyFrame()
{
wxRibbonPage* home = new wxRibbonPage(m_ribbon, wxID_ANY, wxT("Examples"), ribbon_xpm);
wxRibbonPanel *toolbar_panel = new wxRibbonPanel(home, wxID_ANY, wxT("Toolbar"), wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxRIBBON_PANEL_NO_AUTO_MINIMISE);
wxRibbonPanel *toolbar_panel = new wxRibbonPanel(home, wxID_ANY, wxT("Toolbar"),
wxNullBitmap, wxDefaultPosition, wxDefaultSize,
wxRIBBON_PANEL_NO_AUTO_MINIMISE);
wxRibbonToolBar *toolbar = new wxRibbonToolBar(toolbar_panel, ID_MAIN_TOOLBAR);
toolbar->AddTool(wxID_ANY, align_left_xpm);
toolbar->AddTool(wxID_ANY, align_center_xpm);
@@ -229,44 +231,60 @@ MyFrame::MyFrame()
toolbar->AddTool(wxID_ANY, wxArtProvider::GetBitmap(wxART_REPORT_VIEW, wxART_OTHER, wxSize(16, 15)));
toolbar->AddTool(wxID_ANY, wxArtProvider::GetBitmap(wxART_LIST_VIEW, wxART_OTHER, wxSize(16, 15)));
toolbar->AddSeparator();
toolbar->AddHybridTool(ID_POSITION_LEFT, position_left_xpm);
toolbar->AddHybridTool(ID_POSITION_TOP, position_top_xpm);
toolbar->AddHybridTool(ID_POSITION_LEFT, position_left_xpm,
"Align ribbonbar vertically\non the left\nfor demonstration purposes");
toolbar->AddHybridTool(ID_POSITION_TOP, position_top_xpm,
"Align the ribbonbar horizontally\nat the top\nfor demonstration purposes");
toolbar->AddSeparator();
toolbar->AddHybridTool(wxID_PRINT, wxArtProvider::GetBitmap(wxART_PRINT, wxART_OTHER, wxSize(16, 15)));
toolbar->AddHybridTool(wxID_PRINT, wxArtProvider::GetBitmap(wxART_PRINT, wxART_OTHER, wxSize(16, 15)),
"This is the Print button tooltip\ndemonstrating a tooltip");
toolbar->SetRows(2, 3);
wxRibbonPanel *selection_panel = new wxRibbonPanel(home, wxID_ANY, wxT("Selection"), wxBitmap(selection_panel_xpm));
wxRibbonButtonBar *selection = new wxRibbonButtonBar(selection_panel);
selection->AddButton(ID_SELECTION_EXPAND_V, wxT("Expand Vertically"), wxBitmap(expand_selection_v_xpm), wxEmptyString);
selection->AddButton(ID_SELECTION_EXPAND_V, wxT("Expand Vertically"), wxBitmap(expand_selection_v_xpm),
"This is a tooltip for Expand Vertically\ndemonstrating a tooltip");
selection->AddButton(ID_SELECTION_EXPAND_H, wxT("Expand Horizontally"), wxBitmap(expand_selection_h_xpm), wxEmptyString);
selection->AddButton(ID_SELECTION_CONTRACT, wxT("Contract"), wxBitmap(auto_crop_selection_xpm), wxBitmap(auto_crop_selection_small_xpm));
wxRibbonPanel *shapes_panel = new wxRibbonPanel(home, wxID_ANY, wxT("Shapes"), wxBitmap(circle_small_xpm));
wxRibbonButtonBar *shapes = new wxRibbonButtonBar(shapes_panel);
shapes->AddButton(ID_CIRCLE, wxT("Circle"), wxBitmap(circle_xpm), wxBitmap(circle_small_xpm));
shapes->AddButton(ID_CIRCLE, wxT("Circle"), wxBitmap(circle_xpm), wxBitmap(circle_small_xpm),
wxNullBitmap, wxNullBitmap, wxRIBBON_BUTTON_NORMAL,
"This is a tooltip for the circle button\ndemonstrating another tooltip");
shapes->AddButton(ID_CROSS, wxT("Cross"), wxBitmap(cross_xpm), wxEmptyString);
shapes->AddHybridButton(ID_TRIANGLE, wxT("Triangle"), wxBitmap(triangle_xpm));
shapes->AddButton(ID_SQUARE, wxT("Square"), wxBitmap(square_xpm), wxEmptyString);
shapes->AddDropdownButton(ID_POLYGON, wxT("Other Polygon"), wxBitmap(hexagon_xpm), wxEmptyString);
wxRibbonPanel *sizer_panel = new wxRibbonPanel(home, wxID_ANY, wxT("Panel with Sizer"),
wxNullBitmap, wxDefaultPosition, wxDefaultSize,
wxRIBBON_PANEL_EXT_BUTTON);
wxRibbonPanel *sizer_panel = new wxRibbonPanel(home, wxID_ANY, wxT("Panel with Sizer"),
wxNullBitmap, wxDefaultPosition, wxDefaultSize,
wxRIBBON_PANEL_DEFAULT_STYLE);
wxArrayString as;
as.Add("Item 1");
as.Add("Item 2");
wxComboBox* sizer_panelcombo = new wxComboBox(sizer_panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, as, wxCB_READONLY);
wxComboBox* sizer_panelcombo2 = new wxComboBox(sizer_panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, as, wxCB_READONLY);
as.Add("Item 1 using a box sizer now");
as.Add("Item 2 using a box sizer now");
wxComboBox* sizer_panelcombo = new wxComboBox(sizer_panel, wxID_ANY,
wxEmptyString,
wxDefaultPosition, wxDefaultSize,
as, wxCB_READONLY);
wxComboBox* sizer_panelcombo2 = new wxComboBox(sizer_panel, wxID_ANY,
wxEmptyString,
wxDefaultPosition, wxDefaultSize,
as, wxCB_READONLY);
sizer_panelcombo->Select(0);
sizer_panelcombo2->Select(1);
sizer_panelcombo->SetMinSize(wxSize(150, -1));
sizer_panelcombo2->SetMinSize(wxSize(150, -1));
wxSizer* sizer_panelsizer = new wxWrapSizer(wxHORIZONTAL);
sizer_panelsizer->Add(sizer_panelcombo, 2, wxALL|wxEXPAND, 2);
sizer_panelsizer->Add(sizer_panelcombo2, 2, wxALL|wxEXPAND, 2);
//not using wxWrapSizer(wxHORIZONTAL) as it reports an incorrect min height
wxSizer* sizer_panelsizer = new wxBoxSizer(wxVERTICAL);
sizer_panelsizer->AddStretchSpacer(1);
sizer_panelsizer->Add(sizer_panelcombo, 0, wxALL|wxEXPAND, 2);
sizer_panelsizer->Add(sizer_panelcombo2, 0, wxALL|wxEXPAND, 2);
sizer_panelsizer->AddStretchSpacer(1);
sizer_panel->SetSizer(sizer_panelsizer);
wxFont label_font(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT);

View File

@@ -1144,12 +1144,27 @@ void wxRibbonMSWArtProvider::ReallyDrawTabSeparator(wxWindow* wnd, const wxRect&
}
void wxRibbonMSWArtProvider::DrawPartialPageBackground(wxDC& dc,
wxWindow* WXUNUSED(wnd), const wxRect& rect, wxRibbonPage* page,
wxWindow* wnd, const wxRect& rect, wxRibbonPage* page,
wxPoint offset, bool hovered)
{
wxRect background(page->GetSize());
page->AdjustRectToIncludeScrollButtons(&background);
background.height -= 2;
wxRect background;
// Expanded panels need a background - the expanded panel at
// best size may have a greater Y dimension higher than when
// on the bar if it has a sizer. AUI art provider does not need this
// because it paints the panel without reference to its parent's size.
// Expanded panels use a wxFrame as parent (not a wxRibbonPage).
if(wnd->GetSizer() && wnd->GetParent() != page)
{
background = wnd->GetParent()->GetSize();
offset = wxPoint(0,0);
}
else
{
background = page->GetSize();
page->AdjustRectToIncludeScrollButtons(&background);
background.height -= 2;
}
// Page background isn't dependant upon the width of the page
// (at least not the part of it intended to be painted by this
// function). Set to wider than the page itself for when externally

View File

@@ -111,7 +111,7 @@ void wxRibbonPanel::CommonInit(const wxString& label, const wxBitmap& icon, long
SetLabel(label);
m_minimised_size = wxDefaultSize; // Unknown / none
m_smallest_unminimised_size = wxSize(INT_MAX, INT_MAX); // Unknown / none
m_smallest_unminimised_size = wxDefaultSize;// Unknown / none for IsFullySpecified()
m_preferred_expand_direction = wxSOUTH;
m_expanded_dummy = NULL;
m_expanded_panel = NULL;
@@ -234,11 +234,13 @@ void wxRibbonPanel::DoSetSize(int x, int y, int width, int height, int sizeFlags
// will refuse to grow any larger while in limbo between minimised and non.
bool minimised = (m_flags & wxRIBBON_PANEL_NO_AUTO_MINIMISE) == 0 &&
IsMinimised(wxSize(width, height));
IsMinimised(wxSize(width, height)); // check if would be at this size
if(minimised != m_minimised)
{
m_minimised = minimised;
// Note that for sizers, this routine disallows the use of mixed shown
// and hidden controls
// TODO ? use some list of user set invisible children to restore status.
for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
node;
node = node->GetNext())
@@ -252,8 +254,20 @@ void wxRibbonPanel::DoSetSize(int x, int y, int width, int height, int sizeFlags
wxRibbonControl::DoSetSize(x, y, width, height, sizeFlags);
}
// Checks if panel would be minimised at (client size) at_size
bool wxRibbonPanel::IsMinimised(wxSize at_size) const
{
if(GetSizer())
{
// we have no information on size change direction
// so check both
wxSize size = GetMinNotMinimisedSize();
if(size.x > at_size.x || size.y > at_size.y)
return true;
return false;
}
if(!m_minimised_size.IsFullySpecified())
return false;
@@ -302,46 +316,70 @@ wxSize wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction,
return m_expanded_panel->DoGetNextSmallerSize(direction, relative_to);
}
// TODO: Check for, and delegate to, a sizer
// Simple (and common) case of single ribbon child
if(GetChildren().GetCount() == 1)
if(m_art != NULL)
{
wxWindow* child = GetChildren().Item(0)->GetData();
wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
if(m_art != NULL && ribbon_child != NULL)
wxClientDC dc((wxRibbonPanel*) this);
wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
wxSize smaller(-1, -1);
bool minimise = false;
if(GetSizer())
{
wxClientDC dc((wxRibbonPanel*) this);
wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
wxSize smaller = ribbon_child->GetNextSmallerSize(direction, child_relative);
if(smaller == child_relative)
// Get smallest non minimised size
smaller = GetMinSize();
// and adjust to child_relative for parent page
if(m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL)
{
if(CanAutoMinimise())
{
wxSize minimised = m_minimised_size;
switch(direction)
{
case wxHORIZONTAL:
minimised.SetHeight(relative_to.GetHeight());
break;
case wxVERTICAL:
minimised.SetWidth(relative_to.GetWidth());
break;
default:
break;
}
return minimised;
}
else
{
return relative_to;
}
minimise = (child_relative.y <= smaller.y);
if(smaller.x < child_relative.x)
smaller.x = child_relative.x;
}
else
{
return m_art->GetPanelSize(dc, this, smaller, NULL);
minimise = (child_relative.x <= smaller.x);
if(smaller.y < child_relative.y)
smaller.y = child_relative.y;
}
}
else if(GetChildren().GetCount() == 1)
{
// Simple (and common) case of single ribbon child or Sizer
wxWindow* child = GetChildren().Item(0)->GetData();
wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
if(ribbon_child != NULL)
{
smaller = ribbon_child->GetNextSmallerSize(direction, child_relative);
minimise = (smaller == child_relative);
}
}
if(minimise)
{
if(CanAutoMinimise())
{
wxSize minimised = m_minimised_size;
switch(direction)
{
case wxHORIZONTAL:
minimised.SetHeight(relative_to.GetHeight());
break;
case wxVERTICAL:
minimised.SetWidth(relative_to.GetWidth());
break;
default:
break;
}
return minimised;
}
else
{
return relative_to;
}
}
else if(smaller.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
{
return m_art->GetPanelSize(dc, this, smaller, NULL);
}
}
// Fallback: Decrease by 20% (or minimum size, whichever larger)
@@ -399,25 +437,47 @@ wxSize wxRibbonPanel::DoGetNextLargerSize(wxOrientation direction,
}
}
// TODO: Check for, and delegate to, a sizer
// Simple (and common) case of single ribbon child
if(GetChildren().GetCount() == 1)
if(m_art != NULL)
{
wxWindow* child = GetChildren().Item(0)->GetData();
wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
if(ribbon_child != NULL)
wxClientDC dc((wxRibbonPanel*) this);
wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
wxSize larger(-1, -1);
if(GetSizer())
{
// We could just let the sizer expand in flow direction but see comment
// in IsSizingContinuous()
larger = GetPanelSizerBestSize();
// and adjust for page in non flow direction
if(m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL)
{
if(larger.x != child_relative.x)
larger.x = child_relative.x;
}
else if(larger.y != child_relative.y)
{
larger.y = child_relative.y;
}
}
else if(GetChildren().GetCount() == 1)
{
// Simple (and common) case of single ribbon child
wxWindow* child = GetChildren().Item(0)->GetData();
wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
if(ribbon_child != NULL)
{
larger = ribbon_child->GetNextLargerSize(direction, child_relative);
}
}
if(larger.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
{
wxClientDC dc((wxRibbonPanel*) this);
wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
wxSize larger = ribbon_child->GetNextLargerSize(direction, child_relative);
if(larger == child_relative)
{
return relative_to;
}
else
{
wxClientDC dc((wxRibbonPanel*) this);
return m_art->GetPanelSize(dc, this, larger, NULL);
}
}
@@ -468,11 +528,15 @@ wxSize wxRibbonPanel::GetMinSize() const
wxSize wxRibbonPanel::GetMinNotMinimisedSize() const
{
// TODO: Ask sizer
// Common case of no sizer and single child taking up the entire panel
if(GetChildren().GetCount() == 1)
// Ask sizer if present
if(GetSizer())
{
wxClientDC dc((wxRibbonPanel*) this);
return m_art->GetPanelSize(dc, this, GetPanelSizerMinSize(), NULL);
}
else if(GetChildren().GetCount() == 1)
{
// Common case of single child taking up the entire panel
wxWindow* child = GetChildren().Item(0)->GetData();
wxClientDC dc((wxRibbonPanel*) this);
return m_art->GetPanelSize(dc, this, child->GetMinSize(), NULL);
@@ -481,13 +545,47 @@ wxSize wxRibbonPanel::GetMinNotMinimisedSize() const
return wxRibbonControl::GetMinSize();
}
wxSize wxRibbonPanel::GetPanelSizerMinSize() const
{
// Called from Realize() to set m_smallest_unminimised_size and from other
// functions to get the minimum size.
// The panel will be invisible when minimised and sizer calcs will be 0
// Uses m_smallest_unminimised_size in preference to GetSizer()->CalcMin()
// to eliminate flicker.
// Check if is visible and not previously calculated
if(IsShown() && !m_smallest_unminimised_size.IsFullySpecified())
{
return GetSizer()->CalcMin();
}
// else use previously calculated m_smallest_unminimised_size
wxClientDC dc((wxRibbonPanel*) this);
return m_art->GetPanelClientSize(dc,
this,
m_smallest_unminimised_size,
NULL);
}
wxSize wxRibbonPanel::GetPanelSizerBestSize() const
{
wxSize size = GetPanelSizerMinSize();
// TODO allow panel to increase its size beyond minimum size
// by steps similarly to ribbon control panels (preferred for aesthetics)
// or continuously.
return size;
}
wxSize wxRibbonPanel::DoGetBestSize() const
{
// TODO: Ask sizer
// Common case of no sizer and single child taking up the entire panel
if(GetChildren().GetCount() == 1)
// Ask sizer if present
if( GetSizer())
{
wxClientDC dc((wxRibbonPanel*) this);
return m_art->GetPanelSize(dc, this, GetPanelSizerBestSize(), NULL);
}
else if(GetChildren().GetCount() == 1)
{
// Common case of no sizer and single child taking up the entire panel
wxWindow* child = GetChildren().Item(0)->GetData();
wxClientDC dc((wxRibbonPanel*) this);
return m_art->GetPanelSize(dc, this, child->GetBestSize(), NULL);
@@ -516,8 +614,13 @@ bool wxRibbonPanel::Realize()
}
wxSize minimum_children_size(0, 0);
// TODO: Ask sizer if there is one
if(GetChildren().GetCount() == 1)
// Ask sizer if there is one present
if(GetSizer())
{
minimum_children_size = GetPanelSizerMinSize();
}
else if(GetChildren().GetCount() == 1)
{
minimum_children_size = GetChildren().GetFirst()->GetData()->GetMinSize();
}
@@ -583,10 +686,10 @@ bool wxRibbonPanel::Layout()
wxClientDC dc(this);
wxSize size = m_art->GetPanelClientSize(dc, this, GetSize(), &position);
// If there is a sizer, use it instead
if ( GetSizer() )
// If there is a sizer, use it
if(GetSizer())
{
GetSizer()->SetDimension(position.x, position.y, size.GetWidth(), size.GetHeight());
GetSizer()->SetDimension(position, size); // SetSize and Layout()
}
else if(GetChildren().GetCount() == 1)
{
@@ -662,7 +765,13 @@ bool wxRibbonPanel::ShowExpanded()
child->Show();
}
// TODO: Move sizer to new panel
// Move sizer to new panel
if(GetSizer())
{
wxSizer* sizer = GetSizer();
SetSizer(NULL, false);
m_expanded_panel->SetSizer(sizer);
}
m_expanded_panel->Realize();
Refresh();
@@ -783,7 +892,13 @@ bool wxRibbonPanel::HideExpanded()
child->Hide();
}
// TODO: Move sizer back
// Move sizer back
if(GetSizer())
{
wxSizer* sizer = GetSizer();
SetSizer(NULL, false);
m_expanded_dummy->SetSizer(sizer);
}
m_expanded_dummy->m_expanded_panel = NULL;
m_expanded_dummy->Realize();