1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/panel.cpp
3 // Purpose: Ribbon-style container for a group of related tools / controls
4 // Author: Peter Cawley
8 // Copyright: (C) Peter Cawley
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
20 #include "wx/ribbon/panel.h"
21 #include "wx/ribbon/art.h"
22 #include "wx/ribbon/bar.h"
23 #include "wx/dcbuffer.h"
24 #include "wx/display.h"
32 #include "wx/msw/private.h"
35 wxDEFINE_EVENT(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED
, wxRibbonPanelEvent
);
37 IMPLEMENT_DYNAMIC_CLASS(wxRibbonPanelEvent
, wxCommandEvent
)
39 IMPLEMENT_CLASS(wxRibbonPanel
, wxRibbonControl
)
41 BEGIN_EVENT_TABLE(wxRibbonPanel
, wxRibbonControl
)
42 EVT_ENTER_WINDOW(wxRibbonPanel::OnMouseEnter
)
43 EVT_ERASE_BACKGROUND(wxRibbonPanel::OnEraseBackground
)
44 EVT_KILL_FOCUS(wxRibbonPanel::OnKillFocus
)
45 EVT_LEAVE_WINDOW(wxRibbonPanel::OnMouseLeave
)
46 EVT_MOTION(wxRibbonPanel::OnMotion
)
47 EVT_LEFT_DOWN(wxRibbonPanel::OnMouseClick
)
48 EVT_PAINT(wxRibbonPanel::OnPaint
)
49 EVT_SIZE(wxRibbonPanel::OnSize
)
52 wxRibbonPanel::wxRibbonPanel() : m_expanded_dummy(NULL
), m_expanded_panel(NULL
)
56 wxRibbonPanel::wxRibbonPanel(wxWindow
* parent
,
58 const wxString
& label
,
59 const wxBitmap
& minimised_icon
,
63 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
65 CommonInit(label
, minimised_icon
, style
);
68 wxRibbonPanel::~wxRibbonPanel()
72 m_expanded_panel
->m_expanded_dummy
= NULL
;
73 m_expanded_panel
->GetParent()->Destroy();
77 bool wxRibbonPanel::Create(wxWindow
* parent
,
79 const wxString
& label
,
85 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
90 CommonInit(label
, icon
, style
);
95 void wxRibbonPanel::SetArtProvider(wxRibbonArtProvider
* art
)
98 for ( wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
100 node
= node
->GetNext() )
102 wxWindow
* child
= node
->GetData();
103 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
106 ribbon_child
->SetArtProvider(art
);
110 m_expanded_panel
->SetArtProvider(art
);
113 void wxRibbonPanel::CommonInit(const wxString
& label
, const wxBitmap
& icon
, long style
)
118 m_minimised_size
= wxDefaultSize
; // Unknown / none
119 m_smallest_unminimised_size
= wxDefaultSize
;// Unknown / none for IsFullySpecified()
120 m_preferred_expand_direction
= wxSOUTH
;
121 m_expanded_dummy
= NULL
;
122 m_expanded_panel
= NULL
;
124 m_minimised_icon
= icon
;
127 m_ext_button_hovered
= false;
131 wxRibbonControl
* parent
= wxDynamicCast(GetParent(), wxRibbonControl
);
134 m_art
= parent
->GetArtProvider();
139 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
140 SetMinSize(wxSize(20, 20));
143 bool wxRibbonPanel::IsMinimised() const
148 bool wxRibbonPanel::IsHovered() const
153 bool wxRibbonPanel::IsExtButtonHovered() const
155 return m_ext_button_hovered
;
158 void wxRibbonPanel::OnMouseEnter(wxMouseEvent
& evt
)
160 TestPositionForHover(evt
.GetPosition());
163 void wxRibbonPanel::OnMouseEnterChild(wxMouseEvent
& evt
)
165 wxPoint pos
= evt
.GetPosition();
166 wxWindow
*child
= wxDynamicCast(evt
.GetEventObject(), wxWindow
);
169 pos
+= child
->GetPosition();
170 TestPositionForHover(pos
);
175 void wxRibbonPanel::OnMouseLeave(wxMouseEvent
& evt
)
177 TestPositionForHover(evt
.GetPosition());
180 void wxRibbonPanel::OnMouseLeaveChild(wxMouseEvent
& evt
)
182 wxPoint pos
= evt
.GetPosition();
183 wxWindow
*child
= wxDynamicCast(evt
.GetEventObject(), wxWindow
);
186 pos
+= child
->GetPosition();
187 TestPositionForHover(pos
);
192 void wxRibbonPanel::OnMotion(wxMouseEvent
& evt
)
194 TestPositionForHover(evt
.GetPosition());
197 void wxRibbonPanel::TestPositionForHover(const wxPoint
& pos
)
199 bool hovered
= false, ext_button_hovered
= false;
200 if(pos
.x
>= 0 && pos
.y
>= 0)
202 wxSize size
= GetSize();
203 if(pos
.x
< size
.GetWidth() && pos
.y
< size
.GetHeight())
211 ext_button_hovered
= m_ext_button_rect
.Contains(pos
);
213 ext_button_hovered
= false;
215 if(hovered
!= m_hovered
|| ext_button_hovered
!= m_ext_button_hovered
)
218 m_ext_button_hovered
= ext_button_hovered
;
223 void wxRibbonPanel::AddChild(wxWindowBase
*child
)
225 wxRibbonControl::AddChild(child
);
227 // Window enter / leave events count for only the window in question, not
228 // for children of the window. The panel wants to be in the hovered state
229 // whenever the mouse cursor is within its boundary, so the events need to
230 // be attached to children too.
231 child
->Connect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this);
232 child
->Connect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this);
235 void wxRibbonPanel::RemoveChild(wxWindowBase
*child
)
237 child
->Disconnect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this);
238 child
->Disconnect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this);
240 wxRibbonControl::RemoveChild(child
);
243 bool wxRibbonPanel::HasExtButton()const
245 wxRibbonBar
* bar
= GetAncestorRibbonBar();
248 return (m_flags
& wxRIBBON_PANEL_EXT_BUTTON
) &&
249 (bar
->GetWindowStyleFlag() & wxRIBBON_BAR_SHOW_PANEL_EXT_BUTTONS
);
252 void wxRibbonPanel::OnSize(wxSizeEvent
& evt
)
260 void wxRibbonPanel::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
)
262 // At least on MSW, changing the size of a window will cause GetSize() to
263 // report the new size, but a size event may not be handled immediately.
264 // If this minimised check was performed in the OnSize handler, then
265 // GetSize() could return a size much larger than the minimised size while
266 // IsMinimised() returns true. This would then affect layout, as the panel
267 // will refuse to grow any larger while in limbo between minimised and non.
269 bool minimised
= (m_flags
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0 &&
270 IsMinimised(wxSize(width
, height
)); // check if would be at this size
271 if(minimised
!= m_minimised
)
273 m_minimised
= minimised
;
274 // Note that for sizers, this routine disallows the use of mixed shown
275 // and hidden controls
276 // TODO ? use some list of user set invisible children to restore status.
277 for (wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
279 node
= node
->GetNext())
281 node
->GetData()->Show(!minimised
);
287 wxRibbonControl::DoSetSize(x
, y
, width
, height
, sizeFlags
);
290 // Checks if panel would be minimised at (client size) at_size
291 bool wxRibbonPanel::IsMinimised(wxSize at_size
) const
295 // we have no information on size change direction
297 wxSize size
= GetMinNotMinimisedSize();
298 if(size
.x
> at_size
.x
|| size
.y
> at_size
.y
)
304 if(!m_minimised_size
.IsFullySpecified())
307 return (at_size
.GetX() <= m_minimised_size
.GetX() &&
308 at_size
.GetY() <= m_minimised_size
.GetY()) ||
309 at_size
.GetX() < m_smallest_unminimised_size
.GetX() ||
310 at_size
.GetY() < m_smallest_unminimised_size
.GetY();
313 void wxRibbonPanel::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
315 // All painting done in main paint handler to minimise flicker
318 void wxRibbonPanel::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
320 wxAutoBufferedPaintDC
dc(this);
326 m_art
->DrawMinimisedPanel(dc
, this, GetSize(), m_minimised_icon_resized
);
330 m_art
->DrawPanelBackground(dc
, this, GetSize());
335 bool wxRibbonPanel::IsSizingContinuous() const
337 // A panel never sizes continuously, even if all of its children can,
338 // as it would appear out of place along side non-continuous panels.
340 // JS 2012-03-09: introducing wxRIBBON_PANEL_STRETCH to allow
341 // the panel to fill its parent page. For example we might have
342 // a list of styles in one of the pages, which should stretch to
343 // fill available space.
344 return (m_flags
& wxRIBBON_PANEL_STRETCH
) != 0;
347 // Finds the best width and height given the parent's width and height
348 wxSize
wxRibbonPanel::GetBestSizeForParentSize(const wxSize
& parentSize
) const
350 if (GetChildren().GetCount() == 1)
352 wxWindow
* win
= GetChildren().GetFirst()->GetData();
353 wxRibbonControl
* control
= wxDynamicCast(win
, wxRibbonControl
);
356 wxClientDC
temp_dc((wxRibbonPanel
*) this);
357 wxSize clientParentSize
= m_art
->GetPanelClientSize(temp_dc
, this, parentSize
, NULL
);
358 wxSize childSize
= control
->GetBestSizeForParentSize(clientParentSize
);
359 wxSize overallSize
= m_art
->GetPanelSize(temp_dc
, this, childSize
, NULL
);
366 wxSize
wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction
,
367 wxSize relative_to
) const
369 if(m_expanded_panel
!= NULL
)
371 // Next size depends upon children, who are currently in the
373 return m_expanded_panel
->DoGetNextSmallerSize(direction
, relative_to
);
378 wxClientDC
dc((wxRibbonPanel
*) this);
379 wxSize child_relative
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
);
380 wxSize
smaller(-1, -1);
381 bool minimise
= false;
385 // Get smallest non minimised size
386 smaller
= GetMinSize();
387 // and adjust to child_relative for parent page
388 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
390 minimise
= (child_relative
.y
<= smaller
.y
);
391 if(smaller
.x
< child_relative
.x
)
392 smaller
.x
= child_relative
.x
;
396 minimise
= (child_relative
.x
<= smaller
.x
);
397 if(smaller
.y
< child_relative
.y
)
398 smaller
.y
= child_relative
.y
;
401 else if(GetChildren().GetCount() == 1)
403 // Simple (and common) case of single ribbon child or Sizer
404 wxWindow
* child
= GetChildren().Item(0)->GetData();
405 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
406 if(ribbon_child
!= NULL
)
408 smaller
= ribbon_child
->GetNextSmallerSize(direction
, child_relative
);
409 minimise
= (smaller
== child_relative
);
415 if(CanAutoMinimise())
417 wxSize minimised
= m_minimised_size
;
421 minimised
.SetHeight(relative_to
.GetHeight());
424 minimised
.SetWidth(relative_to
.GetWidth());
436 else if(smaller
.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
438 return m_art
->GetPanelSize(dc
, this, smaller
, NULL
);
442 // Fallback: Decrease by 20% (or minimum size, whichever larger)
443 wxSize
current(relative_to
);
444 wxSize
minimum(GetMinSize());
445 if(direction
& wxHORIZONTAL
)
447 current
.x
= (current
.x
* 4) / 5;
448 if(current
.x
< minimum
.x
)
450 current
.x
= minimum
.x
;
453 if(direction
& wxVERTICAL
)
455 current
.y
= (current
.y
* 4) / 5;
456 if(current
.y
< minimum
.y
)
458 current
.y
= minimum
.y
;
464 wxSize
wxRibbonPanel::DoGetNextLargerSize(wxOrientation direction
,
465 wxSize relative_to
) const
467 if(m_expanded_panel
!= NULL
)
469 // Next size depends upon children, who are currently in the
471 return m_expanded_panel
->DoGetNextLargerSize(direction
, relative_to
);
474 if(IsMinimised(relative_to
))
476 wxSize current
= relative_to
;
477 wxSize min_size
= GetMinNotMinimisedSize();
481 if(min_size
.x
> current
.x
&& min_size
.y
== current
.y
)
485 if(min_size
.x
== current
.x
&& min_size
.y
> current
.y
)
489 if(min_size
.x
> current
.x
&& min_size
.y
> current
.y
)
499 wxClientDC
dc((wxRibbonPanel
*) this);
500 wxSize child_relative
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
);
501 wxSize
larger(-1, -1);
505 // We could just let the sizer expand in flow direction but see comment
506 // in IsSizingContinuous()
507 larger
= GetPanelSizerBestSize();
508 // and adjust for page in non flow direction
509 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
511 if(larger
.x
!= child_relative
.x
)
512 larger
.x
= child_relative
.x
;
514 else if(larger
.y
!= child_relative
.y
)
516 larger
.y
= child_relative
.y
;
519 else if(GetChildren().GetCount() == 1)
521 // Simple (and common) case of single ribbon child
522 wxWindow
* child
= GetChildren().Item(0)->GetData();
523 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
524 if(ribbon_child
!= NULL
)
526 larger
= ribbon_child
->GetNextLargerSize(direction
, child_relative
);
530 if(larger
.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
532 if(larger
== child_relative
)
538 return m_art
->GetPanelSize(dc
, this, larger
, NULL
);
543 // Fallback: Increase by 25% (equal to a prior or subsequent 20% decrease)
544 // Note that due to rounding errors, this increase may not exactly equal a
545 // matching decrease - an ideal solution would not have these errors, but
546 // avoiding them is non-trivial unless an increase is by 100% rather than
547 // a fractional amount. This would then be non-ideal as the resizes happen
548 // at very large intervals.
549 wxSize
current(relative_to
);
550 if(direction
& wxHORIZONTAL
)
552 current
.x
= (current
.x
* 5 + 3) / 4;
554 if(direction
& wxVERTICAL
)
556 current
.y
= (current
.y
* 5 + 3) / 4;
561 bool wxRibbonPanel::CanAutoMinimise() const
563 return (m_flags
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0
564 && m_minimised_size
.IsFullySpecified();
567 wxSize
wxRibbonPanel::GetMinSize() const
569 if(m_expanded_panel
!= NULL
)
571 // Minimum size depends upon children, who are currently in the
573 return m_expanded_panel
->GetMinSize();
576 if(CanAutoMinimise())
578 return m_minimised_size
;
582 return GetMinNotMinimisedSize();
586 wxSize
wxRibbonPanel::GetMinNotMinimisedSize() const
588 // Ask sizer if present
591 wxClientDC
dc((wxRibbonPanel
*) this);
592 return m_art
->GetPanelSize(dc
, this, GetPanelSizerMinSize(), NULL
);
594 else if(GetChildren().GetCount() == 1)
596 // Common case of single child taking up the entire panel
597 wxWindow
* child
= GetChildren().Item(0)->GetData();
598 wxClientDC
dc((wxRibbonPanel
*) this);
599 return m_art
->GetPanelSize(dc
, this, child
->GetMinSize(), NULL
);
602 return wxRibbonControl::GetMinSize();
605 wxSize
wxRibbonPanel::GetPanelSizerMinSize() const
607 // Called from Realize() to set m_smallest_unminimised_size and from other
608 // functions to get the minimum size.
609 // The panel will be invisible when minimised and sizer calcs will be 0
610 // Uses m_smallest_unminimised_size in preference to GetSizer()->CalcMin()
611 // to eliminate flicker.
613 // Check if is visible and not previously calculated
614 if(IsShown() && !m_smallest_unminimised_size
.IsFullySpecified())
616 return GetSizer()->CalcMin();
618 // else use previously calculated m_smallest_unminimised_size
619 wxClientDC
dc((wxRibbonPanel
*) this);
620 return m_art
->GetPanelClientSize(dc
,
622 m_smallest_unminimised_size
,
626 wxSize
wxRibbonPanel::GetPanelSizerBestSize() const
628 wxSize size
= GetPanelSizerMinSize();
629 // TODO allow panel to increase its size beyond minimum size
630 // by steps similarly to ribbon control panels (preferred for aesthetics)
635 wxSize
wxRibbonPanel::DoGetBestSize() const
637 // Ask sizer if present
640 wxClientDC
dc((wxRibbonPanel
*) this);
641 return m_art
->GetPanelSize(dc
, this, GetPanelSizerBestSize(), NULL
);
643 else if(GetChildren().GetCount() == 1)
645 // Common case of no sizer and single child taking up the entire panel
646 wxWindow
* child
= GetChildren().Item(0)->GetData();
647 wxClientDC
dc((wxRibbonPanel
*) this);
648 return m_art
->GetPanelSize(dc
, this, child
->GetBestSize(), NULL
);
651 return wxRibbonControl::DoGetBestSize();
654 bool wxRibbonPanel::Realize()
658 for (wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
660 node
= node
->GetNext())
662 wxRibbonControl
* child
= wxDynamicCast(node
->GetData(), wxRibbonControl
);
667 if(!child
->Realize())
673 wxSize
minimum_children_size(0, 0);
675 // Ask sizer if there is one present
678 minimum_children_size
= GetPanelSizerMinSize();
680 else if(GetChildren().GetCount() == 1)
682 minimum_children_size
= GetChildren().GetFirst()->GetData()->GetMinSize();
687 wxClientDC
temp_dc(this);
689 m_smallest_unminimised_size
=
690 m_art
->GetPanelSize(temp_dc
, this, minimum_children_size
, NULL
);
693 wxSize panel_min_size
= GetMinNotMinimisedSize();
694 m_minimised_size
= m_art
->GetMinimisedPanelMinimumSize(temp_dc
, this,
695 &bitmap_size
, &m_preferred_expand_direction
);
696 if(m_minimised_icon
.IsOk() && m_minimised_icon
.GetSize() != bitmap_size
)
698 wxImage
img(m_minimised_icon
.ConvertToImage());
699 img
.Rescale(bitmap_size
.GetWidth(), bitmap_size
.GetHeight(), wxIMAGE_QUALITY_HIGH
);
700 m_minimised_icon_resized
= wxBitmap(img
);
704 m_minimised_icon_resized
= m_minimised_icon
;
706 if(m_minimised_size
.x
> panel_min_size
.x
&&
707 m_minimised_size
.y
> panel_min_size
.y
)
709 // No point in having a minimised size which is larger than the
710 // minimum size which the children can go to.
711 m_minimised_size
= wxSize(-1, -1);
715 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
717 m_minimised_size
.x
= panel_min_size
.x
;
721 m_minimised_size
.y
= panel_min_size
.y
;
727 m_minimised_size
= wxSize(-1, -1);
730 return Layout() && status
;
733 bool wxRibbonPanel::Layout()
737 // Children are all invisible when minimised
741 // Get wxRibbonPanel client size
744 wxSize size
= m_art
->GetPanelClientSize(dc
, this, GetSize(), &position
);
746 // If there is a sizer, use it
749 GetSizer()->SetDimension(position
, size
); // SetSize and Layout()
751 else if(GetChildren().GetCount() == 1)
753 // Common case of no sizer and single child taking up the entire panel
754 wxWindow
* child
= GetChildren().Item(0)->GetData();
755 child
->SetSize(position
.x
, position
.y
, size
.GetWidth(), size
.GetHeight());
759 m_ext_button_rect
= m_art
->GetPanelExtButtonArea(dc
, this, GetSize());
764 void wxRibbonPanel::OnMouseClick(wxMouseEvent
& WXUNUSED(evt
))
768 if(m_expanded_panel
!= NULL
)
777 else if(IsExtButtonHovered())
779 wxRibbonPanelEvent
notification(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED
, GetId());
780 notification
.SetEventObject(this);
781 notification
.SetPanel(this);
782 ProcessEvent(notification
);
786 wxRibbonPanel
* wxRibbonPanel::GetExpandedDummy()
788 return m_expanded_dummy
;
791 wxRibbonPanel
* wxRibbonPanel::GetExpandedPanel()
793 return m_expanded_panel
;
796 bool wxRibbonPanel::ShowExpanded()
802 if(m_expanded_dummy
!= NULL
|| m_expanded_panel
!= NULL
)
807 wxSize size
= GetBestSize();
809 // Special case for flexible panel layout, where GetBestSize doesn't work
810 if (GetFlags() & wxRIBBON_PANEL_FLEXIBLE
)
812 size
= GetBestSizeForParentSize(wxSize(400, 1000));
815 wxPoint pos
= GetExpandedPosition(wxRect(GetScreenPosition(), GetSize()),
816 size
, m_preferred_expand_direction
).GetTopLeft();
818 // Need a top-level frame to contain the expanded panel
819 wxFrame
*container
= new wxFrame(NULL
, wxID_ANY
, GetLabel(),
820 pos
, size
, wxFRAME_NO_TASKBAR
| wxBORDER_NONE
);
822 m_expanded_panel
= new wxRibbonPanel(container
, wxID_ANY
,
823 GetLabel(), m_minimised_icon
, wxPoint(0, 0), size
, (m_flags
/* & ~wxRIBBON_PANEL_FLEXIBLE */));
825 m_expanded_panel
->SetArtProvider(m_art
);
826 m_expanded_panel
->m_expanded_dummy
= this;
828 // Move all children to the new panel.
829 // Conceptually it might be simpler to reparent this entire panel to the
830 // container and create a new panel to sit in its place while expanded.
831 // This approach has a problem though - when the panel is reinserted into
832 // its original parent, it'll be at a different position in the child list
833 // and thus assume a new position.
834 // NB: Children iterators not used as behaviour is not well defined
835 // when iterating over a container which is being emptied
836 while(!GetChildren().IsEmpty())
838 wxWindow
*child
= GetChildren().GetFirst()->GetData();
839 child
->Reparent(m_expanded_panel
);
843 // Move sizer to new panel
846 wxSizer
* sizer
= GetSizer();
847 SetSizer(NULL
, false);
848 m_expanded_panel
->SetSizer(sizer
);
851 m_expanded_panel
->Realize();
853 container
->SetMinClientSize(size
);
855 m_expanded_panel
->SetFocus();
860 bool wxRibbonPanel::ShouldSendEventToDummy(wxEvent
& evt
)
862 // For an expanded panel, filter events between being sent up to the
863 // floating top level window or to the dummy panel sitting in the ribbon
866 // Child focus events should not be redirected, as the child would not be a
867 // child of the window the event is redirected to. All other command events
868 // seem to be suitable for redirecting.
869 return evt
.IsCommandEvent() && evt
.GetEventType() != wxEVT_CHILD_FOCUS
;
872 bool wxRibbonPanel::TryAfter(wxEvent
& evt
)
874 if(m_expanded_dummy
&& ShouldSendEventToDummy(evt
))
876 wxPropagateOnce
propagateOnce(evt
);
877 return m_expanded_dummy
->GetEventHandler()->ProcessEvent(evt
);
881 return wxRibbonControl::TryAfter(evt
);
885 static bool IsAncestorOf(wxWindow
*ancestor
, wxWindow
*window
)
887 while(window
!= NULL
)
889 wxWindow
*parent
= window
->GetParent();
890 if(parent
== ancestor
)
898 void wxRibbonPanel::OnKillFocus(wxFocusEvent
& evt
)
902 wxWindow
*receiver
= evt
.GetWindow();
903 if(IsAncestorOf(this, receiver
))
905 m_child_with_focus
= receiver
;
906 receiver
->Connect(wxEVT_KILL_FOCUS
,
907 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
),
910 else if(receiver
== NULL
|| receiver
!= m_expanded_dummy
)
917 void wxRibbonPanel::OnChildKillFocus(wxFocusEvent
& evt
)
919 if(m_child_with_focus
== NULL
)
920 return; // Should never happen, but a check can't hurt
922 m_child_with_focus
->Disconnect(wxEVT_KILL_FOCUS
,
923 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this);
924 m_child_with_focus
= NULL
;
926 wxWindow
*receiver
= evt
.GetWindow();
927 if(receiver
== this || IsAncestorOf(this, receiver
))
929 m_child_with_focus
= receiver
;
930 receiver
->Connect(wxEVT_KILL_FOCUS
,
931 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this);
934 else if(receiver
== NULL
|| receiver
!= m_expanded_dummy
)
937 // Do not skip event, as the panel has been de-expanded, causing the
938 // child with focus to be reparented (and hidden). If the event
939 // continues propagation then bad things happen.
947 bool wxRibbonPanel::HideExpanded()
949 if(m_expanded_dummy
== NULL
)
953 return m_expanded_panel
->HideExpanded();
961 // Move children back to original panel
962 // NB: Children iterators not used as behaviour is not well defined
963 // when iterating over a container which is being emptied
964 while(!GetChildren().IsEmpty())
966 wxWindow
*child
= GetChildren().GetFirst()->GetData();
967 child
->Reparent(m_expanded_dummy
);
974 wxSizer
* sizer
= GetSizer();
975 SetSizer(NULL
, false);
976 m_expanded_dummy
->SetSizer(sizer
);
979 m_expanded_dummy
->m_expanded_panel
= NULL
;
980 m_expanded_dummy
->Realize();
981 m_expanded_dummy
->Refresh();
982 wxWindow
*parent
= GetParent();
989 wxRect
wxRibbonPanel::GetExpandedPosition(wxRect panel
,
990 wxSize expanded_size
,
991 wxDirection direction
)
994 // 1) Determine primary position based on requested direction
995 // 2) Move the position so that it sits entirely within a display
996 // (for single monitor systems, this moves it into the display region,
997 // but for multiple monitors, it does so without splitting it over
998 // more than one display)
999 // 2.1) Move in the primary axis
1000 // 2.2) Move in the secondary axis
1003 bool primary_x
= false;
1004 int secondary_x
= 0;
1005 int secondary_y
= 0;
1009 pos
.x
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2;
1010 pos
.y
= panel
.GetY() - expanded_size
.GetHeight();
1015 pos
.x
= panel
.GetRight();
1016 pos
.y
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2;
1020 pos
.x
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2;
1021 pos
.y
= panel
.GetBottom();
1027 pos
.x
= panel
.GetX() - expanded_size
.GetWidth();
1028 pos
.y
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2;
1032 wxRect
expanded(pos
, expanded_size
);
1034 wxRect
best(expanded
);
1035 int best_distance
= INT_MAX
;
1037 const unsigned display_n
= wxDisplay::GetCount();
1039 for(display_i
= 0; display_i
< display_n
; ++display_i
)
1041 wxRect display
= wxDisplay(display_i
).GetGeometry();
1043 if(display
.Contains(expanded
))
1047 else if(display
.Intersects(expanded
))
1049 wxRect
new_rect(expanded
);
1054 if(expanded
.GetRight() > display
.GetRight())
1056 distance
= expanded
.GetRight() - display
.GetRight();
1057 new_rect
.x
-= distance
;
1059 else if(expanded
.GetLeft() < display
.GetLeft())
1061 distance
= display
.GetLeft() - expanded
.GetLeft();
1062 new_rect
.x
+= distance
;
1067 if(expanded
.GetBottom() > display
.GetBottom())
1069 distance
= expanded
.GetBottom() - display
.GetBottom();
1070 new_rect
.y
-= distance
;
1072 else if(expanded
.GetTop() < display
.GetTop())
1074 distance
= display
.GetTop() - expanded
.GetTop();
1075 new_rect
.y
+= distance
;
1078 if(!display
.Contains(new_rect
))
1080 // Tried moving in primary axis, but failed.
1081 // Hence try moving in the secondary axis.
1082 int dx
= secondary_x
* (panel
.GetWidth() + expanded_size
.GetWidth());
1083 int dy
= secondary_y
* (panel
.GetHeight() + expanded_size
.GetHeight());
1087 // Squaring makes secondary moves more expensive (and also
1088 // prevents a negative cost)
1089 distance
+= dx
* dx
+ dy
* dy
;
1091 if(display
.Contains(new_rect
) && distance
< best_distance
)
1094 best_distance
= distance
;
1102 void wxRibbonPanel::HideIfExpanded()
1104 wxStaticCast(m_parent
, wxRibbonPage
)->HideIfExpanded();
1107 #endif // wxUSE_RIBBON