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
);
41 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TAB_LEFT_DCLICK
, wxRibbonBarEvent
);
43 IMPLEMENT_CLASS(wxRibbonBar
, wxRibbonControl
)
44 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent
, wxNotifyEvent
)
46 BEGIN_EVENT_TABLE(wxRibbonBar
, wxRibbonControl
)
47 EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground
)
48 EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave
)
49 EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown
)
50 EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp
)
51 EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown
)
52 EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp
)
53 EVT_MOTION(wxRibbonBar::OnMouseMove
)
54 EVT_PAINT(wxRibbonBar::OnPaint
)
55 EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown
)
56 EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp
)
57 EVT_LEFT_DCLICK(wxRibbonBar::OnMouseDoubleClick
)
58 EVT_SIZE(wxRibbonBar::OnSize
)
61 void wxRibbonBar::AddPage(wxRibbonPage
*page
)
63 wxRibbonPageTabInfo info
;
68 // info.rect not set (intentional)
70 wxClientDC
dcTemp(this);
71 wxString label
= wxEmptyString
;
72 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
73 label
= page
->GetLabel();
74 wxBitmap icon
= wxNullBitmap
;
75 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
76 icon
= page
->GetIcon();
77 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
79 &info
.small_begin_need_separator_width
,
80 &info
.small_must_have_separator_width
,
85 m_tabs_total_width_ideal
= info
.ideal_width
;
86 m_tabs_total_width_minimum
= info
.minimum_width
;
90 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
91 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
92 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
96 page
->Hide(); // Most likely case is that this new page is not the active tab
97 page
->SetArtProvider(m_art
);
99 if(m_pages
.GetCount() == 1)
101 SetActivePage((size_t)0);
105 bool wxRibbonBar::DismissExpandedPanel()
107 if(m_current_page
== -1)
109 return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel();
112 void wxRibbonBar::ShowPanels(bool show
)
114 m_arePanelsShown
= show
;
115 SetMinSize(wxSize(GetSize().GetWidth(), DoGetBestSize().GetHeight()));
117 GetParent()->Layout();
120 void wxRibbonBar::SetWindowStyleFlag(long style
)
124 m_art
->SetFlags(style
);
127 long wxRibbonBar::GetWindowStyleFlag() const
132 bool wxRibbonBar::Realize()
136 wxClientDC
dcTemp(this);
137 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
138 size_t numtabs
= m_pages
.GetCount();
140 for(i
= 0; i
< numtabs
; ++i
)
142 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
143 RepositionPage(info
.page
);
144 if(!info
.page
->Realize())
148 wxString label
= wxEmptyString
;
149 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
150 label
= info
.page
->GetLabel();
151 wxBitmap icon
= wxNullBitmap
;
152 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
153 icon
= info
.page
->GetIcon();
154 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
156 &info
.small_begin_need_separator_width
,
157 &info
.small_must_have_separator_width
,
158 &info
.minimum_width
);
162 m_tabs_total_width_ideal
= info
.ideal_width
;
163 m_tabs_total_width_minimum
= info
.minimum_width
;
167 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
168 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
171 m_tab_height
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
);
173 RecalculateMinSize();
174 RecalculateTabSizes();
180 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
)
184 int hovered_page
= -1;
185 bool refresh_tabs
= false;
188 // It is quite likely that the mouse moved a small amount and is still over the same tab
189 if(m_current_hovered_page
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
))
191 hovered_page
= m_current_hovered_page
;
192 // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
193 if(m_tab_scroll_buttons_shown
)
195 if(x
>= m_tab_scroll_right_button_rect
.GetX() || x
< m_tab_scroll_left_button_rect
.GetRight())
203 HitTestTabs(evt
.GetPosition(), &hovered_page
);
206 if(hovered_page
!= m_current_hovered_page
)
208 if(m_current_hovered_page
!= -1)
210 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
212 m_current_hovered_page
= hovered_page
;
213 if(m_current_hovered_page
!= -1)
215 m_pages
.Item((int)m_current_hovered_page
).hovered
= true;
219 if(m_tab_scroll_buttons_shown
)
221 #define SET_FLAG(variable, flag) \
222 { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
223 #define UNSET_FLAG(variable, flag) \
224 { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
226 if(m_tab_scroll_left_button_rect
.Contains(x
, y
))
227 SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
229 UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
231 if(m_tab_scroll_right_button_rect
.Contains(x
, y
))
232 SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
234 UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
244 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
246 // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
247 // can leave the window quickly and leave a tab in the hovered state.
248 bool refresh_tabs
= false;
249 if(m_current_hovered_page
!= -1)
251 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
252 m_current_hovered_page
= -1;
255 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
257 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
260 if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
262 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
271 wxRibbonPage
* wxRibbonBar::GetPage(int n
)
273 if(n
< 0 || (size_t)n
>= m_pages
.GetCount())
275 return m_pages
.Item(n
).page
;
278 bool wxRibbonBar::SetActivePage(size_t page
)
280 if(m_current_page
== (int)page
)
285 if(page
>= m_pages
.GetCount())
290 if(m_current_page
!= -1)
292 m_pages
.Item((size_t)m_current_page
).active
= false;
293 m_pages
.Item((size_t)m_current_page
).page
->Hide();
295 m_current_page
= (int)page
;
296 m_pages
.Item(page
).active
= true;
298 wxRibbonPage
* wnd
= m_pages
.Item(page
).page
;
308 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
)
310 size_t numpages
= m_pages
.GetCount();
312 for(i
= 0; i
< numpages
; ++i
)
314 if(m_pages
.Item(i
).page
== page
)
316 return SetActivePage(i
);
322 int wxRibbonBar::GetActivePage() const
324 return m_current_page
;
327 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
)
329 m_tab_margin_left
= left
;
330 m_tab_margin_right
= right
;
332 RecalculateTabSizes();
335 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo
**first
, wxRibbonPageTabInfo
**second
)
337 return (**first
).small_must_have_separator_width
- (**second
).small_must_have_separator_width
;
340 void wxRibbonBar::RecalculateTabSizes()
342 size_t numtabs
= m_pages
.GetCount();
347 int width
= GetSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
;
348 int tabsep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
349 int x
= m_tab_margin_left
;
352 if(width
>= m_tabs_total_width_ideal
)
354 // Simple case: everything at ideal width
356 for(i
= 0; i
< numtabs
; ++i
)
358 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
361 info
.rect
.width
= info
.ideal_width
;
362 info
.rect
.height
= m_tab_height
;
363 x
+= info
.rect
.width
+ tabsep
;
365 m_tab_scroll_buttons_shown
= false;
366 m_tab_scroll_left_button_rect
.SetWidth(0);
367 m_tab_scroll_right_button_rect
.SetWidth(0);
369 else if(width
< m_tabs_total_width_minimum
)
371 // Simple case: everything minimum with scrollbar
373 for(i
= 0; i
< numtabs
; ++i
)
375 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
378 info
.rect
.width
= info
.minimum_width
;
379 info
.rect
.height
= m_tab_height
;
380 x
+= info
.rect
.width
+ tabsep
;
382 if(!m_tab_scroll_buttons_shown
)
384 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
385 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
386 m_tab_scroll_buttons_shown
= true;
389 wxClientDC
temp_dc(this);
390 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());
391 m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
);
392 m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
);
393 m_tab_scroll_left_button_rect
.SetY(0);
394 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());
395 m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
);
396 m_tab_scroll_right_button_rect
.SetX(GetClientSize().GetWidth() - m_tab_margin_right
- m_tab_scroll_right_button_rect
.GetWidth());
397 m_tab_scroll_right_button_rect
.SetY(0);
399 if(m_tab_scroll_amount
== 0)
401 m_tab_scroll_left_button_rect
.SetWidth(0);
403 else if(m_tab_scroll_amount
+ width
>= m_tabs_total_width_minimum
)
405 m_tab_scroll_amount
= m_tabs_total_width_minimum
- width
;
406 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
407 m_tab_scroll_right_button_rect
.SetWidth(0);
409 for(i
= 0; i
< numtabs
; ++i
)
411 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
412 info
.rect
.x
-= m_tab_scroll_amount
;
417 m_tab_scroll_buttons_shown
= false;
418 m_tab_scroll_left_button_rect
.SetWidth(0);
419 m_tab_scroll_right_button_rect
.SetWidth(0);
420 // Complex case: everything sized such that: minimum <= width < ideal
423 1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
424 2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
425 3) Uniformly reduce all tabs down to their minimum width
427 int smallest_tab_width
= INT_MAX
;
428 int total_small_width
= tabsep
* (numtabs
- 1);
430 for(i
= 0; i
< numtabs
; ++i
)
432 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
433 if(info
.small_must_have_separator_width
< smallest_tab_width
)
435 smallest_tab_width
= info
.small_must_have_separator_width
;
437 total_small_width
+= info
.small_must_have_separator_width
;
439 if(width
>= total_small_width
)
442 int total_delta
= m_tabs_total_width_ideal
- total_small_width
;
443 total_small_width
-= tabsep
* (numtabs
- 1);
444 width
-= tabsep
* (numtabs
- 1);
445 for(i
= 0; i
< numtabs
; ++i
)
447 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
448 int delta
= info
.ideal_width
- info
.small_must_have_separator_width
;
451 info
.rect
.width
= info
.small_must_have_separator_width
+ delta
* (width
- total_small_width
) / total_delta
;
452 info
.rect
.height
= m_tab_height
;
454 x
+= info
.rect
.width
+ tabsep
;
455 total_delta
-= delta
;
456 total_small_width
-= info
.small_must_have_separator_width
;
457 width
-= info
.rect
.width
;
462 total_small_width
= tabsep
* (numtabs
- 1);
463 for(i
= 0; i
< numtabs
; ++i
)
465 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
466 if(info
.minimum_width
< smallest_tab_width
)
468 total_small_width
+= smallest_tab_width
;
472 total_small_width
+= info
.minimum_width
;
475 if(width
>= total_small_width
)
478 wxRibbonPageTabInfoArray sorted_pages
;
479 for(i
= 0; i
< numtabs
; ++i
)
481 // Sneaky obj array trickery to not copy the tab descriptors
482 sorted_pages
.Add(&m_pages
.Item(i
));
484 sorted_pages
.Sort(OrderPageTabInfoBySmallWidthAsc
);
485 width
-= tabsep
* (numtabs
- 1);
486 for(i
= 0; i
< numtabs
; ++i
)
488 wxRibbonPageTabInfo
& info
= sorted_pages
.Item(i
);
489 if(info
.small_must_have_separator_width
* (int)(numtabs
- i
) <= width
)
491 info
.rect
.width
= info
.small_must_have_separator_width
;;
495 info
.rect
.width
= width
/ (numtabs
- i
);
497 width
-= info
.rect
.width
;
499 for(i
= 0; i
< numtabs
; ++i
)
501 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
504 info
.rect
.height
= m_tab_height
;
505 x
+= info
.rect
.width
+ tabsep
;
506 sorted_pages
.Detach(numtabs
- (i
+ 1));
512 total_small_width
= (smallest_tab_width
+ tabsep
) * numtabs
- tabsep
;
513 int total_delta
= total_small_width
- m_tabs_total_width_minimum
;
514 total_small_width
= m_tabs_total_width_minimum
- tabsep
* (numtabs
- 1);
515 width
-= tabsep
* (numtabs
- 1);
516 for(i
= 0; i
< numtabs
; ++i
)
518 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
519 int delta
= smallest_tab_width
- info
.minimum_width
;
522 info
.rect
.width
= info
.minimum_width
+ delta
* (width
- total_small_width
) / total_delta
;
523 info
.rect
.height
= m_tab_height
;
525 x
+= info
.rect
.width
+ tabsep
;
526 total_delta
-= delta
;
527 total_small_width
-= info
.minimum_width
;
528 width
-= info
.rect
.width
;
535 wxRibbonBar::wxRibbonBar()
538 m_tabs_total_width_ideal
= 0;
539 m_tabs_total_width_minimum
= 0;
540 m_tab_margin_left
= 0;
541 m_tab_margin_right
= 0;
543 m_tab_scroll_amount
= 0;
545 m_current_hovered_page
= -1;
546 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
547 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
548 m_tab_scroll_buttons_shown
= false;
549 m_arePanelsShown
= true;
552 wxRibbonBar::wxRibbonBar(wxWindow
* parent
,
557 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
562 wxRibbonBar::~wxRibbonBar()
564 SetArtProvider(NULL
);
567 bool wxRibbonBar::Create(wxWindow
* parent
,
573 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
581 void wxRibbonBar::CommonInit(long style
)
583 SetName(wxT("wxRibbonBar"));
586 m_tabs_total_width_ideal
= 0;
587 m_tabs_total_width_minimum
= 0;
588 m_tab_margin_left
= 50;
589 m_tab_margin_right
= 20;
590 m_tab_height
= 20; // initial guess
591 m_tab_scroll_amount
= 0;
593 m_current_hovered_page
= -1;
594 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
595 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
596 m_tab_scroll_buttons_shown
= false;
597 m_arePanelsShown
= true;
601 SetArtProvider(new wxRibbonDefaultArtProvider
);
603 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
606 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
)
608 wxRibbonArtProvider
*old
= m_art
;
613 art
->SetFlags(m_flags
);
615 size_t numpages
= m_pages
.GetCount();
617 for(i
= 0; i
< numpages
; ++i
)
619 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
620 if(page
->GetArtProvider() != art
)
622 page
->SetArtProvider(art
);
629 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
631 wxAutoBufferedPaintDC
dc(this);
633 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
)
635 // Nothing to do in the tab area, and the page area is handled by the active page
639 DoEraseBackground(dc
);
641 size_t numtabs
= m_pages
.GetCount();
642 double sep_visibility
= 0.0;
643 bool draw_sep
= false;
644 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
645 if(m_tab_scroll_buttons_shown
)
647 tabs_rect
.x
+= m_tab_scroll_left_button_rect
.GetWidth();
648 tabs_rect
.width
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth();
651 for(i
= 0; i
< numtabs
; ++i
)
653 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
655 dc
.DestroyClippingRegion();
656 if(m_tab_scroll_buttons_shown
)
658 if(!tabs_rect
.Intersects(info
.rect
))
660 dc
.SetClippingRegion(tabs_rect
);
662 dc
.SetClippingRegion(info
.rect
);
663 m_art
->DrawTab(dc
, this, info
);
665 if(info
.rect
.width
< info
.small_begin_need_separator_width
)
668 if(info
.rect
.width
< info
.small_must_have_separator_width
)
670 sep_visibility
+= 1.0;
674 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
);
680 wxRect rect
= m_pages
.Item(0).rect
;
681 rect
.width
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
682 sep_visibility
/= (double)numtabs
;
683 for(i
= 0; i
< numtabs
- 1; ++i
)
685 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
686 rect
.x
= info
.rect
.x
+ info
.rect
.width
;
688 if(m_tab_scroll_buttons_shown
&& !tabs_rect
.Intersects(rect
))
693 dc
.DestroyClippingRegion();
694 dc
.SetClippingRegion(rect
);
695 m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
);
698 if(m_tab_scroll_buttons_shown
)
700 dc
.DestroyClippingRegion();
701 if(m_tab_scroll_left_button_rect
.GetWidth() != 0)
703 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
);
705 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
707 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
);
712 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
714 // Background painting done in main paint handler to reduce screen flicker
717 void wxRibbonBar::DoEraseBackground(wxDC
& dc
)
719 wxRect
tabs(GetSize());
720 tabs
.height
= m_tab_height
;
721 m_art
->DrawTabCtrlBackground(dc
, this, tabs
);
724 void wxRibbonBar::OnSize(wxSizeEvent
& evt
)
726 RecalculateTabSizes();
727 if(m_current_page
!= -1)
729 RepositionPage(m_pages
.Item(m_current_page
).page
);
736 void wxRibbonBar::RepositionPage(wxRibbonPage
*page
)
740 page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h
- m_tab_height
);
743 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
)
745 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
746 if(m_tab_scroll_buttons_shown
)
748 tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth());
749 tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth());
751 if(tabs_rect
.Contains(position
))
753 size_t numtabs
= m_pages
.GetCount();
755 for(i
= 0; i
< numtabs
; ++i
)
757 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
758 if(info
.rect
.Contains(position
))
775 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
)
777 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
778 if(tab
&& tab
!= &m_pages
.Item(m_current_page
))
780 wxRibbonBarEvent
query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
);
781 query
.SetEventObject(this);
782 ProcessWindowEvent(query
);
783 if(query
.IsAllowed())
785 SetActivePage(query
.GetPage());
787 wxRibbonBarEvent
notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
);
788 notification
.SetEventObject(this);
789 ProcessWindowEvent(notification
);
794 if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition()))
796 m_tab_scroll_left_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
799 else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition()))
801 m_tab_scroll_right_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
807 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
))
809 if(!m_tab_scroll_buttons_shown
)
815 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
819 else if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
825 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
826 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
827 ScrollTabBar(amount
* 8);
831 void wxRibbonBar::ScrollTabBar(int amount
)
833 bool show_left
= true;
834 bool show_right
= true;
835 if(m_tab_scroll_amount
+ amount
<= 0)
837 amount
= -m_tab_scroll_amount
;
840 else if(m_tab_scroll_amount
+ amount
+ (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
) >= m_tabs_total_width_minimum
)
842 amount
= m_tabs_total_width_minimum
- m_tab_scroll_amount
- (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
);
849 m_tab_scroll_amount
+= amount
;
850 size_t numtabs
= m_pages
.GetCount();
852 for(i
= 0; i
< numtabs
; ++i
)
854 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
855 info
.rect
.SetX(info
.rect
.GetX() - amount
);
857 if(show_right
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) ||
858 show_left
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0))
860 wxClientDC
temp_dc(this);
863 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());
867 m_tab_scroll_left_button_rect
.SetWidth(0);
872 if(m_tab_scroll_right_button_rect
.GetWidth() == 0)
874 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());
875 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth());
880 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
882 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
883 m_tab_scroll_right_button_rect
.SetWidth(0);
891 void wxRibbonBar::RefreshTabBar()
893 wxRect
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
);
894 Refresh(false, &tab_rect
);
897 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
)
899 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
);
902 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
)
904 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
);
907 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
)
909 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
);
912 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
)
914 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
);
917 void wxRibbonBar::OnMouseDoubleClick(wxMouseEvent
& evt
)
919 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_LEFT_DCLICK
);
922 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
)
924 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
927 wxRibbonBarEvent
notification(tab_event_type
, GetId(), tab
->page
);
928 notification
.SetEventObject(this);
929 ProcessWindowEvent(notification
);
933 void wxRibbonBar::RecalculateMinSize()
935 wxSize
min_size(wxDefaultCoord
, wxDefaultCoord
);
936 size_t numtabs
= m_pages
.GetCount();
939 min_size
= m_pages
.Item(0).page
->GetMinSize();
942 for(i
= 1; i
< numtabs
; ++i
)
944 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
945 wxSize page_min
= info
.page
->GetMinSize();
947 min_size
.x
= wxMax(min_size
.x
, page_min
.x
);
948 min_size
.y
= wxMax(min_size
.y
, page_min
.y
);
951 if(min_size
.y
!= wxDefaultCoord
)
953 // TODO: Decide on best course of action when min height is unspecified
954 // - should we specify it to the tab minimum, or leave it unspecified?
955 min_size
.IncBy(0, m_tab_height
);
958 m_minWidth
= min_size
.GetWidth();
959 m_minHeight
= m_arePanelsShown
? min_size
.GetHeight() : m_tab_height
;
962 wxSize
wxRibbonBar::DoGetBestSize() const
965 if(m_current_page
!= -1)
967 best
= m_pages
.Item(m_current_page
).page
->GetBestSize();
969 if(best
.GetHeight() == wxDefaultCoord
)
971 best
.SetHeight(m_tab_height
);
975 best
.IncBy(0, m_tab_height
);
977 if(!m_arePanelsShown
)
979 best
.SetHeight(m_tab_height
);
984 #endif // wxUSE_RIBBON