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
;
69 // info.rect not set (intentional)
71 wxClientDC
dcTemp(this);
72 wxString label
= wxEmptyString
;
73 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
74 label
= page
->GetLabel();
75 wxBitmap icon
= wxNullBitmap
;
76 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
77 icon
= page
->GetIcon();
78 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
80 &info
.small_begin_need_separator_width
,
81 &info
.small_must_have_separator_width
,
86 m_tabs_total_width_ideal
= info
.ideal_width
;
87 m_tabs_total_width_minimum
= info
.minimum_width
;
91 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
92 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
93 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
97 page
->Hide(); // Most likely case is that this new page is not the active tab
98 page
->SetArtProvider(m_art
);
100 if(m_pages
.GetCount() == 1)
102 SetActivePage((size_t)0);
106 bool wxRibbonBar::DismissExpandedPanel()
108 if(m_current_page
== -1)
110 return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel();
113 void wxRibbonBar::ShowPanels(bool show
)
115 m_arePanelsShown
= show
;
116 SetMinSize(wxSize(GetSize().GetWidth(), DoGetBestSize().GetHeight()));
118 GetParent()->Layout();
121 void wxRibbonBar::SetWindowStyleFlag(long style
)
125 m_art
->SetFlags(style
);
128 long wxRibbonBar::GetWindowStyleFlag() const
133 bool wxRibbonBar::Realize()
137 wxClientDC
dcTemp(this);
138 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
139 size_t numtabs
= m_pages
.GetCount();
141 for(i
= 0; i
< numtabs
; ++i
)
143 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
144 RepositionPage(info
.page
);
145 if(!info
.page
->Realize())
149 wxString label
= wxEmptyString
;
150 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
151 label
= info
.page
->GetLabel();
152 wxBitmap icon
= wxNullBitmap
;
153 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
154 icon
= info
.page
->GetIcon();
155 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
157 &info
.small_begin_need_separator_width
,
158 &info
.small_must_have_separator_width
,
159 &info
.minimum_width
);
163 m_tabs_total_width_ideal
= info
.ideal_width
;
164 m_tabs_total_width_minimum
= info
.minimum_width
;
168 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
169 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
172 m_tab_height
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
);
174 RecalculateMinSize();
175 RecalculateTabSizes();
181 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
)
185 int hovered_page
= -1;
186 bool refresh_tabs
= false;
189 // It is quite likely that the mouse moved a small amount and is still over the same tab
190 if(m_current_hovered_page
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
))
192 hovered_page
= m_current_hovered_page
;
193 // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
194 if(m_tab_scroll_buttons_shown
)
196 if(x
>= m_tab_scroll_right_button_rect
.GetX() || x
< m_tab_scroll_left_button_rect
.GetRight())
204 HitTestTabs(evt
.GetPosition(), &hovered_page
);
207 if(hovered_page
!= m_current_hovered_page
)
209 if(m_current_hovered_page
!= -1)
211 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
213 m_current_hovered_page
= hovered_page
;
214 if(m_current_hovered_page
!= -1)
216 m_pages
.Item((int)m_current_hovered_page
).hovered
= true;
220 if(m_tab_scroll_buttons_shown
)
222 #define SET_FLAG(variable, flag) \
223 { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
224 #define UNSET_FLAG(variable, flag) \
225 { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
227 if(m_tab_scroll_left_button_rect
.Contains(x
, y
))
228 SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
230 UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
232 if(m_tab_scroll_right_button_rect
.Contains(x
, y
))
233 SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
235 UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
245 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
247 // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
248 // can leave the window quickly and leave a tab in the hovered state.
249 bool refresh_tabs
= false;
250 if(m_current_hovered_page
!= -1)
252 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
253 m_current_hovered_page
= -1;
256 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
258 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
261 if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
263 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
272 wxRibbonPage
* wxRibbonBar::GetPage(int n
)
274 if(n
< 0 || (size_t)n
>= m_pages
.GetCount())
276 return m_pages
.Item(n
).page
;
279 size_t wxRibbonBar::GetPageCount() const
281 return m_pages
.GetCount();
284 void wxRibbonBar::DeletePage(size_t n
)
286 if(n
< m_pages
.GetCount())
288 wxRibbonPage
*page
= m_pages
.Item(n
).page
;
290 // Schedule page object for destruction and not destroying directly
291 // as this function can be called in an event handler and page functions
292 // can be called afeter removing.
293 // Like in wxRibbonButtonBar::OnMouseUp
294 if(!wxTheApp
->IsScheduledForDestruction(page
))
296 wxTheApp
->ScheduleForDestruction(page
);
301 if(m_current_page
== static_cast<int>(n
))
305 if(m_pages
.GetCount() > 0)
307 if(n
>= m_pages
.GetCount())
309 SetActivePage(m_pages
.GetCount() - 1);
313 SetActivePage(n
- 1);
317 else if(m_current_page
> static_cast<int>(n
))
324 void wxRibbonBar::ClearPages()
327 for(i
=0; i
<m_pages
.GetCount(); i
++)
329 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
330 // Schedule page object for destruction and not destroying directly
331 // as this function can be called in an event handler and page functions
332 // can be called afeter removing.
333 // Like in wxRibbonButtonBar::OnMouseUp
334 if(!wxTheApp
->IsScheduledForDestruction(page
))
336 wxTheApp
->ScheduleForDestruction(page
);
345 bool wxRibbonBar::SetActivePage(size_t page
)
347 if(m_current_page
== (int)page
)
352 if(page
>= m_pages
.GetCount())
357 if(m_current_page
!= -1)
359 m_pages
.Item((size_t)m_current_page
).active
= false;
360 m_pages
.Item((size_t)m_current_page
).page
->Hide();
362 m_current_page
= (int)page
;
363 m_pages
.Item(page
).active
= true;
365 wxRibbonPage
* wnd
= m_pages
.Item(page
).page
;
375 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
)
377 size_t numpages
= m_pages
.GetCount();
379 for(i
= 0; i
< numpages
; ++i
)
381 if(m_pages
.Item(i
).page
== page
)
383 return SetActivePage(i
);
389 int wxRibbonBar::GetActivePage() const
391 return m_current_page
;
394 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
)
396 m_tab_margin_left
= left
;
397 m_tab_margin_right
= right
;
399 RecalculateTabSizes();
402 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo
**first
, wxRibbonPageTabInfo
**second
)
404 return (**first
).small_must_have_separator_width
- (**second
).small_must_have_separator_width
;
407 void wxRibbonBar::RecalculateTabSizes()
409 size_t numtabs
= m_pages
.GetCount();
414 int width
= GetSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
;
415 int tabsep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
416 int x
= m_tab_margin_left
;
419 if(width
>= m_tabs_total_width_ideal
)
421 // Simple case: everything at ideal width
423 for(i
= 0; i
< numtabs
; ++i
)
425 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
428 info
.rect
.width
= info
.ideal_width
;
429 info
.rect
.height
= m_tab_height
;
430 x
+= info
.rect
.width
+ tabsep
;
432 m_tab_scroll_buttons_shown
= false;
433 m_tab_scroll_left_button_rect
.SetWidth(0);
434 m_tab_scroll_right_button_rect
.SetWidth(0);
436 else if(width
< m_tabs_total_width_minimum
)
438 // Simple case: everything minimum with scrollbar
440 for(i
= 0; i
< numtabs
; ++i
)
442 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
445 info
.rect
.width
= info
.minimum_width
;
446 info
.rect
.height
= m_tab_height
;
447 x
+= info
.rect
.width
+ tabsep
;
449 if(!m_tab_scroll_buttons_shown
)
451 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
452 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
453 m_tab_scroll_buttons_shown
= true;
456 wxClientDC
temp_dc(this);
457 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());
458 m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
);
459 m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
);
460 m_tab_scroll_left_button_rect
.SetY(0);
461 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());
462 m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
);
463 m_tab_scroll_right_button_rect
.SetX(GetClientSize().GetWidth() - m_tab_margin_right
- m_tab_scroll_right_button_rect
.GetWidth());
464 m_tab_scroll_right_button_rect
.SetY(0);
466 if(m_tab_scroll_amount
== 0)
468 m_tab_scroll_left_button_rect
.SetWidth(0);
470 else if(m_tab_scroll_amount
+ width
>= m_tabs_total_width_minimum
)
472 m_tab_scroll_amount
= m_tabs_total_width_minimum
- width
;
473 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
474 m_tab_scroll_right_button_rect
.SetWidth(0);
476 for(i
= 0; i
< numtabs
; ++i
)
478 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
479 info
.rect
.x
-= m_tab_scroll_amount
;
484 m_tab_scroll_buttons_shown
= false;
485 m_tab_scroll_left_button_rect
.SetWidth(0);
486 m_tab_scroll_right_button_rect
.SetWidth(0);
487 // Complex case: everything sized such that: minimum <= width < ideal
490 1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
491 2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
492 3) Uniformly reduce all tabs down to their minimum width
494 int smallest_tab_width
= INT_MAX
;
495 int total_small_width
= tabsep
* (numtabs
- 1);
497 for(i
= 0; i
< numtabs
; ++i
)
499 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
500 if(info
.small_must_have_separator_width
< smallest_tab_width
)
502 smallest_tab_width
= info
.small_must_have_separator_width
;
504 total_small_width
+= info
.small_must_have_separator_width
;
506 if(width
>= total_small_width
)
509 int total_delta
= m_tabs_total_width_ideal
- total_small_width
;
510 total_small_width
-= tabsep
* (numtabs
- 1);
511 width
-= tabsep
* (numtabs
- 1);
512 for(i
= 0; i
< numtabs
; ++i
)
514 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
515 int delta
= info
.ideal_width
- info
.small_must_have_separator_width
;
518 info
.rect
.width
= info
.small_must_have_separator_width
+ delta
* (width
- total_small_width
) / total_delta
;
519 info
.rect
.height
= m_tab_height
;
521 x
+= info
.rect
.width
+ tabsep
;
522 total_delta
-= delta
;
523 total_small_width
-= info
.small_must_have_separator_width
;
524 width
-= info
.rect
.width
;
529 total_small_width
= tabsep
* (numtabs
- 1);
530 for(i
= 0; i
< numtabs
; ++i
)
532 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
533 if(info
.minimum_width
< smallest_tab_width
)
535 total_small_width
+= smallest_tab_width
;
539 total_small_width
+= info
.minimum_width
;
542 if(width
>= total_small_width
)
545 wxRibbonPageTabInfoArray sorted_pages
;
546 for(i
= 0; i
< numtabs
; ++i
)
548 // Sneaky obj array trickery to not copy the tab descriptors
549 sorted_pages
.Add(&m_pages
.Item(i
));
551 sorted_pages
.Sort(OrderPageTabInfoBySmallWidthAsc
);
552 width
-= tabsep
* (numtabs
- 1);
553 for(i
= 0; i
< numtabs
; ++i
)
555 wxRibbonPageTabInfo
& info
= sorted_pages
.Item(i
);
556 if(info
.small_must_have_separator_width
* (int)(numtabs
- i
) <= width
)
558 info
.rect
.width
= info
.small_must_have_separator_width
;;
562 info
.rect
.width
= width
/ (numtabs
- i
);
564 width
-= info
.rect
.width
;
566 for(i
= 0; i
< numtabs
; ++i
)
568 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
571 info
.rect
.height
= m_tab_height
;
572 x
+= info
.rect
.width
+ tabsep
;
573 sorted_pages
.Detach(numtabs
- (i
+ 1));
579 total_small_width
= (smallest_tab_width
+ tabsep
) * numtabs
- tabsep
;
580 int total_delta
= total_small_width
- m_tabs_total_width_minimum
;
581 total_small_width
= m_tabs_total_width_minimum
- tabsep
* (numtabs
- 1);
582 width
-= tabsep
* (numtabs
- 1);
583 for(i
= 0; i
< numtabs
; ++i
)
585 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
586 int delta
= smallest_tab_width
- info
.minimum_width
;
589 info
.rect
.width
= info
.minimum_width
+ delta
* (width
- total_small_width
) / total_delta
;
590 info
.rect
.height
= m_tab_height
;
592 x
+= info
.rect
.width
+ tabsep
;
593 total_delta
-= delta
;
594 total_small_width
-= info
.minimum_width
;
595 width
-= info
.rect
.width
;
602 wxRibbonBar::wxRibbonBar()
605 m_tabs_total_width_ideal
= 0;
606 m_tabs_total_width_minimum
= 0;
607 m_tab_margin_left
= 0;
608 m_tab_margin_right
= 0;
610 m_tab_scroll_amount
= 0;
612 m_current_hovered_page
= -1;
613 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
614 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
615 m_tab_scroll_buttons_shown
= false;
616 m_arePanelsShown
= true;
619 wxRibbonBar::wxRibbonBar(wxWindow
* parent
,
624 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
629 wxRibbonBar::~wxRibbonBar()
631 SetArtProvider(NULL
);
634 bool wxRibbonBar::Create(wxWindow
* parent
,
640 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
648 void wxRibbonBar::CommonInit(long style
)
650 SetName(wxT("wxRibbonBar"));
653 m_tabs_total_width_ideal
= 0;
654 m_tabs_total_width_minimum
= 0;
655 m_tab_margin_left
= 50;
656 m_tab_margin_right
= 20;
657 m_tab_height
= 20; // initial guess
658 m_tab_scroll_amount
= 0;
660 m_current_hovered_page
= -1;
661 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
662 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
663 m_tab_scroll_buttons_shown
= false;
664 m_arePanelsShown
= true;
668 SetArtProvider(new wxRibbonDefaultArtProvider
);
670 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
673 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
)
675 wxRibbonArtProvider
*old
= m_art
;
680 art
->SetFlags(m_flags
);
682 size_t numpages
= m_pages
.GetCount();
684 for(i
= 0; i
< numpages
; ++i
)
686 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
687 if(page
->GetArtProvider() != art
)
689 page
->SetArtProvider(art
);
696 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
698 wxAutoBufferedPaintDC
dc(this);
700 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
)
702 // Nothing to do in the tab area, and the page area is handled by the active page
706 DoEraseBackground(dc
);
708 size_t numtabs
= m_pages
.GetCount();
709 double sep_visibility
= 0.0;
710 bool draw_sep
= false;
711 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
712 if(m_tab_scroll_buttons_shown
)
714 tabs_rect
.x
+= m_tab_scroll_left_button_rect
.GetWidth();
715 tabs_rect
.width
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth();
718 for(i
= 0; i
< numtabs
; ++i
)
720 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
722 dc
.DestroyClippingRegion();
723 if(m_tab_scroll_buttons_shown
)
725 if(!tabs_rect
.Intersects(info
.rect
))
727 dc
.SetClippingRegion(tabs_rect
);
729 dc
.SetClippingRegion(info
.rect
);
730 m_art
->DrawTab(dc
, this, info
);
732 if(info
.rect
.width
< info
.small_begin_need_separator_width
)
735 if(info
.rect
.width
< info
.small_must_have_separator_width
)
737 sep_visibility
+= 1.0;
741 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
);
747 wxRect rect
= m_pages
.Item(0).rect
;
748 rect
.width
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
749 sep_visibility
/= (double)numtabs
;
750 for(i
= 0; i
< numtabs
- 1; ++i
)
752 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
753 rect
.x
= info
.rect
.x
+ info
.rect
.width
;
755 if(m_tab_scroll_buttons_shown
&& !tabs_rect
.Intersects(rect
))
760 dc
.DestroyClippingRegion();
761 dc
.SetClippingRegion(rect
);
762 m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
);
765 if(m_tab_scroll_buttons_shown
)
767 dc
.DestroyClippingRegion();
768 if(m_tab_scroll_left_button_rect
.GetWidth() != 0)
770 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
);
772 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
774 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
);
779 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
781 // Background painting done in main paint handler to reduce screen flicker
784 void wxRibbonBar::DoEraseBackground(wxDC
& dc
)
786 wxRect
tabs(GetSize());
787 tabs
.height
= m_tab_height
;
788 m_art
->DrawTabCtrlBackground(dc
, this, tabs
);
791 void wxRibbonBar::OnSize(wxSizeEvent
& evt
)
793 RecalculateTabSizes();
794 if(m_current_page
!= -1)
796 RepositionPage(m_pages
.Item(m_current_page
).page
);
803 void wxRibbonBar::RepositionPage(wxRibbonPage
*page
)
807 page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h
- m_tab_height
);
810 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
)
812 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
813 if(m_tab_scroll_buttons_shown
)
815 tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth());
816 tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth());
818 if(tabs_rect
.Contains(position
))
820 size_t numtabs
= m_pages
.GetCount();
822 for(i
= 0; i
< numtabs
; ++i
)
824 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
825 if(info
.rect
.Contains(position
))
842 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
)
844 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
845 if(tab
&& tab
!= &m_pages
.Item(m_current_page
))
847 wxRibbonBarEvent
query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
);
848 query
.SetEventObject(this);
849 ProcessWindowEvent(query
);
850 if(query
.IsAllowed())
852 SetActivePage(query
.GetPage());
854 wxRibbonBarEvent
notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
);
855 notification
.SetEventObject(this);
856 ProcessWindowEvent(notification
);
861 if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition()))
863 m_tab_scroll_left_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
866 else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition()))
868 m_tab_scroll_right_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
874 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
))
876 if(!m_tab_scroll_buttons_shown
)
882 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
886 else if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
892 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
893 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
894 ScrollTabBar(amount
* 8);
898 void wxRibbonBar::ScrollTabBar(int amount
)
900 bool show_left
= true;
901 bool show_right
= true;
902 if(m_tab_scroll_amount
+ amount
<= 0)
904 amount
= -m_tab_scroll_amount
;
907 else if(m_tab_scroll_amount
+ amount
+ (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
) >= m_tabs_total_width_minimum
)
909 amount
= m_tabs_total_width_minimum
- m_tab_scroll_amount
- (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
);
916 m_tab_scroll_amount
+= amount
;
917 size_t numtabs
= m_pages
.GetCount();
919 for(i
= 0; i
< numtabs
; ++i
)
921 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
922 info
.rect
.SetX(info
.rect
.GetX() - amount
);
924 if(show_right
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) ||
925 show_left
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0))
927 wxClientDC
temp_dc(this);
930 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());
934 m_tab_scroll_left_button_rect
.SetWidth(0);
939 if(m_tab_scroll_right_button_rect
.GetWidth() == 0)
941 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());
942 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth());
947 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
949 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
950 m_tab_scroll_right_button_rect
.SetWidth(0);
958 void wxRibbonBar::RefreshTabBar()
960 wxRect
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
);
961 Refresh(false, &tab_rect
);
964 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
)
966 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
);
969 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
)
971 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
);
974 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
)
976 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
);
979 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
)
981 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
);
984 void wxRibbonBar::OnMouseDoubleClick(wxMouseEvent
& evt
)
986 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_LEFT_DCLICK
);
989 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
)
991 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
994 wxRibbonBarEvent
notification(tab_event_type
, GetId(), tab
->page
);
995 notification
.SetEventObject(this);
996 ProcessWindowEvent(notification
);
1000 void wxRibbonBar::RecalculateMinSize()
1002 wxSize
min_size(wxDefaultCoord
, wxDefaultCoord
);
1003 size_t numtabs
= m_pages
.GetCount();
1006 min_size
= m_pages
.Item(0).page
->GetMinSize();
1009 for(i
= 1; i
< numtabs
; ++i
)
1011 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1012 wxSize page_min
= info
.page
->GetMinSize();
1014 min_size
.x
= wxMax(min_size
.x
, page_min
.x
);
1015 min_size
.y
= wxMax(min_size
.y
, page_min
.y
);
1018 if(min_size
.y
!= wxDefaultCoord
)
1020 // TODO: Decide on best course of action when min height is unspecified
1021 // - should we specify it to the tab minimum, or leave it unspecified?
1022 min_size
.IncBy(0, m_tab_height
);
1025 m_minWidth
= min_size
.GetWidth();
1026 m_minHeight
= m_arePanelsShown
? min_size
.GetHeight() : m_tab_height
;
1029 wxSize
wxRibbonBar::DoGetBestSize() const
1032 if(m_current_page
!= -1)
1034 best
= m_pages
.Item(m_current_page
).page
->GetBestSize();
1036 if(best
.GetHeight() == wxDefaultCoord
)
1038 best
.SetHeight(m_tab_height
);
1042 best
.IncBy(0, m_tab_height
);
1044 if(!m_arePanelsShown
)
1046 best
.SetHeight(m_tab_height
);
1051 #endif // wxUSE_RIBBON