1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/renderer.cpp
3 // Purpose: implementation of wxRendererNative for wxGTK
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 20.07.2003
7 // Copyright: (c) 2003 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22
23 #include "wx/renderer.h"
24
25 #ifndef WX_PRECOMP
26 #include "wx/window.h"
27 #include "wx/dcclient.h"
28 #include "wx/settings.h"
29 #include "wx/module.h"
30 #endif
31
32 #include "wx/dcgraph.h"
33 #ifndef __WXGTK3__
34 #include "wx/gtk/dc.h"
35 #include "wx/gtk/private/wrapgtk.h"
36 #if wxUSE_GRAPHICS_CONTEXT && defined(GDK_WINDOWING_X11)
37 #include <gdk/gdkx.h>
38 #include <cairo-xlib.h>
39 #endif
40 #endif
41
42 #include "wx/gtk/private.h"
43 #include "wx/gtk/private/stylecontext.h"
44 #include "wx/gtk/private/value.h"
45
46 #if defined(__WXGTK3__) && !GTK_CHECK_VERSION(3,14,0)
47 #define GTK_STATE_FLAG_CHECKED (1 << 11)
48 #endif
49
50 // ----------------------------------------------------------------------------
51 // wxRendererGTK: our wxRendererNative implementation
52 // ----------------------------------------------------------------------------
53
54 class WXDLLEXPORT wxRendererGTK : public wxDelegateRendererNative
55 {
56 public:
57 // draw the header control button (used by wxListCtrl)
58 virtual int DrawHeaderButton(wxWindow *win,
59 wxDC& dc,
60 const wxRect& rect,
61 int flags = 0,
62 wxHeaderSortIconType sortArrow = wxHDR_SORT_ICON_NONE,
63 wxHeaderButtonParams* params = NULL) wxOVERRIDE;
64
65 virtual int GetHeaderButtonHeight(wxWindow *win) wxOVERRIDE;
66
67 virtual int GetHeaderButtonMargin(wxWindow *win) wxOVERRIDE;
68
69
70 // draw the expanded/collapsed icon for a tree control item
71 virtual void DrawTreeItemButton(wxWindow *win,
72 wxDC& dc,
73 const wxRect& rect,
74 int flags = 0) wxOVERRIDE;
75
76 virtual void DrawSplitterBorder(wxWindow *win,
77 wxDC& dc,
78 const wxRect& rect,
79 int flags = 0) wxOVERRIDE;
80 virtual void DrawSplitterSash(wxWindow *win,
81 wxDC& dc,
82 const wxSize& size,
83 wxCoord position,
84 wxOrientation orient,
85 int flags = 0) wxOVERRIDE;
86
87 virtual void DrawComboBoxDropButton(wxWindow *win,
88 wxDC& dc,
89 const wxRect& rect,
90 int flags = 0) wxOVERRIDE;
91
92 virtual void DrawDropArrow(wxWindow *win,
93 wxDC& dc,
94 const wxRect& rect,
95 int flags = 0) wxOVERRIDE;
96
97 virtual void DrawCheckBox(wxWindow *win,
98 wxDC& dc,
99 const wxRect& rect,
100 int flags = 0) wxOVERRIDE;
101
102 virtual void DrawPushButton(wxWindow *win,
103 wxDC& dc,
104 const wxRect& rect,
105 int flags = 0) wxOVERRIDE;
106
107 virtual void DrawItemSelectionRect(wxWindow *win,
108 wxDC& dc,
109 const wxRect& rect,
110 int flags = 0) wxOVERRIDE;
111
112 virtual void DrawChoice(wxWindow* win,
113 wxDC& dc,
114 const wxRect& rect,
115 int flags=0) wxOVERRIDE;
116
117 virtual void DrawComboBox(wxWindow* win,
118 wxDC& dc,
119 const wxRect& rect,
120 int flags=0) wxOVERRIDE;
121
122 virtual void DrawTextCtrl(wxWindow* win,
123 wxDC& dc,
124 const wxRect& rect,
125 int flags=0) wxOVERRIDE;
126
127 virtual void DrawRadioBitmap(wxWindow* win,
128 wxDC& dc,
129 const wxRect& rect,
130 int flags=0) wxOVERRIDE;
131
132 virtual void DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags = 0) wxOVERRIDE;
133
134 virtual wxSize GetCheckBoxSize(wxWindow *win, int flags = 0) wxOVERRIDE;
135
136 virtual wxSplitterRenderParams GetSplitterParams(const wxWindow *win) wxOVERRIDE;
137 };
138
139 // ============================================================================
140 // implementation
141 // ============================================================================
142
143 /* static */
GetDefault()144 wxRendererNative& wxRendererNative::GetDefault()
145 {
146 static wxRendererGTK s_rendererGTK;
147
148 return s_rendererGTK;
149 }
150
151 #ifdef __WXGTK3__
152 #define NULL_RECT
153 typedef cairo_t wxGTKDrawable;
154
wxGetGTKDrawable(const wxDC & dc)155 static cairo_t* wxGetGTKDrawable(const wxDC& dc)
156 {
157 wxGraphicsContext* gc = dc.GetGraphicsContext();
158 wxCHECK_MSG(gc, NULL, "cannot use wxRendererNative on wxDC of this type");
159 return static_cast<cairo_t*>(gc->GetNativeContext());
160 }
161
162 static const GtkStateFlags stateTypeToFlags[] = {
163 GTK_STATE_FLAG_NORMAL, GTK_STATE_FLAG_ACTIVE, GTK_STATE_FLAG_PRELIGHT,
164 GTK_STATE_FLAG_SELECTED, GTK_STATE_FLAG_INSENSITIVE, GTK_STATE_FLAG_INCONSISTENT,
165 GTK_STATE_FLAG_FOCUSED
166 };
167
168 #else
169 #define NULL_RECT NULL,
170 typedef GdkWindow wxGTKDrawable;
171
wxGetGTKDrawable(wxDC & dc)172 static GdkWindow* wxGetGTKDrawable(wxDC& dc)
173 {
174 GdkWindow* gdk_window = NULL;
175
176 #if wxUSE_GRAPHICS_CONTEXT && defined(GDK_WINDOWING_X11)
177 cairo_t* cr = NULL;
178 wxGraphicsContext* gc = dc.GetGraphicsContext();
179 if (gc)
180 cr = static_cast<cairo_t*>(gc->GetNativeContext());
181 if (cr)
182 {
183 cairo_surface_t* surf = cairo_get_target(cr);
184 if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_XLIB)
185 {
186 gdk_window = static_cast<GdkWindow*>(
187 gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(surf)));
188 }
189 }
190 if (gdk_window == NULL)
191 #endif
192 {
193 wxDCImpl *impl = dc.GetImpl();
194 wxGTKDCImpl *gtk_impl = wxDynamicCast( impl, wxGTKDCImpl );
195 if (gtk_impl)
196 gdk_window = gtk_impl->GetGDKWindow();
197 else
198 wxFAIL_MSG("cannot use wxRendererNative on wxDC of this type");
199 }
200
201 return gdk_window;
202 }
203 #endif
204
205 // ----------------------------------------------------------------------------
206 // list/tree controls drawing
207 // ----------------------------------------------------------------------------
208
209 int
DrawHeaderButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags,wxHeaderSortIconType sortArrow,wxHeaderButtonParams * params)210 wxRendererGTK::DrawHeaderButton(wxWindow *win,
211 wxDC& dc,
212 const wxRect& rect,
213 int flags,
214 wxHeaderSortIconType sortArrow,
215 wxHeaderButtonParams* params)
216 {
217 GtkWidget *button = wxGTKPrivate::GetHeaderButtonWidget();
218 if (flags & wxCONTROL_SPECIAL)
219 button = wxGTKPrivate::GetHeaderButtonWidgetFirst();
220 if (flags & wxCONTROL_DIRTY)
221 button = wxGTKPrivate::GetHeaderButtonWidgetLast();
222
223 GtkStateType state = GTK_STATE_NORMAL;
224 if (flags & wxCONTROL_DISABLED)
225 state = GTK_STATE_INSENSITIVE;
226 else
227 {
228 if (flags & wxCONTROL_CURRENT)
229 state = GTK_STATE_PRELIGHT;
230 }
231
232 #ifdef __WXGTK3__
233 cairo_t* cr = wxGetGTKDrawable(dc);
234 if (cr == NULL)
235 return 0;
236
237 // AddTreeviewHeaderButton() is only available in 3.20 or later.
238 #if GTK_CHECK_VERSION(3,20,0)
239 if (gtk_check_version(3,20,0) == NULL)
240 {
241 int pos = 1;
242 if (flags & wxCONTROL_SPECIAL)
243 pos = 0;
244 if (flags & wxCONTROL_DIRTY)
245 pos = 2;
246
247 wxGtkStyleContext sc(dc.GetContentScaleFactor());
248 sc.AddTreeviewHeaderButton(pos);
249
250 gtk_style_context_set_state(sc, stateTypeToFlags[state]);
251 gtk_render_background(sc, cr, rect.x, rect.y, rect.width, rect.height);
252 gtk_render_frame(sc, cr, rect.x, rect.y, rect.width, rect.height);
253 }
254 else
255 #endif // GTK >= 3.20
256 {
257 GtkStyleContext* sc = gtk_widget_get_style_context(button);
258 gtk_style_context_save(sc);
259 gtk_style_context_set_state(sc, stateTypeToFlags[state]);
260 gtk_render_background(sc, cr, rect.x, rect.y, rect.width, rect.height);
261 gtk_render_frame(sc, cr, rect.x, rect.y, rect.width, rect.height);
262 gtk_style_context_restore(sc);
263 }
264 #else
265 int x_diff = 0;
266 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
267 x_diff = rect.width;
268
269 GdkWindow* gdk_window = wxGetGTKDrawable(dc);
270 gtk_paint_box
271 (
272 gtk_widget_get_style(button),
273 gdk_window,
274 state,
275 GTK_SHADOW_OUT,
276 NULL,
277 button,
278 "button",
279 dc.LogicalToDeviceX(rect.x) - x_diff, rect.y, rect.width, rect.height
280 );
281 #endif
282
283 return DrawHeaderButtonContents(win, dc, rect, flags, sortArrow, params);
284 }
285
GetHeaderButtonHeight(wxWindow * WXUNUSED (win))286 int wxRendererGTK::GetHeaderButtonHeight(wxWindow *WXUNUSED(win))
287 {
288 GtkWidget *button = wxGTKPrivate::GetHeaderButtonWidget();
289
290 GtkRequisition req;
291 #ifdef __WXGTK3__
292 gtk_widget_get_preferred_height(button, NULL, &req.height);
293 #else
294 GTK_WIDGET_GET_CLASS(button)->size_request(button, &req);
295 #endif
296
297 return req.height;
298 }
299
GetHeaderButtonMargin(wxWindow * WXUNUSED (win))300 int wxRendererGTK::GetHeaderButtonMargin(wxWindow *WXUNUSED(win))
301 {
302 return 0; // TODO: How to determine the real margin?
303 }
304
305
306 // draw a ">" or "v" button
307 void
DrawTreeItemButton(wxWindow * WXUNUSED_IN_GTK3 (win),wxDC & dc,const wxRect & rect,int flags)308 wxRendererGTK::DrawTreeItemButton(wxWindow* WXUNUSED_IN_GTK3(win),
309 wxDC& dc, const wxRect& rect, int flags)
310 {
311 wxGTKDrawable* drawable = wxGetGTKDrawable(dc);
312 if (drawable == NULL)
313 return;
314
315 GtkWidget *tree = wxGTKPrivate::GetTreeWidget();
316
317 #ifdef __WXGTK3__
318 int state = GTK_STATE_FLAG_NORMAL;
319 if (flags & wxCONTROL_EXPANDED)
320 {
321 state = GTK_STATE_FLAG_ACTIVE;
322 if (gtk_check_version(3,14,0) == NULL)
323 state = GTK_STATE_FLAG_CHECKED;
324 }
325 if (flags & wxCONTROL_CURRENT)
326 state |= GTK_STATE_FLAG_PRELIGHT;
327
328 int expander_size;
329 gtk_widget_style_get(tree, "expander-size", &expander_size, NULL);
330 // +1 to match GtkTreeView behavior
331 expander_size++;
332 const int x = rect.x + (rect.width - expander_size) / 2;
333 const int y = rect.y + (rect.width - expander_size) / 2;
334
335 GtkStyleContext* sc = gtk_widget_get_style_context(tree);
336 gtk_style_context_save(sc);
337 gtk_style_context_set_state(sc, GtkStateFlags(state));
338 gtk_style_context_add_class(sc, GTK_STYLE_CLASS_EXPANDER);
339 gtk_render_expander(sc, drawable, x, y, expander_size, expander_size);
340 gtk_style_context_restore(sc);
341 #else
342 int x_diff = 0;
343 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
344 x_diff = rect.width;
345
346 GtkStateType state;
347 if ( flags & wxCONTROL_CURRENT )
348 state = GTK_STATE_PRELIGHT;
349 else
350 state = GTK_STATE_NORMAL;
351
352 // x and y parameters specify the center of the expander
353 gtk_paint_expander
354 (
355 gtk_widget_get_style(tree),
356 drawable,
357 state,
358 NULL,
359 tree,
360 "treeview",
361 dc.LogicalToDeviceX(rect.x) + rect.width / 2 - x_diff,
362 dc.LogicalToDeviceY(rect.y) + rect.height / 2,
363 flags & wxCONTROL_EXPANDED ? GTK_EXPANDER_EXPANDED
364 : GTK_EXPANDER_COLLAPSED
365 );
366 #endif
367 }
368
369
370 // ----------------------------------------------------------------------------
371 // splitter sash drawing
372 // ----------------------------------------------------------------------------
373
GetGtkSplitterFullSize(GtkWidget * widget)374 static int GetGtkSplitterFullSize(GtkWidget* widget)
375 {
376 gint handle_size;
377 gtk_widget_style_get(widget, "handle_size", &handle_size, NULL);
378 // Narrow handles don't work well with wxSplitterWindow
379 if (handle_size < 5)
380 handle_size = 5;
381
382 return handle_size;
383 }
384
385 wxSplitterRenderParams
GetSplitterParams(const wxWindow * WXUNUSED (win))386 wxRendererGTK::GetSplitterParams(const wxWindow *WXUNUSED(win))
387 {
388 // we don't draw any border, hence 0 for the second field
389 return wxSplitterRenderParams
390 (
391 GetGtkSplitterFullSize(wxGTKPrivate::GetSplitterWidget()),
392 0,
393 true // hot sensitive
394 );
395 }
396
397 void
DrawSplitterBorder(wxWindow * WXUNUSED (win),wxDC & WXUNUSED (dc),const wxRect & WXUNUSED (rect),int WXUNUSED (flags))398 wxRendererGTK::DrawSplitterBorder(wxWindow * WXUNUSED(win),
399 wxDC& WXUNUSED(dc),
400 const wxRect& WXUNUSED(rect),
401 int WXUNUSED(flags))
402 {
403 // nothing to do
404 }
405
406 void
DrawSplitterSash(wxWindow * win,wxDC & dc,const wxSize & size,wxCoord position,wxOrientation orient,int flags)407 wxRendererGTK::DrawSplitterSash(wxWindow* win,
408 wxDC& dc,
409 const wxSize& size,
410 wxCoord position,
411 wxOrientation orient,
412 int flags)
413 {
414 if (gtk_widget_get_window(win->m_wxwindow) == NULL)
415 {
416 // window not realized yet
417 return;
418 }
419
420 wxGTKDrawable* drawable = wxGetGTKDrawable(dc);
421 if (drawable == NULL)
422 return;
423
424 // are we drawing vertical or horizontal splitter?
425 const bool isVert = orient == wxVERTICAL;
426
427 GtkWidget* widget = wxGTKPrivate::GetSplitterWidget(orient);
428 const int full_size = GetGtkSplitterFullSize(widget);
429
430 GdkRectangle rect;
431
432 if ( isVert )
433 {
434 rect.x = position;
435 rect.y = 0;
436 rect.width = full_size;
437 rect.height = size.y;
438 }
439 else // horz
440 {
441 rect.x = 0;
442 rect.y = position;
443 rect.height = full_size;
444 rect.width = size.x;
445 }
446
447 #ifdef __WXGTK3__
448 wxGtkStyleContext sc(dc.GetContentScaleFactor());
449 sc.AddWindow();
450 gtk_render_background(sc, drawable, rect.x, rect.y, rect.width, rect.height);
451
452 sc.Add(GTK_TYPE_PANED, "paned", "pane-separator", NULL);
453 if (gtk_check_version(3,20,0) == NULL)
454 sc.Add("separator");
455
456 gtk_style_context_set_state(sc,
457 flags & wxCONTROL_CURRENT ? GTK_STATE_FLAG_PRELIGHT : GTK_STATE_FLAG_NORMAL);
458 gtk_render_handle(sc, drawable, rect.x, rect.y, rect.width, rect.height);
459 #else
460 int x_diff = 0;
461 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
462 x_diff = rect.width;
463
464 GdkWindow* gdk_window = wxGetGTKDrawable(dc);
465 if (gdk_window == NULL)
466 return;
467 gtk_paint_handle
468 (
469 gtk_widget_get_style(win->m_wxwindow),
470 gdk_window,
471 flags & wxCONTROL_CURRENT ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
472 GTK_SHADOW_NONE,
473 NULL /* no clipping */,
474 win->m_wxwindow,
475 "paned",
476 dc.LogicalToDeviceX(rect.x) - x_diff,
477 dc.LogicalToDeviceY(rect.y),
478 rect.width,
479 rect.height,
480 isVert ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL
481 );
482 #endif
483 }
484
485 void
DrawDropArrow(wxWindow *,wxDC & dc,const wxRect & rect,int flags)486 wxRendererGTK::DrawDropArrow(wxWindow*,
487 wxDC& dc,
488 const wxRect& rect,
489 int flags)
490 {
491 GtkWidget *button = wxGTKPrivate::GetButtonWidget();
492
493 // If we give WX_PIZZA(win->m_wxwindow)->bin_window as
494 // a window for gtk_paint_xxx function, then it won't
495 // work for wxMemoryDC. So that is why we assume wxDC
496 // is wxWindowDC (wxClientDC, wxMemoryDC and wxPaintDC
497 // are derived from it) and use its m_window.
498
499 // draw arrow so that there is even space horizontally
500 // on both sides
501 const int size = rect.width / 2;
502 const int x = rect.x + (size + 1) / 2;
503 const int y = rect.y + (rect.height - size + 1) / 2;
504
505 GtkStateType state;
506
507 if ( flags & wxCONTROL_PRESSED )
508 state = GTK_STATE_ACTIVE;
509 else if ( flags & wxCONTROL_DISABLED )
510 state = GTK_STATE_INSENSITIVE;
511 else if ( flags & wxCONTROL_CURRENT )
512 state = GTK_STATE_PRELIGHT;
513 else
514 state = GTK_STATE_NORMAL;
515
516 #ifdef __WXGTK3__
517 cairo_t* cr = wxGetGTKDrawable(dc);
518 if (cr)
519 {
520 gtk_widget_set_state_flags(button, stateTypeToFlags[state], true);
521 GtkStyleContext* sc = gtk_widget_get_style_context(button);
522 gtk_render_arrow(sc, cr, G_PI, x, y, size);
523 }
524 #else
525 GdkWindow* gdk_window = wxGetGTKDrawable(dc);
526 if (gdk_window == NULL)
527 return;
528 // draw arrow on button
529 gtk_paint_arrow
530 (
531 gtk_widget_get_style(button),
532 gdk_window,
533 state,
534 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
535 NULL,
536 button,
537 "arrow",
538 GTK_ARROW_DOWN,
539 FALSE,
540 x, y,
541 size, size
542 );
543 #endif
544 }
545
546 void
DrawComboBoxDropButton(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)547 wxRendererGTK::DrawComboBoxDropButton(wxWindow *win,
548 wxDC& dc,
549 const wxRect& rect,
550 int flags)
551 {
552 DrawPushButton(win,dc,rect,flags);
553 DrawDropArrow(win,dc,rect);
554 }
555
556 // Helper used by GetCheckBoxSize() and DrawCheckBox().
557 namespace
558 {
559
560 struct CheckBoxInfo
561 {
562 #ifdef __WXGTK3__
CheckBoxInfo__anonc4cc220c0111::CheckBoxInfo563 CheckBoxInfo(wxGtkStyleContext& sc, int flags)
564 {
565 wxUnusedVar(flags);
566
567 sc.AddCheckButton();
568 if (gtk_check_version(3,20,0) == NULL)
569 {
570 sc.Add("check");
571 gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL,
572 "min-width", &indicator_width,
573 "min-height", &indicator_height,
574 NULL);
575
576 GtkBorder border, padding;
577 gtk_style_context_get_border(sc, GTK_STATE_FLAG_NORMAL, &border);
578 gtk_style_context_get_padding(sc, GTK_STATE_FLAG_NORMAL, &padding);
579
580 margin_left = border.left + padding.left;
581 margin_top = border.top + padding.top;
582 margin_right = border.right + padding.right;
583 margin_bottom = border.bottom + padding.bottom;
584 }
585 else
586 {
587 wxGtkValue value( G_TYPE_INT);
588
589 gtk_style_context_get_style_property(sc, "indicator-size", value);
590 indicator_width =
591 indicator_height = g_value_get_int(value);
592
593 gtk_style_context_get_style_property(sc, "indicator-spacing", value);
594 margin_left =
595 margin_top =
596 margin_right =
597 margin_bottom = g_value_get_int(value);
598 }
599 }
600 #else // !__WXGTK3__
601 CheckBoxInfo(GtkWidget* button, int flags)
602 {
603 gint indicator_size, indicator_margin;
604 gtk_widget_style_get(button,
605 "indicator_size", &indicator_size,
606 "indicator_spacing", &indicator_margin,
607 NULL);
608
609 // If wxCONTROL_CELL is set then we want to get the size of wxCheckBox
610 // control to draw the check mark centered and at the same position as
611 // wxCheckBox does, so offset the check mark itself by the focus margin
612 // in the same way as gtk_real_check_button_draw_indicator() does it, see
613 // https://github.com/GNOME/gtk/blob/GTK_2_16_0/gtk/gtkcheckbutton.c#L374
614 if ( flags & wxCONTROL_CELL )
615 {
616 gint focus_width, focus_pad;
617 gtk_widget_style_get(button,
618 "focus-line-width", &focus_width,
619 "focus-padding", &focus_pad,
620 NULL);
621
622 indicator_margin += focus_width + focus_pad;
623 }
624
625 // In GTK 2 width and height are the same and so are left/right and
626 // top/bottom.
627 indicator_width =
628 indicator_height = indicator_size;
629
630 margin_left =
631 margin_top =
632 margin_right =
633 margin_bottom = indicator_margin;
634 }
635 #endif // __WXGTK3__/!__WXGTK3__
636
637 // Make sure we fit into the provided rectangle, eliminating margins and
638 // even reducing the size if necessary.
FitInto__anonc4cc220c0111::CheckBoxInfo639 void FitInto(const wxRect& rect)
640 {
641 if ( indicator_width > rect.width )
642 {
643 indicator_width = rect.width;
644 margin_left =
645 margin_right = 0;
646 }
647 else if ( indicator_width + margin_left + margin_right > rect.width )
648 {
649 margin_left =
650 margin_right = (rect.width - indicator_width) / 2;
651 }
652
653 if ( indicator_height > rect.height )
654 {
655 indicator_height = rect.height;
656 margin_top =
657 margin_bottom = 0;
658 }
659 else if ( indicator_height + margin_top + margin_bottom > rect.height )
660 {
661 margin_top =
662 margin_bottom = (rect.height - indicator_height) / 2;
663 }
664 }
665
666 gint indicator_width,
667 indicator_height;
668 gint margin_left,
669 margin_top,
670 margin_right,
671 margin_bottom;
672 };
673
674 } // anonymous namespace
675
676 wxSize
GetCheckBoxSize(wxWindow * win,int flags)677 wxRendererGTK::GetCheckBoxSize(wxWindow* win, int flags)
678 {
679 wxSize size;
680 // Even though we don't use the window in this implementation, still check
681 // that it's valid to avoid surprises when running the same code under the
682 // other platforms.
683 wxCHECK_MSG(win, size, "Must have a valid window");
684
685 #ifdef __WXGTK3__
686 wxGtkStyleContext sc(win->GetContentScaleFactor());
687
688 const CheckBoxInfo info(sc, flags);
689 #else // !__WXGTK3__
690 GtkWidget* button = wxGTKPrivate::GetCheckButtonWidget();
691
692 const CheckBoxInfo info(button, flags);
693 #endif // __WXGTK3__/!__WXGTK3__
694
695 size.x = info.indicator_width + info.margin_left + info.margin_right;
696 size.y = info.indicator_height + info.margin_top + info.margin_bottom;
697
698 return size;
699 }
700
701 void
DrawCheckBox(wxWindow *,wxDC & dc,const wxRect & rect,int flags)702 wxRendererGTK::DrawCheckBox(wxWindow*,
703 wxDC& dc,
704 const wxRect& rect,
705 int flags )
706 {
707 #ifdef __WXGTK3__
708 cairo_t* cr = wxGetGTKDrawable(dc);
709 if (cr == NULL)
710 return;
711
712 int state = GTK_STATE_FLAG_NORMAL;
713 if (flags & wxCONTROL_CHECKED)
714 {
715 state = GTK_STATE_FLAG_ACTIVE;
716 if (gtk_check_version(3,14,0) == NULL)
717 state = GTK_STATE_FLAG_CHECKED;
718 }
719 if (flags & wxCONTROL_DISABLED)
720 state |= GTK_STATE_FLAG_INSENSITIVE;
721 if (flags & wxCONTROL_UNDETERMINED)
722 state |= GTK_STATE_FLAG_INCONSISTENT;
723 if (flags & wxCONTROL_CURRENT)
724 state |= GTK_STATE_FLAG_PRELIGHT;
725
726 wxGtkStyleContext sc(dc.GetContentScaleFactor());
727
728 CheckBoxInfo info(sc, flags);
729 info.FitInto(rect);
730
731 const int w = info.indicator_width + info.margin_left + info.margin_right;
732 const int h = info.indicator_height + info.margin_top + info.margin_bottom;
733
734 int x = rect.x + (rect.width - w) / 2;
735 int y = rect.y + (rect.height - h) / 2;
736
737 const bool isRTL = dc.GetLayoutDirection() == wxLayout_RightToLeft;
738 if (isRTL)
739 {
740 // checkbox is not mirrored
741 cairo_save(cr);
742 cairo_scale(cr, -1, 1);
743 x = -x - w;
744 }
745
746 if (gtk_check_version(3,20,0) == NULL)
747 {
748 gtk_style_context_set_state(sc, GtkStateFlags(state));
749 gtk_render_background(sc, cr, x, y, w, h);
750 gtk_render_frame(sc, cr, x, y, w, h);
751
752 // check is rendered in content area
753 gtk_render_check(sc, cr,
754 x + info.margin_left, y + info.margin_top,
755 info.indicator_width, info.indicator_height);
756 }
757 else
758 {
759 // need save/restore for GTK+ 3.6 & 3.8
760 gtk_style_context_save(sc);
761 gtk_style_context_set_state(sc, GtkStateFlags(state));
762 gtk_render_background(sc, cr, x, y, w, h);
763 gtk_render_frame(sc, cr, x, y, w, h);
764 gtk_style_context_add_class(sc, "check");
765 gtk_render_check(sc, cr, x, y, w, h);
766 gtk_style_context_restore(sc);
767 }
768 if (isRTL)
769 cairo_restore(cr);
770
771 #else // !__WXGTK3__
772 GtkWidget* button = wxGTKPrivate::GetCheckButtonWidget();
773
774 CheckBoxInfo info(button, flags);
775 info.FitInto(rect);
776
777 GtkStateType state;
778
779 if ( flags & wxCONTROL_PRESSED )
780 state = GTK_STATE_ACTIVE;
781 else if ( flags & wxCONTROL_DISABLED )
782 state = GTK_STATE_INSENSITIVE;
783 else if ( flags & wxCONTROL_CURRENT )
784 state = GTK_STATE_PRELIGHT;
785 else
786 state = GTK_STATE_NORMAL;
787
788 GtkShadowType shadow_type;
789
790 if ( flags & wxCONTROL_UNDETERMINED )
791 shadow_type = GTK_SHADOW_ETCHED_IN;
792 else if ( flags & wxCONTROL_CHECKED )
793 shadow_type = GTK_SHADOW_IN;
794 else
795 shadow_type = GTK_SHADOW_OUT;
796
797 GdkWindow* gdk_window = wxGetGTKDrawable(dc);
798 if (gdk_window == NULL)
799 return;
800
801 gtk_paint_check
802 (
803 gtk_widget_get_style(button),
804 gdk_window,
805 state,
806 shadow_type,
807 NULL,
808 button,
809 "cellcheck",
810 dc.LogicalToDeviceX(rect.x) + info.margin_left,
811 dc.LogicalToDeviceY(rect.y) + (rect.height - info.indicator_height) / 2,
812 info.indicator_width, info.indicator_height
813 );
814 #endif // __WXGTK3__/!__WXGTK3__
815 }
816
817 void
DrawPushButton(wxWindow *,wxDC & dc,const wxRect & rect,int flags)818 wxRendererGTK::DrawPushButton(wxWindow*,
819 wxDC& dc,
820 const wxRect& rect,
821 int flags)
822 {
823 GtkWidget *button = wxGTKPrivate::GetButtonWidget();
824
825 // draw button
826 GtkStateType state;
827
828 if ( flags & wxCONTROL_PRESSED )
829 state = GTK_STATE_ACTIVE;
830 else if ( flags & wxCONTROL_DISABLED )
831 state = GTK_STATE_INSENSITIVE;
832 else if ( flags & wxCONTROL_CURRENT )
833 state = GTK_STATE_PRELIGHT;
834 else
835 state = GTK_STATE_NORMAL;
836
837 #ifdef __WXGTK3__
838 cairo_t* cr = wxGetGTKDrawable(dc);
839 if (cr)
840 {
841 GtkStyleContext* sc = gtk_widget_get_style_context(button);
842 gtk_style_context_save(sc);
843 gtk_style_context_set_state(sc, stateTypeToFlags[state]);
844 gtk_render_background(sc, cr, rect.x, rect.y, rect.width, rect.height);
845 gtk_render_frame(sc, cr, rect.x, rect.y, rect.width, rect.height);
846 gtk_style_context_restore(sc);
847 }
848 #else
849 GdkWindow* gdk_window = wxGetGTKDrawable(dc);
850 if (gdk_window == NULL)
851 return;
852
853 gtk_paint_box
854 (
855 gtk_widget_get_style(button),
856 gdk_window,
857 state,
858 flags & wxCONTROL_PRESSED ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
859 NULL,
860 button,
861 "button",
862 dc.LogicalToDeviceX(rect.x),
863 dc.LogicalToDeviceY(rect.y),
864 rect.width,
865 rect.height
866 );
867 #endif
868 }
869
870 void
DrawItemSelectionRect(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)871 wxRendererGTK::DrawItemSelectionRect(wxWindow* win,
872 wxDC& dc,
873 const wxRect& rect,
874 int flags )
875 {
876 wxGTKDrawable* drawable = wxGetGTKDrawable(dc);
877 if (drawable == NULL)
878 return;
879
880 if (flags & wxCONTROL_SELECTED)
881 {
882 GtkWidget* treeWidget = wxGTKPrivate::GetTreeWidget();
883
884 #ifdef __WXGTK3__
885 GtkStyleContext* sc = gtk_widget_get_style_context(treeWidget);
886 gtk_style_context_save(sc);
887 int state = GTK_STATE_FLAG_SELECTED;
888 if (flags & wxCONTROL_FOCUSED)
889 state |= GTK_STATE_FLAG_FOCUSED;
890 gtk_style_context_set_state(sc, GtkStateFlags(state));
891 gtk_style_context_add_class(sc, GTK_STYLE_CLASS_CELL);
892 gtk_render_background(sc, drawable, rect.x, rect.y, rect.width, rect.height);
893 gtk_style_context_restore(sc);
894 #else
895 int x_diff = 0;
896 if (win->GetLayoutDirection() == wxLayout_RightToLeft)
897 x_diff = rect.width;
898
899 // the wxCONTROL_FOCUSED state is deduced
900 // directly from the m_wxwindow by GTK+
901 gtk_paint_flat_box(gtk_widget_get_style(treeWidget),
902 drawable,
903 GTK_STATE_SELECTED,
904 GTK_SHADOW_NONE,
905 NULL_RECT
906 win->m_wxwindow,
907 "cell_even",
908 dc.LogicalToDeviceX(rect.x) - x_diff,
909 dc.LogicalToDeviceY(rect.y),
910 rect.width,
911 rect.height );
912 #endif
913 }
914
915 if ((flags & wxCONTROL_CURRENT) && (flags & wxCONTROL_FOCUSED))
916 DrawFocusRect(win, dc, rect, flags);
917 }
918
DrawFocusRect(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)919 void wxRendererGTK::DrawFocusRect(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
920 {
921 wxGTKDrawable* drawable = wxGetGTKDrawable(dc);
922 if (drawable == NULL)
923 return;
924
925 GtkStateType state;
926 if (flags & wxCONTROL_SELECTED)
927 state = GTK_STATE_SELECTED;
928 else
929 state = GTK_STATE_NORMAL;
930
931 #ifdef __WXGTK3__
932 GtkStyleContext* sc = gtk_widget_get_style_context(win->m_widget);
933 gtk_style_context_save(sc);
934 gtk_style_context_set_state(sc, stateTypeToFlags[state]);
935 gtk_render_focus(sc, drawable, rect.x, rect.y, rect.width, rect.height);
936 gtk_style_context_restore(sc);
937 #else
938 gtk_paint_focus( gtk_widget_get_style(win->m_widget),
939 drawable,
940 state,
941 NULL_RECT
942 win->m_wxwindow,
943 NULL,
944 dc.LogicalToDeviceX(rect.x),
945 dc.LogicalToDeviceY(rect.y),
946 rect.width,
947 rect.height );
948 #endif
949 }
950
951 // Uses the theme to draw the border and fill for something like a wxTextCtrl
DrawTextCtrl(wxWindow *,wxDC & dc,const wxRect & rect,int flags)952 void wxRendererGTK::DrawTextCtrl(wxWindow*, wxDC& dc, const wxRect& rect, int flags)
953 {
954 wxGTKDrawable* drawable = wxGetGTKDrawable(dc);
955 if (drawable == NULL)
956 return;
957
958 #ifdef __WXGTK3__
959 int state = GTK_STATE_FLAG_NORMAL;
960 if (flags & wxCONTROL_FOCUSED)
961 state = GTK_STATE_FLAG_FOCUSED;
962 if (flags & wxCONTROL_DISABLED)
963 state = GTK_STATE_FLAG_INSENSITIVE;
964
965 wxGtkStyleContext sc(dc.GetContentScaleFactor());
966 sc.Add(GTK_TYPE_ENTRY, "entry", "entry", NULL);
967
968 gtk_style_context_set_state(sc, GtkStateFlags(state));
969 gtk_render_background(sc, drawable, rect.x, rect.y, rect.width, rect.height);
970 gtk_render_frame(sc, drawable, rect.x, rect.y, rect.width, rect.height);
971 #else
972 GtkWidget* entry = wxGTKPrivate::GetTextEntryWidget();
973
974 GtkStateType state = GTK_STATE_NORMAL;
975 if ( flags & wxCONTROL_DISABLED )
976 state = GTK_STATE_INSENSITIVE;
977
978 gtk_widget_set_can_focus(entry, (flags & wxCONTROL_CURRENT) != 0);
979
980 gtk_paint_shadow
981 (
982 gtk_widget_get_style(entry),
983 drawable,
984 state,
985 GTK_SHADOW_OUT,
986 NULL_RECT
987 entry,
988 "entry",
989 dc.LogicalToDeviceX(rect.x),
990 dc.LogicalToDeviceY(rect.y),
991 rect.width,
992 rect.height
993 );
994 #endif
995 }
996
997 // Draw the equivalent of a wxComboBox
DrawComboBox(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)998 void wxRendererGTK::DrawComboBox(wxWindow* win, wxDC& dc, const wxRect& rect, int flags)
999 {
1000 wxGTKDrawable* drawable = wxGetGTKDrawable(dc);
1001 if (drawable == NULL)
1002 return;
1003
1004 GtkWidget* combo = wxGTKPrivate::GetComboBoxWidget();
1005
1006 GtkStateType state = GTK_STATE_NORMAL;
1007 if ( flags & wxCONTROL_DISABLED )
1008 state = GTK_STATE_INSENSITIVE;
1009
1010 gtk_widget_set_can_focus(combo, (flags & wxCONTROL_CURRENT) != 0);
1011
1012 #ifdef __WXGTK3__
1013 GtkStyleContext* sc = gtk_widget_get_style_context(combo);
1014 gtk_style_context_save(sc);
1015 gtk_style_context_set_state(sc, stateTypeToFlags[state]);
1016 gtk_render_background(sc, drawable, rect.x, rect.y, rect.width, rect.height);
1017 gtk_render_frame(sc, drawable, rect.x, rect.y, rect.width, rect.height);
1018 gtk_style_context_restore(sc);
1019 wxRect r = rect;
1020 r.x += r.width - r.height;
1021 r.width = r.height;
1022 DrawComboBoxDropButton(win, dc, r, flags);
1023 #else
1024 wxUnusedVar(win);
1025 gtk_paint_shadow
1026 (
1027 gtk_widget_get_style(combo),
1028 drawable,
1029 state,
1030 GTK_SHADOW_OUT,
1031 NULL_RECT
1032 combo,
1033 "combobox",
1034 dc.LogicalToDeviceX(rect.x),
1035 dc.LogicalToDeviceY(rect.y),
1036 rect.width,
1037 rect.height
1038 );
1039
1040 wxRect r = rect;
1041 int extent = rect.height / 2;
1042 r.x += rect.width - extent - extent/2;
1043 r.y += extent/2;
1044 r.width = extent;
1045 r.height = extent;
1046
1047 gtk_paint_arrow
1048 (
1049 gtk_widget_get_style(combo),
1050 drawable,
1051 state,
1052 GTK_SHADOW_OUT,
1053 NULL_RECT
1054 combo,
1055 "arrow",
1056 GTK_ARROW_DOWN,
1057 TRUE,
1058 dc.LogicalToDeviceX(r.x),
1059 dc.LogicalToDeviceY(r.y),
1060 r.width,
1061 r.height
1062 );
1063
1064 r = rect;
1065 r.x += rect.width - 2*extent;
1066 r.width = 2;
1067
1068 gtk_paint_box
1069 (
1070 gtk_widget_get_style(combo),
1071 drawable,
1072 state,
1073 GTK_SHADOW_ETCHED_OUT,
1074 NULL_RECT
1075 combo,
1076 "vseparator",
1077 dc.LogicalToDeviceX(r.x),
1078 dc.LogicalToDeviceY(r.y+1),
1079 r.width,
1080 r.height-2
1081 );
1082 #endif
1083 }
1084
DrawChoice(wxWindow * win,wxDC & dc,const wxRect & rect,int flags)1085 void wxRendererGTK::DrawChoice(wxWindow* win, wxDC& dc,
1086 const wxRect& rect, int flags)
1087 {
1088 DrawComboBox( win, dc, rect, flags );
1089 }
1090
1091
1092 // Draw a themed radio button
DrawRadioBitmap(wxWindow *,wxDC & dc,const wxRect & rect,int flags)1093 void wxRendererGTK::DrawRadioBitmap(wxWindow*, wxDC& dc, const wxRect& rect, int flags)
1094 {
1095 wxGTKDrawable* drawable = wxGetGTKDrawable(dc);
1096 if (drawable == NULL)
1097 return;
1098
1099 #ifdef __WXGTK3__
1100 int state = GTK_STATE_FLAG_NORMAL;
1101 if (flags & wxCONTROL_CHECKED)
1102 {
1103 state = GTK_STATE_FLAG_ACTIVE;
1104 if (gtk_check_version(3,14,0) == NULL)
1105 state = GTK_STATE_FLAG_CHECKED;
1106 }
1107 if (flags & wxCONTROL_DISABLED)
1108 state |= GTK_STATE_FLAG_INSENSITIVE;
1109 if (flags & wxCONTROL_UNDETERMINED)
1110 state |= GTK_STATE_FLAG_INCONSISTENT;
1111 if (flags & wxCONTROL_CURRENT)
1112 state |= GTK_STATE_FLAG_PRELIGHT;
1113
1114 int min_width, min_height;
1115 wxGtkStyleContext sc(dc.GetContentScaleFactor());
1116 sc.Add(GTK_TYPE_RADIO_BUTTON, "radiobutton", NULL);
1117 if (gtk_check_version(3,20,0) == NULL)
1118 {
1119 sc.Add("radio");
1120 gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL,
1121 "min-width", &min_width, "min-height", &min_height, NULL);
1122 }
1123 else
1124 {
1125 wxGtkValue value( G_TYPE_INT);
1126 gtk_style_context_get_style_property(sc, "indicator-size", value);
1127 min_width = g_value_get_int(value);
1128 min_height = min_width;
1129 }
1130
1131 // need save/restore for GTK+ 3.6 & 3.8
1132 gtk_style_context_save(sc);
1133 gtk_style_context_set_state(sc, GtkStateFlags(state));
1134 const int x = rect.x + (rect.width - min_width) / 2;
1135 const int y = rect.y + (rect.height - min_height) / 2;
1136 gtk_render_background(sc, drawable, x, y, min_width, min_height);
1137 gtk_render_frame(sc, drawable, x, y, min_width, min_height);
1138 gtk_style_context_add_class(sc, "radio");
1139 gtk_render_option(sc, drawable, x, y, min_width, min_height);
1140 gtk_style_context_restore(sc);
1141 #else
1142 GtkWidget* button = wxGTKPrivate::GetRadioButtonWidget();
1143
1144 GtkShadowType shadow_type = GTK_SHADOW_OUT;
1145 if ( flags & wxCONTROL_CHECKED )
1146 shadow_type = GTK_SHADOW_IN;
1147 else if ( flags & wxCONTROL_UNDETERMINED )
1148 shadow_type = GTK_SHADOW_ETCHED_IN;
1149
1150 GtkStateType state = GTK_STATE_NORMAL;
1151 if ( flags & wxCONTROL_DISABLED )
1152 state = GTK_STATE_INSENSITIVE;
1153 if ( flags & wxCONTROL_PRESSED )
1154 state = GTK_STATE_ACTIVE;
1155 /*
1156 Don't know when to set this
1157 state_type = GTK_STATE_PRELIGHT;
1158 */
1159
1160 gtk_paint_option
1161 (
1162 gtk_widget_get_style(button),
1163 drawable,
1164 state,
1165 shadow_type,
1166 NULL_RECT
1167 button,
1168 "radiobutton",
1169 dc.LogicalToDeviceX(rect.x),
1170 dc.LogicalToDeviceY(rect.y),
1171 rect.width, rect.height
1172 );
1173 #endif
1174 }
1175