Fix gap box coordinates in GTK AUI tab art code.
[wxWidgets.git] / src / aui / tabartgtk.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/aui/tabartgtk.cpp
3 // Purpose: implementation of the wxAuiGTKTabArt
4 // Author: Jens Lody and Teodor Petrov
5 // Modified by:
6 // Created: 2012-03-23
7 // RCS-ID: $Id$
8 // Copyright: (c) 2012 Jens Lody <jens@codeblocks.org>
9 // and Teodor Petrov
10 // Licence: wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #if wxUSE_AUI
29
30 #ifndef WX_PRECOMP
31 #include "wx/dc.h"
32 #include "wx/dcclient.h"
33 #include "wx/settings.h"
34 #include "wx/image.h"
35 #endif
36
37 #include "wx/gtk/dc.h"
38 #include "wx/gtk/private.h"
39
40 #include <gtk/gtk.h>
41
42 #include "wx/aui/auibook.h"
43 #include "wx/aui/tabartgtk.h"
44 #include "wx/renderer.h"
45
46 namespace
47 {
48
49 static int s_CloseIconSize = 16; // default size
50
51 }
52
53 wxAuiGtkTabArt::wxAuiGtkTabArt()//:
54 // m_Xthickness(0),
55 // m_Ythickness(0),
56 // m_TabHBorder(0),
57 // m_TabVBorder(0)
58
59 {
60 }
61
62 wxAuiTabArt* wxAuiGtkTabArt::Clone()
63 {
64 wxAuiGtkTabArt* clone = new wxAuiGtkTabArt();
65
66 clone->SetNormalFont(m_normalFont);
67 clone->SetSelectedFont(m_normalFont);
68 clone->SetMeasuringFont(m_normalFont);
69
70 return clone;
71 }
72
73 void wxAuiGtkTabArt::DrawBackground(wxDC& dc, wxWindow* WXUNUSED(wnd), const wxRect& rect)
74 {
75 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
76 GdkWindow* window = impldc->GetGDKWindow();
77
78 gtk_style_apply_default_background(gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget()),
79 window,
80 true,
81 GTK_STATE_NORMAL,
82 NULL,
83 rect.x, rect.y, rect.width, rect.height);
84 }
85
86 void ButtonStateAndShadow(int button_state, GtkStateType &state, GtkShadowType &shadow)
87 {
88
89 if (button_state & wxAUI_BUTTON_STATE_DISABLED)
90 {
91 state = GTK_STATE_INSENSITIVE;
92 shadow = GTK_SHADOW_ETCHED_IN;
93 }
94 else if (button_state & wxAUI_BUTTON_STATE_HOVER)
95 {
96 state = GTK_STATE_PRELIGHT;
97 shadow = GTK_SHADOW_OUT;
98 }
99 else if (button_state & wxAUI_BUTTON_STATE_PRESSED)
100 {
101 state = GTK_STATE_ACTIVE;
102 shadow = GTK_SHADOW_IN;
103 }
104 else
105 {
106 state = GTK_STATE_NORMAL;
107 shadow = GTK_SHADOW_OUT;
108 }
109 }
110
111 wxRect DrawCloseButton(wxDC& dc,
112 GtkWidget *widget,
113 int button_state,
114 wxRect const &in_rect,
115 int orientation,
116 GdkRectangle* clipRect)
117 {
118 GtkStyle *style_button = gtk_widget_get_style(wxGTKPrivate::GetButtonWidget());
119 int xthickness = style_button->xthickness;
120 int ythickness = style_button->ythickness;
121
122 wxBitmap bmp(gtk_widget_render_icon(widget, GTK_STOCK_CLOSE, GTK_ICON_SIZE_SMALL_TOOLBAR, "tab"));
123
124 if(bmp.GetWidth() != s_CloseIconSize || bmp.GetHeight() != s_CloseIconSize)
125 {
126 wxImage img = bmp.ConvertToImage();
127 img.Rescale(s_CloseIconSize, s_CloseIconSize);
128 bmp = img;
129 }
130
131 int button_size = s_CloseIconSize + 2 * xthickness;
132
133 wxRect out_rect;
134
135 if (orientation == wxLEFT)
136 out_rect.x = in_rect.x - ythickness;
137 else
138 out_rect.x = in_rect.x + in_rect.width - button_size - ythickness;
139
140 out_rect.y = in_rect.y + (in_rect.height - button_size) / 2;
141 out_rect.width = button_size;
142 out_rect.height = button_size;
143
144 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
145 GdkWindow* window = impldc->GetGDKWindow();
146
147 if (button_state == wxAUI_BUTTON_STATE_HOVER)
148 {
149 gtk_paint_box(style_button, window,
150 GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, clipRect, widget, "button",
151 out_rect.x, out_rect.y, out_rect.width, out_rect.height);
152 }
153 else if (button_state == wxAUI_BUTTON_STATE_PRESSED)
154 {
155 gtk_paint_box(style_button, window,
156 GTK_STATE_ACTIVE, GTK_SHADOW_IN, clipRect, widget, "button",
157 out_rect.x, out_rect.y, out_rect.width, out_rect.height);
158 }
159
160
161 dc.DrawBitmap(bmp, out_rect.x + xthickness, out_rect.y + ythickness, true);
162
163 return out_rect;
164 }
165
166 void wxAuiGtkTabArt::DrawTab(wxDC& dc, wxWindow* wnd, const wxAuiNotebookPage& page,
167 const wxRect& in_rect, int close_button_state, wxRect* out_tab_rect,
168 wxRect* out_button_rect, int* x_extent)
169 {
170 GtkWidget *widget = wnd->GetHandle();
171 GtkStyle *style_notebook = gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget());
172
173 wxRect const &window_rect = wnd->GetRect();
174
175 int focus_width = 0;
176
177 gtk_widget_style_get(wxGTKPrivate::GetNotebookWidget(),
178 "focus-line-width", &focus_width,
179 NULL);
180
181 int gap_x = 0, gap_width = 0;
182 int tab_pos;
183 if (m_flags &wxAUI_NB_BOTTOM)
184 tab_pos = wxAUI_NB_BOTTOM;
185 else //if (m_flags & wxAUI_NB_TOP) {}
186 tab_pos = wxAUI_NB_TOP;
187
188 // TODO: else if (m_flags &wxAUI_NB_LEFT) {}
189 // TODO: else if (m_flags &wxAUI_NB_RIGHT) {}
190
191 // figure out the size of the tab
192 wxSize tab_size = GetTabSize(dc, wnd, page.caption, page.bitmap,
193 page.active, close_button_state, x_extent);
194
195 wxRect tab_rect = in_rect;
196 tab_rect.width = tab_size.x;
197 tab_rect.height = tab_size.y;
198 tab_rect.y += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
199
200 if (page.active)
201 tab_rect.height += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
202 // if no bitmap is set, we need a tiny correction
203 if (! page.bitmap.IsOk())
204 tab_rect.height += 1;
205
206 int gap_height = 6 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
207 int gap_y = tab_rect.y - gap_height;
208
209 switch (tab_pos)
210 {
211 case wxAUI_NB_TOP:
212 tab_rect.y -= 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
213 if (!page.active)
214 tab_rect.y += 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
215 gap_y = tab_rect.y + tab_rect.height - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
216 // fall through
217 case wxAUI_NB_BOTTOM:
218 gap_x = tab_rect.x - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder / 2;
219 gap_width = tab_rect.width;
220 break;
221 }
222 tab_rect.y += GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
223 gap_y += GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder / 2;
224
225 int padding = focus_width + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
226
227 int clip_width = tab_rect.width;
228 if (tab_rect.x + tab_rect.width > in_rect.x + in_rect.width)
229 clip_width = (in_rect.x + in_rect.width) - tab_rect.x;
230
231 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);
232
233 GdkRectangle area;
234 area.x = tab_rect.x - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
235 area.y = tab_rect.y - 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
236 area.width = clip_width + GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
237 area.height = tab_rect.height + 2 * GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_hborder;
238
239 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
240 GdkWindow* window = impldc->GetGDKWindow();
241
242 if (tab_pos == wxAUI_NB_BOTTOM)
243 {
244 if (page.active)
245 {
246 gtk_paint_box_gap(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
247 NULL, widget,
248 const_cast<char*>("notebook"),
249 1, gap_y,
250 window_rect.width, gap_height,
251 GTK_POS_BOTTOM, gap_x , gap_width);
252 }
253 gtk_paint_extension(style_notebook, window,
254 page.active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
255 &area, widget,
256 const_cast<char*>("tab"),
257 tab_rect.x, tab_rect.y,
258 tab_rect.width, tab_rect.height,
259 GTK_POS_TOP);
260 }
261 else
262 {
263 if (page.active)
264 {
265 gtk_paint_box_gap(style_notebook, window, GTK_STATE_NORMAL, GTK_SHADOW_OUT,
266 NULL, widget,
267 const_cast<char*>("notebook"),
268 1, gap_y,
269 window_rect.width, gap_height,
270 GTK_POS_TOP, gap_x , gap_width);
271 }
272 gtk_paint_extension(style_notebook, window,
273 page.active ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE, GTK_SHADOW_OUT,
274 &area, widget,
275 const_cast<char*>("tab"),
276 tab_rect.x, tab_rect.y,
277 tab_rect.width, tab_rect.height,
278 GTK_POS_BOTTOM);
279 }
280
281 wxCoord textX = tab_rect.x + padding + style_notebook->xthickness;
282
283 int bitmap_offset = 0;
284 if (page.bitmap.IsOk())
285 {
286 bitmap_offset = textX;
287
288 // draw bitmap
289 int bitmapY = tab_rect.y +(tab_rect.height - page.bitmap.GetHeight()) / 2;
290 if(!page.active)
291 {
292 if (tab_pos == wxAUI_NB_TOP)
293 bitmapY += style_notebook->ythickness / 2;
294 else
295 bitmapY -= style_notebook->ythickness / 2;
296 }
297 dc.DrawBitmap(page.bitmap,
298 bitmap_offset,
299 bitmapY,
300 true);
301
302 textX += page.bitmap.GetWidth() + padding;
303 }
304
305 wxCoord textW, textH, textY;
306
307 dc.SetFont(m_normalFont);
308 dc.GetTextExtent(page.caption, &textW, &textH);
309 textY = tab_rect.y + (tab_rect.height - textH) / 2;
310 if(!page.active)
311 {
312 if (tab_pos == wxAUI_NB_TOP)
313 textY += style_notebook->ythickness / 2;
314 else
315 textY -= style_notebook->ythickness / 2;
316 }
317
318 // draw tab text
319 GdkColor text_colour = page.active ? style_notebook->fg[GTK_STATE_NORMAL] : style_notebook->fg[GTK_STATE_ACTIVE];
320 dc.SetTextForeground(wxColor(text_colour));
321 GdkRectangle focus_area;
322
323 int padding_focus = padding - focus_width;
324 focus_area.x = tab_rect.x + padding_focus;
325 focus_area.y = textY - focus_width;
326 focus_area.width = tab_rect.width - 2 * padding_focus;
327 focus_area.height = textH + 2 * focus_width;
328
329 if(page.active && (wnd->FindFocus() == wnd) && focus_area.x <= (area.x + area.width))
330 {
331 // clipping seems not to work here, so we we have to recalc the focus-area manually
332 if((focus_area.x + focus_area.width) > (area.x + area.width))
333 focus_area.width = area.x + area.width - focus_area.x + focus_width - GTK_NOTEBOOK (wxGTKPrivate::GetNotebookWidget())->tab_vborder;
334 gtk_paint_focus (style_notebook, window,
335 GTK_STATE_ACTIVE, NULL, widget, "tab",
336 focus_area.x, focus_area.y, focus_area.width, focus_area.height);
337 }
338
339 dc.DrawText(page.caption, textX, textY);
340
341 // draw close-button on tab (if enabled)
342 if (close_button_state != wxAUI_BUTTON_STATE_HIDDEN)
343 {
344 wxRect rect(tab_rect.x, tab_rect.y, tab_rect.width - style_notebook->xthickness, tab_rect.height);
345 if(!page.active)
346 {
347 if (tab_pos == wxAUI_NB_TOP)
348 rect.y += style_notebook->ythickness / 2;
349 else
350 rect.y -= style_notebook->ythickness / 2;
351 }
352 *out_button_rect = DrawCloseButton(dc, widget, close_button_state, rect, wxRIGHT, &area);
353 }
354
355 tab_rect.width = std::min(tab_rect.width, clip_width);
356 *out_tab_rect = tab_rect;
357
358 dc.DestroyClippingRegion();
359 }
360
361 wxRect DrawSimpleArrow(wxDC& dc,
362 GtkWidget *widget,
363 int button_state,
364 wxRect const &in_rect,
365 int orientation,
366 GtkArrowType arrow_type)
367 {
368 int scroll_arrow_hlength, scroll_arrow_vlength;
369 gtk_widget_style_get(widget,
370 "scroll-arrow-hlength", &scroll_arrow_hlength,
371 "scroll-arrow-vlength", &scroll_arrow_vlength,
372 NULL);
373
374 GtkStateType state;
375 GtkShadowType shadow;
376 ButtonStateAndShadow(button_state, state, shadow);
377
378 wxRect out_rect;
379
380 if (orientation == wxLEFT)
381 out_rect.x = in_rect.x;
382 else
383 out_rect.x = in_rect.x + in_rect.width - scroll_arrow_hlength;
384 out_rect.y = (in_rect.y + in_rect.height - 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness - scroll_arrow_vlength) / 2;
385 out_rect.width = scroll_arrow_hlength;
386 out_rect.height = scroll_arrow_vlength;
387
388 wxGTKDCImpl *impldc = (wxGTKDCImpl*) dc.GetImpl();
389 GdkWindow* window = impldc->GetGDKWindow();
390 gtk_paint_arrow (gtk_widget_get_style(wxGTKPrivate::GetButtonWidget()), window, state, shadow, NULL, widget, "notebook",
391 arrow_type, TRUE, out_rect.x, out_rect.y, out_rect.width, out_rect.height);
392
393 return out_rect;
394 }
395
396 void wxAuiGtkTabArt::DrawButton(wxDC& dc, wxWindow* wnd,
397 const wxRect& in_rect,
398 int bitmap_id,
399 int button_state,
400 int orientation,
401 wxRect* out_rect)
402 {
403 GtkWidget *widget = wnd->GetHandle();
404 wxRect rect = in_rect;
405 if (m_flags &wxAUI_NB_BOTTOM)
406 rect.y += 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
407
408 switch (bitmap_id)
409 {
410 case wxAUI_BUTTON_CLOSE:
411 rect.y -= 2 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
412 rect = DrawCloseButton(dc, widget, button_state, rect, orientation, NULL);
413 break;
414
415 case wxAUI_BUTTON_LEFT:
416 rect = DrawSimpleArrow(dc, widget, button_state, rect, orientation, GTK_ARROW_LEFT);
417 break;
418
419 case wxAUI_BUTTON_RIGHT:
420 rect = DrawSimpleArrow(dc, widget, button_state, rect, orientation, GTK_ARROW_RIGHT);
421 break;
422
423 case wxAUI_BUTTON_WINDOWLIST:
424 {
425 rect.height -= 4 * gtk_widget_get_style(wxGTKPrivate::GetButtonWidget())->ythickness;
426 rect.width = rect.height;
427 rect.x = in_rect.x + in_rect.width - rect.width;
428
429 if (button_state == wxAUI_BUTTON_STATE_HOVER)
430 wxRendererNative::Get().DrawComboBoxDropButton(wnd, dc, rect, wxCONTROL_CURRENT);
431 else if (button_state == wxAUI_BUTTON_STATE_PRESSED)
432 wxRendererNative::Get().DrawComboBoxDropButton(wnd, dc, rect, wxCONTROL_PRESSED);
433 else
434 wxRendererNative::Get().DrawDropArrow(wnd, dc, rect);
435 }
436 break;
437 }
438
439 *out_rect = rect;
440 }
441
442
443 int wxAuiGtkTabArt::GetBestTabCtrlSize(wxWindow* wnd,
444 const wxAuiNotebookPageArray& pages,
445 const wxSize& required_bmp_size)
446 {
447 SetMeasuringFont(m_normalFont);
448 SetSelectedFont(m_normalFont);
449 int tab_height = 3 * gtk_widget_get_style(wxGTKPrivate::GetNotebookWidget())->ythickness + wxAuiGenericTabArt::GetBestTabCtrlSize(wnd, pages, required_bmp_size);
450 return tab_height;
451 }
452
453 wxSize wxAuiGtkTabArt::GetTabSize(wxDC& dc,
454 wxWindow* wnd,
455 const wxString& caption,
456 const wxBitmap& bitmap,
457 bool active,
458 int close_button_state,
459 int* x_extent)
460 {
461 wxSize s = wxAuiGenericTabArt::GetTabSize(dc, wnd, caption, bitmap, active, close_button_state, x_extent);
462
463 int overlap = 0;
464 gtk_widget_style_get (wnd->GetHandle(),
465 "focus-line-width", &overlap,
466 NULL);
467 *x_extent -= overlap;
468 return s;
469 }
470 #endif // wxUSE_AUI