// Author: Peter Cawley
// Modified by:
// Created: 2009-05-25
-// RCS-ID: $Id$
// Copyright: (C) Peter Cawley
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/ribbon/bar.h"
#include "wx/dcbuffer.h"
#include "wx/display.h"
+#include "wx/sizer.h"
#ifndef WX_PRECOMP
#include "wx/frame.h"
#include "wx/msw/private.h"
#endif
+wxDEFINE_EVENT(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, wxRibbonPanelEvent);
+
+IMPLEMENT_DYNAMIC_CLASS(wxRibbonPanelEvent, wxCommandEvent)
+
IMPLEMENT_CLASS(wxRibbonPanel, wxRibbonControl)
BEGIN_EVENT_TABLE(wxRibbonPanel, wxRibbonControl)
EVT_ERASE_BACKGROUND(wxRibbonPanel::OnEraseBackground)
EVT_KILL_FOCUS(wxRibbonPanel::OnKillFocus)
EVT_LEAVE_WINDOW(wxRibbonPanel::OnMouseLeave)
+ EVT_MOTION(wxRibbonPanel::OnMotion)
EVT_LEFT_DOWN(wxRibbonPanel::OnMouseClick)
EVT_PAINT(wxRibbonPanel::OnPaint)
EVT_SIZE(wxRibbonPanel::OnSize)
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;
m_minimised_icon = icon;
m_minimised = false;
m_hovered = false;
+ m_ext_button_hovered = false;
if(m_art == NULL)
{
return m_hovered;
}
+bool wxRibbonPanel::IsExtButtonHovered() const
+{
+ return m_ext_button_hovered;
+}
+
void wxRibbonPanel::OnMouseEnter(wxMouseEvent& evt)
{
TestPositionForHover(evt.GetPosition());
evt.Skip();
}
+void wxRibbonPanel::OnMotion(wxMouseEvent& evt)
+{
+ TestPositionForHover(evt.GetPosition());
+}
+
void wxRibbonPanel::TestPositionForHover(const wxPoint& pos)
{
- bool hovered = false;
+ bool hovered = false, ext_button_hovered = false;
if(pos.x >= 0 && pos.y >= 0)
{
wxSize size = GetSize();
hovered = true;
}
}
- if(hovered != m_hovered)
+ if(hovered)
+ {
+ if(HasExtButton())
+ ext_button_hovered = m_ext_button_rect.Contains(pos);
+ else
+ ext_button_hovered = false;
+ }
+ if(hovered != m_hovered || ext_button_hovered != m_ext_button_hovered)
{
m_hovered = hovered;
+ m_ext_button_hovered = ext_button_hovered;
Refresh(false);
}
}
// for children of the window. The panel wants to be in the hovered state
// whenever the mouse cursor is within its boundary, so the events need to
// be attached to children too.
- child->Connect(wxEVT_ENTER_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseEnterChild, NULL, this);
- child->Connect(wxEVT_LEAVE_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseLeaveChild, NULL, this);
+ child->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseEnterChild), NULL, this);
+ child->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseLeaveChild), NULL, this);
}
void wxRibbonPanel::RemoveChild(wxWindowBase *child)
{
- child->Disconnect(wxEVT_ENTER_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseEnterChild, NULL, this);
- child->Disconnect(wxEVT_LEAVE_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseLeaveChild, NULL, this);
+ child->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseEnterChild), NULL, this);
+ child->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseLeaveChild), NULL, this);
wxRibbonControl::RemoveChild(child);
}
+bool wxRibbonPanel::HasExtButton()const
+{
+ wxRibbonBar* bar = GetAncestorRibbonBar();
+ if(bar==NULL)
+ return false;
+ return (m_flags & wxRIBBON_PANEL_EXT_BUTTON) &&
+ (bar->GetWindowStyleFlag() & wxRIBBON_BAR_SHOW_PANEL_EXT_BUTTONS);
+}
+
void wxRibbonPanel::OnSize(wxSizeEvent& evt)
{
if(GetAutoLayout())
// 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())
Refresh();
}
-
+
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;
return (at_size.GetX() <= m_minimised_size.GetX() &&
- at_size.GetY() <= m_minimised_size.GetY()) ||
+ at_size.GetY() <= m_minimised_size.GetY()) ||
at_size.GetX() < m_smallest_unminimised_size.GetX() ||
at_size.GetY() < m_smallest_unminimised_size.GetY();
}
{
// A panel never sizes continuously, even if all of its children can,
// as it would appear out of place along side non-continuous panels.
- return false;
+
+ // JS 2012-03-09: introducing wxRIBBON_PANEL_STRETCH to allow
+ // the panel to fill its parent page. For example we might have
+ // a list of styles in one of the pages, which should stretch to
+ // fill available space.
+ return (m_flags & wxRIBBON_PANEL_STRETCH) != 0;
+}
+
+// Finds the best width and height given the parent's width and height
+wxSize wxRibbonPanel::GetBestSizeForParentSize(const wxSize& parentSize) const
+{
+ if (GetChildren().GetCount() == 1)
+ {
+ wxWindow* win = GetChildren().GetFirst()->GetData();
+ wxRibbonControl* control = wxDynamicCast(win, wxRibbonControl);
+ if (control)
+ {
+ wxClientDC temp_dc((wxRibbonPanel*) this);
+ wxSize clientParentSize = m_art->GetPanelClientSize(temp_dc, this, parentSize, NULL);
+ wxSize childSize = control->GetBestSizeForParentSize(clientParentSize);
+ wxSize overallSize = m_art->GetPanelSize(temp_dc, this, childSize, NULL);
+ return overallSize;
+ }
+ }
+ return GetSize();
}
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
+ minimise = (child_relative.y <= smaller.y);
+ if(smaller.x < child_relative.x)
+ smaller.x = child_relative.x;
+ }
+ else
+ {
+ 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)
{
- return relative_to;
+ case wxHORIZONTAL:
+ minimised.SetHeight(relative_to.GetHeight());
+ break;
+ case wxVERTICAL:
+ minimised.SetWidth(relative_to.GetWidth());
+ break;
+ default:
+ break;
}
+ return minimised;
}
else
{
- return m_art->GetPanelSize(dc, this, smaller, NULL);
+ 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)
}
}
- // 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);
}
}
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);
return wxRibbonControl::GetMinSize();
}
-wxSize wxRibbonPanel::DoGetBestSize() const
+wxSize wxRibbonPanel::GetPanelSizerMinSize() const
{
- // TODO: Ask sizer
+ // 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);
+}
- // Common case of no sizer and single child taking up the entire panel
- if(GetChildren().GetCount() == 1)
+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
+{
+ // 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);
}
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();
}
return true;
}
- // TODO: Delegate to a sizer
+ // Get wxRibbonPanel client size
+ wxPoint position;
+ wxClientDC dc(this);
+ wxSize size = m_art->GetPanelClientSize(dc, this, GetSize(), &position);
- // Common case of no sizer and single child taking up the entire panel
- if(GetChildren().GetCount() == 1)
+ // If there is a sizer, use it
+ if(GetSizer())
{
+ GetSizer()->SetDimension(position, size); // SetSize and Layout()
+ }
+ else if(GetChildren().GetCount() == 1)
+ {
+ // Common case of no sizer and single child taking up the entire panel
wxWindow* child = GetChildren().Item(0)->GetData();
- wxPoint position;
- wxClientDC dc(this);
- wxSize size = m_art->GetPanelClientSize(dc, this, GetSize(), &position);
child->SetSize(position.x, position.y, size.GetWidth(), size.GetHeight());
}
+
+ if(HasExtButton())
+ m_ext_button_rect = m_art->GetPanelExtButtonArea(dc, this, GetSize());
+
return true;
}
ShowExpanded();
}
}
+ else if(IsExtButtonHovered())
+ {
+ wxRibbonPanelEvent notification(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, GetId());
+ notification.SetEventObject(this);
+ notification.SetPanel(this);
+ ProcessEvent(notification);
+ }
}
wxRibbonPanel* wxRibbonPanel::GetExpandedDummy()
}
wxSize size = GetBestSize();
+
+ // Special case for flexible panel layout, where GetBestSize doesn't work
+ if (GetFlags() & wxRIBBON_PANEL_FLEXIBLE)
+ {
+ size = GetBestSizeForParentSize(wxSize(400, 1000));
+ }
+
wxPoint pos = GetExpandedPosition(wxRect(GetScreenPosition(), GetSize()),
size, m_preferred_expand_direction).GetTopLeft();
pos, size, wxFRAME_NO_TASKBAR | wxBORDER_NONE);
m_expanded_panel = new wxRibbonPanel(container, wxID_ANY,
- GetLabel(), m_minimised_icon, wxPoint(0, 0), size, m_flags);
+ GetLabel(), m_minimised_icon, wxPoint(0, 0), size, (m_flags /* & ~wxRIBBON_PANEL_FLEXIBLE */));
m_expanded_panel->SetArtProvider(m_art);
m_expanded_panel->m_expanded_dummy = this;
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();
+ container->SetMinClientSize(size);
container->Show();
m_expanded_panel->SetFocus();
HideExpanded();
// Do not skip event, as the panel has been de-expanded, causing the
// child with focus to be reparented (and hidden). If the event
- // continues propogation then bad things happen.
+ // continues propagation then bad things happen.
}
else
{
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();
return best;
}
+void wxRibbonPanel::HideIfExpanded()
+{
+ wxStaticCast(m_parent, wxRibbonPage)->HideIfExpanded();
+}
+
#endif // wxUSE_RIBBON