1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/ribbon/bar.cpp
3 // Purpose: Top-level component of the ribbon-bar-style interface
4 // Author: Peter Cawley
8 // Copyright: (C) Peter Cawley
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
20 #include "wx/ribbon/bar.h"
21 #include "wx/ribbon/art.h"
22 #include "wx/dcbuffer.h"
29 #include "wx/msw/private.h"
32 #include "wx/arrimpl.cpp"
34 WX_DEFINE_USER_EXPORTED_OBJARRAY(wxRibbonPageTabInfoArray
)
36 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, wxRibbonBarEvent
);
37 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, wxRibbonBarEvent
);
38 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
, wxRibbonBarEvent
);
39 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
, wxRibbonBarEvent
);
40 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
, wxRibbonBarEvent
);
41 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
, wxRibbonBarEvent
);
42 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_LEFT_DCLICK
, wxRibbonBarEvent
);
44 IMPLEMENT_CLASS(wxRibbonBar
, wxRibbonControl
)
45 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent
, wxNotifyEvent
)
47 BEGIN_EVENT_TABLE(wxRibbonBar
, wxRibbonControl
)
48 EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground
)
49 EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave
)
50 EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown
)
51 EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp
)
52 EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown
)
53 EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp
)
54 EVT_MOTION(wxRibbonBar::OnMouseMove
)
55 EVT_PAINT(wxRibbonBar::OnPaint
)
56 EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown
)
57 EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp
)
58 EVT_LEFT_DCLICK(wxRibbonBar::OnMouseDoubleClick
)
59 EVT_SIZE(wxRibbonBar::OnSize
)
62 void wxRibbonBar::AddPage(wxRibbonPage
*page
)
64 wxRibbonPageTabInfo info
;
70 // info.rect not set (intentional)
72 wxClientDC
dcTemp(this);
73 wxString label
= wxEmptyString
;
74 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
75 label
= page
->GetLabel();
76 wxBitmap icon
= wxNullBitmap
;
77 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
78 icon
= page
->GetIcon();
79 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
81 &info
.small_begin_need_separator_width
,
82 &info
.small_must_have_separator_width
,
87 m_tabs_total_width_ideal
= info
.ideal_width
;
88 m_tabs_total_width_minimum
= info
.minimum_width
;
92 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
93 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
94 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
98 page
->Hide(); // Most likely case is that this new page is not the active tab
99 page
->SetArtProvider(m_art
);
101 if(m_pages
.GetCount() == 1)
103 SetActivePage((size_t)0);
107 bool wxRibbonBar::DismissExpandedPanel()
109 if(m_current_page
== -1)
111 return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel();
114 void wxRibbonBar::ShowPanels(bool show
)
116 m_arePanelsShown
= show
;
117 SetMinSize(wxSize(GetSize().GetWidth(), DoGetBestSize().GetHeight()));
119 GetParent()->Layout();
122 void wxRibbonBar::SetWindowStyleFlag(long style
)
126 m_art
->SetFlags(style
);
129 long wxRibbonBar::GetWindowStyleFlag() const
134 bool wxRibbonBar::Realize()
138 wxClientDC
dcTemp(this);
139 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
140 size_t numtabs
= m_pages
.GetCount();
142 for(i
= 0; i
< numtabs
; ++i
)
144 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
147 RepositionPage(info
.page
);
148 if(!info
.page
->Realize())
152 wxString label
= wxEmptyString
;
153 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
154 label
= info
.page
->GetLabel();
155 wxBitmap icon
= wxNullBitmap
;
156 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
157 icon
= info
.page
->GetIcon();
158 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
160 &info
.small_begin_need_separator_width
,
161 &info
.small_must_have_separator_width
,
162 &info
.minimum_width
);
166 m_tabs_total_width_ideal
= info
.ideal_width
;
167 m_tabs_total_width_minimum
= info
.minimum_width
;
171 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
172 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
175 m_tab_height
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
);
177 RecalculateMinSize();
178 RecalculateTabSizes();
184 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
)
188 int hovered_page
= -1;
189 bool refresh_tabs
= false;
192 // It is quite likely that the mouse moved a small amount and is still over the same tab
193 if(m_current_hovered_page
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
))
195 hovered_page
= m_current_hovered_page
;
196 // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
197 if(m_tab_scroll_buttons_shown
)
199 if(x
>= m_tab_scroll_right_button_rect
.GetX() || x
< m_tab_scroll_left_button_rect
.GetRight())
207 HitTestTabs(evt
.GetPosition(), &hovered_page
);
210 if(hovered_page
!= m_current_hovered_page
)
212 if(m_current_hovered_page
!= -1)
214 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
216 m_current_hovered_page
= hovered_page
;
217 if(m_current_hovered_page
!= -1)
219 m_pages
.Item((int)m_current_hovered_page
).hovered
= true;
223 if(m_tab_scroll_buttons_shown
)
225 #define SET_FLAG(variable, flag) \
226 { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
227 #define UNSET_FLAG(variable, flag) \
228 { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
230 if(m_tab_scroll_left_button_rect
.Contains(x
, y
))
231 SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
233 UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
235 if(m_tab_scroll_right_button_rect
.Contains(x
, y
))
236 SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
238 UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
248 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
250 // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
251 // can leave the window quickly and leave a tab in the hovered state.
252 bool refresh_tabs
= false;
253 if(m_current_hovered_page
!= -1)
255 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
256 m_current_hovered_page
= -1;
259 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
261 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
264 if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
266 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
275 wxRibbonPage
* wxRibbonBar::GetPage(int n
)
277 if(n
< 0 || (size_t)n
>= m_pages
.GetCount())
279 return m_pages
.Item(n
).page
;
282 size_t wxRibbonBar::GetPageCount() const
284 return m_pages
.GetCount();
287 bool wxRibbonBar::IsPageShown(size_t page
) const
289 if (page
>= m_pages
.GetCount())
291 return m_pages
.Item(page
).shown
;
294 void wxRibbonBar::ShowPage(size_t page
, bool show
)
296 if(page
>= m_pages
.GetCount())
298 m_pages
.Item(page
).shown
= show
;
301 void wxRibbonBar::DeletePage(size_t n
)
303 if(n
< m_pages
.GetCount())
305 wxRibbonPage
*page
= m_pages
.Item(n
).page
;
307 // Schedule page object for destruction and not destroying directly
308 // as this function can be called in an event handler and page functions
309 // can be called afeter removing.
310 // Like in wxRibbonButtonBar::OnMouseUp
311 if(!wxTheApp
->IsScheduledForDestruction(page
))
313 wxTheApp
->ScheduleForDestruction(page
);
318 if(m_current_page
== static_cast<int>(n
))
322 if(m_pages
.GetCount() > 0)
324 if(n
>= m_pages
.GetCount())
326 SetActivePage(m_pages
.GetCount() - 1);
330 SetActivePage(n
- 1);
334 else if(m_current_page
> static_cast<int>(n
))
341 void wxRibbonBar::ClearPages()
344 for(i
=0; i
<m_pages
.GetCount(); i
++)
346 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
347 // Schedule page object for destruction and not destroying directly
348 // as this function can be called in an event handler and page functions
349 // can be called afeter removing.
350 // Like in wxRibbonButtonBar::OnMouseUp
351 if(!wxTheApp
->IsScheduledForDestruction(page
))
353 wxTheApp
->ScheduleForDestruction(page
);
362 bool wxRibbonBar::SetActivePage(size_t page
)
364 if(m_current_page
== (int)page
)
369 if(page
>= m_pages
.GetCount())
374 if(m_current_page
!= -1)
376 m_pages
.Item((size_t)m_current_page
).active
= false;
377 m_pages
.Item((size_t)m_current_page
).page
->Hide();
379 m_current_page
= (int)page
;
380 m_pages
.Item(page
).active
= true;
381 m_pages
.Item(page
).shown
= true;
383 wxRibbonPage
* wnd
= m_pages
.Item(page
).page
;
393 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
)
395 size_t numpages
= m_pages
.GetCount();
397 for(i
= 0; i
< numpages
; ++i
)
399 if(m_pages
.Item(i
).page
== page
)
401 return SetActivePage(i
);
407 int wxRibbonBar::GetPageNumber(wxRibbonPage
* page
) const
409 size_t numpages
= m_pages
.GetCount();
410 for(size_t i
= 0; i
< numpages
; ++i
)
412 if(m_pages
.Item(i
).page
== page
)
421 int wxRibbonBar::GetActivePage() const
423 return m_current_page
;
426 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
)
428 m_tab_margin_left
= left
;
429 m_tab_margin_right
= right
;
431 RecalculateTabSizes();
434 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo
**first
, wxRibbonPageTabInfo
**second
)
436 return (**first
).small_must_have_separator_width
- (**second
).small_must_have_separator_width
;
439 void wxRibbonBar::RecalculateTabSizes()
441 size_t numtabs
= m_pages
.GetCount();
446 int width
= GetSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
;
447 int tabsep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
448 int x
= m_tab_margin_left
;
451 if(width
>= m_tabs_total_width_ideal
)
453 // Simple case: everything at ideal width
455 for(i
= 0; i
< numtabs
; ++i
)
457 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
462 info
.rect
.width
= info
.ideal_width
;
463 info
.rect
.height
= m_tab_height
;
464 x
+= info
.rect
.width
+ tabsep
;
466 m_tab_scroll_buttons_shown
= false;
467 m_tab_scroll_left_button_rect
.SetWidth(0);
468 m_tab_scroll_right_button_rect
.SetWidth(0);
470 else if(width
< m_tabs_total_width_minimum
)
472 // Simple case: everything minimum with scrollbar
474 for(i
= 0; i
< numtabs
; ++i
)
476 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
481 info
.rect
.width
= info
.minimum_width
;
482 info
.rect
.height
= m_tab_height
;
483 x
+= info
.rect
.width
+ tabsep
;
485 if(!m_tab_scroll_buttons_shown
)
487 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
488 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
489 m_tab_scroll_buttons_shown
= true;
492 wxClientDC
temp_dc(this);
493 m_tab_scroll_left_button_rect
.SetWidth(m_art
->GetScrollButtonMinimumSize(temp_dc
, this, wxRIBBON_SCROLL_BTN_LEFT
| wxRIBBON_SCROLL_BTN_NORMAL
| wxRIBBON_SCROLL_BTN_FOR_TABS
).GetWidth());
494 m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
);
495 m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
);
496 m_tab_scroll_left_button_rect
.SetY(0);
497 m_tab_scroll_right_button_rect
.SetWidth(m_art
->GetScrollButtonMinimumSize(temp_dc
, this, wxRIBBON_SCROLL_BTN_RIGHT
| wxRIBBON_SCROLL_BTN_NORMAL
| wxRIBBON_SCROLL_BTN_FOR_TABS
).GetWidth());
498 m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
);
499 m_tab_scroll_right_button_rect
.SetX(GetClientSize().GetWidth() - m_tab_margin_right
- m_tab_scroll_right_button_rect
.GetWidth());
500 m_tab_scroll_right_button_rect
.SetY(0);
502 if(m_tab_scroll_amount
== 0)
504 m_tab_scroll_left_button_rect
.SetWidth(0);
506 else if(m_tab_scroll_amount
+ width
>= m_tabs_total_width_minimum
)
508 m_tab_scroll_amount
= m_tabs_total_width_minimum
- width
;
509 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
510 m_tab_scroll_right_button_rect
.SetWidth(0);
512 for(i
= 0; i
< numtabs
; ++i
)
514 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
517 info
.rect
.x
-= m_tab_scroll_amount
;
522 m_tab_scroll_buttons_shown
= false;
523 m_tab_scroll_left_button_rect
.SetWidth(0);
524 m_tab_scroll_right_button_rect
.SetWidth(0);
525 // Complex case: everything sized such that: minimum <= width < ideal
528 1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
529 2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
530 3) Uniformly reduce all tabs down to their minimum width
532 int smallest_tab_width
= INT_MAX
;
533 int total_small_width
= tabsep
* (numtabs
- 1);
535 for(i
= 0; i
< numtabs
; ++i
)
537 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
540 if(info
.small_must_have_separator_width
< smallest_tab_width
)
542 smallest_tab_width
= info
.small_must_have_separator_width
;
544 total_small_width
+= info
.small_must_have_separator_width
;
546 if(width
>= total_small_width
)
549 int total_delta
= m_tabs_total_width_ideal
- total_small_width
;
550 total_small_width
-= tabsep
* (numtabs
- 1);
551 width
-= tabsep
* (numtabs
- 1);
552 for(i
= 0; i
< numtabs
; ++i
)
554 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
557 int delta
= info
.ideal_width
- info
.small_must_have_separator_width
;
560 info
.rect
.width
= info
.small_must_have_separator_width
+ delta
* (width
- total_small_width
) / total_delta
;
561 info
.rect
.height
= m_tab_height
;
563 x
+= info
.rect
.width
+ tabsep
;
564 total_delta
-= delta
;
565 total_small_width
-= info
.small_must_have_separator_width
;
566 width
-= info
.rect
.width
;
571 total_small_width
= tabsep
* (numtabs
- 1);
572 for(i
= 0; i
< numtabs
; ++i
)
574 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
577 if(info
.minimum_width
< smallest_tab_width
)
579 total_small_width
+= smallest_tab_width
;
583 total_small_width
+= info
.minimum_width
;
586 if(width
>= total_small_width
)
589 wxRibbonPageTabInfoArray sorted_pages
;
590 for(i
= 0; i
< numtabs
; ++i
)
592 // Sneaky obj array trickery to not copy the tab descriptors
593 if (!m_pages
.Item(i
).shown
)
595 sorted_pages
.Add(&m_pages
.Item(i
));
597 sorted_pages
.Sort(OrderPageTabInfoBySmallWidthAsc
);
598 width
-= tabsep
* (numtabs
- 1);
599 for(i
= 0; i
< numtabs
; ++i
)
601 wxRibbonPageTabInfo
& info
= sorted_pages
.Item(i
);
604 if(info
.small_must_have_separator_width
* (int)(numtabs
- i
) <= width
)
606 info
.rect
.width
= info
.small_must_have_separator_width
;;
610 info
.rect
.width
= width
/ (numtabs
- i
);
612 width
-= info
.rect
.width
;
614 for(i
= 0; i
< numtabs
; ++i
)
616 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
621 info
.rect
.height
= m_tab_height
;
622 x
+= info
.rect
.width
+ tabsep
;
623 sorted_pages
.Detach(numtabs
- (i
+ 1));
629 total_small_width
= (smallest_tab_width
+ tabsep
) * numtabs
- tabsep
;
630 int total_delta
= total_small_width
- m_tabs_total_width_minimum
;
631 total_small_width
= m_tabs_total_width_minimum
- tabsep
* (numtabs
- 1);
632 width
-= tabsep
* (numtabs
- 1);
633 for(i
= 0; i
< numtabs
; ++i
)
635 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
638 int delta
= smallest_tab_width
- info
.minimum_width
;
641 info
.rect
.width
= info
.minimum_width
+ delta
* (width
- total_small_width
) / total_delta
;
642 info
.rect
.height
= m_tab_height
;
644 x
+= info
.rect
.width
+ tabsep
;
645 total_delta
-= delta
;
646 total_small_width
-= info
.minimum_width
;
647 width
-= info
.rect
.width
;
654 wxRibbonBar::wxRibbonBar()
657 m_tabs_total_width_ideal
= 0;
658 m_tabs_total_width_minimum
= 0;
659 m_tab_margin_left
= 0;
660 m_tab_margin_right
= 0;
662 m_tab_scroll_amount
= 0;
664 m_current_hovered_page
= -1;
665 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
666 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
667 m_tab_scroll_buttons_shown
= false;
668 m_arePanelsShown
= true;
671 wxRibbonBar::wxRibbonBar(wxWindow
* parent
,
676 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
681 wxRibbonBar::~wxRibbonBar()
683 SetArtProvider(NULL
);
686 bool wxRibbonBar::Create(wxWindow
* parent
,
692 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
700 void wxRibbonBar::CommonInit(long style
)
702 SetName(wxT("wxRibbonBar"));
705 m_tabs_total_width_ideal
= 0;
706 m_tabs_total_width_minimum
= 0;
707 m_tab_margin_left
= 50;
708 m_tab_margin_right
= 20;
709 m_tab_height
= 20; // initial guess
710 m_tab_scroll_amount
= 0;
712 m_current_hovered_page
= -1;
713 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
714 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
715 m_tab_scroll_buttons_shown
= false;
716 m_arePanelsShown
= true;
720 SetArtProvider(new wxRibbonDefaultArtProvider
);
722 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
725 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
)
727 wxRibbonArtProvider
*old
= m_art
;
732 art
->SetFlags(m_flags
);
734 size_t numpages
= m_pages
.GetCount();
736 for(i
= 0; i
< numpages
; ++i
)
738 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
739 if(page
->GetArtProvider() != art
)
741 page
->SetArtProvider(art
);
748 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
750 wxAutoBufferedPaintDC
dc(this);
752 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
)
754 // Nothing to do in the tab area, and the page area is handled by the active page
758 DoEraseBackground(dc
);
760 size_t numtabs
= m_pages
.GetCount();
761 double sep_visibility
= 0.0;
762 bool draw_sep
= false;
763 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
764 if(m_tab_scroll_buttons_shown
)
766 tabs_rect
.x
+= m_tab_scroll_left_button_rect
.GetWidth();
767 tabs_rect
.width
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth();
770 for(i
= 0; i
< numtabs
; ++i
)
772 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
776 dc
.DestroyClippingRegion();
777 if(m_tab_scroll_buttons_shown
)
779 if(!tabs_rect
.Intersects(info
.rect
))
781 dc
.SetClippingRegion(tabs_rect
);
783 dc
.SetClippingRegion(info
.rect
);
784 m_art
->DrawTab(dc
, this, info
);
786 if(info
.rect
.width
< info
.small_begin_need_separator_width
)
789 if(info
.rect
.width
< info
.small_must_have_separator_width
)
791 sep_visibility
+= 1.0;
795 sep_visibility
+= (double)(info
.small_begin_need_separator_width
- info
.rect
.width
) / (double)(info
.small_begin_need_separator_width
- info
.small_must_have_separator_width
);
801 wxRect rect
= m_pages
.Item(0).rect
;
802 rect
.width
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
803 sep_visibility
/= (double)numtabs
;
804 for(i
= 0; i
< numtabs
- 1; ++i
)
806 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
809 rect
.x
= info
.rect
.x
+ info
.rect
.width
;
811 if(m_tab_scroll_buttons_shown
&& !tabs_rect
.Intersects(rect
))
816 dc
.DestroyClippingRegion();
817 dc
.SetClippingRegion(rect
);
818 m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
);
821 if(m_tab_scroll_buttons_shown
)
823 dc
.DestroyClippingRegion();
824 if(m_tab_scroll_left_button_rect
.GetWidth() != 0)
826 m_art
->DrawScrollButton(dc
, this, m_tab_scroll_left_button_rect
, wxRIBBON_SCROLL_BTN_LEFT
| m_tab_scroll_left_button_state
| wxRIBBON_SCROLL_BTN_FOR_TABS
);
828 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
830 m_art
->DrawScrollButton(dc
, this, m_tab_scroll_right_button_rect
, wxRIBBON_SCROLL_BTN_RIGHT
| m_tab_scroll_right_button_state
| wxRIBBON_SCROLL_BTN_FOR_TABS
);
835 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
837 // Background painting done in main paint handler to reduce screen flicker
840 void wxRibbonBar::DoEraseBackground(wxDC
& dc
)
842 wxRect
tabs(GetSize());
843 tabs
.height
= m_tab_height
;
844 m_art
->DrawTabCtrlBackground(dc
, this, tabs
);
847 void wxRibbonBar::OnSize(wxSizeEvent
& evt
)
849 RecalculateTabSizes();
850 if(m_current_page
!= -1)
852 RepositionPage(m_pages
.Item(m_current_page
).page
);
859 void wxRibbonBar::RepositionPage(wxRibbonPage
*page
)
863 page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h
- m_tab_height
);
866 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
)
868 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
869 if(m_tab_scroll_buttons_shown
)
871 tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth());
872 tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth());
874 if(tabs_rect
.Contains(position
))
876 size_t numtabs
= m_pages
.GetCount();
878 for(i
= 0; i
< numtabs
; ++i
)
880 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
883 if(info
.rect
.Contains(position
))
900 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
)
902 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
903 if(tab
&& tab
!= &m_pages
.Item(m_current_page
))
905 wxRibbonBarEvent
query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
);
906 query
.SetEventObject(this);
907 ProcessWindowEvent(query
);
908 if(query
.IsAllowed())
910 SetActivePage(query
.GetPage());
912 wxRibbonBarEvent
notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
);
913 notification
.SetEventObject(this);
914 ProcessWindowEvent(notification
);
919 if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition()))
921 m_tab_scroll_left_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
924 else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition()))
926 m_tab_scroll_right_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
932 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
))
934 if(!m_tab_scroll_buttons_shown
)
940 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
944 else if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
950 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
951 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
952 ScrollTabBar(amount
* 8);
956 void wxRibbonBar::ScrollTabBar(int amount
)
958 bool show_left
= true;
959 bool show_right
= true;
960 if(m_tab_scroll_amount
+ amount
<= 0)
962 amount
= -m_tab_scroll_amount
;
965 else if(m_tab_scroll_amount
+ amount
+ (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
) >= m_tabs_total_width_minimum
)
967 amount
= m_tabs_total_width_minimum
- m_tab_scroll_amount
- (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
);
974 m_tab_scroll_amount
+= amount
;
975 size_t numtabs
= m_pages
.GetCount();
977 for(i
= 0; i
< numtabs
; ++i
)
979 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
982 info
.rect
.SetX(info
.rect
.GetX() - amount
);
984 if(show_right
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) ||
985 show_left
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0))
987 wxClientDC
temp_dc(this);
990 m_tab_scroll_left_button_rect
.SetWidth(m_art
->GetScrollButtonMinimumSize(temp_dc
, this, wxRIBBON_SCROLL_BTN_LEFT
| wxRIBBON_SCROLL_BTN_NORMAL
| wxRIBBON_SCROLL_BTN_FOR_TABS
).GetWidth());
994 m_tab_scroll_left_button_rect
.SetWidth(0);
999 if(m_tab_scroll_right_button_rect
.GetWidth() == 0)
1001 m_tab_scroll_right_button_rect
.SetWidth(m_art
->GetScrollButtonMinimumSize(temp_dc
, this, wxRIBBON_SCROLL_BTN_RIGHT
| wxRIBBON_SCROLL_BTN_NORMAL
| wxRIBBON_SCROLL_BTN_FOR_TABS
).GetWidth());
1002 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth());
1007 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
1009 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
1010 m_tab_scroll_right_button_rect
.SetWidth(0);
1018 void wxRibbonBar::RefreshTabBar()
1020 wxRect
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
);
1021 Refresh(false, &tab_rect
);
1024 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
)
1026 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
);
1029 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
)
1031 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
);
1034 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
)
1036 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
);
1039 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
)
1041 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
);
1044 void wxRibbonBar::OnMouseDoubleClick(wxMouseEvent
& evt
)
1046 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_LEFT_DCLICK
);
1049 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
)
1051 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
1054 wxRibbonBarEvent
notification(tab_event_type
, GetId(), tab
->page
);
1055 notification
.SetEventObject(this);
1056 ProcessWindowEvent(notification
);
1060 void wxRibbonBar::RecalculateMinSize()
1062 wxSize
min_size(wxDefaultCoord
, wxDefaultCoord
);
1063 size_t numtabs
= m_pages
.GetCount();
1066 min_size
= m_pages
.Item(0).page
->GetMinSize();
1069 for(i
= 1; i
< numtabs
; ++i
)
1071 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1074 wxSize page_min
= info
.page
->GetMinSize();
1076 min_size
.x
= wxMax(min_size
.x
, page_min
.x
);
1077 min_size
.y
= wxMax(min_size
.y
, page_min
.y
);
1080 if(min_size
.y
!= wxDefaultCoord
)
1082 // TODO: Decide on best course of action when min height is unspecified
1083 // - should we specify it to the tab minimum, or leave it unspecified?
1084 min_size
.IncBy(0, m_tab_height
);
1087 m_minWidth
= min_size
.GetWidth();
1088 m_minHeight
= m_arePanelsShown
? min_size
.GetHeight() : m_tab_height
;
1091 wxSize
wxRibbonBar::DoGetBestSize() const
1094 if(m_current_page
!= -1)
1096 best
= m_pages
.Item(m_current_page
).page
->GetBestSize();
1098 if(best
.GetHeight() == wxDefaultCoord
)
1100 best
.SetHeight(m_tab_height
);
1104 best
.IncBy(0, m_tab_height
);
1106 if(!m_arePanelsShown
)
1108 best
.SetHeight(m_tab_height
);
1113 #endif // wxUSE_RIBBON