1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/panel.cpp
3 // Purpose: Ribbon-style container for a group of related tools / controls
4 // Author: Peter Cawley
7 // Copyright: (C) Peter Cawley
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
19 #include "wx/ribbon/panel.h"
20 #include "wx/ribbon/art.h"
21 #include "wx/ribbon/bar.h"
22 #include "wx/dcbuffer.h"
23 #include "wx/display.h"
31 #include "wx/msw/private.h"
34 wxDEFINE_EVENT(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED
, wxRibbonPanelEvent
);
36 IMPLEMENT_DYNAMIC_CLASS(wxRibbonPanelEvent
, wxCommandEvent
)
38 IMPLEMENT_CLASS(wxRibbonPanel
, wxRibbonControl
)
40 BEGIN_EVENT_TABLE(wxRibbonPanel
, wxRibbonControl
)
41 EVT_ENTER_WINDOW(wxRibbonPanel::OnMouseEnter
)
42 EVT_ERASE_BACKGROUND(wxRibbonPanel::OnEraseBackground
)
43 EVT_KILL_FOCUS(wxRibbonPanel::OnKillFocus
)
44 EVT_LEAVE_WINDOW(wxRibbonPanel::OnMouseLeave
)
45 EVT_MOTION(wxRibbonPanel::OnMotion
)
46 EVT_LEFT_DOWN(wxRibbonPanel::OnMouseClick
)
47 EVT_PAINT(wxRibbonPanel::OnPaint
)
48 EVT_SIZE(wxRibbonPanel::OnSize
)
51 wxRibbonPanel::wxRibbonPanel() : m_expanded_dummy(NULL
), m_expanded_panel(NULL
)
55 wxRibbonPanel::wxRibbonPanel(wxWindow
* parent
,
57 const wxString
& label
,
58 const wxBitmap
& minimised_icon
,
62 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
64 CommonInit(label
, minimised_icon
, style
);
67 wxRibbonPanel::~wxRibbonPanel()
71 m_expanded_panel
->m_expanded_dummy
= NULL
;
72 m_expanded_panel
->GetParent()->Destroy();
76 bool wxRibbonPanel::Create(wxWindow
* parent
,
78 const wxString
& label
,
84 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
89 CommonInit(label
, icon
, style
);
94 void wxRibbonPanel::SetArtProvider(wxRibbonArtProvider
* art
)
97 for ( wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
99 node
= node
->GetNext() )
101 wxWindow
* child
= node
->GetData();
102 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
105 ribbon_child
->SetArtProvider(art
);
109 m_expanded_panel
->SetArtProvider(art
);
112 void wxRibbonPanel::CommonInit(const wxString
& label
, const wxBitmap
& icon
, long style
)
117 m_minimised_size
= wxDefaultSize
; // Unknown / none
118 m_smallest_unminimised_size
= wxDefaultSize
;// Unknown / none for IsFullySpecified()
119 m_preferred_expand_direction
= wxSOUTH
;
120 m_expanded_dummy
= NULL
;
121 m_expanded_panel
= NULL
;
123 m_minimised_icon
= icon
;
126 m_ext_button_hovered
= false;
130 wxRibbonControl
* parent
= wxDynamicCast(GetParent(), wxRibbonControl
);
133 m_art
= parent
->GetArtProvider();
138 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
139 SetMinSize(wxSize(20, 20));
142 bool wxRibbonPanel::IsMinimised() const
147 bool wxRibbonPanel::IsHovered() const
152 bool wxRibbonPanel::IsExtButtonHovered() const
154 return m_ext_button_hovered
;
157 void wxRibbonPanel::OnMouseEnter(wxMouseEvent
& evt
)
159 TestPositionForHover(evt
.GetPosition());
162 void wxRibbonPanel::OnMouseEnterChild(wxMouseEvent
& evt
)
164 wxPoint pos
= evt
.GetPosition();
165 wxWindow
*child
= wxDynamicCast(evt
.GetEventObject(), wxWindow
);
168 pos
+= child
->GetPosition();
169 TestPositionForHover(pos
);
174 void wxRibbonPanel::OnMouseLeave(wxMouseEvent
& evt
)
176 TestPositionForHover(evt
.GetPosition());
179 void wxRibbonPanel::OnMouseLeaveChild(wxMouseEvent
& evt
)
181 wxPoint pos
= evt
.GetPosition();
182 wxWindow
*child
= wxDynamicCast(evt
.GetEventObject(), wxWindow
);
185 pos
+= child
->GetPosition();
186 TestPositionForHover(pos
);
191 void wxRibbonPanel::OnMotion(wxMouseEvent
& evt
)
193 TestPositionForHover(evt
.GetPosition());
196 void wxRibbonPanel::TestPositionForHover(const wxPoint
& pos
)
198 bool hovered
= false, ext_button_hovered
= false;
199 if(pos
.x
>= 0 && pos
.y
>= 0)
201 wxSize size
= GetSize();
202 if(pos
.x
< size
.GetWidth() && pos
.y
< size
.GetHeight())
210 ext_button_hovered
= m_ext_button_rect
.Contains(pos
);
212 ext_button_hovered
= false;
214 if(hovered
!= m_hovered
|| ext_button_hovered
!= m_ext_button_hovered
)
217 m_ext_button_hovered
= ext_button_hovered
;
222 void wxRibbonPanel::AddChild(wxWindowBase
*child
)
224 wxRibbonControl::AddChild(child
);
226 // Window enter / leave events count for only the window in question, not
227 // for children of the window. The panel wants to be in the hovered state
228 // whenever the mouse cursor is within its boundary, so the events need to
229 // be attached to children too.
230 child
->Connect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this);
231 child
->Connect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this);
234 void wxRibbonPanel::RemoveChild(wxWindowBase
*child
)
236 child
->Disconnect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this);
237 child
->Disconnect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this);
239 wxRibbonControl::RemoveChild(child
);
242 bool wxRibbonPanel::HasExtButton()const
244 wxRibbonBar
* bar
= GetAncestorRibbonBar();
247 return (m_flags
& wxRIBBON_PANEL_EXT_BUTTON
) &&
248 (bar
->GetWindowStyleFlag() & wxRIBBON_BAR_SHOW_PANEL_EXT_BUTTONS
);
251 void wxRibbonPanel::OnSize(wxSizeEvent
& evt
)
259 void wxRibbonPanel::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
)
261 // At least on MSW, changing the size of a window will cause GetSize() to
262 // report the new size, but a size event may not be handled immediately.
263 // If this minimised check was performed in the OnSize handler, then
264 // GetSize() could return a size much larger than the minimised size while
265 // IsMinimised() returns true. This would then affect layout, as the panel
266 // will refuse to grow any larger while in limbo between minimised and non.
268 bool minimised
= (m_flags
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0 &&
269 IsMinimised(wxSize(width
, height
)); // check if would be at this size
270 if(minimised
!= m_minimised
)
272 m_minimised
= minimised
;
273 // Note that for sizers, this routine disallows the use of mixed shown
274 // and hidden controls
275 // TODO ? use some list of user set invisible children to restore status.
276 for (wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
278 node
= node
->GetNext())
280 node
->GetData()->Show(!minimised
);
286 wxRibbonControl::DoSetSize(x
, y
, width
, height
, sizeFlags
);
289 // Checks if panel would be minimised at (client size) at_size
290 bool wxRibbonPanel::IsMinimised(wxSize at_size
) const
294 // we have no information on size change direction
296 wxSize size
= GetMinNotMinimisedSize();
297 if(size
.x
> at_size
.x
|| size
.y
> at_size
.y
)
303 if(!m_minimised_size
.IsFullySpecified())
306 return (at_size
.GetX() <= m_minimised_size
.GetX() &&
307 at_size
.GetY() <= m_minimised_size
.GetY()) ||
308 at_size
.GetX() < m_smallest_unminimised_size
.GetX() ||
309 at_size
.GetY() < m_smallest_unminimised_size
.GetY();
312 void wxRibbonPanel::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
314 // All painting done in main paint handler to minimise flicker
317 void wxRibbonPanel::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
319 wxAutoBufferedPaintDC
dc(this);
325 m_art
->DrawMinimisedPanel(dc
, this, GetSize(), m_minimised_icon_resized
);
329 m_art
->DrawPanelBackground(dc
, this, GetSize());
334 bool wxRibbonPanel::IsSizingContinuous() const
336 // A panel never sizes continuously, even if all of its children can,
337 // as it would appear out of place along side non-continuous panels.
339 // JS 2012-03-09: introducing wxRIBBON_PANEL_STRETCH to allow
340 // the panel to fill its parent page. For example we might have
341 // a list of styles in one of the pages, which should stretch to
342 // fill available space.
343 return (m_flags
& wxRIBBON_PANEL_STRETCH
) != 0;
346 // Finds the best width and height given the parent's width and height
347 wxSize
wxRibbonPanel::GetBestSizeForParentSize(const wxSize
& parentSize
) const
349 if (GetChildren().GetCount() == 1)
351 wxWindow
* win
= GetChildren().GetFirst()->GetData();
352 wxRibbonControl
* control
= wxDynamicCast(win
, wxRibbonControl
);
355 wxClientDC
temp_dc((wxRibbonPanel
*) this);
356 wxSize clientParentSize
= m_art
->GetPanelClientSize(temp_dc
, this, parentSize
, NULL
);
357 wxSize childSize
= control
->GetBestSizeForParentSize(clientParentSize
);
358 wxSize overallSize
= m_art
->GetPanelSize(temp_dc
, this, childSize
, NULL
);
365 wxSize
wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction
,
366 wxSize relative_to
) const
368 if(m_expanded_panel
!= NULL
)
370 // Next size depends upon children, who are currently in the
372 return m_expanded_panel
->DoGetNextSmallerSize(direction
, relative_to
);
377 wxClientDC
dc((wxRibbonPanel
*) this);
378 wxSize child_relative
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
);
379 wxSize
smaller(-1, -1);
380 bool minimise
= false;
384 // Get smallest non minimised size
385 smaller
= GetMinSize();
386 // and adjust to child_relative for parent page
387 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
389 minimise
= (child_relative
.y
<= smaller
.y
);
390 if(smaller
.x
< child_relative
.x
)
391 smaller
.x
= child_relative
.x
;
395 minimise
= (child_relative
.x
<= smaller
.x
);
396 if(smaller
.y
< child_relative
.y
)
397 smaller
.y
= child_relative
.y
;
400 else if(GetChildren().GetCount() == 1)
402 // Simple (and common) case of single ribbon child or Sizer
403 wxWindow
* child
= GetChildren().Item(0)->GetData();
404 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
405 if(ribbon_child
!= NULL
)
407 smaller
= ribbon_child
->GetNextSmallerSize(direction
, child_relative
);
408 minimise
= (smaller
== child_relative
);
414 if(CanAutoMinimise())
416 wxSize minimised
= m_minimised_size
;
420 minimised
.SetHeight(relative_to
.GetHeight());
423 minimised
.SetWidth(relative_to
.GetWidth());
435 else if(smaller
.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
437 return m_art
->GetPanelSize(dc
, this, smaller
, NULL
);
441 // Fallback: Decrease by 20% (or minimum size, whichever larger)
442 wxSize
current(relative_to
);
443 wxSize
minimum(GetMinSize());
444 if(direction
& wxHORIZONTAL
)
446 current
.x
= (current
.x
* 4) / 5;
447 if(current
.x
< minimum
.x
)
449 current
.x
= minimum
.x
;
452 if(direction
& wxVERTICAL
)
454 current
.y
= (current
.y
* 4) / 5;
455 if(current
.y
< minimum
.y
)
457 current
.y
= minimum
.y
;
463 wxSize
wxRibbonPanel::DoGetNextLargerSize(wxOrientation direction
,
464 wxSize relative_to
) const
466 if(m_expanded_panel
!= NULL
)
468 // Next size depends upon children, who are currently in the
470 return m_expanded_panel
->DoGetNextLargerSize(direction
, relative_to
);
473 if(IsMinimised(relative_to
))
475 wxSize current
= relative_to
;
476 wxSize min_size
= GetMinNotMinimisedSize();
480 if(min_size
.x
> current
.x
&& min_size
.y
== current
.y
)
484 if(min_size
.x
== current
.x
&& min_size
.y
> current
.y
)
488 if(min_size
.x
> current
.x
&& min_size
.y
> current
.y
)
498 wxClientDC
dc((wxRibbonPanel
*) this);
499 wxSize child_relative
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
);
500 wxSize
larger(-1, -1);
504 // We could just let the sizer expand in flow direction but see comment
505 // in IsSizingContinuous()
506 larger
= GetPanelSizerBestSize();
507 // and adjust for page in non flow direction
508 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
510 if(larger
.x
!= child_relative
.x
)
511 larger
.x
= child_relative
.x
;
513 else if(larger
.y
!= child_relative
.y
)
515 larger
.y
= child_relative
.y
;
518 else if(GetChildren().GetCount() == 1)
520 // Simple (and common) case of single ribbon child
521 wxWindow
* child
= GetChildren().Item(0)->GetData();
522 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
523 if(ribbon_child
!= NULL
)
525 larger
= ribbon_child
->GetNextLargerSize(direction
, child_relative
);
529 if(larger
.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
531 if(larger
== child_relative
)
537 return m_art
->GetPanelSize(dc
, this, larger
, NULL
);
542 // Fallback: Increase by 25% (equal to a prior or subsequent 20% decrease)
543 // Note that due to rounding errors, this increase may not exactly equal a
544 // matching decrease - an ideal solution would not have these errors, but
545 // avoiding them is non-trivial unless an increase is by 100% rather than
546 // a fractional amount. This would then be non-ideal as the resizes happen
547 // at very large intervals.
548 wxSize
current(relative_to
);
549 if(direction
& wxHORIZONTAL
)
551 current
.x
= (current
.x
* 5 + 3) / 4;
553 if(direction
& wxVERTICAL
)
555 current
.y
= (current
.y
* 5 + 3) / 4;
560 bool wxRibbonPanel::CanAutoMinimise() const
562 return (m_flags
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0
563 && m_minimised_size
.IsFullySpecified();
566 wxSize
wxRibbonPanel::GetMinSize() const
568 if(m_expanded_panel
!= NULL
)
570 // Minimum size depends upon children, who are currently in the
572 return m_expanded_panel
->GetMinSize();
575 if(CanAutoMinimise())
577 return m_minimised_size
;
581 return GetMinNotMinimisedSize();
585 wxSize
wxRibbonPanel::GetMinNotMinimisedSize() const
587 // Ask sizer if present
590 wxClientDC
dc((wxRibbonPanel
*) this);
591 return m_art
->GetPanelSize(dc
, this, GetPanelSizerMinSize(), NULL
);
593 else if(GetChildren().GetCount() == 1)
595 // Common case of single child taking up the entire panel
596 wxWindow
* child
= GetChildren().Item(0)->GetData();
597 wxClientDC
dc((wxRibbonPanel
*) this);
598 return m_art
->GetPanelSize(dc
, this, child
->GetMinSize(), NULL
);
601 return wxRibbonControl::GetMinSize();
604 wxSize
wxRibbonPanel::GetPanelSizerMinSize() const
606 // Called from Realize() to set m_smallest_unminimised_size and from other
607 // functions to get the minimum size.
608 // The panel will be invisible when minimised and sizer calcs will be 0
609 // Uses m_smallest_unminimised_size in preference to GetSizer()->CalcMin()
610 // to eliminate flicker.
612 // Check if is visible and not previously calculated
613 if(IsShown() && !m_smallest_unminimised_size
.IsFullySpecified())
615 return GetSizer()->CalcMin();
617 // else use previously calculated m_smallest_unminimised_size
618 wxClientDC
dc((wxRibbonPanel
*) this);
619 return m_art
->GetPanelClientSize(dc
,
621 m_smallest_unminimised_size
,
625 wxSize
wxRibbonPanel::GetPanelSizerBestSize() const
627 wxSize size
= GetPanelSizerMinSize();
628 // TODO allow panel to increase its size beyond minimum size
629 // by steps similarly to ribbon control panels (preferred for aesthetics)
634 wxSize
wxRibbonPanel::DoGetBestSize() const
636 // Ask sizer if present
639 wxClientDC
dc((wxRibbonPanel
*) this);
640 return m_art
->GetPanelSize(dc
, this, GetPanelSizerBestSize(), NULL
);
642 else if(GetChildren().GetCount() == 1)
644 // Common case of no sizer and single child taking up the entire panel
645 wxWindow
* child
= GetChildren().Item(0)->GetData();
646 wxClientDC
dc((wxRibbonPanel
*) this);
647 return m_art
->GetPanelSize(dc
, this, child
->GetBestSize(), NULL
);
650 return wxRibbonControl::DoGetBestSize();
653 bool wxRibbonPanel::Realize()
657 for (wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
659 node
= node
->GetNext())
661 wxRibbonControl
* child
= wxDynamicCast(node
->GetData(), wxRibbonControl
);
666 if(!child
->Realize())
672 wxSize
minimum_children_size(0, 0);
674 // Ask sizer if there is one present
677 minimum_children_size
= GetPanelSizerMinSize();
679 else if(GetChildren().GetCount() == 1)
681 minimum_children_size
= GetChildren().GetFirst()->GetData()->GetMinSize();
686 wxClientDC
temp_dc(this);
688 m_smallest_unminimised_size
=
689 m_art
->GetPanelSize(temp_dc
, this, minimum_children_size
, NULL
);
692 wxSize panel_min_size
= GetMinNotMinimisedSize();
693 m_minimised_size
= m_art
->GetMinimisedPanelMinimumSize(temp_dc
, this,
694 &bitmap_size
, &m_preferred_expand_direction
);
695 if(m_minimised_icon
.IsOk() && m_minimised_icon
.GetSize() != bitmap_size
)
697 wxImage
img(m_minimised_icon
.ConvertToImage());
698 img
.Rescale(bitmap_size
.GetWidth(), bitmap_size
.GetHeight(), wxIMAGE_QUALITY_HIGH
);
699 m_minimised_icon_resized
= wxBitmap(img
);
703 m_minimised_icon_resized
= m_minimised_icon
;
705 if(m_minimised_size
.x
> panel_min_size
.x
&&
706 m_minimised_size
.y
> panel_min_size
.y
)
708 // No point in having a minimised size which is larger than the
709 // minimum size which the children can go to.
710 m_minimised_size
= wxSize(-1, -1);
714 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
716 m_minimised_size
.x
= panel_min_size
.x
;
720 m_minimised_size
.y
= panel_min_size
.y
;
726 m_minimised_size
= wxSize(-1, -1);
729 return Layout() && status
;
732 bool wxRibbonPanel::Layout()
736 // Children are all invisible when minimised
740 // Get wxRibbonPanel client size
743 wxSize size
= m_art
->GetPanelClientSize(dc
, this, GetSize(), &position
);
745 // If there is a sizer, use it
748 GetSizer()->SetDimension(position
, size
); // SetSize and Layout()
750 else if(GetChildren().GetCount() == 1)
752 // Common case of no sizer and single child taking up the entire panel
753 wxWindow
* child
= GetChildren().Item(0)->GetData();
754 child
->SetSize(position
.x
, position
.y
, size
.GetWidth(), size
.GetHeight());
758 m_ext_button_rect
= m_art
->GetPanelExtButtonArea(dc
, this, GetSize());
763 void wxRibbonPanel::OnMouseClick(wxMouseEvent
& WXUNUSED(evt
))
767 if(m_expanded_panel
!= NULL
)
776 else if(IsExtButtonHovered())
778 wxRibbonPanelEvent
notification(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED
, GetId());
779 notification
.SetEventObject(this);
780 notification
.SetPanel(this);
781 ProcessEvent(notification
);
785 wxRibbonPanel
* wxRibbonPanel::GetExpandedDummy()
787 return m_expanded_dummy
;
790 wxRibbonPanel
* wxRibbonPanel::GetExpandedPanel()
792 return m_expanded_panel
;
795 bool wxRibbonPanel::ShowExpanded()
801 if(m_expanded_dummy
!= NULL
|| m_expanded_panel
!= NULL
)
806 wxSize size
= GetBestSize();
808 // Special case for flexible panel layout, where GetBestSize doesn't work
809 if (GetFlags() & wxRIBBON_PANEL_FLEXIBLE
)
811 size
= GetBestSizeForParentSize(wxSize(400, 1000));
814 wxPoint pos
= GetExpandedPosition(wxRect(GetScreenPosition(), GetSize()),
815 size
, m_preferred_expand_direction
).GetTopLeft();
817 // Need a top-level frame to contain the expanded panel
818 wxFrame
*container
= new wxFrame(NULL
, wxID_ANY
, GetLabel(),
819 pos
, size
, wxFRAME_NO_TASKBAR
| wxBORDER_NONE
);
821 m_expanded_panel
= new wxRibbonPanel(container
, wxID_ANY
,
822 GetLabel(), m_minimised_icon
, wxPoint(0, 0), size
, (m_flags
/* & ~wxRIBBON_PANEL_FLEXIBLE */));
824 m_expanded_panel
->SetArtProvider(m_art
);
825 m_expanded_panel
->m_expanded_dummy
= this;
827 // Move all children to the new panel.
828 // Conceptually it might be simpler to reparent this entire panel to the
829 // container and create a new panel to sit in its place while expanded.
830 // This approach has a problem though - when the panel is reinserted into
831 // its original parent, it'll be at a different position in the child list
832 // and thus assume a new position.
833 // NB: Children iterators not used as behaviour is not well defined
834 // when iterating over a container which is being emptied
835 while(!GetChildren().IsEmpty())
837 wxWindow
*child
= GetChildren().GetFirst()->GetData();
838 child
->Reparent(m_expanded_panel
);
842 // Move sizer to new panel
845 wxSizer
* sizer
= GetSizer();
846 SetSizer(NULL
, false);
847 m_expanded_panel
->SetSizer(sizer
);
850 m_expanded_panel
->Realize();
852 container
->SetMinClientSize(size
);
854 m_expanded_panel
->SetFocus();
859 bool wxRibbonPanel::ShouldSendEventToDummy(wxEvent
& evt
)
861 // For an expanded panel, filter events between being sent up to the
862 // floating top level window or to the dummy panel sitting in the ribbon
865 // Child focus events should not be redirected, as the child would not be a
866 // child of the window the event is redirected to. All other command events
867 // seem to be suitable for redirecting.
868 return evt
.IsCommandEvent() && evt
.GetEventType() != wxEVT_CHILD_FOCUS
;
871 bool wxRibbonPanel::TryAfter(wxEvent
& evt
)
873 if(m_expanded_dummy
&& ShouldSendEventToDummy(evt
))
875 wxPropagateOnce
propagateOnce(evt
);
876 return m_expanded_dummy
->GetEventHandler()->ProcessEvent(evt
);
880 return wxRibbonControl::TryAfter(evt
);
884 static bool IsAncestorOf(wxWindow
*ancestor
, wxWindow
*window
)
886 while(window
!= NULL
)
888 wxWindow
*parent
= window
->GetParent();
889 if(parent
== ancestor
)
897 void wxRibbonPanel::OnKillFocus(wxFocusEvent
& evt
)
901 wxWindow
*receiver
= evt
.GetWindow();
902 if(IsAncestorOf(this, receiver
))
904 m_child_with_focus
= receiver
;
905 receiver
->Connect(wxEVT_KILL_FOCUS
,
906 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
),
909 else if(receiver
== NULL
|| receiver
!= m_expanded_dummy
)
916 void wxRibbonPanel::OnChildKillFocus(wxFocusEvent
& evt
)
918 if(m_child_with_focus
== NULL
)
919 return; // Should never happen, but a check can't hurt
921 m_child_with_focus
->Disconnect(wxEVT_KILL_FOCUS
,
922 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this);
923 m_child_with_focus
= NULL
;
925 wxWindow
*receiver
= evt
.GetWindow();
926 if(receiver
== this || IsAncestorOf(this, receiver
))
928 m_child_with_focus
= receiver
;
929 receiver
->Connect(wxEVT_KILL_FOCUS
,
930 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this);
933 else if(receiver
== NULL
|| receiver
!= m_expanded_dummy
)
936 // Do not skip event, as the panel has been de-expanded, causing the
937 // child with focus to be reparented (and hidden). If the event
938 // continues propagation then bad things happen.
946 bool wxRibbonPanel::HideExpanded()
948 if(m_expanded_dummy
== NULL
)
952 return m_expanded_panel
->HideExpanded();
960 // Move children back to original panel
961 // NB: Children iterators not used as behaviour is not well defined
962 // when iterating over a container which is being emptied
963 while(!GetChildren().IsEmpty())
965 wxWindow
*child
= GetChildren().GetFirst()->GetData();
966 child
->Reparent(m_expanded_dummy
);
973 wxSizer
* sizer
= GetSizer();
974 SetSizer(NULL
, false);
975 m_expanded_dummy
->SetSizer(sizer
);
978 m_expanded_dummy
->m_expanded_panel
= NULL
;
979 m_expanded_dummy
->Realize();
980 m_expanded_dummy
->Refresh();
981 wxWindow
*parent
= GetParent();
988 wxRect
wxRibbonPanel::GetExpandedPosition(wxRect panel
,
989 wxSize expanded_size
,
990 wxDirection direction
)
993 // 1) Determine primary position based on requested direction
994 // 2) Move the position so that it sits entirely within a display
995 // (for single monitor systems, this moves it into the display region,
996 // but for multiple monitors, it does so without splitting it over
997 // more than one display)
998 // 2.1) Move in the primary axis
999 // 2.2) Move in the secondary axis
1002 bool primary_x
= false;
1003 int secondary_x
= 0;
1004 int secondary_y
= 0;
1008 pos
.x
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2;
1009 pos
.y
= panel
.GetY() - expanded_size
.GetHeight();
1014 pos
.x
= panel
.GetRight();
1015 pos
.y
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2;
1019 pos
.x
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2;
1020 pos
.y
= panel
.GetBottom();
1026 pos
.x
= panel
.GetX() - expanded_size
.GetWidth();
1027 pos
.y
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2;
1031 wxRect
expanded(pos
, expanded_size
);
1033 wxRect
best(expanded
);
1034 int best_distance
= INT_MAX
;
1036 const unsigned display_n
= wxDisplay::GetCount();
1038 for(display_i
= 0; display_i
< display_n
; ++display_i
)
1040 wxRect display
= wxDisplay(display_i
).GetGeometry();
1042 if(display
.Contains(expanded
))
1046 else if(display
.Intersects(expanded
))
1048 wxRect
new_rect(expanded
);
1053 if(expanded
.GetRight() > display
.GetRight())
1055 distance
= expanded
.GetRight() - display
.GetRight();
1056 new_rect
.x
-= distance
;
1058 else if(expanded
.GetLeft() < display
.GetLeft())
1060 distance
= display
.GetLeft() - expanded
.GetLeft();
1061 new_rect
.x
+= distance
;
1066 if(expanded
.GetBottom() > display
.GetBottom())
1068 distance
= expanded
.GetBottom() - display
.GetBottom();
1069 new_rect
.y
-= distance
;
1071 else if(expanded
.GetTop() < display
.GetTop())
1073 distance
= display
.GetTop() - expanded
.GetTop();
1074 new_rect
.y
+= distance
;
1077 if(!display
.Contains(new_rect
))
1079 // Tried moving in primary axis, but failed.
1080 // Hence try moving in the secondary axis.
1081 int dx
= secondary_x
* (panel
.GetWidth() + expanded_size
.GetWidth());
1082 int dy
= secondary_y
* (panel
.GetHeight() + expanded_size
.GetHeight());
1086 // Squaring makes secondary moves more expensive (and also
1087 // prevents a negative cost)
1088 distance
+= dx
* dx
+ dy
* dy
;
1090 if(display
.Contains(new_rect
) && distance
< best_distance
)
1093 best_distance
= distance
;
1101 void wxRibbonPanel::HideIfExpanded()
1103 wxStaticCast(m_parent
, wxRibbonPage
)->HideIfExpanded();
1106 #endif // wxUSE_RIBBON