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"
24 #include "wx/vector.h"
30 #include "wx/msw/private.h"
33 #include "wx/arrimpl.cpp"
35 WX_DEFINE_USER_EXPORTED_OBJARRAY(wxRibbonPageTabInfoArray
)
37 wxDEFINE_EVENT(wxEVT_RIBBONBAR_PAGE_CHANGED
, wxRibbonBarEvent
);
38 wxDEFINE_EVENT(wxEVT_RIBBONBAR_PAGE_CHANGING
, wxRibbonBarEvent
);
39 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_MIDDLE_DOWN
, wxRibbonBarEvent
);
40 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_MIDDLE_UP
, wxRibbonBarEvent
);
41 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_RIGHT_DOWN
, wxRibbonBarEvent
);
42 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_RIGHT_UP
, wxRibbonBarEvent
);
43 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TAB_LEFT_DCLICK
, wxRibbonBarEvent
);
44 wxDEFINE_EVENT(wxEVT_RIBBONBAR_TOGGLED
, wxRibbonBarEvent
);
45 wxDEFINE_EVENT(wxEVT_RIBBONBAR_HELP_CLICK
, wxRibbonBarEvent
);
47 IMPLEMENT_CLASS(wxRibbonBar
, wxRibbonControl
)
48 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent
, wxNotifyEvent
)
50 BEGIN_EVENT_TABLE(wxRibbonBar
, wxRibbonControl
)
51 EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground
)
52 EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave
)
53 EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown
)
54 EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp
)
55 EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown
)
56 EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp
)
57 EVT_MOTION(wxRibbonBar::OnMouseMove
)
58 EVT_PAINT(wxRibbonBar::OnPaint
)
59 EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown
)
60 EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp
)
61 EVT_LEFT_DCLICK(wxRibbonBar::OnMouseDoubleClick
)
62 EVT_SIZE(wxRibbonBar::OnSize
)
63 EVT_KILL_FOCUS(wxRibbonBar::OnKillFocus
)
66 void wxRibbonBar::AddPage(wxRibbonPage
*page
)
68 wxRibbonPageTabInfo info
;
73 info
.highlight
= false;
75 // info.rect not set (intentional)
77 wxClientDC
dcTemp(this);
78 wxString label
= wxEmptyString
;
79 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
80 label
= page
->GetLabel();
81 wxBitmap icon
= wxNullBitmap
;
82 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
83 icon
= page
->GetIcon();
84 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
86 &info
.small_begin_need_separator_width
,
87 &info
.small_must_have_separator_width
,
92 m_tabs_total_width_ideal
= info
.ideal_width
;
93 m_tabs_total_width_minimum
= info
.minimum_width
;
97 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
98 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
99 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
103 page
->Hide(); // Most likely case is that this new page is not the active tab
104 page
->SetArtProvider(m_art
);
106 if(m_pages
.GetCount() == 1)
108 SetActivePage((size_t)0);
112 bool wxRibbonBar::DismissExpandedPanel()
114 if(m_current_page
== -1)
116 return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel();
119 void wxRibbonBar::ShowPanels(bool show
)
121 m_arePanelsShown
= show
;
122 SetMinSize(wxSize(GetSize().GetWidth(), DoGetBestSize().GetHeight()));
124 GetParent()->Layout();
127 void wxRibbonBar::SetWindowStyleFlag(long style
)
131 m_art
->SetFlags(style
);
134 long wxRibbonBar::GetWindowStyleFlag() const
139 bool wxRibbonBar::Realize()
143 wxClientDC
dcTemp(this);
144 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
145 size_t numtabs
= m_pages
.GetCount();
147 for(i
= 0; i
< numtabs
; ++i
)
149 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
152 RepositionPage(info
.page
);
153 if(!info
.page
->Realize())
157 wxString label
= wxEmptyString
;
158 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
159 label
= info
.page
->GetLabel();
160 wxBitmap icon
= wxNullBitmap
;
161 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
162 icon
= info
.page
->GetIcon();
163 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
165 &info
.small_begin_need_separator_width
,
166 &info
.small_must_have_separator_width
,
167 &info
.minimum_width
);
171 m_tabs_total_width_ideal
= info
.ideal_width
;
172 m_tabs_total_width_minimum
= info
.minimum_width
;
176 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
177 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
180 m_tab_height
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
);
182 RecalculateMinSize();
183 RecalculateTabSizes();
189 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
)
193 int hovered_page
= -1;
194 bool refresh_tabs
= false;
197 // It is quite likely that the mouse moved a small amount and is still over the same tab
198 if(m_current_hovered_page
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
))
200 hovered_page
= m_current_hovered_page
;
201 // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
202 if(m_tab_scroll_buttons_shown
)
204 if(x
>= m_tab_scroll_right_button_rect
.GetX() || x
< m_tab_scroll_left_button_rect
.GetRight())
212 HitTestTabs(evt
.GetPosition(), &hovered_page
);
215 if(hovered_page
!= m_current_hovered_page
)
217 if(m_current_hovered_page
!= -1)
219 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
221 m_current_hovered_page
= hovered_page
;
222 if(m_current_hovered_page
!= -1)
224 m_pages
.Item((int)m_current_hovered_page
).hovered
= true;
228 if(m_tab_scroll_buttons_shown
)
230 #define SET_FLAG(variable, flag) \
231 { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
232 #define UNSET_FLAG(variable, flag) \
233 { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
235 if(m_tab_scroll_left_button_rect
.Contains(x
, y
))
236 SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
238 UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
240 if(m_tab_scroll_right_button_rect
.Contains(x
, y
))
241 SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
243 UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
251 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
252 HitTestRibbonButton(m_toggle_button_rect
, evt
.GetPosition(), m_toggle_button_hovered
);
253 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
254 HitTestRibbonButton(m_help_button_rect
, evt
.GetPosition(), m_help_button_hovered
);
257 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
259 // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
260 // can leave the window quickly and leave a tab in the hovered state.
261 bool refresh_tabs
= false;
262 if(m_current_hovered_page
!= -1)
264 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
265 m_current_hovered_page
= -1;
268 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
270 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
273 if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
275 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
282 if(m_toggle_button_hovered
)
284 m_bar_hovered
= false;
285 m_toggle_button_hovered
= false;
288 if ( m_help_button_hovered
)
290 m_help_button_hovered
= false;
291 m_bar_hovered
= false;
296 wxRibbonPage
* wxRibbonBar::GetPage(int n
)
298 if(n
< 0 || (size_t)n
>= m_pages
.GetCount())
300 return m_pages
.Item(n
).page
;
303 size_t wxRibbonBar::GetPageCount() const
305 return m_pages
.GetCount();
308 bool wxRibbonBar::IsPageShown(size_t page
) const
310 if (page
>= m_pages
.GetCount())
312 return m_pages
.Item(page
).shown
;
315 void wxRibbonBar::ShowPage(size_t page
, bool show
)
317 if(page
>= m_pages
.GetCount())
319 m_pages
.Item(page
).shown
= show
;
322 bool wxRibbonBar::IsPageHighlighted(size_t page
) const
324 if (page
>= m_pages
.GetCount())
326 return m_pages
.Item(page
).highlight
;
329 void wxRibbonBar::AddPageHighlight(size_t page
, bool highlight
)
331 if(page
>= m_pages
.GetCount())
333 m_pages
.Item(page
).highlight
= highlight
;
336 void wxRibbonBar::DeletePage(size_t n
)
338 if(n
< m_pages
.GetCount())
340 wxRibbonPage
*page
= m_pages
.Item(n
).page
;
342 // Schedule page object for destruction and not destroying directly
343 // as this function can be called in an event handler and page functions
344 // can be called afeter removing.
345 // Like in wxRibbonButtonBar::OnMouseUp
346 if(!wxTheApp
->IsScheduledForDestruction(page
))
348 wxTheApp
->ScheduleForDestruction(page
);
353 if(m_current_page
== static_cast<int>(n
))
357 if(m_pages
.GetCount() > 0)
359 if(n
>= m_pages
.GetCount())
361 SetActivePage(m_pages
.GetCount() - 1);
365 SetActivePage(n
- 1);
369 else if(m_current_page
> static_cast<int>(n
))
376 void wxRibbonBar::ClearPages()
379 for(i
=0; i
<m_pages
.GetCount(); i
++)
381 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
382 // Schedule page object for destruction and not destroying directly
383 // as this function can be called in an event handler and page functions
384 // can be called afeter removing.
385 // Like in wxRibbonButtonBar::OnMouseUp
386 if(!wxTheApp
->IsScheduledForDestruction(page
))
388 wxTheApp
->ScheduleForDestruction(page
);
397 bool wxRibbonBar::SetActivePage(size_t page
)
399 if(m_current_page
== (int)page
)
404 if(page
>= m_pages
.GetCount())
409 if(m_current_page
!= -1)
411 m_pages
.Item((size_t)m_current_page
).active
= false;
412 m_pages
.Item((size_t)m_current_page
).page
->Hide();
414 m_current_page
= (int)page
;
415 m_pages
.Item(page
).active
= true;
416 m_pages
.Item(page
).shown
= true;
418 wxRibbonPage
* wnd
= m_pages
.Item(page
).page
;
428 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
)
430 size_t numpages
= m_pages
.GetCount();
432 for(i
= 0; i
< numpages
; ++i
)
434 if(m_pages
.Item(i
).page
== page
)
436 return SetActivePage(i
);
442 int wxRibbonBar::GetPageNumber(wxRibbonPage
* page
) const
444 size_t numpages
= m_pages
.GetCount();
445 for(size_t i
= 0; i
< numpages
; ++i
)
447 if(m_pages
.Item(i
).page
== page
)
456 int wxRibbonBar::GetActivePage() const
458 return m_current_page
;
461 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
)
463 m_tab_margin_left
= left
;
464 m_tab_margin_right
= right
;
466 RecalculateTabSizes();
469 struct PageComparedBySmallWidthAsc
471 wxEXPLICIT
PageComparedBySmallWidthAsc(wxRibbonPageTabInfo
* page
)
476 bool operator<(const PageComparedBySmallWidthAsc
& other
) const
478 return m_page
->small_must_have_separator_width
479 < other
.m_page
->small_must_have_separator_width
;
482 wxRibbonPageTabInfo
*m_page
;
485 void wxRibbonBar::RecalculateTabSizes()
487 size_t numtabs
= m_pages
.GetCount();
492 int width
= GetSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
;
493 int tabsep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
494 int x
= m_tab_margin_left
;
497 if(width
>= m_tabs_total_width_ideal
)
499 // Simple case: everything at ideal width
501 for(i
= 0; i
< numtabs
; ++i
)
503 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
508 info
.rect
.width
= info
.ideal_width
;
509 info
.rect
.height
= m_tab_height
;
510 x
+= info
.rect
.width
+ tabsep
;
512 m_tab_scroll_buttons_shown
= false;
513 m_tab_scroll_left_button_rect
.SetWidth(0);
514 m_tab_scroll_right_button_rect
.SetWidth(0);
516 else if(width
< m_tabs_total_width_minimum
)
518 // Simple case: everything minimum with scrollbar
520 for(i
= 0; i
< numtabs
; ++i
)
522 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
527 info
.rect
.width
= info
.minimum_width
;
528 info
.rect
.height
= m_tab_height
;
529 x
+= info
.rect
.width
+ tabsep
;
531 if(!m_tab_scroll_buttons_shown
)
533 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
534 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
535 m_tab_scroll_buttons_shown
= true;
538 wxClientDC
temp_dc(this);
539 int right_button_pos
= GetClientSize().GetWidth() - m_tab_margin_right
- m_tab_scroll_right_button_rect
.GetWidth();
540 if ( right_button_pos
< m_tab_margin_left
)
541 right_button_pos
= m_tab_margin_left
;
543 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());
544 m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
);
545 m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
);
546 m_tab_scroll_left_button_rect
.SetY(0);
547 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());
548 m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
);
549 m_tab_scroll_right_button_rect
.SetX(right_button_pos
);
550 m_tab_scroll_right_button_rect
.SetY(0);
552 if(m_tab_scroll_amount
== 0)
554 m_tab_scroll_left_button_rect
.SetWidth(0);
556 else if(m_tab_scroll_amount
+ width
>= m_tabs_total_width_minimum
)
558 m_tab_scroll_amount
= m_tabs_total_width_minimum
- width
;
559 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
560 m_tab_scroll_right_button_rect
.SetWidth(0);
562 for(i
= 0; i
< numtabs
; ++i
)
564 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
567 info
.rect
.x
-= m_tab_scroll_amount
;
572 m_tab_scroll_buttons_shown
= false;
573 m_tab_scroll_left_button_rect
.SetWidth(0);
574 m_tab_scroll_right_button_rect
.SetWidth(0);
575 // Complex case: everything sized such that: minimum <= width < ideal
578 1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
579 2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
580 3) Uniformly reduce all tabs down to their minimum width
582 int smallest_tab_width
= INT_MAX
;
583 int total_small_width
= tabsep
* (numtabs
- 1);
585 for(i
= 0; i
< numtabs
; ++i
)
587 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
590 if(info
.small_must_have_separator_width
< smallest_tab_width
)
592 smallest_tab_width
= info
.small_must_have_separator_width
;
594 total_small_width
+= info
.small_must_have_separator_width
;
596 if(width
>= total_small_width
)
599 int total_delta
= m_tabs_total_width_ideal
- total_small_width
;
600 total_small_width
-= tabsep
* (numtabs
- 1);
601 width
-= tabsep
* (numtabs
- 1);
602 for(i
= 0; i
< numtabs
; ++i
)
604 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
607 int delta
= info
.ideal_width
- info
.small_must_have_separator_width
;
610 info
.rect
.width
= info
.small_must_have_separator_width
+ delta
* (width
- total_small_width
) / total_delta
;
611 info
.rect
.height
= m_tab_height
;
613 x
+= info
.rect
.width
+ tabsep
;
614 total_delta
-= delta
;
615 total_small_width
-= info
.small_must_have_separator_width
;
616 width
-= info
.rect
.width
;
621 total_small_width
= tabsep
* (numtabs
- 1);
622 for(i
= 0; i
< numtabs
; ++i
)
624 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
627 if(info
.minimum_width
< smallest_tab_width
)
629 total_small_width
+= smallest_tab_width
;
633 total_small_width
+= info
.minimum_width
;
636 if(width
>= total_small_width
)
639 wxVector
<PageComparedBySmallWidthAsc
> sorted_pages
;
640 sorted_pages
.reserve(numtabs
);
641 for ( i
= 0; i
< numtabs
; ++i
)
642 sorted_pages
.push_back(PageComparedBySmallWidthAsc(&m_pages
.Item(i
)));
644 wxVectorSort(sorted_pages
);
645 width
-= tabsep
* (numtabs
- 1);
646 for(i
= 0; i
< numtabs
; ++i
)
648 wxRibbonPageTabInfo
* info
= sorted_pages
[i
].m_page
;
651 if(info
->small_must_have_separator_width
* (int)(numtabs
- i
) <= width
)
653 info
->rect
.width
= info
->small_must_have_separator_width
;;
657 info
->rect
.width
= width
/ (numtabs
- i
);
659 width
-= info
->rect
.width
;
661 for(i
= 0; i
< numtabs
; ++i
)
663 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
668 info
.rect
.height
= m_tab_height
;
669 x
+= info
.rect
.width
+ tabsep
;
675 total_small_width
= (smallest_tab_width
+ tabsep
) * numtabs
- tabsep
;
676 int total_delta
= total_small_width
- m_tabs_total_width_minimum
;
677 total_small_width
= m_tabs_total_width_minimum
- tabsep
* (numtabs
- 1);
678 width
-= tabsep
* (numtabs
- 1);
679 for(i
= 0; i
< numtabs
; ++i
)
681 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
684 int delta
= smallest_tab_width
- info
.minimum_width
;
687 info
.rect
.width
= info
.minimum_width
+ delta
* (width
- total_small_width
) / total_delta
;
688 info
.rect
.height
= m_tab_height
;
690 x
+= info
.rect
.width
+ tabsep
;
691 total_delta
-= delta
;
692 total_small_width
-= info
.minimum_width
;
693 width
-= info
.rect
.width
;
700 wxRibbonBar::wxRibbonBar()
703 m_tabs_total_width_ideal
= 0;
704 m_tabs_total_width_minimum
= 0;
705 m_tab_margin_left
= 0;
706 m_tab_margin_right
= 0;
708 m_tab_scroll_amount
= 0;
710 m_current_hovered_page
= -1;
711 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
712 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
713 m_tab_scroll_buttons_shown
= false;
714 m_arePanelsShown
= true;
715 m_help_button_hovered
= false;
718 wxRibbonBar::wxRibbonBar(wxWindow
* parent
,
723 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
728 wxRibbonBar::~wxRibbonBar()
730 SetArtProvider(NULL
);
733 bool wxRibbonBar::Create(wxWindow
* parent
,
739 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
747 void wxRibbonBar::CommonInit(long style
)
749 SetName(wxT("wxRibbonBar"));
752 m_tabs_total_width_ideal
= 0;
753 m_tabs_total_width_minimum
= 0;
754 m_tab_margin_left
= 50;
755 m_tab_margin_right
= 20;
756 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
757 m_tab_margin_right
+= 20;
758 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
759 m_tab_margin_right
+= 20;
760 m_tab_height
= 20; // initial guess
761 m_tab_scroll_amount
= 0;
763 m_current_hovered_page
= -1;
764 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
765 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
766 m_tab_scroll_buttons_shown
= false;
767 m_arePanelsShown
= true;
771 SetArtProvider(new wxRibbonDefaultArtProvider
);
773 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
775 m_toggle_button_hovered
= false;
776 m_bar_hovered
= false;
778 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
781 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
)
783 wxRibbonArtProvider
*old
= m_art
;
788 art
->SetFlags(m_flags
);
790 size_t numpages
= m_pages
.GetCount();
792 for(i
= 0; i
< numpages
; ++i
)
794 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
795 if(page
->GetArtProvider() != art
)
797 page
->SetArtProvider(art
);
804 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
806 wxAutoBufferedPaintDC
dc(this);
808 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
)
810 // Nothing to do in the tab area, and the page area is handled by the active page
814 DoEraseBackground(dc
);
816 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
817 m_help_button_rect
= m_art
->GetRibbonHelpButtonArea(GetSize());
818 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
819 m_toggle_button_rect
= m_art
->GetBarToggleButtonArea(GetSize());
821 size_t numtabs
= m_pages
.GetCount();
822 double sep_visibility
= 0.0;
823 bool draw_sep
= false;
824 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
825 if(m_tab_scroll_buttons_shown
)
827 tabs_rect
.x
+= m_tab_scroll_left_button_rect
.GetWidth();
828 tabs_rect
.width
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth();
831 for(i
= 0; i
< numtabs
; ++i
)
833 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
837 dc
.DestroyClippingRegion();
838 if(m_tab_scroll_buttons_shown
)
840 if(!tabs_rect
.Intersects(info
.rect
))
842 dc
.SetClippingRegion(tabs_rect
);
844 dc
.SetClippingRegion(info
.rect
);
845 m_art
->DrawTab(dc
, this, info
);
847 if(info
.rect
.width
< info
.small_begin_need_separator_width
)
850 if(info
.rect
.width
< info
.small_must_have_separator_width
)
852 sep_visibility
+= 1.0;
856 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
);
862 wxRect rect
= m_pages
.Item(0).rect
;
863 rect
.width
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
864 sep_visibility
/= (double)numtabs
;
865 for(i
= 0; i
< numtabs
- 1; ++i
)
867 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
870 rect
.x
= info
.rect
.x
+ info
.rect
.width
;
872 if(m_tab_scroll_buttons_shown
&& !tabs_rect
.Intersects(rect
))
877 dc
.DestroyClippingRegion();
878 dc
.SetClippingRegion(rect
);
879 m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
);
882 if(m_tab_scroll_buttons_shown
)
884 if(m_tab_scroll_left_button_rect
.GetWidth() != 0)
886 dc
.DestroyClippingRegion();
887 dc
.SetClippingRegion(m_tab_scroll_left_button_rect
);
888 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
);
890 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
892 dc
.DestroyClippingRegion();
893 dc
.SetClippingRegion(m_tab_scroll_right_button_rect
);
894 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
);
898 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
899 m_art
->DrawHelpButton(dc
, this, m_help_button_rect
);
900 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
901 m_art
->DrawToggleButton(dc
, this, m_toggle_button_rect
, m_ribbon_state
);
905 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
907 // Background painting done in main paint handler to reduce screen flicker
910 void wxRibbonBar::DoEraseBackground(wxDC
& dc
)
912 wxRect
tabs(GetSize());
913 tabs
.height
= m_tab_height
;
914 m_art
->DrawTabCtrlBackground(dc
, this, tabs
);
917 void wxRibbonBar::OnSize(wxSizeEvent
& evt
)
919 RecalculateTabSizes();
920 if(m_current_page
!= -1)
922 RepositionPage(m_pages
.Item(m_current_page
).page
);
929 void wxRibbonBar::RepositionPage(wxRibbonPage
*page
)
933 page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h
- m_tab_height
);
936 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
)
938 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
939 if(m_tab_scroll_buttons_shown
)
941 tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth());
942 tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth());
944 if(tabs_rect
.Contains(position
))
946 size_t numtabs
= m_pages
.GetCount();
948 for(i
= 0; i
< numtabs
; ++i
)
950 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
953 if(info
.rect
.Contains(position
))
970 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
)
972 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
976 if ( m_ribbon_state
== wxRIBBON_BAR_MINIMIZED
)
979 m_ribbon_state
= wxRIBBON_BAR_EXPANDED
;
981 else if ( (tab
== &m_pages
.Item(m_current_page
)) && (m_ribbon_state
== wxRIBBON_BAR_EXPANDED
) )
984 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
989 if ( m_ribbon_state
== wxRIBBON_BAR_EXPANDED
)
992 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
995 if(tab
&& tab
!= &m_pages
.Item(m_current_page
))
997 wxRibbonBarEvent
query(wxEVT_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
);
998 query
.SetEventObject(this);
999 ProcessWindowEvent(query
);
1000 if(query
.IsAllowed())
1002 SetActivePage(query
.GetPage());
1004 wxRibbonBarEvent
notification(wxEVT_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
);
1005 notification
.SetEventObject(this);
1006 ProcessWindowEvent(notification
);
1009 else if(tab
== NULL
)
1011 if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition()))
1013 m_tab_scroll_left_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
1016 else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition()))
1018 m_tab_scroll_right_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
1023 wxPoint position
= evt
.GetPosition();
1025 if(position
.x
>= 0 && position
.y
>= 0)
1027 wxSize size
= GetSize();
1028 if(position
.x
< size
.GetWidth() && position
.y
< size
.GetHeight())
1030 if(m_toggle_button_rect
.Contains(position
))
1032 bool pshown
= ArePanelsShown();
1033 ShowPanels(!pshown
);
1035 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
1037 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
1038 wxRibbonBarEvent
event(wxEVT_RIBBONBAR_TOGGLED
, GetId());
1039 event
.SetEventObject(this);
1040 ProcessWindowEvent(event
);
1042 if ( m_help_button_rect
.Contains(position
) )
1044 wxRibbonBarEvent
event(wxEVT_RIBBONBAR_HELP_CLICK
, GetId());
1045 event
.SetEventObject(this);
1046 ProcessWindowEvent(event
);
1052 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
))
1054 if(!m_tab_scroll_buttons_shown
)
1060 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
1064 else if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
1070 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
1071 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
1072 ScrollTabBar(amount
* 8);
1076 void wxRibbonBar::ScrollTabBar(int amount
)
1078 bool show_left
= true;
1079 bool show_right
= true;
1080 if(m_tab_scroll_amount
+ amount
<= 0)
1082 amount
= -m_tab_scroll_amount
;
1085 else if(m_tab_scroll_amount
+ amount
+ (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
) >= m_tabs_total_width_minimum
)
1087 amount
= m_tabs_total_width_minimum
- m_tab_scroll_amount
- (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
);
1094 m_tab_scroll_amount
+= amount
;
1095 size_t numtabs
= m_pages
.GetCount();
1097 for(i
= 0; i
< numtabs
; ++i
)
1099 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1102 info
.rect
.SetX(info
.rect
.GetX() - amount
);
1104 if(show_right
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) ||
1105 show_left
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0))
1107 wxClientDC
temp_dc(this);
1110 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());
1114 m_tab_scroll_left_button_rect
.SetWidth(0);
1119 if(m_tab_scroll_right_button_rect
.GetWidth() == 0)
1121 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());
1122 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth());
1127 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
1129 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
1130 m_tab_scroll_right_button_rect
.SetWidth(0);
1138 void wxRibbonBar::RefreshTabBar()
1140 wxRect
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
);
1141 Refresh(false, &tab_rect
);
1144 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
)
1146 DoMouseButtonCommon(evt
, wxEVT_RIBBONBAR_TAB_MIDDLE_DOWN
);
1149 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
)
1151 DoMouseButtonCommon(evt
, wxEVT_RIBBONBAR_TAB_MIDDLE_UP
);
1154 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
)
1156 DoMouseButtonCommon(evt
, wxEVT_RIBBONBAR_TAB_RIGHT_DOWN
);
1159 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
)
1161 DoMouseButtonCommon(evt
, wxEVT_RIBBONBAR_TAB_RIGHT_UP
);
1164 void wxRibbonBar::OnMouseDoubleClick(wxMouseEvent
& evt
)
1166 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
1168 if ( tab
&& tab
== &m_pages
.Item(m_current_page
) )
1170 if ( m_ribbon_state
== wxRIBBON_BAR_PINNED
)
1172 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
1177 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
1183 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
)
1185 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
1188 wxRibbonBarEvent
notification(tab_event_type
, GetId(), tab
->page
);
1189 notification
.SetEventObject(this);
1190 ProcessWindowEvent(notification
);
1194 void wxRibbonBar::RecalculateMinSize()
1196 wxSize
min_size(wxDefaultCoord
, wxDefaultCoord
);
1197 size_t numtabs
= m_pages
.GetCount();
1200 min_size
= m_pages
.Item(0).page
->GetMinSize();
1203 for(i
= 1; i
< numtabs
; ++i
)
1205 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1208 wxSize page_min
= info
.page
->GetMinSize();
1210 min_size
.x
= wxMax(min_size
.x
, page_min
.x
);
1211 min_size
.y
= wxMax(min_size
.y
, page_min
.y
);
1214 if(min_size
.y
!= wxDefaultCoord
)
1216 // TODO: Decide on best course of action when min height is unspecified
1217 // - should we specify it to the tab minimum, or leave it unspecified?
1218 min_size
.IncBy(0, m_tab_height
);
1221 m_minWidth
= min_size
.GetWidth();
1222 m_minHeight
= m_arePanelsShown
? min_size
.GetHeight() : m_tab_height
;
1225 wxSize
wxRibbonBar::DoGetBestSize() const
1228 if(m_current_page
!= -1)
1230 best
= m_pages
.Item(m_current_page
).page
->GetBestSize();
1232 if(best
.GetHeight() == wxDefaultCoord
)
1234 best
.SetHeight(m_tab_height
);
1238 best
.IncBy(0, m_tab_height
);
1240 if(!m_arePanelsShown
)
1242 best
.SetHeight(m_tab_height
);
1247 void wxRibbonBar::HitTestRibbonButton(const wxRect
& rect
, const wxPoint
& position
, bool &hover_flag
)
1249 bool hovered
= false, toggle_button_hovered
= false;
1250 if(position
.x
>= 0 && position
.y
>= 0)
1252 wxSize size
= GetSize();
1253 if(position
.x
< size
.GetWidth() && position
.y
< size
.GetHeight())
1260 toggle_button_hovered
= rect
.Contains(position
);
1262 if ( hovered
!= m_bar_hovered
|| toggle_button_hovered
!= hover_flag
)
1264 m_bar_hovered
= hovered
;
1265 hover_flag
= toggle_button_hovered
;
1271 void wxRibbonBar::HideIfExpanded()
1273 if ( m_ribbon_state
== wxRIBBON_BAR_EXPANDED
)
1276 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
1281 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
1285 void wxRibbonBar::OnKillFocus(wxFocusEvent
& WXUNUSED(evt
))
1290 #endif // wxUSE_RIBBON