1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/aui/tabartgtk.cpp
3 // Purpose: implementation of the wxAuiGTKTabArt
4 // Author: Jens Lody and Teodor Petrov
7 // Copyright: (c) 2012 Jens Lody <jens@codeblocks.org>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
36 #include "wx/gtk/dc.h"
37 #include "wx/gtk/private.h"
41 #include "wx/aui/auibook.h"
42 #include "wx/aui/tabartgtk.h"
43 #include "wx/renderer.h"
48 static int s_CloseIconSize
= 16; // default size
52 wxAuiGtkTabArt::wxAuiGtkTabArt()
57 wxAuiTabArt
* wxAuiGtkTabArt::Clone()
59 wxAuiGtkTabArt
* clone
= new wxAuiGtkTabArt();
61 clone
->SetNormalFont(m_normalFont
);
62 clone
->SetSelectedFont(m_normalFont
);
63 clone
->SetMeasuringFont(m_normalFont
);
68 void wxAuiGtkTabArt::DrawBackground(wxDC
& dc
, wxWindow
* WXUNUSED(wnd
), const wxRect
& rect
)
70 wxGTKDCImpl
*impldc
= (wxGTKDCImpl
*) dc
.GetImpl();
71 GdkWindow
* window
= impldc
->GetGDKWindow();
73 gtk_style_apply_default_background(gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget()),
78 rect
.x
, rect
.y
, rect
.width
, rect
.height
);
81 void wxAuiGtkTabArt::DrawBorder(wxDC
& WXUNUSED(dc
), wxWindow
* wnd
, const wxRect
& rect
)
83 int generic_border_width
= wxAuiGenericTabArt::GetBorderWidth(wnd
);
86 if (!wnd
->m_wxwindow
) return;
87 if (!gtk_widget_is_drawable(wnd
->m_wxwindow
)) return;
89 GtkStyle
*style_notebook
= gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget());
91 gtk_paint_box(style_notebook
, wnd
->GTKGetDrawingWindow(), GTK_STATE_NORMAL
, GTK_SHADOW_OUT
,
92 NULL
, wnd
->m_wxwindow
,
93 const_cast<char*>("notebook"),
94 rect
.x
+ generic_border_width
+ 1, rect
.y
+ generic_border_width
+ 1,
95 rect
.width
- (generic_border_width
+ 1), rect
.height
- (generic_border_width
+ 1));
98 void ButtonStateAndShadow(int button_state
, GtkStateType
&state
, GtkShadowType
&shadow
)
101 if (button_state
& wxAUI_BUTTON_STATE_DISABLED
)
103 state
= GTK_STATE_INSENSITIVE
;
104 shadow
= GTK_SHADOW_ETCHED_IN
;
106 else if (button_state
& wxAUI_BUTTON_STATE_HOVER
)
108 state
= GTK_STATE_PRELIGHT
;
109 shadow
= GTK_SHADOW_OUT
;
111 else if (button_state
& wxAUI_BUTTON_STATE_PRESSED
)
113 state
= GTK_STATE_ACTIVE
;
114 shadow
= GTK_SHADOW_IN
;
118 state
= GTK_STATE_NORMAL
;
119 shadow
= GTK_SHADOW_OUT
;
123 wxRect
DrawCloseButton(wxDC
& dc
,
126 wxRect
const &in_rect
,
128 GdkRectangle
* clipRect
)
130 GtkStyle
*style_button
= gtk_widget_get_style(wxGTKPrivate::GetButtonWidget());
131 int xthickness
= style_button
->xthickness
;
132 int ythickness
= style_button
->ythickness
;
134 wxBitmap
bmp(gtk_widget_render_icon(widget
, GTK_STOCK_CLOSE
, GTK_ICON_SIZE_SMALL_TOOLBAR
, "tab"));
136 if(bmp
.GetWidth() != s_CloseIconSize
|| bmp
.GetHeight() != s_CloseIconSize
)
138 wxImage img
= bmp
.ConvertToImage();
139 img
.Rescale(s_CloseIconSize
, s_CloseIconSize
);
143 int button_size
= s_CloseIconSize
+ 2 * xthickness
;
147 if (orientation
== wxLEFT
)
148 out_rect
.x
= in_rect
.x
- ythickness
;
150 out_rect
.x
= in_rect
.x
+ in_rect
.width
- button_size
- ythickness
;
152 out_rect
.y
= in_rect
.y
+ (in_rect
.height
- button_size
) / 2;
153 out_rect
.width
= button_size
;
154 out_rect
.height
= button_size
;
156 wxGTKDCImpl
*impldc
= (wxGTKDCImpl
*) dc
.GetImpl();
157 GdkWindow
* window
= impldc
->GetGDKWindow();
159 if (button_state
== wxAUI_BUTTON_STATE_HOVER
)
161 gtk_paint_box(style_button
, window
,
162 GTK_STATE_PRELIGHT
, GTK_SHADOW_OUT
, clipRect
, widget
, "button",
163 out_rect
.x
, out_rect
.y
, out_rect
.width
, out_rect
.height
);
165 else if (button_state
== wxAUI_BUTTON_STATE_PRESSED
)
167 gtk_paint_box(style_button
, window
,
168 GTK_STATE_ACTIVE
, GTK_SHADOW_IN
, clipRect
, widget
, "button",
169 out_rect
.x
, out_rect
.y
, out_rect
.width
, out_rect
.height
);
173 dc
.DrawBitmap(bmp
, out_rect
.x
+ xthickness
, out_rect
.y
+ ythickness
, true);
178 void wxAuiGtkTabArt::DrawTab(wxDC
& dc
, wxWindow
* wnd
, const wxAuiNotebookPage
& page
,
179 const wxRect
& in_rect
, int close_button_state
, wxRect
* out_tab_rect
,
180 wxRect
* out_button_rect
, int* x_extent
)
182 GtkWidget
*widget
= wnd
->GetHandle();
183 GtkStyle
*style_notebook
= gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget());
185 wxRect
const &window_rect
= wnd
->GetRect();
189 gtk_widget_style_get(wxGTKPrivate::GetNotebookWidget(),
190 "focus-line-width", &focus_width
,
194 if (m_flags
&wxAUI_NB_BOTTOM
)
195 tab_pos
= wxAUI_NB_BOTTOM
;
196 else //if (m_flags & wxAUI_NB_TOP) {}
197 tab_pos
= wxAUI_NB_TOP
;
199 // TODO: else if (m_flags &wxAUI_NB_LEFT) {}
200 // TODO: else if (m_flags &wxAUI_NB_RIGHT) {}
202 // figure out the size of the tab
203 wxSize tab_size
= GetTabSize(dc
, wnd
, page
.caption
, page
.bitmap
,
204 page
.active
, close_button_state
, x_extent
);
206 wxRect tab_rect
= in_rect
;
207 tab_rect
.width
= tab_size
.x
;
208 tab_rect
.height
= tab_size
.y
;
209 tab_rect
.y
+= 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
212 tab_rect
.height
+= 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
214 int gap_rect_height
= 10 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
215 int gap_rect_x
= 1, gap_start
= 0, gap_width
= 0;
216 int gap_rect_y
= tab_rect
.y
- gap_rect_height
;
217 int gap_rect_width
= window_rect
.width
;
222 tab_rect
.y
-= 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
224 tab_rect
.y
+= 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
225 gap_rect_y
= tab_rect
.y
+ tab_rect
.height
- GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
/ 2;
227 case wxAUI_NB_BOTTOM
:
228 gap_start
= tab_rect
.x
- GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder
/ 2;
229 gap_width
= tab_rect
.width
;
231 // TODO: case wxAUI_NB_LEFT: break;
232 // TODO: case wxAUI_NB_RIGHT: break;
234 tab_rect
.y
+= GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
/ 2;
235 gap_rect_y
+= GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
/ 2;
237 int padding
= focus_width
+ GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
239 int clip_width
= tab_rect
.width
;
240 if (tab_rect
.x
+ tab_rect
.width
> in_rect
.x
+ in_rect
.width
)
241 clip_width
= (in_rect
.x
+ in_rect
.width
) - tab_rect
.x
;
243 dc
.SetClippingRegion(tab_rect
.x
, tab_rect
.y
- GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder
, clip_width
, tab_rect
.height
+ GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder
);
246 area
.x
= tab_rect
.x
- GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder
;
247 area
.y
= tab_rect
.y
- 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
248 area
.width
= clip_width
+ GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder
;
249 area
.height
= tab_rect
.height
+ 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
;
251 wxGTKDCImpl
*impldc
= (wxGTKDCImpl
*) dc
.GetImpl();
252 GdkWindow
* window
= impldc
->GetGDKWindow();
254 // Before drawing the active tab itself, draw a box without border, because some themes
255 // have transparent gaps and a line would be visible at the bottom of the tab
257 gtk_paint_box(style_notebook
, window
, GTK_STATE_NORMAL
, GTK_SHADOW_NONE
,
259 const_cast<char*>("notebook"),
260 gap_rect_x
, gap_rect_y
,
261 gap_rect_width
, gap_rect_height
);
263 if (tab_pos
== wxAUI_NB_BOTTOM
)
267 gtk_paint_box_gap(style_notebook
, window
, GTK_STATE_NORMAL
, GTK_SHADOW_OUT
,
269 const_cast<char*>("notebook"),
270 gap_rect_x
, gap_rect_y
,
271 gap_rect_width
, gap_rect_height
,
272 GTK_POS_BOTTOM
, gap_start
, gap_width
);
274 gtk_paint_extension(style_notebook
, window
,
275 page
.active
? GTK_STATE_NORMAL
: GTK_STATE_ACTIVE
, GTK_SHADOW_OUT
,
277 const_cast<char*>("tab"),
278 tab_rect
.x
, tab_rect
.y
,
279 tab_rect
.width
, tab_rect
.height
,
286 gtk_paint_box_gap(style_notebook
, window
, GTK_STATE_NORMAL
, GTK_SHADOW_OUT
,
288 const_cast<char*>("notebook"),
289 gap_rect_x
, gap_rect_y
,
290 gap_rect_width
, gap_rect_height
,
291 GTK_POS_TOP
, gap_start
, gap_width
);
293 gtk_paint_extension(style_notebook
, window
,
294 page
.active
? GTK_STATE_NORMAL
: GTK_STATE_ACTIVE
, GTK_SHADOW_OUT
,
296 const_cast<char*>("tab"),
297 tab_rect
.x
, tab_rect
.y
,
298 tab_rect
.width
, tab_rect
.height
,
302 // After drawing the inactive tab itself, draw a box with the same dimensions as the gap-box,
303 // otherwise we don't get a gap-box, if the active tab is invisible
305 gtk_paint_box(style_notebook
, window
, GTK_STATE_NORMAL
, GTK_SHADOW_OUT
,
307 const_cast<char*>("notebook"),
308 gap_rect_x
, gap_rect_y
,
309 gap_rect_width
, gap_rect_height
);
311 wxCoord textX
= tab_rect
.x
+ padding
+ style_notebook
->xthickness
;
313 int bitmap_offset
= 0;
314 if (page
.bitmap
.IsOk())
316 bitmap_offset
= textX
;
319 int bitmapY
= tab_rect
.y
+(tab_rect
.height
- page
.bitmap
.GetHeight()) / 2;
322 if (tab_pos
== wxAUI_NB_TOP
)
323 bitmapY
+= style_notebook
->ythickness
/ 2;
325 bitmapY
-= style_notebook
->ythickness
/ 2;
327 dc
.DrawBitmap(page
.bitmap
,
332 textX
+= page
.bitmap
.GetWidth() + padding
;
335 wxCoord textW
, textH
, textY
;
337 dc
.SetFont(m_normalFont
);
338 dc
.GetTextExtent(page
.caption
, &textW
, &textH
);
339 textY
= tab_rect
.y
+ (tab_rect
.height
- textH
) / 2;
342 if (tab_pos
== wxAUI_NB_TOP
)
343 textY
+= style_notebook
->ythickness
/ 2;
345 textY
-= style_notebook
->ythickness
/ 2;
349 GdkColor text_colour
= page
.active
? style_notebook
->fg
[GTK_STATE_NORMAL
] : style_notebook
->fg
[GTK_STATE_ACTIVE
];
350 dc
.SetTextForeground(wxColor(text_colour
));
351 GdkRectangle focus_area
;
353 int padding_focus
= padding
- focus_width
;
354 focus_area
.x
= tab_rect
.x
+ padding_focus
;
355 focus_area
.y
= textY
- focus_width
;
356 focus_area
.width
= tab_rect
.width
- 2 * padding_focus
;
357 focus_area
.height
= textH
+ 2 * focus_width
;
359 if(page
.active
&& (wnd
->FindFocus() == wnd
) && focus_area
.x
<= (area
.x
+ area
.width
))
361 // clipping seems not to work here, so we we have to recalc the focus-area manually
362 if((focus_area
.x
+ focus_area
.width
) > (area
.x
+ area
.width
))
363 focus_area
.width
= area
.x
+ area
.width
- focus_area
.x
+ focus_width
- GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder
;
364 gtk_paint_focus (style_notebook
, window
,
365 GTK_STATE_ACTIVE
, NULL
, widget
, "tab",
366 focus_area
.x
, focus_area
.y
, focus_area
.width
, focus_area
.height
);
369 dc
.DrawText(page
.caption
, textX
, textY
);
371 // draw close-button on tab (if enabled)
372 if (close_button_state
!= wxAUI_BUTTON_STATE_HIDDEN
)
374 wxRect
rect(tab_rect
.x
, tab_rect
.y
, tab_rect
.width
- style_notebook
->xthickness
, tab_rect
.height
);
377 if (tab_pos
== wxAUI_NB_TOP
)
378 rect
.y
+= style_notebook
->ythickness
/ 2;
380 rect
.y
-= style_notebook
->ythickness
/ 2;
382 *out_button_rect
= DrawCloseButton(dc
, widget
, close_button_state
, rect
, wxRIGHT
, &area
);
385 if ( clip_width
< tab_rect
.width
)
386 tab_rect
.width
= clip_width
;
387 *out_tab_rect
= tab_rect
;
389 dc
.DestroyClippingRegion();
392 wxRect
DrawSimpleArrow(wxDC
& dc
,
395 wxRect
const &in_rect
,
397 GtkArrowType arrow_type
)
399 int scroll_arrow_hlength
, scroll_arrow_vlength
;
400 gtk_widget_style_get(widget
,
401 "scroll-arrow-hlength", &scroll_arrow_hlength
,
402 "scroll-arrow-vlength", &scroll_arrow_vlength
,
406 GtkShadowType shadow
;
407 ButtonStateAndShadow(button_state
, state
, shadow
);
411 if (orientation
== wxLEFT
)
412 out_rect
.x
= in_rect
.x
;
414 out_rect
.x
= in_rect
.x
+ in_rect
.width
- scroll_arrow_hlength
;
415 out_rect
.y
= (in_rect
.y
+ in_rect
.height
- 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness
- scroll_arrow_vlength
) / 2;
416 out_rect
.width
= scroll_arrow_hlength
;
417 out_rect
.height
= scroll_arrow_vlength
;
419 wxGTKDCImpl
*impldc
= (wxGTKDCImpl
*) dc
.GetImpl();
420 GdkWindow
* window
= impldc
->GetGDKWindow();
421 gtk_paint_arrow (gtk_widget_get_style(wxGTKPrivate::GetButtonWidget()), window
, state
, shadow
, NULL
, widget
, "notebook",
422 arrow_type
, TRUE
, out_rect
.x
, out_rect
.y
, out_rect
.width
, out_rect
.height
);
427 void wxAuiGtkTabArt::DrawButton(wxDC
& dc
, wxWindow
* wnd
,
428 const wxRect
& in_rect
,
434 GtkWidget
*widget
= wnd
->GetHandle();
435 wxRect rect
= in_rect
;
436 if (m_flags
&wxAUI_NB_BOTTOM
)
437 rect
.y
+= 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness
;
441 case wxAUI_BUTTON_CLOSE
:
442 rect
.y
-= 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness
;
443 rect
= DrawCloseButton(dc
, widget
, button_state
, rect
, orientation
, NULL
);
446 case wxAUI_BUTTON_LEFT
:
447 rect
= DrawSimpleArrow(dc
, widget
, button_state
, rect
, orientation
, GTK_ARROW_LEFT
);
450 case wxAUI_BUTTON_RIGHT
:
451 rect
= DrawSimpleArrow(dc
, widget
, button_state
, rect
, orientation
, GTK_ARROW_RIGHT
);
454 case wxAUI_BUTTON_WINDOWLIST
:
456 rect
.height
-= 4 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness
;
457 rect
.width
= rect
.height
;
458 rect
.x
= in_rect
.x
+ in_rect
.width
- rect
.width
;
460 if (button_state
== wxAUI_BUTTON_STATE_HOVER
)
461 wxRendererNative::Get().DrawComboBoxDropButton(wnd
, dc
, rect
, wxCONTROL_CURRENT
);
462 else if (button_state
== wxAUI_BUTTON_STATE_PRESSED
)
463 wxRendererNative::Get().DrawComboBoxDropButton(wnd
, dc
, rect
, wxCONTROL_PRESSED
);
465 wxRendererNative::Get().DrawDropArrow(wnd
, dc
, rect
);
474 int wxAuiGtkTabArt::GetBestTabCtrlSize(wxWindow
* wnd
,
475 const wxAuiNotebookPageArray
& pages
,
476 const wxSize
& required_bmp_size
)
478 SetMeasuringFont(m_normalFont
);
479 SetSelectedFont(m_normalFont
);
480 int tab_height
= 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness
+ wxAuiGenericTabArt::GetBestTabCtrlSize(wnd
, pages
, required_bmp_size
);
484 int wxAuiGtkTabArt::GetBorderWidth(wxWindow
* wnd
)
486 return wxAuiGenericTabArt::GetBorderWidth(wnd
) + wxMax(GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder
,
487 GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder
);
490 int wxAuiGtkTabArt::GetAdditionalBorderSpace(wxWindow
* wnd
)
492 return 2 * GetBorderWidth(wnd
);
495 wxSize
wxAuiGtkTabArt::GetTabSize(wxDC
& dc
,
497 const wxString
& caption
,
498 const wxBitmap
& bitmap
,
500 int close_button_state
,
503 wxSize s
= wxAuiGenericTabArt::GetTabSize(dc
, wnd
, caption
, bitmap
, active
, close_button_state
, x_extent
);
506 gtk_widget_style_get (wnd
->GetHandle(),
507 "focus-line-width", &overlap
,
509 *x_extent
-= overlap
;