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"
28 #include "wx/msw/private.h"
31 #include "wx/arrimpl.cpp"
33 WX_DEFINE_USER_EXPORTED_OBJARRAY(wxRibbonPageTabInfoArray
)
35 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, wxRibbonBarEvent
);
36 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, wxRibbonBarEvent
);
37 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
, wxRibbonBarEvent
);
38 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
, wxRibbonBarEvent
);
39 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
, wxRibbonBarEvent
);
40 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
, wxRibbonBarEvent
);
42 IMPLEMENT_CLASS(wxRibbonBar
, wxRibbonControl
)
43 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent
, wxNotifyEvent
)
45 BEGIN_EVENT_TABLE(wxRibbonBar
, wxRibbonControl
)
46 EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground
)
47 EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave
)
48 EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown
)
49 EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp
)
50 EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown
)
51 EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp
)
52 EVT_MOTION(wxRibbonBar::OnMouseMove
)
53 EVT_PAINT(wxRibbonBar::OnPaint
)
54 EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown
)
55 EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp
)
56 EVT_SIZE(wxRibbonBar::OnSize
)
59 void wxRibbonBar::AddPage(wxRibbonPage
*page
)
61 wxRibbonPageTabInfo info
;
66 // info.rect not set (intentional)
68 wxClientDC
dcTemp(this);
69 wxString label
= wxEmptyString
;
70 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
71 label
= page
->GetLabel();
72 wxBitmap icon
= wxNullBitmap
;
73 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
74 icon
= page
->GetIcon();
75 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
77 &info
.small_begin_need_separator_width
,
78 &info
.small_must_have_separator_width
,
83 m_tabs_total_width_ideal
= info
.ideal_width
;
84 m_tabs_total_width_minimum
= info
.minimum_width
;
88 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
89 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
90 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
94 page
->Hide(); // Most likely case is that this new page is not the active tab
95 page
->SetArtProvider(m_art
);
97 if(m_pages
.GetCount() == 1)
99 SetActivePage((size_t)0);
103 bool wxRibbonBar::DismissExpandedPanel()
105 if(m_current_page
== -1)
107 return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel();
110 void wxRibbonBar::SetWindowStyleFlag(long style
)
114 m_art
->SetFlags(style
);
117 long wxRibbonBar::GetWindowStyleFlag() const
122 bool wxRibbonBar::Realize()
126 wxClientDC
dcTemp(this);
127 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
128 size_t numtabs
= m_pages
.GetCount();
130 for(i
= 0; i
< numtabs
; ++i
)
132 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
133 RepositionPage(info
.page
);
134 if(!info
.page
->Realize())
138 wxString label
= wxEmptyString
;
139 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
140 label
= info
.page
->GetLabel();
141 wxBitmap icon
= wxNullBitmap
;
142 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
143 icon
= info
.page
->GetIcon();
144 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
146 &info
.small_begin_need_separator_width
,
147 &info
.small_must_have_separator_width
,
148 &info
.minimum_width
);
152 m_tabs_total_width_ideal
= info
.ideal_width
;
153 m_tabs_total_width_minimum
= info
.minimum_width
;
157 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
158 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
161 m_tab_height
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
);
163 RecalculateMinSize();
164 RecalculateTabSizes();
170 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
)
174 int hovered_page
= -1;
175 bool refresh_tabs
= false;
178 // It is quite likely that the mouse moved a small amount and is still over the same tab
179 if(m_current_hovered_page
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
))
181 hovered_page
= m_current_hovered_page
;
182 // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
183 if(m_tab_scroll_buttons_shown
)
185 if(x
>= m_tab_scroll_right_button_rect
.GetX() || x
< m_tab_scroll_left_button_rect
.GetRight())
193 HitTestTabs(evt
.GetPosition(), &hovered_page
);
196 if(hovered_page
!= m_current_hovered_page
)
198 if(m_current_hovered_page
!= -1)
200 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
202 m_current_hovered_page
= hovered_page
;
203 if(m_current_hovered_page
!= -1)
205 m_pages
.Item((int)m_current_hovered_page
).hovered
= true;
209 if(m_tab_scroll_buttons_shown
)
211 #define SET_FLAG(variable, flag) \
212 { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
213 #define UNSET_FLAG(variable, flag) \
214 { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
216 if(m_tab_scroll_left_button_rect
.Contains(x
, y
))
217 SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
219 UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
221 if(m_tab_scroll_right_button_rect
.Contains(x
, y
))
222 SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
224 UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
234 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
236 // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
237 // can leave the window quickly and leave a tab in the hovered state.
238 bool refresh_tabs
= false;
239 if(m_current_hovered_page
!= -1)
241 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
242 m_current_hovered_page
= -1;
245 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
247 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
250 if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
252 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
261 wxRibbonPage
* wxRibbonBar::GetPage(int n
)
263 if(n
< 0 || (size_t)n
>= m_pages
.GetCount())
265 return m_pages
.Item(n
).page
;
268 bool wxRibbonBar::SetActivePage(size_t page
)
270 if(m_current_page
== (int)page
)
275 if(page
>= m_pages
.GetCount())
280 if(m_current_page
!= -1)
282 m_pages
.Item((size_t)m_current_page
).active
= false;
283 m_pages
.Item((size_t)m_current_page
).page
->Hide();
285 m_current_page
= (int)page
;
286 m_pages
.Item(page
).active
= true;
288 wxRibbonPage
* wnd
= m_pages
.Item(page
).page
;
298 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
)
300 size_t numpages
= m_pages
.GetCount();
302 for(i
= 0; i
< numpages
; ++i
)
304 if(m_pages
.Item(i
).page
== page
)
306 return SetActivePage(i
);
312 int wxRibbonBar::GetActivePage() const
314 return m_current_page
;
317 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
)
319 m_tab_margin_left
= left
;
320 m_tab_margin_right
= right
;
322 RecalculateTabSizes();
325 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo
**first
, wxRibbonPageTabInfo
**second
)
327 return (**first
).small_must_have_separator_width
- (**second
).small_must_have_separator_width
;
330 void wxRibbonBar::RecalculateTabSizes()
332 size_t numtabs
= m_pages
.GetCount();
337 int width
= GetSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
;
338 int tabsep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
339 int x
= m_tab_margin_left
;
342 if(width
>= m_tabs_total_width_ideal
)
344 // Simple case: everything at ideal width
346 for(i
= 0; i
< numtabs
; ++i
)
348 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
351 info
.rect
.width
= info
.ideal_width
;
352 info
.rect
.height
= m_tab_height
;
353 x
+= info
.rect
.width
+ tabsep
;
355 m_tab_scroll_buttons_shown
= false;
356 m_tab_scroll_left_button_rect
.SetWidth(0);
357 m_tab_scroll_right_button_rect
.SetWidth(0);
359 else if(width
< m_tabs_total_width_minimum
)
361 // Simple case: everything minimum with scrollbar
363 for(i
= 0; i
< numtabs
; ++i
)
365 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
368 info
.rect
.width
= info
.minimum_width
;
369 info
.rect
.height
= m_tab_height
;
370 x
+= info
.rect
.width
+ tabsep
;
372 if(!m_tab_scroll_buttons_shown
)
374 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
375 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
376 m_tab_scroll_buttons_shown
= true;
379 wxClientDC
temp_dc(this);
380 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());
381 m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
);
382 m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
);
383 m_tab_scroll_left_button_rect
.SetY(0);
384 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());
385 m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
);
386 m_tab_scroll_right_button_rect
.SetX(GetClientSize().GetWidth() - m_tab_margin_right
- m_tab_scroll_right_button_rect
.GetWidth());
387 m_tab_scroll_right_button_rect
.SetY(0);
389 if(m_tab_scroll_amount
== 0)
391 m_tab_scroll_left_button_rect
.SetWidth(0);
393 else if(m_tab_scroll_amount
+ width
>= m_tabs_total_width_minimum
)
395 m_tab_scroll_amount
= m_tabs_total_width_minimum
- width
;
396 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
397 m_tab_scroll_right_button_rect
.SetWidth(0);
399 for(i
= 0; i
< numtabs
; ++i
)
401 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
402 info
.rect
.x
-= m_tab_scroll_amount
;
407 m_tab_scroll_buttons_shown
= false;
408 m_tab_scroll_left_button_rect
.SetWidth(0);
409 m_tab_scroll_right_button_rect
.SetWidth(0);
410 // Complex case: everything sized such that: minimum <= width < ideal
413 1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
414 2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
415 3) Uniformly reduce all tabs down to their minimum width
417 int smallest_tab_width
= INT_MAX
;
418 int total_small_width
= tabsep
* (numtabs
- 1);
420 for(i
= 0; i
< numtabs
; ++i
)
422 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
423 if(info
.small_must_have_separator_width
< smallest_tab_width
)
425 smallest_tab_width
= info
.small_must_have_separator_width
;
427 total_small_width
+= info
.small_must_have_separator_width
;
429 if(width
>= total_small_width
)
432 int total_delta
= m_tabs_total_width_ideal
- total_small_width
;
433 total_small_width
-= tabsep
* (numtabs
- 1);
434 width
-= tabsep
* (numtabs
- 1);
435 for(i
= 0; i
< numtabs
; ++i
)
437 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
438 int delta
= info
.ideal_width
- info
.small_must_have_separator_width
;
441 info
.rect
.width
= info
.small_must_have_separator_width
+ delta
* (width
- total_small_width
) / total_delta
;
442 info
.rect
.height
= m_tab_height
;
444 x
+= info
.rect
.width
+ tabsep
;
445 total_delta
-= delta
;
446 total_small_width
-= info
.small_must_have_separator_width
;
447 width
-= info
.rect
.width
;
452 total_small_width
= tabsep
* (numtabs
- 1);
453 for(i
= 0; i
< numtabs
; ++i
)
455 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
456 if(info
.minimum_width
< smallest_tab_width
)
458 total_small_width
+= smallest_tab_width
;
462 total_small_width
+= info
.minimum_width
;
465 if(width
>= total_small_width
)
468 wxRibbonPageTabInfoArray sorted_pages
;
469 for(i
= 0; i
< numtabs
; ++i
)
471 // Sneaky obj array trickery to not copy the tab descriptors
472 sorted_pages
.Add(&m_pages
.Item(i
));
474 sorted_pages
.Sort(OrderPageTabInfoBySmallWidthAsc
);
475 width
-= tabsep
* (numtabs
- 1);
476 for(i
= 0; i
< numtabs
; ++i
)
478 wxRibbonPageTabInfo
& info
= sorted_pages
.Item(i
);
479 if(info
.small_must_have_separator_width
* (int)(numtabs
- i
) <= width
)
481 info
.rect
.width
= info
.small_must_have_separator_width
;;
485 info
.rect
.width
= width
/ (numtabs
- i
);
487 width
-= info
.rect
.width
;
489 for(i
= 0; i
< numtabs
; ++i
)
491 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
494 info
.rect
.height
= m_tab_height
;
495 x
+= info
.rect
.width
+ tabsep
;
496 sorted_pages
.Detach(numtabs
- (i
+ 1));
502 total_small_width
= (smallest_tab_width
+ tabsep
) * numtabs
- tabsep
;
503 int total_delta
= total_small_width
- m_tabs_total_width_minimum
;
504 total_small_width
= m_tabs_total_width_minimum
- tabsep
* (numtabs
- 1);
505 width
-= tabsep
* (numtabs
- 1);
506 for(i
= 0; i
< numtabs
; ++i
)
508 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
509 int delta
= smallest_tab_width
- info
.minimum_width
;
512 info
.rect
.width
= info
.minimum_width
+ delta
* (width
- total_small_width
) / total_delta
;
513 info
.rect
.height
= m_tab_height
;
515 x
+= info
.rect
.width
+ tabsep
;
516 total_delta
-= delta
;
517 total_small_width
-= info
.minimum_width
;
518 width
-= info
.rect
.width
;
525 wxRibbonBar::wxRibbonBar()
528 m_tabs_total_width_ideal
= 0;
529 m_tabs_total_width_minimum
= 0;
530 m_tab_margin_left
= 0;
531 m_tab_margin_right
= 0;
533 m_tab_scroll_amount
= 0;
535 m_current_hovered_page
= -1;
536 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
537 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
538 m_tab_scroll_buttons_shown
= false;
541 wxRibbonBar::wxRibbonBar(wxWindow
* parent
,
546 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
551 wxRibbonBar::~wxRibbonBar()
553 SetArtProvider(NULL
);
556 bool wxRibbonBar::Create(wxWindow
* parent
,
562 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
570 void wxRibbonBar::CommonInit(long style
)
572 SetName(wxT("wxRibbonBar"));
575 m_tabs_total_width_ideal
= 0;
576 m_tabs_total_width_minimum
= 0;
577 m_tab_margin_left
= 50;
578 m_tab_margin_right
= 20;
579 m_tab_height
= 20; // initial guess
580 m_tab_scroll_amount
= 0;
582 m_current_hovered_page
= -1;
583 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
584 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
585 m_tab_scroll_buttons_shown
= false;
589 SetArtProvider(new wxRibbonDefaultArtProvider
);
591 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
594 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
)
596 wxRibbonArtProvider
*old
= m_art
;
601 art
->SetFlags(m_flags
);
603 size_t numpages
= m_pages
.GetCount();
605 for(i
= 0; i
< numpages
; ++i
)
607 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
608 if(page
->GetArtProvider() != art
)
610 page
->SetArtProvider(art
);
617 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
619 wxAutoBufferedPaintDC
dc(this);
621 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
)
623 // Nothing to do in the tab area, and the page area is handled by the active page
627 DoEraseBackground(dc
);
629 size_t numtabs
= m_pages
.GetCount();
630 double sep_visibility
= 0.0;
631 bool draw_sep
= false;
632 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
633 if(m_tab_scroll_buttons_shown
)
635 tabs_rect
.x
+= m_tab_scroll_left_button_rect
.GetWidth();
636 tabs_rect
.width
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth();
639 for(i
= 0; i
< numtabs
; ++i
)
641 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
643 dc
.DestroyClippingRegion();
644 if(m_tab_scroll_buttons_shown
)
646 if(!tabs_rect
.Intersects(info
.rect
))
648 dc
.SetClippingRegion(tabs_rect
);
650 dc
.SetClippingRegion(info
.rect
);
651 m_art
->DrawTab(dc
, this, info
);
653 if(info
.rect
.width
< info
.small_begin_need_separator_width
)
656 if(info
.rect
.width
< info
.small_must_have_separator_width
)
658 sep_visibility
+= 1.0;
662 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
);
668 wxRect rect
= m_pages
.Item(0).rect
;
669 rect
.width
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
670 sep_visibility
/= (double)numtabs
;
671 for(i
= 0; i
< numtabs
- 1; ++i
)
673 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
674 rect
.x
= info
.rect
.x
+ info
.rect
.width
;
676 if(m_tab_scroll_buttons_shown
&& !tabs_rect
.Intersects(rect
))
681 dc
.DestroyClippingRegion();
682 dc
.SetClippingRegion(rect
);
683 m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
);
686 if(m_tab_scroll_buttons_shown
)
688 dc
.DestroyClippingRegion();
689 if(m_tab_scroll_left_button_rect
.GetWidth() != 0)
691 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
);
693 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
695 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
);
700 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
702 // Background painting done in main paint handler to reduce screen flicker
705 void wxRibbonBar::DoEraseBackground(wxDC
& dc
)
707 wxRect
tabs(GetSize());
708 tabs
.height
= m_tab_height
;
709 m_art
->DrawTabCtrlBackground(dc
, this, tabs
);
712 void wxRibbonBar::OnSize(wxSizeEvent
& evt
)
714 RecalculateTabSizes();
715 if(m_current_page
!= -1)
717 RepositionPage(m_pages
.Item(m_current_page
).page
);
724 void wxRibbonBar::RepositionPage(wxRibbonPage
*page
)
728 page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h
- m_tab_height
);
731 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
)
733 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
734 if(m_tab_scroll_buttons_shown
)
736 tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth());
737 tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth());
739 if(tabs_rect
.Contains(position
))
741 size_t numtabs
= m_pages
.GetCount();
743 for(i
= 0; i
< numtabs
; ++i
)
745 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
746 if(info
.rect
.Contains(position
))
763 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
)
765 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
766 if(tab
&& tab
!= &m_pages
.Item(m_current_page
))
768 wxRibbonBarEvent
query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
);
769 query
.SetEventObject(this);
770 ProcessWindowEvent(query
);
771 if(query
.IsAllowed())
773 SetActivePage(query
.GetPage());
775 wxRibbonBarEvent
notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
);
776 notification
.SetEventObject(this);
777 ProcessWindowEvent(notification
);
782 if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition()))
784 m_tab_scroll_left_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
787 else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition()))
789 m_tab_scroll_right_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
795 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
))
797 if(!m_tab_scroll_buttons_shown
)
803 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
807 else if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
813 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
814 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
815 ScrollTabBar(amount
* 8);
819 void wxRibbonBar::ScrollTabBar(int amount
)
821 bool show_left
= true;
822 bool show_right
= true;
823 if(m_tab_scroll_amount
+ amount
<= 0)
825 amount
= -m_tab_scroll_amount
;
828 else if(m_tab_scroll_amount
+ amount
+ (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
) >= m_tabs_total_width_minimum
)
830 amount
= m_tabs_total_width_minimum
- m_tab_scroll_amount
- (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
);
837 m_tab_scroll_amount
+= amount
;
838 size_t numtabs
= m_pages
.GetCount();
840 for(i
= 0; i
< numtabs
; ++i
)
842 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
843 info
.rect
.SetX(info
.rect
.GetX() - amount
);
845 if(show_right
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) ||
846 show_left
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0))
848 wxClientDC
temp_dc(this);
851 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());
855 m_tab_scroll_left_button_rect
.SetWidth(0);
860 if(m_tab_scroll_right_button_rect
.GetWidth() == 0)
862 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());
863 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth());
868 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
870 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
871 m_tab_scroll_right_button_rect
.SetWidth(0);
879 void wxRibbonBar::RefreshTabBar()
881 wxRect
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
);
882 Refresh(false, &tab_rect
);
885 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
)
887 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
);
890 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
)
892 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
);
895 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
)
897 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
);
900 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
)
902 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
);
905 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
)
907 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
910 wxRibbonBarEvent
notification(tab_event_type
, GetId(), tab
->page
);
911 notification
.SetEventObject(this);
912 ProcessWindowEvent(notification
);
916 void wxRibbonBar::RecalculateMinSize()
918 wxSize
min_size(wxDefaultCoord
, wxDefaultCoord
);
919 size_t numtabs
= m_pages
.GetCount();
922 min_size
= m_pages
.Item(0).page
->GetMinSize();
925 for(i
= 1; i
< numtabs
; ++i
)
927 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
928 wxSize page_min
= info
.page
->GetMinSize();
930 min_size
.x
= wxMax(min_size
.x
, page_min
.x
);
931 min_size
.y
= wxMax(min_size
.y
, page_min
.y
);
934 if(min_size
.y
!= wxDefaultCoord
)
936 // TODO: Decide on best course of action when min height is unspecified
937 // - should we specify it to the tab minimum, or leave it unspecified?
938 min_size
.IncBy(0, m_tab_height
);
941 m_minWidth
= min_size
.GetWidth();
942 m_minHeight
= min_size
.GetHeight();
945 wxSize
wxRibbonBar::DoGetBestSize() const
948 if(m_current_page
!= -1)
950 best
= m_pages
.Item(m_current_page
).page
->GetBestSize();
952 if(best
.GetHeight() == wxDefaultCoord
)
954 best
.SetHeight(m_tab_height
);
958 best
.IncBy(0, m_tab_height
);
963 #endif // wxUSE_RIBBON