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
);
43 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_TOGGLED
, wxRibbonBarEvent
);
45 IMPLEMENT_CLASS(wxRibbonBar
, wxRibbonControl
)
46 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent
, wxNotifyEvent
)
48 BEGIN_EVENT_TABLE(wxRibbonBar
, wxRibbonControl
)
49 EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground
)
50 EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave
)
51 EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown
)
52 EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp
)
53 EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown
)
54 EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp
)
55 EVT_MOTION(wxRibbonBar::OnMouseMove
)
56 EVT_PAINT(wxRibbonBar::OnPaint
)
57 EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown
)
58 EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp
)
59 EVT_LEFT_DCLICK(wxRibbonBar::OnMouseDoubleClick
)
60 EVT_SIZE(wxRibbonBar::OnSize
)
63 void wxRibbonBar::AddPage(wxRibbonPage
*page
)
65 wxRibbonPageTabInfo info
;
70 info
.highlight
= false;
72 // info.rect not set (intentional)
74 wxClientDC
dcTemp(this);
75 wxString label
= wxEmptyString
;
76 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
77 label
= page
->GetLabel();
78 wxBitmap icon
= wxNullBitmap
;
79 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
80 icon
= page
->GetIcon();
81 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
83 &info
.small_begin_need_separator_width
,
84 &info
.small_must_have_separator_width
,
89 m_tabs_total_width_ideal
= info
.ideal_width
;
90 m_tabs_total_width_minimum
= info
.minimum_width
;
94 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
95 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
96 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
100 page
->Hide(); // Most likely case is that this new page is not the active tab
101 page
->SetArtProvider(m_art
);
103 if(m_pages
.GetCount() == 1)
105 SetActivePage((size_t)0);
109 bool wxRibbonBar::DismissExpandedPanel()
111 if(m_current_page
== -1)
113 return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel();
116 void wxRibbonBar::ShowPanels(bool show
)
118 m_arePanelsShown
= show
;
119 SetMinSize(wxSize(GetSize().GetWidth(), DoGetBestSize().GetHeight()));
121 GetParent()->Layout();
124 void wxRibbonBar::SetWindowStyleFlag(long style
)
128 m_art
->SetFlags(style
);
131 long wxRibbonBar::GetWindowStyleFlag() const
136 bool wxRibbonBar::Realize()
140 wxClientDC
dcTemp(this);
141 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
142 size_t numtabs
= m_pages
.GetCount();
144 for(i
= 0; i
< numtabs
; ++i
)
146 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
149 RepositionPage(info
.page
);
150 if(!info
.page
->Realize())
154 wxString label
= wxEmptyString
;
155 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
156 label
= info
.page
->GetLabel();
157 wxBitmap icon
= wxNullBitmap
;
158 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
159 icon
= info
.page
->GetIcon();
160 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
162 &info
.small_begin_need_separator_width
,
163 &info
.small_must_have_separator_width
,
164 &info
.minimum_width
);
168 m_tabs_total_width_ideal
= info
.ideal_width
;
169 m_tabs_total_width_minimum
= info
.minimum_width
;
173 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
174 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
177 m_tab_height
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
);
179 RecalculateMinSize();
180 RecalculateTabSizes();
186 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
)
190 int hovered_page
= -1;
191 bool refresh_tabs
= false;
194 // It is quite likely that the mouse moved a small amount and is still over the same tab
195 if(m_current_hovered_page
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
))
197 hovered_page
= m_current_hovered_page
;
198 // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
199 if(m_tab_scroll_buttons_shown
)
201 if(x
>= m_tab_scroll_right_button_rect
.GetX() || x
< m_tab_scroll_left_button_rect
.GetRight())
209 HitTestTabs(evt
.GetPosition(), &hovered_page
);
212 if(hovered_page
!= m_current_hovered_page
)
214 if(m_current_hovered_page
!= -1)
216 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
218 m_current_hovered_page
= hovered_page
;
219 if(m_current_hovered_page
!= -1)
221 m_pages
.Item((int)m_current_hovered_page
).hovered
= true;
225 if(m_tab_scroll_buttons_shown
)
227 #define SET_FLAG(variable, flag) \
228 { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
229 #define UNSET_FLAG(variable, flag) \
230 { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
232 if(m_tab_scroll_left_button_rect
.Contains(x
, y
))
233 SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
235 UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
237 if(m_tab_scroll_right_button_rect
.Contains(x
, y
))
238 SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
240 UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
248 HitTestToggleButton(evt
.GetPosition());
251 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
253 // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
254 // can leave the window quickly and leave a tab in the hovered state.
255 bool refresh_tabs
= false;
256 if(m_current_hovered_page
!= -1)
258 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
259 m_current_hovered_page
= -1;
262 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
264 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
267 if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
269 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
276 if(m_toggle_button_hovered
)
278 m_bar_hovered
= false;
279 m_toggle_button_hovered
= false;
284 wxRibbonPage
* wxRibbonBar::GetPage(int n
)
286 if(n
< 0 || (size_t)n
>= m_pages
.GetCount())
288 return m_pages
.Item(n
).page
;
291 size_t wxRibbonBar::GetPageCount() const
293 return m_pages
.GetCount();
296 bool wxRibbonBar::IsPageShown(size_t page
) const
298 if (page
>= m_pages
.GetCount())
300 return m_pages
.Item(page
).shown
;
303 void wxRibbonBar::ShowPage(size_t page
, bool show
)
305 if(page
>= m_pages
.GetCount())
307 m_pages
.Item(page
).shown
= show
;
310 bool wxRibbonBar::IsPageHighlighted(size_t page
) const
312 if (page
>= m_pages
.GetCount())
314 return m_pages
.Item(page
).highlight
;
317 void wxRibbonBar::AddPageHighlight(size_t page
, bool highlight
)
319 if(page
>= m_pages
.GetCount())
321 m_pages
.Item(page
).highlight
= highlight
;
324 void wxRibbonBar::DeletePage(size_t n
)
326 if(n
< m_pages
.GetCount())
328 wxRibbonPage
*page
= m_pages
.Item(n
).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
);
341 if(m_current_page
== static_cast<int>(n
))
345 if(m_pages
.GetCount() > 0)
347 if(n
>= m_pages
.GetCount())
349 SetActivePage(m_pages
.GetCount() - 1);
353 SetActivePage(n
- 1);
357 else if(m_current_page
> static_cast<int>(n
))
364 void wxRibbonBar::ClearPages()
367 for(i
=0; i
<m_pages
.GetCount(); i
++)
369 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
370 // Schedule page object for destruction and not destroying directly
371 // as this function can be called in an event handler and page functions
372 // can be called afeter removing.
373 // Like in wxRibbonButtonBar::OnMouseUp
374 if(!wxTheApp
->IsScheduledForDestruction(page
))
376 wxTheApp
->ScheduleForDestruction(page
);
385 bool wxRibbonBar::SetActivePage(size_t page
)
387 if(m_current_page
== (int)page
)
392 if(page
>= m_pages
.GetCount())
397 if(m_current_page
!= -1)
399 m_pages
.Item((size_t)m_current_page
).active
= false;
400 m_pages
.Item((size_t)m_current_page
).page
->Hide();
402 m_current_page
= (int)page
;
403 m_pages
.Item(page
).active
= true;
404 m_pages
.Item(page
).shown
= true;
406 wxRibbonPage
* wnd
= m_pages
.Item(page
).page
;
416 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
)
418 size_t numpages
= m_pages
.GetCount();
420 for(i
= 0; i
< numpages
; ++i
)
422 if(m_pages
.Item(i
).page
== page
)
424 return SetActivePage(i
);
430 int wxRibbonBar::GetPageNumber(wxRibbonPage
* page
) const
432 size_t numpages
= m_pages
.GetCount();
433 for(size_t i
= 0; i
< numpages
; ++i
)
435 if(m_pages
.Item(i
).page
== page
)
444 int wxRibbonBar::GetActivePage() const
446 return m_current_page
;
449 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
)
451 m_tab_margin_left
= left
;
452 m_tab_margin_right
= right
;
454 RecalculateTabSizes();
457 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo
**first
, wxRibbonPageTabInfo
**second
)
459 return (**first
).small_must_have_separator_width
- (**second
).small_must_have_separator_width
;
462 void wxRibbonBar::RecalculateTabSizes()
464 size_t numtabs
= m_pages
.GetCount();
469 int width
= GetSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
;
470 int tabsep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
471 int x
= m_tab_margin_left
;
474 if(width
>= m_tabs_total_width_ideal
)
476 // Simple case: everything at ideal width
478 for(i
= 0; i
< numtabs
; ++i
)
480 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
485 info
.rect
.width
= info
.ideal_width
;
486 info
.rect
.height
= m_tab_height
;
487 x
+= info
.rect
.width
+ tabsep
;
489 m_tab_scroll_buttons_shown
= false;
490 m_tab_scroll_left_button_rect
.SetWidth(0);
491 m_tab_scroll_right_button_rect
.SetWidth(0);
493 else if(width
< m_tabs_total_width_minimum
)
495 // Simple case: everything minimum with scrollbar
497 for(i
= 0; i
< numtabs
; ++i
)
499 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
504 info
.rect
.width
= info
.minimum_width
;
505 info
.rect
.height
= m_tab_height
;
506 x
+= info
.rect
.width
+ tabsep
;
508 if(!m_tab_scroll_buttons_shown
)
510 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
511 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
512 m_tab_scroll_buttons_shown
= true;
515 wxClientDC
temp_dc(this);
516 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());
517 m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
);
518 m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
);
519 m_tab_scroll_left_button_rect
.SetY(0);
520 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());
521 m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
);
522 m_tab_scroll_right_button_rect
.SetX(GetClientSize().GetWidth() - m_tab_margin_right
- m_tab_scroll_right_button_rect
.GetWidth());
523 m_tab_scroll_right_button_rect
.SetY(0);
525 if(m_tab_scroll_amount
== 0)
527 m_tab_scroll_left_button_rect
.SetWidth(0);
529 else if(m_tab_scroll_amount
+ width
>= m_tabs_total_width_minimum
)
531 m_tab_scroll_amount
= m_tabs_total_width_minimum
- width
;
532 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
533 m_tab_scroll_right_button_rect
.SetWidth(0);
535 for(i
= 0; i
< numtabs
; ++i
)
537 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
540 info
.rect
.x
-= m_tab_scroll_amount
;
545 m_tab_scroll_buttons_shown
= false;
546 m_tab_scroll_left_button_rect
.SetWidth(0);
547 m_tab_scroll_right_button_rect
.SetWidth(0);
548 // Complex case: everything sized such that: minimum <= width < ideal
551 1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
552 2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
553 3) Uniformly reduce all tabs down to their minimum width
555 int smallest_tab_width
= INT_MAX
;
556 int total_small_width
= tabsep
* (numtabs
- 1);
558 for(i
= 0; i
< numtabs
; ++i
)
560 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
563 if(info
.small_must_have_separator_width
< smallest_tab_width
)
565 smallest_tab_width
= info
.small_must_have_separator_width
;
567 total_small_width
+= info
.small_must_have_separator_width
;
569 if(width
>= total_small_width
)
572 int total_delta
= m_tabs_total_width_ideal
- total_small_width
;
573 total_small_width
-= tabsep
* (numtabs
- 1);
574 width
-= tabsep
* (numtabs
- 1);
575 for(i
= 0; i
< numtabs
; ++i
)
577 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
580 int delta
= info
.ideal_width
- info
.small_must_have_separator_width
;
583 info
.rect
.width
= info
.small_must_have_separator_width
+ delta
* (width
- total_small_width
) / total_delta
;
584 info
.rect
.height
= m_tab_height
;
586 x
+= info
.rect
.width
+ tabsep
;
587 total_delta
-= delta
;
588 total_small_width
-= info
.small_must_have_separator_width
;
589 width
-= info
.rect
.width
;
594 total_small_width
= tabsep
* (numtabs
- 1);
595 for(i
= 0; i
< numtabs
; ++i
)
597 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
600 if(info
.minimum_width
< smallest_tab_width
)
602 total_small_width
+= smallest_tab_width
;
606 total_small_width
+= info
.minimum_width
;
609 if(width
>= total_small_width
)
612 wxRibbonPageTabInfoArray sorted_pages
;
613 for(i
= 0; i
< numtabs
; ++i
)
615 // Sneaky obj array trickery to not copy the tab descriptors
616 if (!m_pages
.Item(i
).shown
)
618 sorted_pages
.Add(&m_pages
.Item(i
));
620 sorted_pages
.Sort(OrderPageTabInfoBySmallWidthAsc
);
621 width
-= tabsep
* (numtabs
- 1);
622 for(i
= 0; i
< numtabs
; ++i
)
624 wxRibbonPageTabInfo
& info
= sorted_pages
.Item(i
);
627 if(info
.small_must_have_separator_width
* (int)(numtabs
- i
) <= width
)
629 info
.rect
.width
= info
.small_must_have_separator_width
;;
633 info
.rect
.width
= width
/ (numtabs
- i
);
635 width
-= info
.rect
.width
;
637 for(i
= 0; i
< numtabs
; ++i
)
639 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
644 info
.rect
.height
= m_tab_height
;
645 x
+= info
.rect
.width
+ tabsep
;
646 sorted_pages
.Detach(numtabs
- (i
+ 1));
652 total_small_width
= (smallest_tab_width
+ tabsep
) * numtabs
- tabsep
;
653 int total_delta
= total_small_width
- m_tabs_total_width_minimum
;
654 total_small_width
= m_tabs_total_width_minimum
- tabsep
* (numtabs
- 1);
655 width
-= tabsep
* (numtabs
- 1);
656 for(i
= 0; i
< numtabs
; ++i
)
658 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
661 int delta
= smallest_tab_width
- info
.minimum_width
;
664 info
.rect
.width
= info
.minimum_width
+ delta
* (width
- total_small_width
) / total_delta
;
665 info
.rect
.height
= m_tab_height
;
667 x
+= info
.rect
.width
+ tabsep
;
668 total_delta
-= delta
;
669 total_small_width
-= info
.minimum_width
;
670 width
-= info
.rect
.width
;
677 wxRibbonBar::wxRibbonBar()
680 m_tabs_total_width_ideal
= 0;
681 m_tabs_total_width_minimum
= 0;
682 m_tab_margin_left
= 0;
683 m_tab_margin_right
= 0;
685 m_tab_scroll_amount
= 0;
687 m_current_hovered_page
= -1;
688 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
689 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
690 m_tab_scroll_buttons_shown
= false;
691 m_arePanelsShown
= true;
694 wxRibbonBar::wxRibbonBar(wxWindow
* parent
,
699 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
704 wxRibbonBar::~wxRibbonBar()
706 SetArtProvider(NULL
);
709 bool wxRibbonBar::Create(wxWindow
* parent
,
715 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
723 void wxRibbonBar::CommonInit(long style
)
725 SetName(wxT("wxRibbonBar"));
728 m_tabs_total_width_ideal
= 0;
729 m_tabs_total_width_minimum
= 0;
730 m_tab_margin_left
= 50;
731 m_tab_margin_right
= 20;
732 m_tab_height
= 20; // initial guess
733 m_tab_scroll_amount
= 0;
735 m_current_hovered_page
= -1;
736 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
737 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
738 m_tab_scroll_buttons_shown
= false;
739 m_arePanelsShown
= true;
743 SetArtProvider(new wxRibbonDefaultArtProvider
);
745 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
747 m_toggle_button_hovered
= false;
748 m_bar_hovered
= false;
751 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
)
753 wxRibbonArtProvider
*old
= m_art
;
758 art
->SetFlags(m_flags
);
760 size_t numpages
= m_pages
.GetCount();
762 for(i
= 0; i
< numpages
; ++i
)
764 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
765 if(page
->GetArtProvider() != art
)
767 page
->SetArtProvider(art
);
774 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
776 wxAutoBufferedPaintDC
dc(this);
778 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
)
780 // Nothing to do in the tab area, and the page area is handled by the active page
784 DoEraseBackground(dc
);
786 m_toggle_button_rect
= m_art
->GetBarToggleButtonArea(dc
, this, GetSize());
788 size_t numtabs
= m_pages
.GetCount();
789 double sep_visibility
= 0.0;
790 bool draw_sep
= false;
791 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
792 if(m_tab_scroll_buttons_shown
)
794 tabs_rect
.x
+= m_tab_scroll_left_button_rect
.GetWidth();
795 tabs_rect
.width
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth();
798 for(i
= 0; i
< numtabs
; ++i
)
800 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
804 dc
.DestroyClippingRegion();
805 if(m_tab_scroll_buttons_shown
)
807 if(!tabs_rect
.Intersects(info
.rect
))
809 dc
.SetClippingRegion(tabs_rect
);
811 dc
.SetClippingRegion(info
.rect
);
812 m_art
->DrawTab(dc
, this, info
);
814 if(info
.rect
.width
< info
.small_begin_need_separator_width
)
817 if(info
.rect
.width
< info
.small_must_have_separator_width
)
819 sep_visibility
+= 1.0;
823 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
);
829 wxRect rect
= m_pages
.Item(0).rect
;
830 rect
.width
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
831 sep_visibility
/= (double)numtabs
;
832 for(i
= 0; i
< numtabs
- 1; ++i
)
834 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
837 rect
.x
= info
.rect
.x
+ info
.rect
.width
;
839 if(m_tab_scroll_buttons_shown
&& !tabs_rect
.Intersects(rect
))
844 dc
.DestroyClippingRegion();
845 dc
.SetClippingRegion(rect
);
846 m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
);
849 if(m_tab_scroll_buttons_shown
)
851 dc
.DestroyClippingRegion();
852 if(m_tab_scroll_left_button_rect
.GetWidth() != 0)
854 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
);
856 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
858 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
);
861 wxRect
rect(GetClientSize().GetWidth() - 30, 6, 12, 12);
862 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
863 m_art
->DrawToggleButton(dc
, this, rect
, ArePanelsShown());
866 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
868 // Background painting done in main paint handler to reduce screen flicker
871 void wxRibbonBar::DoEraseBackground(wxDC
& dc
)
873 wxRect
tabs(GetSize());
874 tabs
.height
= m_tab_height
;
875 m_art
->DrawTabCtrlBackground(dc
, this, tabs
);
878 void wxRibbonBar::OnSize(wxSizeEvent
& evt
)
880 RecalculateTabSizes();
881 if(m_current_page
!= -1)
883 RepositionPage(m_pages
.Item(m_current_page
).page
);
890 void wxRibbonBar::RepositionPage(wxRibbonPage
*page
)
894 page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h
- m_tab_height
);
897 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
)
899 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
900 if(m_tab_scroll_buttons_shown
)
902 tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth());
903 tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth());
905 if(tabs_rect
.Contains(position
))
907 size_t numtabs
= m_pages
.GetCount();
909 for(i
= 0; i
< numtabs
; ++i
)
911 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
914 if(info
.rect
.Contains(position
))
931 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
)
933 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
934 if(tab
&& tab
!= &m_pages
.Item(m_current_page
))
936 wxRibbonBarEvent
query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
);
937 query
.SetEventObject(this);
938 ProcessWindowEvent(query
);
939 if(query
.IsAllowed())
941 SetActivePage(query
.GetPage());
943 wxRibbonBarEvent
notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
);
944 notification
.SetEventObject(this);
945 ProcessWindowEvent(notification
);
950 if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition()))
952 m_tab_scroll_left_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
955 else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition()))
957 m_tab_scroll_right_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
962 wxPoint position
= evt
.GetPosition();
964 if(position
.x
>= 0 && position
.y
>= 0)
966 wxSize size
= GetSize();
967 if(position
.x
< size
.GetWidth() && position
.y
< size
.GetHeight())
969 if(m_toggle_button_rect
.Contains(position
))
971 ShowPanels(!ArePanelsShown());
972 wxRibbonBarEvent
event(wxEVT_COMMAND_RIBBONBAR_TOGGLED
, GetId());
973 event
.SetEventObject(this);
974 ProcessWindowEvent(event
);
980 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
))
982 if(!m_tab_scroll_buttons_shown
)
988 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
992 else if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
998 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
999 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
1000 ScrollTabBar(amount
* 8);
1004 void wxRibbonBar::ScrollTabBar(int amount
)
1006 bool show_left
= true;
1007 bool show_right
= true;
1008 if(m_tab_scroll_amount
+ amount
<= 0)
1010 amount
= -m_tab_scroll_amount
;
1013 else if(m_tab_scroll_amount
+ amount
+ (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
) >= m_tabs_total_width_minimum
)
1015 amount
= m_tabs_total_width_minimum
- m_tab_scroll_amount
- (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
);
1022 m_tab_scroll_amount
+= amount
;
1023 size_t numtabs
= m_pages
.GetCount();
1025 for(i
= 0; i
< numtabs
; ++i
)
1027 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1030 info
.rect
.SetX(info
.rect
.GetX() - amount
);
1032 if(show_right
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) ||
1033 show_left
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0))
1035 wxClientDC
temp_dc(this);
1038 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());
1042 m_tab_scroll_left_button_rect
.SetWidth(0);
1047 if(m_tab_scroll_right_button_rect
.GetWidth() == 0)
1049 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());
1050 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth());
1055 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
1057 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
1058 m_tab_scroll_right_button_rect
.SetWidth(0);
1066 void wxRibbonBar::RefreshTabBar()
1068 wxRect
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
);
1069 Refresh(false, &tab_rect
);
1072 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
)
1074 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
);
1077 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
)
1079 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
);
1082 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
)
1084 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
);
1087 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
)
1089 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
);
1092 void wxRibbonBar::OnMouseDoubleClick(wxMouseEvent
& evt
)
1094 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_LEFT_DCLICK
);
1097 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
)
1099 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
1102 wxRibbonBarEvent
notification(tab_event_type
, GetId(), tab
->page
);
1103 notification
.SetEventObject(this);
1104 ProcessWindowEvent(notification
);
1108 void wxRibbonBar::RecalculateMinSize()
1110 wxSize
min_size(wxDefaultCoord
, wxDefaultCoord
);
1111 size_t numtabs
= m_pages
.GetCount();
1114 min_size
= m_pages
.Item(0).page
->GetMinSize();
1117 for(i
= 1; i
< numtabs
; ++i
)
1119 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1122 wxSize page_min
= info
.page
->GetMinSize();
1124 min_size
.x
= wxMax(min_size
.x
, page_min
.x
);
1125 min_size
.y
= wxMax(min_size
.y
, page_min
.y
);
1128 if(min_size
.y
!= wxDefaultCoord
)
1130 // TODO: Decide on best course of action when min height is unspecified
1131 // - should we specify it to the tab minimum, or leave it unspecified?
1132 min_size
.IncBy(0, m_tab_height
);
1135 m_minWidth
= min_size
.GetWidth();
1136 m_minHeight
= m_arePanelsShown
? min_size
.GetHeight() : m_tab_height
;
1139 wxSize
wxRibbonBar::DoGetBestSize() const
1142 if(m_current_page
!= -1)
1144 best
= m_pages
.Item(m_current_page
).page
->GetBestSize();
1146 if(best
.GetHeight() == wxDefaultCoord
)
1148 best
.SetHeight(m_tab_height
);
1152 best
.IncBy(0, m_tab_height
);
1154 if(!m_arePanelsShown
)
1156 best
.SetHeight(m_tab_height
);
1161 void wxRibbonBar::HitTestToggleButton(wxPoint position
)
1163 bool hovered
= false, toggle_button_hovered
= false;
1164 if(position
.x
>= 0 && position
.y
>= 0)
1166 wxSize size
= GetSize();
1167 if(position
.x
< size
.GetWidth() && position
.y
< size
.GetHeight())
1174 toggle_button_hovered
= (m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
) &&
1175 m_toggle_button_rect
.Contains(position
);
1177 if(hovered
!= m_bar_hovered
|| toggle_button_hovered
!= m_toggle_button_hovered
)
1179 m_bar_hovered
= hovered
;
1180 m_toggle_button_hovered
= toggle_button_hovered
;
1186 #endif // wxUSE_RIBBON