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
);
44 wxDEFINE_EVENT(wxEVT_COMMAND_RIBBONBAR_HELP_CLICKED
, wxRibbonBarEvent
);
46 IMPLEMENT_CLASS(wxRibbonBar
, wxRibbonControl
)
47 IMPLEMENT_DYNAMIC_CLASS(wxRibbonBarEvent
, wxNotifyEvent
)
49 BEGIN_EVENT_TABLE(wxRibbonBar
, wxRibbonControl
)
50 EVT_ERASE_BACKGROUND(wxRibbonBar::OnEraseBackground
)
51 EVT_LEAVE_WINDOW(wxRibbonBar::OnMouseLeave
)
52 EVT_LEFT_DOWN(wxRibbonBar::OnMouseLeftDown
)
53 EVT_LEFT_UP(wxRibbonBar::OnMouseLeftUp
)
54 EVT_MIDDLE_DOWN(wxRibbonBar::OnMouseMiddleDown
)
55 EVT_MIDDLE_UP(wxRibbonBar::OnMouseMiddleUp
)
56 EVT_MOTION(wxRibbonBar::OnMouseMove
)
57 EVT_PAINT(wxRibbonBar::OnPaint
)
58 EVT_RIGHT_DOWN(wxRibbonBar::OnMouseRightDown
)
59 EVT_RIGHT_UP(wxRibbonBar::OnMouseRightUp
)
60 EVT_LEFT_DCLICK(wxRibbonBar::OnMouseDoubleClick
)
61 EVT_SIZE(wxRibbonBar::OnSize
)
62 EVT_KILL_FOCUS(wxRibbonBar::OnKillFocus
)
65 void wxRibbonBar::AddPage(wxRibbonPage
*page
)
67 wxRibbonPageTabInfo info
;
72 info
.highlight
= false;
74 // info.rect not set (intentional)
76 wxClientDC
dcTemp(this);
77 wxString label
= wxEmptyString
;
78 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
79 label
= page
->GetLabel();
80 wxBitmap icon
= wxNullBitmap
;
81 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
82 icon
= page
->GetIcon();
83 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
85 &info
.small_begin_need_separator_width
,
86 &info
.small_must_have_separator_width
,
91 m_tabs_total_width_ideal
= info
.ideal_width
;
92 m_tabs_total_width_minimum
= info
.minimum_width
;
96 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
97 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
98 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
102 page
->Hide(); // Most likely case is that this new page is not the active tab
103 page
->SetArtProvider(m_art
);
105 if(m_pages
.GetCount() == 1)
107 SetActivePage((size_t)0);
111 bool wxRibbonBar::DismissExpandedPanel()
113 if(m_current_page
== -1)
115 return m_pages
.Item(m_current_page
).page
->DismissExpandedPanel();
118 void wxRibbonBar::ShowPanels(bool show
)
120 m_arePanelsShown
= show
;
121 SetMinSize(wxSize(GetSize().GetWidth(), DoGetBestSize().GetHeight()));
123 GetParent()->Layout();
126 void wxRibbonBar::SetWindowStyleFlag(long style
)
130 m_art
->SetFlags(style
);
133 long wxRibbonBar::GetWindowStyleFlag() const
138 bool wxRibbonBar::Realize()
142 wxClientDC
dcTemp(this);
143 int sep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
144 size_t numtabs
= m_pages
.GetCount();
146 for(i
= 0; i
< numtabs
; ++i
)
148 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
151 RepositionPage(info
.page
);
152 if(!info
.page
->Realize())
156 wxString label
= wxEmptyString
;
157 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_LABELS
)
158 label
= info
.page
->GetLabel();
159 wxBitmap icon
= wxNullBitmap
;
160 if(m_flags
& wxRIBBON_BAR_SHOW_PAGE_ICONS
)
161 icon
= info
.page
->GetIcon();
162 m_art
->GetBarTabWidth(dcTemp
, this, label
, icon
,
164 &info
.small_begin_need_separator_width
,
165 &info
.small_must_have_separator_width
,
166 &info
.minimum_width
);
170 m_tabs_total_width_ideal
= info
.ideal_width
;
171 m_tabs_total_width_minimum
= info
.minimum_width
;
175 m_tabs_total_width_ideal
+= sep
+ info
.ideal_width
;
176 m_tabs_total_width_minimum
+= sep
+ info
.minimum_width
;
179 m_tab_height
= m_art
->GetTabCtrlHeight(dcTemp
, this, m_pages
);
181 RecalculateMinSize();
182 RecalculateTabSizes();
188 void wxRibbonBar::OnMouseMove(wxMouseEvent
& evt
)
192 int hovered_page
= -1;
193 bool refresh_tabs
= false;
196 // It is quite likely that the mouse moved a small amount and is still over the same tab
197 if(m_current_hovered_page
!= -1 && m_pages
.Item((size_t)m_current_hovered_page
).rect
.Contains(x
, y
))
199 hovered_page
= m_current_hovered_page
;
200 // But be careful, if tabs can be scrolled, then parts of the tab rect may not be valid
201 if(m_tab_scroll_buttons_shown
)
203 if(x
>= m_tab_scroll_right_button_rect
.GetX() || x
< m_tab_scroll_left_button_rect
.GetRight())
211 HitTestTabs(evt
.GetPosition(), &hovered_page
);
214 if(hovered_page
!= m_current_hovered_page
)
216 if(m_current_hovered_page
!= -1)
218 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
220 m_current_hovered_page
= hovered_page
;
221 if(m_current_hovered_page
!= -1)
223 m_pages
.Item((int)m_current_hovered_page
).hovered
= true;
227 if(m_tab_scroll_buttons_shown
)
229 #define SET_FLAG(variable, flag) \
230 { if(((variable) & (flag)) != (flag)) { variable |= (flag); refresh_tabs = true; }}
231 #define UNSET_FLAG(variable, flag) \
232 { if((variable) & (flag)) { variable &= ~(flag); refresh_tabs = true; }}
234 if(m_tab_scroll_left_button_rect
.Contains(x
, y
))
235 SET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
237 UNSET_FLAG(m_tab_scroll_left_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
239 if(m_tab_scroll_right_button_rect
.Contains(x
, y
))
240 SET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
242 UNSET_FLAG(m_tab_scroll_right_button_state
, wxRIBBON_SCROLL_BTN_HOVERED
)
250 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
251 HitTestRibbonButton(m_toggle_button_rect
, evt
.GetPosition(), m_toggle_button_hovered
);
252 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
253 HitTestRibbonButton(m_help_button_rect
, evt
.GetPosition(), m_help_button_hovered
);
256 void wxRibbonBar::OnMouseLeave(wxMouseEvent
& WXUNUSED(evt
))
258 // The ribbon bar is (usually) at the top of a window, and at least on MSW, the mouse
259 // can leave the window quickly and leave a tab in the hovered state.
260 bool refresh_tabs
= false;
261 if(m_current_hovered_page
!= -1)
263 m_pages
.Item((int)m_current_hovered_page
).hovered
= false;
264 m_current_hovered_page
= -1;
267 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
269 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
272 if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_HOVERED
)
274 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_HOVERED
;
281 if(m_toggle_button_hovered
)
283 m_bar_hovered
= false;
284 m_toggle_button_hovered
= false;
287 if ( m_help_button_hovered
)
289 m_help_button_hovered
= false;
290 m_bar_hovered
= false;
295 wxRibbonPage
* wxRibbonBar::GetPage(int n
)
297 if(n
< 0 || (size_t)n
>= m_pages
.GetCount())
299 return m_pages
.Item(n
).page
;
302 size_t wxRibbonBar::GetPageCount() const
304 return m_pages
.GetCount();
307 bool wxRibbonBar::IsPageShown(size_t page
) const
309 if (page
>= m_pages
.GetCount())
311 return m_pages
.Item(page
).shown
;
314 void wxRibbonBar::ShowPage(size_t page
, bool show
)
316 if(page
>= m_pages
.GetCount())
318 m_pages
.Item(page
).shown
= show
;
321 bool wxRibbonBar::IsPageHighlighted(size_t page
) const
323 if (page
>= m_pages
.GetCount())
325 return m_pages
.Item(page
).highlight
;
328 void wxRibbonBar::AddPageHighlight(size_t page
, bool highlight
)
330 if(page
>= m_pages
.GetCount())
332 m_pages
.Item(page
).highlight
= highlight
;
335 void wxRibbonBar::DeletePage(size_t n
)
337 if(n
< m_pages
.GetCount())
339 wxRibbonPage
*page
= m_pages
.Item(n
).page
;
341 // Schedule page object for destruction and not destroying directly
342 // as this function can be called in an event handler and page functions
343 // can be called afeter removing.
344 // Like in wxRibbonButtonBar::OnMouseUp
345 if(!wxTheApp
->IsScheduledForDestruction(page
))
347 wxTheApp
->ScheduleForDestruction(page
);
352 if(m_current_page
== static_cast<int>(n
))
356 if(m_pages
.GetCount() > 0)
358 if(n
>= m_pages
.GetCount())
360 SetActivePage(m_pages
.GetCount() - 1);
364 SetActivePage(n
- 1);
368 else if(m_current_page
> static_cast<int>(n
))
375 void wxRibbonBar::ClearPages()
378 for(i
=0; i
<m_pages
.GetCount(); i
++)
380 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
381 // Schedule page object for destruction and not destroying directly
382 // as this function can be called in an event handler and page functions
383 // can be called afeter removing.
384 // Like in wxRibbonButtonBar::OnMouseUp
385 if(!wxTheApp
->IsScheduledForDestruction(page
))
387 wxTheApp
->ScheduleForDestruction(page
);
396 bool wxRibbonBar::SetActivePage(size_t page
)
398 if(m_current_page
== (int)page
)
403 if(page
>= m_pages
.GetCount())
408 if(m_current_page
!= -1)
410 m_pages
.Item((size_t)m_current_page
).active
= false;
411 m_pages
.Item((size_t)m_current_page
).page
->Hide();
413 m_current_page
= (int)page
;
414 m_pages
.Item(page
).active
= true;
415 m_pages
.Item(page
).shown
= true;
417 wxRibbonPage
* wnd
= m_pages
.Item(page
).page
;
427 bool wxRibbonBar::SetActivePage(wxRibbonPage
* page
)
429 size_t numpages
= m_pages
.GetCount();
431 for(i
= 0; i
< numpages
; ++i
)
433 if(m_pages
.Item(i
).page
== page
)
435 return SetActivePage(i
);
441 int wxRibbonBar::GetPageNumber(wxRibbonPage
* page
) const
443 size_t numpages
= m_pages
.GetCount();
444 for(size_t i
= 0; i
< numpages
; ++i
)
446 if(m_pages
.Item(i
).page
== page
)
455 int wxRibbonBar::GetActivePage() const
457 return m_current_page
;
460 void wxRibbonBar::SetTabCtrlMargins(int left
, int right
)
462 m_tab_margin_left
= left
;
463 m_tab_margin_right
= right
;
465 RecalculateTabSizes();
468 static int OrderPageTabInfoBySmallWidthAsc(wxRibbonPageTabInfo
**first
, wxRibbonPageTabInfo
**second
)
470 return (**first
).small_must_have_separator_width
- (**second
).small_must_have_separator_width
;
473 void wxRibbonBar::RecalculateTabSizes()
475 size_t numtabs
= m_pages
.GetCount();
480 int width
= GetSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
;
481 int tabsep
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
482 int x
= m_tab_margin_left
;
485 if(width
>= m_tabs_total_width_ideal
)
487 // Simple case: everything at ideal width
489 for(i
= 0; i
< numtabs
; ++i
)
491 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
496 info
.rect
.width
= info
.ideal_width
;
497 info
.rect
.height
= m_tab_height
;
498 x
+= info
.rect
.width
+ tabsep
;
500 m_tab_scroll_buttons_shown
= false;
501 m_tab_scroll_left_button_rect
.SetWidth(0);
502 m_tab_scroll_right_button_rect
.SetWidth(0);
504 else if(width
< m_tabs_total_width_minimum
)
506 // Simple case: everything minimum with scrollbar
508 for(i
= 0; i
< numtabs
; ++i
)
510 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
515 info
.rect
.width
= info
.minimum_width
;
516 info
.rect
.height
= m_tab_height
;
517 x
+= info
.rect
.width
+ tabsep
;
519 if(!m_tab_scroll_buttons_shown
)
521 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
522 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
523 m_tab_scroll_buttons_shown
= true;
526 wxClientDC
temp_dc(this);
527 int right_button_pos
= GetClientSize().GetWidth() - m_tab_margin_right
- m_tab_scroll_right_button_rect
.GetWidth();
528 if ( right_button_pos
< m_tab_margin_left
)
529 right_button_pos
= m_tab_margin_left
;
531 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());
532 m_tab_scroll_left_button_rect
.SetHeight(m_tab_height
);
533 m_tab_scroll_left_button_rect
.SetX(m_tab_margin_left
);
534 m_tab_scroll_left_button_rect
.SetY(0);
535 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());
536 m_tab_scroll_right_button_rect
.SetHeight(m_tab_height
);
537 m_tab_scroll_right_button_rect
.SetX(right_button_pos
);
538 m_tab_scroll_right_button_rect
.SetY(0);
540 if(m_tab_scroll_amount
== 0)
542 m_tab_scroll_left_button_rect
.SetWidth(0);
544 else if(m_tab_scroll_amount
+ width
>= m_tabs_total_width_minimum
)
546 m_tab_scroll_amount
= m_tabs_total_width_minimum
- width
;
547 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
548 m_tab_scroll_right_button_rect
.SetWidth(0);
550 for(i
= 0; i
< numtabs
; ++i
)
552 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
555 info
.rect
.x
-= m_tab_scroll_amount
;
560 m_tab_scroll_buttons_shown
= false;
561 m_tab_scroll_left_button_rect
.SetWidth(0);
562 m_tab_scroll_right_button_rect
.SetWidth(0);
563 // Complex case: everything sized such that: minimum <= width < ideal
566 1) Uniformly reduce all tab widths from ideal to small_must_have_separator_width
567 2) Reduce the largest tab by 1 pixel, repeating until all tabs are same width (or at minimum)
568 3) Uniformly reduce all tabs down to their minimum width
570 int smallest_tab_width
= INT_MAX
;
571 int total_small_width
= tabsep
* (numtabs
- 1);
573 for(i
= 0; i
< numtabs
; ++i
)
575 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
578 if(info
.small_must_have_separator_width
< smallest_tab_width
)
580 smallest_tab_width
= info
.small_must_have_separator_width
;
582 total_small_width
+= info
.small_must_have_separator_width
;
584 if(width
>= total_small_width
)
587 int total_delta
= m_tabs_total_width_ideal
- total_small_width
;
588 total_small_width
-= tabsep
* (numtabs
- 1);
589 width
-= tabsep
* (numtabs
- 1);
590 for(i
= 0; i
< numtabs
; ++i
)
592 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
595 int delta
= info
.ideal_width
- info
.small_must_have_separator_width
;
598 info
.rect
.width
= info
.small_must_have_separator_width
+ delta
* (width
- total_small_width
) / total_delta
;
599 info
.rect
.height
= m_tab_height
;
601 x
+= info
.rect
.width
+ tabsep
;
602 total_delta
-= delta
;
603 total_small_width
-= info
.small_must_have_separator_width
;
604 width
-= info
.rect
.width
;
609 total_small_width
= tabsep
* (numtabs
- 1);
610 for(i
= 0; i
< numtabs
; ++i
)
612 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
615 if(info
.minimum_width
< smallest_tab_width
)
617 total_small_width
+= smallest_tab_width
;
621 total_small_width
+= info
.minimum_width
;
624 if(width
>= total_small_width
)
627 wxRibbonPageTabInfoArray sorted_pages
;
628 for(i
= 0; i
< numtabs
; ++i
)
630 // Sneaky obj array trickery to not copy the tab descriptors
631 if (!m_pages
.Item(i
).shown
)
633 sorted_pages
.Add(&m_pages
.Item(i
));
635 sorted_pages
.Sort(OrderPageTabInfoBySmallWidthAsc
);
636 width
-= tabsep
* (numtabs
- 1);
637 for(i
= 0; i
< numtabs
; ++i
)
639 wxRibbonPageTabInfo
& info
= sorted_pages
.Item(i
);
642 if(info
.small_must_have_separator_width
* (int)(numtabs
- i
) <= width
)
644 info
.rect
.width
= info
.small_must_have_separator_width
;;
648 info
.rect
.width
= width
/ (numtabs
- i
);
650 width
-= info
.rect
.width
;
652 for(i
= 0; i
< numtabs
; ++i
)
654 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
659 info
.rect
.height
= m_tab_height
;
660 x
+= info
.rect
.width
+ tabsep
;
661 sorted_pages
.Detach(numtabs
- (i
+ 1));
667 total_small_width
= (smallest_tab_width
+ tabsep
) * numtabs
- tabsep
;
668 int total_delta
= total_small_width
- m_tabs_total_width_minimum
;
669 total_small_width
= m_tabs_total_width_minimum
- tabsep
* (numtabs
- 1);
670 width
-= tabsep
* (numtabs
- 1);
671 for(i
= 0; i
< numtabs
; ++i
)
673 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
676 int delta
= smallest_tab_width
- info
.minimum_width
;
679 info
.rect
.width
= info
.minimum_width
+ delta
* (width
- total_small_width
) / total_delta
;
680 info
.rect
.height
= m_tab_height
;
682 x
+= info
.rect
.width
+ tabsep
;
683 total_delta
-= delta
;
684 total_small_width
-= info
.minimum_width
;
685 width
-= info
.rect
.width
;
692 wxRibbonBar::wxRibbonBar()
695 m_tabs_total_width_ideal
= 0;
696 m_tabs_total_width_minimum
= 0;
697 m_tab_margin_left
= 0;
698 m_tab_margin_right
= 0;
700 m_tab_scroll_amount
= 0;
702 m_current_hovered_page
= -1;
703 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
704 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
705 m_tab_scroll_buttons_shown
= false;
706 m_arePanelsShown
= true;
709 wxRibbonBar::wxRibbonBar(wxWindow
* parent
,
714 : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
)
719 wxRibbonBar::~wxRibbonBar()
721 SetArtProvider(NULL
);
724 bool wxRibbonBar::Create(wxWindow
* parent
,
730 if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
))
738 void wxRibbonBar::CommonInit(long style
)
740 SetName(wxT("wxRibbonBar"));
743 m_tabs_total_width_ideal
= 0;
744 m_tabs_total_width_minimum
= 0;
745 m_tab_margin_left
= 50;
746 m_tab_margin_right
= 20;
747 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
748 m_tab_margin_right
+= 20;
749 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
750 m_tab_margin_right
+= 20;
751 m_tab_height
= 20; // initial guess
752 m_tab_scroll_amount
= 0;
754 m_current_hovered_page
= -1;
755 m_tab_scroll_left_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
756 m_tab_scroll_right_button_state
= wxRIBBON_SCROLL_BTN_NORMAL
;
757 m_tab_scroll_buttons_shown
= false;
758 m_arePanelsShown
= true;
762 SetArtProvider(new wxRibbonDefaultArtProvider
);
764 SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
766 m_toggle_button_hovered
= false;
767 m_bar_hovered
= false;
769 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
772 void wxRibbonBar::SetArtProvider(wxRibbonArtProvider
* art
)
774 wxRibbonArtProvider
*old
= m_art
;
779 art
->SetFlags(m_flags
);
781 size_t numpages
= m_pages
.GetCount();
783 for(i
= 0; i
< numpages
; ++i
)
785 wxRibbonPage
*page
= m_pages
.Item(i
).page
;
786 if(page
->GetArtProvider() != art
)
788 page
->SetArtProvider(art
);
795 void wxRibbonBar::OnPaint(wxPaintEvent
& WXUNUSED(evt
))
797 wxAutoBufferedPaintDC
dc(this);
799 if(GetUpdateRegion().Contains(0, 0, GetClientSize().GetWidth(), m_tab_height
) == wxOutRegion
)
801 // Nothing to do in the tab area, and the page area is handled by the active page
805 DoEraseBackground(dc
);
807 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
808 m_help_button_rect
= m_art
->GetRibbonHelpButtonArea(GetSize());
809 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
810 m_toggle_button_rect
= m_art
->GetBarToggleButtonArea(GetSize());
812 size_t numtabs
= m_pages
.GetCount();
813 double sep_visibility
= 0.0;
814 bool draw_sep
= false;
815 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
816 if(m_tab_scroll_buttons_shown
)
818 tabs_rect
.x
+= m_tab_scroll_left_button_rect
.GetWidth();
819 tabs_rect
.width
-= m_tab_scroll_left_button_rect
.GetWidth() + m_tab_scroll_right_button_rect
.GetWidth();
822 for(i
= 0; i
< numtabs
; ++i
)
824 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
828 dc
.DestroyClippingRegion();
829 if(m_tab_scroll_buttons_shown
)
831 if(!tabs_rect
.Intersects(info
.rect
))
833 dc
.SetClippingRegion(tabs_rect
);
835 dc
.SetClippingRegion(info
.rect
);
836 m_art
->DrawTab(dc
, this, info
);
838 if(info
.rect
.width
< info
.small_begin_need_separator_width
)
841 if(info
.rect
.width
< info
.small_must_have_separator_width
)
843 sep_visibility
+= 1.0;
847 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
);
853 wxRect rect
= m_pages
.Item(0).rect
;
854 rect
.width
= m_art
->GetMetric(wxRIBBON_ART_TAB_SEPARATION_SIZE
);
855 sep_visibility
/= (double)numtabs
;
856 for(i
= 0; i
< numtabs
- 1; ++i
)
858 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
861 rect
.x
= info
.rect
.x
+ info
.rect
.width
;
863 if(m_tab_scroll_buttons_shown
&& !tabs_rect
.Intersects(rect
))
868 dc
.DestroyClippingRegion();
869 dc
.SetClippingRegion(rect
);
870 m_art
->DrawTabSeparator(dc
, this, rect
, sep_visibility
);
873 if(m_tab_scroll_buttons_shown
)
875 if(m_tab_scroll_left_button_rect
.GetWidth() != 0)
877 dc
.DestroyClippingRegion();
878 dc
.SetClippingRegion(m_tab_scroll_left_button_rect
);
879 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
);
881 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
883 dc
.DestroyClippingRegion();
884 dc
.SetClippingRegion(m_tab_scroll_right_button_rect
);
885 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
);
889 if ( m_flags
& wxRIBBON_BAR_SHOW_HELP_BUTTON
)
890 m_art
->DrawHelpButton(dc
, this, m_help_button_rect
);
891 if ( m_flags
& wxRIBBON_BAR_SHOW_TOGGLE_BUTTON
)
892 m_art
->DrawToggleButton(dc
, this, m_toggle_button_rect
, m_ribbon_state
);
896 void wxRibbonBar::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
))
898 // Background painting done in main paint handler to reduce screen flicker
901 void wxRibbonBar::DoEraseBackground(wxDC
& dc
)
903 wxRect
tabs(GetSize());
904 tabs
.height
= m_tab_height
;
905 m_art
->DrawTabCtrlBackground(dc
, this, tabs
);
908 void wxRibbonBar::OnSize(wxSizeEvent
& evt
)
910 RecalculateTabSizes();
911 if(m_current_page
!= -1)
913 RepositionPage(m_pages
.Item(m_current_page
).page
);
920 void wxRibbonBar::RepositionPage(wxRibbonPage
*page
)
924 page
->SetSizeWithScrollButtonAdjustment(0, m_tab_height
, w
, h
- m_tab_height
);
927 wxRibbonPageTabInfo
* wxRibbonBar::HitTestTabs(wxPoint position
, int* index
)
929 wxRect
tabs_rect(m_tab_margin_left
, 0, GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
, m_tab_height
);
930 if(m_tab_scroll_buttons_shown
)
932 tabs_rect
.SetX(tabs_rect
.GetX() + m_tab_scroll_left_button_rect
.GetWidth());
933 tabs_rect
.SetWidth(tabs_rect
.GetWidth() - m_tab_scroll_left_button_rect
.GetWidth() - m_tab_scroll_right_button_rect
.GetWidth());
935 if(tabs_rect
.Contains(position
))
937 size_t numtabs
= m_pages
.GetCount();
939 for(i
= 0; i
< numtabs
; ++i
)
941 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
944 if(info
.rect
.Contains(position
))
961 void wxRibbonBar::OnMouseLeftDown(wxMouseEvent
& evt
)
963 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
967 if ( m_ribbon_state
== wxRIBBON_BAR_MINIMIZED
)
970 m_ribbon_state
= wxRIBBON_BAR_EXPANDED
;
972 else if ( (tab
== &m_pages
.Item(m_current_page
)) && (m_ribbon_state
== wxRIBBON_BAR_EXPANDED
) )
975 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
980 if ( m_ribbon_state
== wxRIBBON_BAR_EXPANDED
)
983 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
986 if(tab
&& tab
!= &m_pages
.Item(m_current_page
))
988 wxRibbonBarEvent
query(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGING
, GetId(), tab
->page
);
989 query
.SetEventObject(this);
990 ProcessWindowEvent(query
);
991 if(query
.IsAllowed())
993 SetActivePage(query
.GetPage());
995 wxRibbonBarEvent
notification(wxEVT_COMMAND_RIBBONBAR_PAGE_CHANGED
, GetId(), m_pages
.Item(m_current_page
).page
);
996 notification
.SetEventObject(this);
997 ProcessWindowEvent(notification
);
1000 else if(tab
== NULL
)
1002 if(m_tab_scroll_left_button_rect
.Contains(evt
.GetPosition()))
1004 m_tab_scroll_left_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
1007 else if(m_tab_scroll_right_button_rect
.Contains(evt
.GetPosition()))
1009 m_tab_scroll_right_button_state
|= wxRIBBON_SCROLL_BTN_ACTIVE
| wxRIBBON_SCROLL_BTN_HOVERED
;
1014 wxPoint position
= evt
.GetPosition();
1016 if(position
.x
>= 0 && position
.y
>= 0)
1018 wxSize size
= GetSize();
1019 if(position
.x
< size
.GetWidth() && position
.y
< size
.GetHeight())
1021 if(m_toggle_button_rect
.Contains(position
))
1023 bool pshown
= ArePanelsShown();
1024 ShowPanels(!pshown
);
1026 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
1028 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
1029 wxRibbonBarEvent
event(wxEVT_COMMAND_RIBBONBAR_TOGGLED
, GetId());
1030 event
.SetEventObject(this);
1031 ProcessWindowEvent(event
);
1033 if ( m_help_button_rect
.Contains(position
) )
1035 wxRibbonBarEvent
event(wxEVT_COMMAND_RIBBONBAR_HELP_CLICKED
, GetId());
1036 event
.SetEventObject(this);
1037 ProcessWindowEvent(event
);
1043 void wxRibbonBar::OnMouseLeftUp(wxMouseEvent
& WXUNUSED(evt
))
1045 if(!m_tab_scroll_buttons_shown
)
1051 if(m_tab_scroll_left_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
1055 else if(m_tab_scroll_right_button_state
& wxRIBBON_SCROLL_BTN_ACTIVE
)
1061 m_tab_scroll_left_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
1062 m_tab_scroll_right_button_state
&= ~wxRIBBON_SCROLL_BTN_ACTIVE
;
1063 ScrollTabBar(amount
* 8);
1067 void wxRibbonBar::ScrollTabBar(int amount
)
1069 bool show_left
= true;
1070 bool show_right
= true;
1071 if(m_tab_scroll_amount
+ amount
<= 0)
1073 amount
= -m_tab_scroll_amount
;
1076 else if(m_tab_scroll_amount
+ amount
+ (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
) >= m_tabs_total_width_minimum
)
1078 amount
= m_tabs_total_width_minimum
- m_tab_scroll_amount
- (GetClientSize().GetWidth() - m_tab_margin_left
- m_tab_margin_right
);
1085 m_tab_scroll_amount
+= amount
;
1086 size_t numtabs
= m_pages
.GetCount();
1088 for(i
= 0; i
< numtabs
; ++i
)
1090 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1093 info
.rect
.SetX(info
.rect
.GetX() - amount
);
1095 if(show_right
!= (m_tab_scroll_right_button_rect
.GetWidth() != 0) ||
1096 show_left
!= (m_tab_scroll_left_button_rect
.GetWidth() != 0))
1098 wxClientDC
temp_dc(this);
1101 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());
1105 m_tab_scroll_left_button_rect
.SetWidth(0);
1110 if(m_tab_scroll_right_button_rect
.GetWidth() == 0)
1112 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());
1113 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() - m_tab_scroll_right_button_rect
.GetWidth());
1118 if(m_tab_scroll_right_button_rect
.GetWidth() != 0)
1120 m_tab_scroll_right_button_rect
.SetX(m_tab_scroll_right_button_rect
.GetX() + m_tab_scroll_right_button_rect
.GetWidth());
1121 m_tab_scroll_right_button_rect
.SetWidth(0);
1129 void wxRibbonBar::RefreshTabBar()
1131 wxRect
tab_rect(0, 0, GetClientSize().GetWidth(), m_tab_height
);
1132 Refresh(false, &tab_rect
);
1135 void wxRibbonBar::OnMouseMiddleDown(wxMouseEvent
& evt
)
1137 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_DOWN
);
1140 void wxRibbonBar::OnMouseMiddleUp(wxMouseEvent
& evt
)
1142 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_MIDDLE_UP
);
1145 void wxRibbonBar::OnMouseRightDown(wxMouseEvent
& evt
)
1147 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_DOWN
);
1150 void wxRibbonBar::OnMouseRightUp(wxMouseEvent
& evt
)
1152 DoMouseButtonCommon(evt
, wxEVT_COMMAND_RIBBONBAR_TAB_RIGHT_UP
);
1155 void wxRibbonBar::OnMouseDoubleClick(wxMouseEvent
& evt
)
1157 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
1159 if ( tab
&& tab
== &m_pages
.Item(m_current_page
) )
1161 if ( m_ribbon_state
== wxRIBBON_BAR_PINNED
)
1163 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
1168 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
1174 void wxRibbonBar::DoMouseButtonCommon(wxMouseEvent
& evt
, wxEventType tab_event_type
)
1176 wxRibbonPageTabInfo
*tab
= HitTestTabs(evt
.GetPosition());
1179 wxRibbonBarEvent
notification(tab_event_type
, GetId(), tab
->page
);
1180 notification
.SetEventObject(this);
1181 ProcessWindowEvent(notification
);
1185 void wxRibbonBar::RecalculateMinSize()
1187 wxSize
min_size(wxDefaultCoord
, wxDefaultCoord
);
1188 size_t numtabs
= m_pages
.GetCount();
1191 min_size
= m_pages
.Item(0).page
->GetMinSize();
1194 for(i
= 1; i
< numtabs
; ++i
)
1196 wxRibbonPageTabInfo
& info
= m_pages
.Item(i
);
1199 wxSize page_min
= info
.page
->GetMinSize();
1201 min_size
.x
= wxMax(min_size
.x
, page_min
.x
);
1202 min_size
.y
= wxMax(min_size
.y
, page_min
.y
);
1205 if(min_size
.y
!= wxDefaultCoord
)
1207 // TODO: Decide on best course of action when min height is unspecified
1208 // - should we specify it to the tab minimum, or leave it unspecified?
1209 min_size
.IncBy(0, m_tab_height
);
1212 m_minWidth
= min_size
.GetWidth();
1213 m_minHeight
= m_arePanelsShown
? min_size
.GetHeight() : m_tab_height
;
1216 wxSize
wxRibbonBar::DoGetBestSize() const
1219 if(m_current_page
!= -1)
1221 best
= m_pages
.Item(m_current_page
).page
->GetBestSize();
1223 if(best
.GetHeight() == wxDefaultCoord
)
1225 best
.SetHeight(m_tab_height
);
1229 best
.IncBy(0, m_tab_height
);
1231 if(!m_arePanelsShown
)
1233 best
.SetHeight(m_tab_height
);
1238 void wxRibbonBar::HitTestRibbonButton(const wxRect
& rect
, const wxPoint
& position
, bool &hover_flag
)
1240 bool hovered
= false, toggle_button_hovered
= false;
1241 if(position
.x
>= 0 && position
.y
>= 0)
1243 wxSize size
= GetSize();
1244 if(position
.x
< size
.GetWidth() && position
.y
< size
.GetHeight())
1251 toggle_button_hovered
= rect
.Contains(position
);
1253 if ( hovered
!= m_bar_hovered
|| toggle_button_hovered
!= hover_flag
)
1255 m_bar_hovered
= hovered
;
1256 hover_flag
= toggle_button_hovered
;
1262 void wxRibbonBar::HideIfExpanded()
1264 if ( m_ribbon_state
== wxRIBBON_BAR_EXPANDED
)
1267 m_ribbon_state
= wxRIBBON_BAR_MINIMIZED
;
1272 m_ribbon_state
= wxRIBBON_BAR_PINNED
;
1276 void wxRibbonBar::OnKillFocus(wxFocusEvent
& WXUNUSED(evt
))
1281 #endif // wxUSE_RIBBON