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 IMPLEMENT_CLASS(wxRibbonPanel
, wxRibbonControl
)
37 BEGIN_EVENT_TABLE(wxRibbonPanel
, wxRibbonControl
)
38 EVT_ENTER_WINDOW(wxRibbonPanel::OnMouseEnter
)
39 EVT_ERASE_BACKGROUND(wxRibbonPanel::OnEraseBackground
)
40 EVT_KILL_FOCUS(wxRibbonPanel::OnKillFocus
)
41 EVT_LEAVE_WINDOW(wxRibbonPanel::OnMouseLeave
)
42 EVT_LEFT_DOWN(wxRibbonPanel::OnMouseClick
)
43 EVT_PAINT(wxRibbonPanel::OnPaint
)
44 EVT_SIZE(wxRibbonPanel::OnSize
)
47 wxRibbonPanel::wxRibbonPanel() : m_expanded_dummy(NULL
), m_expanded_panel(NULL
)
51 wxRibbonPanel::wxRibbonPanel(wxWindow
* parent
,
53 const wxString
& label
,
54 const wxBitmap
& minimised_icon
,
58 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
60 CommonInit(label
, minimised_icon
, style
);
63 wxRibbonPanel::~wxRibbonPanel()
67 m_expanded_panel
->m_expanded_dummy
= NULL
;
68 m_expanded_panel
->GetParent()->Destroy();
72 bool wxRibbonPanel::Create(wxWindow
* parent
,
74 const wxString
& label
,
80 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
85 CommonInit(label
, icon
, style
);
90 void wxRibbonPanel::SetArtProvider(wxRibbonArtProvider
* art
)
93 for ( wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
95 node
= node
->GetNext() )
97 wxWindow
* child
= node
->GetData();
98 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
101 ribbon_child
->SetArtProvider(art
);
105 m_expanded_panel
->SetArtProvider(art
);
108 void wxRibbonPanel::CommonInit(const wxString
& label
, const wxBitmap
& icon
, long style
)
113 m_minimised_size
= wxDefaultSize
; // Unknown / none
114 m_smallest_unminimised_size
= wxDefaultSize
;// Unknown / none for IsFullySpecified()
115 m_preferred_expand_direction
= wxSOUTH
;
116 m_expanded_dummy
= NULL
;
117 m_expanded_panel
= NULL
;
119 m_minimised_icon
= icon
;
125 wxRibbonControl
* parent
= wxDynamicCast(GetParent(), wxRibbonControl
);
128 m_art
= parent
->GetArtProvider();
133 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
134 SetMinSize(wxSize(20, 20));
137 bool wxRibbonPanel::IsMinimised() const
142 bool wxRibbonPanel::IsHovered() const
147 void wxRibbonPanel::OnMouseEnter(wxMouseEvent
& evt
)
149 TestPositionForHover(evt
.GetPosition());
152 void wxRibbonPanel::OnMouseEnterChild(wxMouseEvent
& evt
)
154 wxPoint pos
= evt
.GetPosition();
155 wxWindow
*child
= wxDynamicCast(evt
.GetEventObject(), wxWindow
);
158 pos
+= child
->GetPosition();
159 TestPositionForHover(pos
);
164 void wxRibbonPanel::OnMouseLeave(wxMouseEvent
& evt
)
166 TestPositionForHover(evt
.GetPosition());
169 void wxRibbonPanel::OnMouseLeaveChild(wxMouseEvent
& evt
)
171 wxPoint pos
= evt
.GetPosition();
172 wxWindow
*child
= wxDynamicCast(evt
.GetEventObject(), wxWindow
);
175 pos
+= child
->GetPosition();
176 TestPositionForHover(pos
);
181 void wxRibbonPanel::TestPositionForHover(const wxPoint
& pos
)
183 bool hovered
= false;
184 if(pos
.x
>= 0 && pos
.y
>= 0)
186 wxSize size
= GetSize();
187 if(pos
.x
< size
.GetWidth() && pos
.y
< size
.GetHeight())
192 if(hovered
!= m_hovered
)
199 void wxRibbonPanel::AddChild(wxWindowBase
*child
)
201 wxRibbonControl::AddChild(child
);
203 // Window enter / leave events count for only the window in question, not
204 // for children of the window. The panel wants to be in the hovered state
205 // whenever the mouse cursor is within its boundary, so the events need to
206 // be attached to children too.
207 child
->Connect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this);
208 child
->Connect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this);
211 void wxRibbonPanel::RemoveChild(wxWindowBase
*child
)
213 child
->Disconnect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this);
214 child
->Disconnect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this);
216 wxRibbonControl::RemoveChild(child
);
219 void wxRibbonPanel::OnSize(wxSizeEvent
& evt
)
227 void wxRibbonPanel::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
)
229 // At least on MSW, changing the size of a window will cause GetSize() to
230 // report the new size, but a size event may not be handled immediately.
231 // If this minimised check was performed in the OnSize handler, then
232 // GetSize() could return a size much larger than the minimised size while
233 // IsMinimised() returns true. This would then affect layout, as the panel
234 // will refuse to grow any larger while in limbo between minimised and non.
236 bool minimised
= (m_flags
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0 &&
237 IsMinimised(wxSize(width
, height
)); // check if would be at this size
238 if(minimised
!= m_minimised
)
240 m_minimised
= minimised
;
241 // Note that for sizers, this routine disallows the use of mixed shown
242 // and hidden controls
243 // TODO ? use some list of user set invisible children to restore status.
244 for (wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
246 node
= node
->GetNext())
248 node
->GetData()->Show(!minimised
);
254 wxRibbonControl::DoSetSize(x
, y
, width
, height
, sizeFlags
);
257 // Checks if panel would be minimised at (client size) at_size
258 bool wxRibbonPanel::IsMinimised(wxSize at_size
) const
262 // we have no information on size change direction
264 wxSize size
= GetMinNotMinimisedSize();
265 if(size
.x
> at_size
.x
|| size
.y
> at_size
.y
)
271 if(!m_minimised_size
.IsFullySpecified())
274 return (at_size
.GetX() <= m_minimised_size
.GetX() &&
275 at_size
.GetY() <= m_minimised_size
.GetY()) ||
276 at_size
.GetX() < m_smallest_unminimised_size
.GetX() ||
277 at_size
.GetY() < m_smallest_unminimised_size
.GetY();
280 void wxRibbonPanel::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
282 // All painting done in main paint handler to minimise flicker
285 void wxRibbonPanel::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
287 wxAutoBufferedPaintDC
dc(this);
293 m_art
->DrawMinimisedPanel(dc
, this, GetSize(), m_minimised_icon_resized
);
297 m_art
->DrawPanelBackground(dc
, this, GetSize());
302 bool wxRibbonPanel::IsSizingContinuous() const
304 // A panel never sizes continuously, even if all of its children can,
305 // as it would appear out of place along side non-continuous panels.
309 wxSize
wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction
,
310 wxSize relative_to
) const
312 if(m_expanded_panel
!= NULL
)
314 // Next size depends upon children, who are currently in the
316 return m_expanded_panel
->DoGetNextSmallerSize(direction
, relative_to
);
321 wxClientDC
dc((wxRibbonPanel
*) this);
322 wxSize child_relative
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
);
323 wxSize
smaller(-1, -1);
324 bool minimise
= false;
328 // Get smallest non minimised size
329 smaller
= GetMinSize();
330 // and adjust to child_relative for parent page
331 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
333 minimise
= (child_relative
.y
<= smaller
.y
);
334 if(smaller
.x
< child_relative
.x
)
335 smaller
.x
= child_relative
.x
;
339 minimise
= (child_relative
.x
<= smaller
.x
);
340 if(smaller
.y
< child_relative
.y
)
341 smaller
.y
= child_relative
.y
;
344 else if(GetChildren().GetCount() == 1)
346 // Simple (and common) case of single ribbon child or Sizer
347 wxWindow
* child
= GetChildren().Item(0)->GetData();
348 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
349 if(ribbon_child
!= NULL
)
351 smaller
= ribbon_child
->GetNextSmallerSize(direction
, child_relative
);
352 minimise
= (smaller
== child_relative
);
358 if(CanAutoMinimise())
360 wxSize minimised
= m_minimised_size
;
364 minimised
.SetHeight(relative_to
.GetHeight());
367 minimised
.SetWidth(relative_to
.GetWidth());
379 else if(smaller
.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
381 return m_art
->GetPanelSize(dc
, this, smaller
, NULL
);
385 // Fallback: Decrease by 20% (or minimum size, whichever larger)
386 wxSize
current(relative_to
);
387 wxSize
minimum(GetMinSize());
388 if(direction
& wxHORIZONTAL
)
390 current
.x
= (current
.x
* 4) / 5;
391 if(current
.x
< minimum
.x
)
393 current
.x
= minimum
.x
;
396 if(direction
& wxVERTICAL
)
398 current
.y
= (current
.y
* 4) / 5;
399 if(current
.y
< minimum
.y
)
401 current
.y
= minimum
.y
;
407 wxSize
wxRibbonPanel::DoGetNextLargerSize(wxOrientation direction
,
408 wxSize relative_to
) const
410 if(m_expanded_panel
!= NULL
)
412 // Next size depends upon children, who are currently in the
414 return m_expanded_panel
->DoGetNextLargerSize(direction
, relative_to
);
417 if(IsMinimised(relative_to
))
419 wxSize current
= relative_to
;
420 wxSize min_size
= GetMinNotMinimisedSize();
424 if(min_size
.x
> current
.x
&& min_size
.y
== current
.y
)
428 if(min_size
.x
== current
.x
&& min_size
.y
> current
.y
)
432 if(min_size
.x
> current
.x
&& min_size
.y
> current
.y
)
442 wxClientDC
dc((wxRibbonPanel
*) this);
443 wxSize child_relative
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
);
444 wxSize
larger(-1, -1);
448 // We could just let the sizer expand in flow direction but see comment
449 // in IsSizingContinuous()
450 larger
= GetPanelSizerBestSize();
451 // and adjust for page in non flow direction
452 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
454 if(larger
.x
!= child_relative
.x
)
455 larger
.x
= child_relative
.x
;
457 else if(larger
.y
!= child_relative
.y
)
459 larger
.y
= child_relative
.y
;
462 else if(GetChildren().GetCount() == 1)
464 // Simple (and common) case of single ribbon child
465 wxWindow
* child
= GetChildren().Item(0)->GetData();
466 wxRibbonControl
* ribbon_child
= wxDynamicCast(child
, wxRibbonControl
);
467 if(ribbon_child
!= NULL
)
469 larger
= ribbon_child
->GetNextLargerSize(direction
, child_relative
);
473 if(larger
.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
475 if(larger
== child_relative
)
481 return m_art
->GetPanelSize(dc
, this, larger
, NULL
);
486 // Fallback: Increase by 25% (equal to a prior or subsequent 20% decrease)
487 // Note that due to rounding errors, this increase may not exactly equal a
488 // matching decrease - an ideal solution would not have these errors, but
489 // avoiding them is non-trivial unless an increase is by 100% rather than
490 // a fractional amount. This would then be non-ideal as the resizes happen
491 // at very large intervals.
492 wxSize
current(relative_to
);
493 if(direction
& wxHORIZONTAL
)
495 current
.x
= (current
.x
* 5 + 3) / 4;
497 if(direction
& wxVERTICAL
)
499 current
.y
= (current
.y
* 5 + 3) / 4;
504 bool wxRibbonPanel::CanAutoMinimise() const
506 return (m_flags
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0
507 && m_minimised_size
.IsFullySpecified();
510 wxSize
wxRibbonPanel::GetMinSize() const
512 if(m_expanded_panel
!= NULL
)
514 // Minimum size depends upon children, who are currently in the
516 return m_expanded_panel
->GetMinSize();
519 if(CanAutoMinimise())
521 return m_minimised_size
;
525 return GetMinNotMinimisedSize();
529 wxSize
wxRibbonPanel::GetMinNotMinimisedSize() const
531 // Ask sizer if present
534 wxClientDC
dc((wxRibbonPanel
*) this);
535 return m_art
->GetPanelSize(dc
, this, GetPanelSizerMinSize(), NULL
);
537 else if(GetChildren().GetCount() == 1)
539 // Common case of single child taking up the entire panel
540 wxWindow
* child
= GetChildren().Item(0)->GetData();
541 wxClientDC
dc((wxRibbonPanel
*) this);
542 return m_art
->GetPanelSize(dc
, this, child
->GetMinSize(), NULL
);
545 return wxRibbonControl::GetMinSize();
548 wxSize
wxRibbonPanel::GetPanelSizerMinSize() const
550 // Called from Realize() to set m_smallest_unminimised_size and from other
551 // functions to get the minimum size.
552 // The panel will be invisible when minimised and sizer calcs will be 0
553 // Uses m_smallest_unminimised_size in preference to GetSizer()->CalcMin()
554 // to eliminate flicker.
556 // Check if is visible and not previously calculated
557 if(IsShown() && !m_smallest_unminimised_size
.IsFullySpecified())
559 return GetSizer()->CalcMin();
561 // else use previously calculated m_smallest_unminimised_size
562 wxClientDC
dc((wxRibbonPanel
*) this);
563 return m_art
->GetPanelClientSize(dc
,
565 m_smallest_unminimised_size
,
569 wxSize
wxRibbonPanel::GetPanelSizerBestSize() const
571 wxSize size
= GetPanelSizerMinSize();
572 // TODO allow panel to increase its size beyond minimum size
573 // by steps similarly to ribbon control panels (preferred for aesthetics)
578 wxSize
wxRibbonPanel::DoGetBestSize() const
580 // Ask sizer if present
583 wxClientDC
dc((wxRibbonPanel
*) this);
584 return m_art
->GetPanelSize(dc
, this, GetPanelSizerBestSize(), NULL
);
586 else if(GetChildren().GetCount() == 1)
588 // Common case of no sizer and single child taking up the entire panel
589 wxWindow
* child
= GetChildren().Item(0)->GetData();
590 wxClientDC
dc((wxRibbonPanel
*) this);
591 return m_art
->GetPanelSize(dc
, this, child
->GetBestSize(), NULL
);
594 return wxRibbonControl::DoGetBestSize();
597 bool wxRibbonPanel::Realize()
601 for (wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
603 node
= node
->GetNext())
605 wxRibbonControl
* child
= wxDynamicCast(node
->GetData(), wxRibbonControl
);
610 if(!child
->Realize())
616 wxSize
minimum_children_size(0, 0);
618 // Ask sizer if there is one present
621 minimum_children_size
= GetPanelSizerMinSize();
623 else if(GetChildren().GetCount() == 1)
625 minimum_children_size
= GetChildren().GetFirst()->GetData()->GetMinSize();
630 wxClientDC
temp_dc(this);
632 m_smallest_unminimised_size
=
633 m_art
->GetPanelSize(temp_dc
, this, minimum_children_size
, NULL
);
636 wxSize panel_min_size
= GetMinNotMinimisedSize();
637 m_minimised_size
= m_art
->GetMinimisedPanelMinimumSize(temp_dc
, this,
638 &bitmap_size
, &m_preferred_expand_direction
);
639 if(m_minimised_icon
.IsOk() && m_minimised_icon
.GetSize() != bitmap_size
)
641 wxImage
img(m_minimised_icon
.ConvertToImage());
642 img
.Rescale(bitmap_size
.GetWidth(), bitmap_size
.GetHeight(), wxIMAGE_QUALITY_HIGH
);
643 m_minimised_icon_resized
= wxBitmap(img
);
647 m_minimised_icon_resized
= m_minimised_icon
;
649 if(m_minimised_size
.x
> panel_min_size
.x
&&
650 m_minimised_size
.y
> panel_min_size
.y
)
652 // No point in having a minimised size which is larger than the
653 // minimum size which the children can go to.
654 m_minimised_size
= wxSize(-1, -1);
658 if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
)
660 m_minimised_size
.x
= panel_min_size
.x
;
664 m_minimised_size
.y
= panel_min_size
.y
;
670 m_minimised_size
= wxSize(-1, -1);
673 return Layout() && status
;
676 bool wxRibbonPanel::Layout()
680 // Children are all invisible when minimised
684 // Get wxRibbonPanel client size
687 wxSize size
= m_art
->GetPanelClientSize(dc
, this, GetSize(), &position
);
689 // If there is a sizer, use it
692 GetSizer()->SetDimension(position
, size
); // SetSize and Layout()
694 else if(GetChildren().GetCount() == 1)
696 // Common case of no sizer and single child taking up the entire panel
697 wxWindow
* child
= GetChildren().Item(0)->GetData();
698 child
->SetSize(position
.x
, position
.y
, size
.GetWidth(), size
.GetHeight());
703 void wxRibbonPanel::OnMouseClick(wxMouseEvent
& WXUNUSED(evt
))
707 if(m_expanded_panel
!= NULL
)
718 wxRibbonPanel
* wxRibbonPanel::GetExpandedDummy()
720 return m_expanded_dummy
;
723 wxRibbonPanel
* wxRibbonPanel::GetExpandedPanel()
725 return m_expanded_panel
;
728 bool wxRibbonPanel::ShowExpanded()
734 if(m_expanded_dummy
!= NULL
|| m_expanded_panel
!= NULL
)
739 wxSize size
= GetBestSize();
740 wxPoint pos
= GetExpandedPosition(wxRect(GetScreenPosition(), GetSize()),
741 size
, m_preferred_expand_direction
).GetTopLeft();
743 // Need a top-level frame to contain the expanded panel
744 wxFrame
*container
= new wxFrame(NULL
, wxID_ANY
, GetLabel(),
745 pos
, size
, wxFRAME_NO_TASKBAR
| wxBORDER_NONE
);
747 m_expanded_panel
= new wxRibbonPanel(container
, wxID_ANY
,
748 GetLabel(), m_minimised_icon
, wxPoint(0, 0), size
, m_flags
);
750 m_expanded_panel
->SetArtProvider(m_art
);
751 m_expanded_panel
->m_expanded_dummy
= this;
753 // Move all children to the new panel.
754 // Conceptually it might be simpler to reparent this entire panel to the
755 // container and create a new panel to sit in its place while expanded.
756 // This approach has a problem though - when the panel is reinserted into
757 // its original parent, it'll be at a different position in the child list
758 // and thus assume a new position.
759 // NB: Children iterators not used as behaviour is not well defined
760 // when iterating over a container which is being emptied
761 while(!GetChildren().IsEmpty())
763 wxWindow
*child
= GetChildren().GetFirst()->GetData();
764 child
->Reparent(m_expanded_panel
);
768 // Move sizer to new panel
771 wxSizer
* sizer
= GetSizer();
772 SetSizer(NULL
, false);
773 m_expanded_panel
->SetSizer(sizer
);
776 m_expanded_panel
->Realize();
779 m_expanded_panel
->SetFocus();
784 bool wxRibbonPanel::ShouldSendEventToDummy(wxEvent
& evt
)
786 // For an expanded panel, filter events between being sent up to the
787 // floating top level window or to the dummy panel sitting in the ribbon
790 // Child focus events should not be redirected, as the child would not be a
791 // child of the window the event is redirected to. All other command events
792 // seem to be suitable for redirecting.
793 return evt
.IsCommandEvent() && evt
.GetEventType() != wxEVT_CHILD_FOCUS
;
796 bool wxRibbonPanel::TryAfter(wxEvent
& evt
)
798 if(m_expanded_dummy
&& ShouldSendEventToDummy(evt
))
800 wxPropagateOnce
propagateOnce(evt
);
801 return m_expanded_dummy
->GetEventHandler()->ProcessEvent(evt
);
805 return wxRibbonControl::TryAfter(evt
);
809 static bool IsAncestorOf(wxWindow
*ancestor
, wxWindow
*window
)
811 while(window
!= NULL
)
813 wxWindow
*parent
= window
->GetParent();
814 if(parent
== ancestor
)
822 void wxRibbonPanel::OnKillFocus(wxFocusEvent
& evt
)
826 wxWindow
*receiver
= evt
.GetWindow();
827 if(IsAncestorOf(this, receiver
))
829 m_child_with_focus
= receiver
;
830 receiver
->Connect(wxEVT_KILL_FOCUS
,
831 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
),
834 else if(receiver
== NULL
|| receiver
!= m_expanded_dummy
)
841 void wxRibbonPanel::OnChildKillFocus(wxFocusEvent
& evt
)
843 if(m_child_with_focus
== NULL
)
844 return; // Should never happen, but a check can't hurt
846 m_child_with_focus
->Disconnect(wxEVT_KILL_FOCUS
,
847 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this);
848 m_child_with_focus
= NULL
;
850 wxWindow
*receiver
= evt
.GetWindow();
851 if(receiver
== this || IsAncestorOf(this, receiver
))
853 m_child_with_focus
= receiver
;
854 receiver
->Connect(wxEVT_KILL_FOCUS
,
855 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this);
858 else if(receiver
== NULL
|| receiver
!= m_expanded_dummy
)
861 // Do not skip event, as the panel has been de-expanded, causing the
862 // child with focus to be reparented (and hidden). If the event
863 // continues propogation then bad things happen.
871 bool wxRibbonPanel::HideExpanded()
873 if(m_expanded_dummy
== NULL
)
877 return m_expanded_panel
->HideExpanded();
885 // Move children back to original panel
886 // NB: Children iterators not used as behaviour is not well defined
887 // when iterating over a container which is being emptied
888 while(!GetChildren().IsEmpty())
890 wxWindow
*child
= GetChildren().GetFirst()->GetData();
891 child
->Reparent(m_expanded_dummy
);
898 wxSizer
* sizer
= GetSizer();
899 SetSizer(NULL
, false);
900 m_expanded_dummy
->SetSizer(sizer
);
903 m_expanded_dummy
->m_expanded_panel
= NULL
;
904 m_expanded_dummy
->Realize();
905 m_expanded_dummy
->Refresh();
906 wxWindow
*parent
= GetParent();
913 wxRect
wxRibbonPanel::GetExpandedPosition(wxRect panel
,
914 wxSize expanded_size
,
915 wxDirection direction
)
918 // 1) Determine primary position based on requested direction
919 // 2) Move the position so that it sits entirely within a display
920 // (for single monitor systems, this moves it into the display region,
921 // but for multiple monitors, it does so without splitting it over
922 // more than one display)
923 // 2.1) Move in the primary axis
924 // 2.2) Move in the secondary axis
927 bool primary_x
= false;
933 pos
.x
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2;
934 pos
.y
= panel
.GetY() - expanded_size
.GetHeight();
939 pos
.x
= panel
.GetRight();
940 pos
.y
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2;
944 pos
.x
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2;
945 pos
.y
= panel
.GetBottom();
951 pos
.x
= panel
.GetX() - expanded_size
.GetWidth();
952 pos
.y
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2;
956 wxRect
expanded(pos
, expanded_size
);
958 wxRect
best(expanded
);
959 int best_distance
= INT_MAX
;
961 const unsigned display_n
= wxDisplay::GetCount();
963 for(display_i
= 0; display_i
< display_n
; ++display_i
)
965 wxRect display
= wxDisplay(display_i
).GetGeometry();
967 if(display
.Contains(expanded
))
971 else if(display
.Intersects(expanded
))
973 wxRect
new_rect(expanded
);
978 if(expanded
.GetRight() > display
.GetRight())
980 distance
= expanded
.GetRight() - display
.GetRight();
981 new_rect
.x
-= distance
;
983 else if(expanded
.GetLeft() < display
.GetLeft())
985 distance
= display
.GetLeft() - expanded
.GetLeft();
986 new_rect
.x
+= distance
;
991 if(expanded
.GetBottom() > display
.GetBottom())
993 distance
= expanded
.GetBottom() - display
.GetBottom();
994 new_rect
.y
-= distance
;
996 else if(expanded
.GetTop() < display
.GetTop())
998 distance
= display
.GetTop() - expanded
.GetTop();
999 new_rect
.y
+= distance
;
1002 if(!display
.Contains(new_rect
))
1004 // Tried moving in primary axis, but failed.
1005 // Hence try moving in the secondary axis.
1006 int dx
= secondary_x
* (panel
.GetWidth() + expanded_size
.GetWidth());
1007 int dy
= secondary_y
* (panel
.GetHeight() + expanded_size
.GetHeight());
1011 // Squaring makes secondary moves more expensive (and also
1012 // prevents a negative cost)
1013 distance
+= dx
* dx
+ dy
* dy
;
1015 if(display
.Contains(new_rect
) && distance
< best_distance
)
1018 best_distance
= distance
;
1026 #endif // wxUSE_RIBBON