1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * This file contains painting functions for each of the gtk2 widgets.
8 * Adapted from the gtkdrawing.c, and gtk+2.0 source.
12 #include <gdk/gdkprivate.h>
14 #include "gtkdrawing.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/ScopeExit.h"
18 #include "WidgetStyleCache.h"
25 static gboolean checkbox_check_state
;
26 static gboolean notebook_has_tab_gap
;
28 static ScrollbarGTKMetrics sScrollbarMetrics
[2];
29 static ScrollbarGTKMetrics sActiveScrollbarMetrics
[2];
30 static ToggleGTKMetrics sCheckboxMetrics
;
31 static ToggleGTKMetrics sRadioMetrics
;
32 static ToggleGTKMetrics sMenuRadioMetrics
;
33 static ToggleGTKMetrics sMenuCheckboxMetrics
;
34 static ToolbarGTKMetrics sToolbarMetrics
;
35 static CSDWindowDecorationSize sToplevelWindowDecorationSize
;
36 static CSDWindowDecorationSize sPopupWindowDecorationSize
;
41 #define ARROW_DOWN G_PI
42 #define ARROW_RIGHT G_PI_2
43 #define ARROW_LEFT (G_PI + G_PI_2)
46 // It's used for debugging only to compare Gecko widget style with
47 // the ones used by Gtk+ applications.
49 style_path_print(GtkStyleContext
*context
)
51 const GtkWidgetPath
* path
= gtk_style_context_get_path(context
);
53 static auto sGtkWidgetPathToStringPtr
=
54 (char * (*)(const GtkWidgetPath
*))
55 dlsym(RTLD_DEFAULT
, "gtk_widget_path_to_string");
57 fprintf(stderr
, "Style path:\n%s\n\n", sGtkWidgetPathToStringPtr(path
));
61 static GtkBorder
operator-(const GtkBorder
& first
, const GtkBorder
& second
) {
63 result
.left
= first
.left
- second
.left
;
64 result
.right
= first
.right
- second
.right
;
65 result
.top
= first
.top
- second
.top
;
66 result
.bottom
= first
.bottom
- second
.bottom
;
70 static GtkBorder
operator+(const GtkBorder
& first
, const GtkBorder
& second
) {
72 result
.left
= first
.left
+ second
.left
;
73 result
.right
= first
.right
+ second
.right
;
74 result
.top
= first
.top
+ second
.top
;
75 result
.bottom
= first
.bottom
+ second
.bottom
;
79 static GtkBorder
operator+=(GtkBorder
& first
, const GtkBorder
& second
) {
80 first
.left
+= second
.left
;
81 first
.right
+= second
.right
;
82 first
.top
+= second
.top
;
83 first
.bottom
+= second
.bottom
;
87 static gint
moz_gtk_get_tab_thickness(GtkStyleContext
* style
);
89 static gint
moz_gtk_menu_item_paint(WidgetNodeType widget
, cairo_t
* cr
,
90 GdkRectangle
* rect
, GtkWidgetState
* state
,
91 GtkTextDirection direction
);
93 static GtkBorder
GetMarginBorderPadding(GtkStyleContext
* aStyle
);
95 static void Inset(GdkRectangle
*, const GtkBorder
&);
97 static void InsetByMargin(GdkRectangle
*, GtkStyleContext
* style
);
99 static void moz_gtk_add_style_margin(GtkStyleContext
* style
, gint
* left
,
100 gint
* top
, gint
* right
, gint
* bottom
) {
103 gtk_style_context_get_margin(style
, gtk_style_context_get_state(style
),
105 *left
+= margin
.left
;
106 *right
+= margin
.right
;
108 *bottom
+= margin
.bottom
;
111 static void moz_gtk_add_style_border(GtkStyleContext
* style
, gint
* left
,
112 gint
* top
, gint
* right
, gint
* bottom
) {
115 gtk_style_context_get_border(style
, gtk_style_context_get_state(style
),
118 *left
+= border
.left
;
119 *right
+= border
.right
;
121 *bottom
+= border
.bottom
;
124 static void moz_gtk_add_style_padding(GtkStyleContext
* style
, gint
* left
,
125 gint
* top
, gint
* right
, gint
* bottom
) {
128 gtk_style_context_get_padding(style
, gtk_style_context_get_state(style
),
131 *left
+= padding
.left
;
132 *right
+= padding
.right
;
134 *bottom
+= padding
.bottom
;
137 static void moz_gtk_add_margin_border_padding(GtkStyleContext
* style
,
138 gint
* left
, gint
* top
,
139 gint
* right
, gint
* bottom
) {
140 moz_gtk_add_style_margin(style
, left
, top
, right
, bottom
);
141 moz_gtk_add_style_border(style
, left
, top
, right
, bottom
);
142 moz_gtk_add_style_padding(style
, left
, top
, right
, bottom
);
145 static void moz_gtk_add_border_padding(GtkStyleContext
* style
, gint
* left
,
146 gint
* top
, gint
* right
, gint
* bottom
) {
147 moz_gtk_add_style_border(style
, left
, top
, right
, bottom
);
148 moz_gtk_add_style_padding(style
, left
, top
, right
, bottom
);
151 // In case there's an error in Gtk theme and preferred size is zero,
152 // return some sane values to pass mozilla automation tests.
153 // It should not happen in real-life.
154 #define MIN_WIDGET_SIZE 10
155 static void moz_gtk_sanity_preferred_size(GtkRequisition
* requisition
) {
156 if (requisition
->width
<= 0) {
157 requisition
->width
= MIN_WIDGET_SIZE
;
159 if (requisition
->height
<= 0) {
160 requisition
->height
= MIN_WIDGET_SIZE
;
164 // GetStateFlagsFromGtkWidgetState() can be safely used for the specific
165 // GtkWidgets that set both prelight and active flags. For other widgets,
166 // either the GtkStateFlags or Gecko's GtkWidgetState need to be carefully
167 // adjusted to match GTK behavior. Although GTK sets insensitive and focus
168 // flags in the generic GtkWidget base class, GTK adds prelight and active
169 // flags only to widgets that are expected to demonstrate prelight or active
170 // states. This contrasts with HTML where any element may have :active and
171 // :hover states, and so Gecko's GtkStateFlags do not necessarily map to GTK
172 // flags. Failure to restrict the flags in the same way as GTK can cause
173 // generic CSS selectors from some themes to unintentionally match elements
174 // that are not expected to change appearance on hover or mouse-down.
175 static GtkStateFlags
GetStateFlagsFromGtkWidgetState(GtkWidgetState
* state
) {
176 GtkStateFlags stateFlags
= GTK_STATE_FLAG_NORMAL
;
179 stateFlags
= GTK_STATE_FLAG_INSENSITIVE
;
181 if (state
->depressed
|| state
->active
)
183 static_cast<GtkStateFlags
>(stateFlags
| GTK_STATE_FLAG_ACTIVE
);
186 static_cast<GtkStateFlags
>(stateFlags
| GTK_STATE_FLAG_PRELIGHT
);
189 static_cast<GtkStateFlags
>(stateFlags
| GTK_STATE_FLAG_FOCUSED
);
192 static_cast<GtkStateFlags
>(stateFlags
| GTK_STATE_FLAG_BACKDROP
);
198 static GtkStateFlags
GetStateFlagsFromGtkTabFlags(GtkTabFlags flags
) {
199 return ((flags
& MOZ_GTK_TAB_SELECTED
) == 0) ? GTK_STATE_FLAG_NORMAL
200 : GTK_STATE_FLAG_ACTIVE
;
203 gint
moz_gtk_init() {
204 if (gtk_major_version
> 3 ||
205 (gtk_major_version
== 3 && gtk_minor_version
>= 14))
206 checkbox_check_state
= GTK_STATE_FLAG_CHECKED
;
208 checkbox_check_state
= GTK_STATE_FLAG_ACTIVE
;
212 return MOZ_GTK_SUCCESS
;
215 void moz_gtk_refresh() {
216 if (gtk_check_version(3, 20, 0) != nullptr) {
217 // Deprecated for Gtk >= 3.20+
218 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_TAB_TOP
);
219 gtk_style_context_get_style(style
, "has-tab-gap", ¬ebook_has_tab_gap
,
222 notebook_has_tab_gap
= true;
225 sScrollbarMetrics
[GTK_ORIENTATION_HORIZONTAL
].initialized
= false;
226 sScrollbarMetrics
[GTK_ORIENTATION_VERTICAL
].initialized
= false;
227 sActiveScrollbarMetrics
[GTK_ORIENTATION_HORIZONTAL
].initialized
= false;
228 sActiveScrollbarMetrics
[GTK_ORIENTATION_VERTICAL
].initialized
= false;
229 sCheckboxMetrics
.initialized
= false;
230 sRadioMetrics
.initialized
= false;
231 sMenuCheckboxMetrics
.initialized
= false;
232 sMenuRadioMetrics
.initialized
= false;
233 sToolbarMetrics
.initialized
= false;
234 sToplevelWindowDecorationSize
.initialized
= false;
235 sPopupWindowDecorationSize
.initialized
= false;
237 /* This will destroy all of our widgets */
241 static gint
moz_gtk_get_focus_outline_size(GtkStyleContext
* style
,
243 gint
* focus_v_width
) {
245 gtk_style_context_get_border(style
, gtk_style_context_get_state(style
),
247 *focus_h_width
= border
.left
;
248 *focus_v_width
= border
.top
;
249 return MOZ_GTK_SUCCESS
;
252 gint
moz_gtk_get_focus_outline_size(gint
* focus_h_width
, gint
* focus_v_width
) {
253 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_ENTRY
);
254 moz_gtk_get_focus_outline_size(style
, focus_h_width
, focus_v_width
);
255 return MOZ_GTK_SUCCESS
;
258 gint
moz_gtk_menuitem_get_horizontal_padding(gint
* horizontal_padding
) {
259 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_MENUITEM
);
260 gtk_style_context_get_style(style
, "horizontal-padding", horizontal_padding
,
262 return MOZ_GTK_SUCCESS
;
265 gint
moz_gtk_checkmenuitem_get_horizontal_padding(gint
* horizontal_padding
) {
266 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_CHECKMENUITEM
);
267 gtk_style_context_get_style(style
, "horizontal-padding", horizontal_padding
,
269 return MOZ_GTK_SUCCESS
;
272 gint
moz_gtk_button_get_default_overflow(gint
* border_top
, gint
* border_left
,
274 gint
* border_right
) {
275 GtkBorder
* default_outside_border
;
277 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_BUTTON
);
278 gtk_style_context_get_style(style
, "default-outside-border",
279 &default_outside_border
, NULL
);
281 if (default_outside_border
) {
282 *border_top
= default_outside_border
->top
;
283 *border_left
= default_outside_border
->left
;
284 *border_bottom
= default_outside_border
->bottom
;
285 *border_right
= default_outside_border
->right
;
286 gtk_border_free(default_outside_border
);
288 *border_top
= *border_left
= *border_bottom
= *border_right
= 0;
290 return MOZ_GTK_SUCCESS
;
293 static gint
moz_gtk_button_get_default_border(gint
* border_top
,
296 gint
* border_right
) {
297 GtkBorder
* default_border
;
299 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_BUTTON
);
300 gtk_style_context_get_style(style
, "default-border", &default_border
, NULL
);
302 if (default_border
) {
303 *border_top
= default_border
->top
;
304 *border_left
= default_border
->left
;
305 *border_bottom
= default_border
->bottom
;
306 *border_right
= default_border
->right
;
307 gtk_border_free(default_border
);
309 /* see gtkbutton.c */
310 *border_top
= *border_left
= *border_bottom
= *border_right
= 1;
312 return MOZ_GTK_SUCCESS
;
315 gint
moz_gtk_splitter_get_metrics(gint orientation
, gint
* size
) {
316 GtkStyleContext
* style
;
317 if (orientation
== GTK_ORIENTATION_HORIZONTAL
) {
318 style
= GetStyleContext(MOZ_GTK_SPLITTER_HORIZONTAL
);
320 style
= GetStyleContext(MOZ_GTK_SPLITTER_VERTICAL
);
322 gtk_style_context_get_style(style
, "handle_size", size
, NULL
);
323 return MOZ_GTK_SUCCESS
;
326 static void CalculateToolbarButtonMetrics(WidgetNodeType aAppearance
,
327 ToolbarButtonGTKMetrics
* aMetrics
) {
328 gint iconWidth
, iconHeight
;
329 if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU
, &iconWidth
, &iconHeight
)) {
330 NS_WARNING("Failed to get Gtk+ icon size for titlebar button!");
332 // Use some reasonable fallback size
337 GtkStyleContext
* style
= GetStyleContext(aAppearance
);
338 gint width
= 0, height
= 0;
339 if (gtk_check_version(3, 20, 0) == nullptr) {
340 gtk_style_context_get(style
, gtk_style_context_get_state(style
),
341 "min-width", &width
, "min-height", &height
, NULL
);
344 // Cover cases when min-width/min-height is not set, it's invalid
345 // or we're running on Gtk+ < 3.20.
346 if (width
< iconWidth
) width
= iconWidth
;
347 if (height
< iconHeight
) height
= iconHeight
;
349 gint left
= 0, top
= 0, right
= 0, bottom
= 0;
350 moz_gtk_add_border_padding(style
, &left
, &top
, &right
, &bottom
);
352 // Button size is calculated as min-width/height + border/padding.
353 width
+= left
+ right
;
354 height
+= top
+ bottom
;
356 // Place icon at button center.
357 aMetrics
->iconXPosition
= (width
- iconWidth
) / 2;
358 aMetrics
->iconYPosition
= (height
- iconHeight
) / 2;
360 aMetrics
->minSizeWithBorderMargin
.width
= width
;
361 aMetrics
->minSizeWithBorderMargin
.height
= height
;
364 // We support LTR layout only here for now.
365 static void CalculateToolbarButtonSpacing(WidgetNodeType aAppearance
,
366 ToolbarButtonGTKMetrics
* aMetrics
) {
367 GtkStyleContext
* style
= GetStyleContext(aAppearance
);
368 gtk_style_context_get_margin(style
, gtk_style_context_get_state(style
),
369 &aMetrics
->buttonMargin
);
371 // Get titlebar spacing, a default one is 6 pixels (gtk/gtkheaderbar.c)
372 gint buttonSpacing
= 6;
373 g_object_get(GetWidget(MOZ_GTK_HEADER_BAR
), "spacing", &buttonSpacing
,
376 // We apply spacing as a margin equaly to both adjacent buttons.
379 if (!aMetrics
->firstButton
) {
380 aMetrics
->buttonMargin
.left
+= buttonSpacing
;
382 if (!aMetrics
->lastButton
) {
383 aMetrics
->buttonMargin
.right
+= buttonSpacing
;
386 aMetrics
->minSizeWithBorderMargin
.width
+=
387 aMetrics
->buttonMargin
.right
+ aMetrics
->buttonMargin
.left
;
388 aMetrics
->minSizeWithBorderMargin
.height
+=
389 aMetrics
->buttonMargin
.top
+ aMetrics
->buttonMargin
.bottom
;
392 size_t GetGtkHeaderBarButtonLayout(Span
<ButtonLayout
> aButtonLayout
,
393 bool* aReversedButtonsPlacement
) {
394 gchar
* decorationLayoutSetting
= nullptr;
395 GtkSettings
* settings
= gtk_settings_get_default();
396 g_object_get(settings
, "gtk-decoration-layout", &decorationLayoutSetting
,
398 auto free
= mozilla::MakeScopeExit([&] { g_free(decorationLayoutSetting
); });
400 // Use a default layout
401 const gchar
* decorationLayout
= "menu:minimize,maximize,close";
402 if (decorationLayoutSetting
) {
403 decorationLayout
= decorationLayoutSetting
;
406 // "minimize,maximize,close:" layout means buttons are on the opposite
407 // titlebar side. close button is always there.
408 if (aReversedButtonsPlacement
) {
409 const char* closeButton
= strstr(decorationLayout
, "close");
410 const char* separator
= strchr(decorationLayout
, ':');
411 *aReversedButtonsPlacement
=
412 closeButton
&& separator
&& closeButton
< separator
;
415 // We check what position a button string is stored in decorationLayout.
417 // decorationLayout gets its value from the GNOME preference:
418 // org.gnome.desktop.vm.preferences.button-layout via the
419 // gtk-decoration-layout property.
421 // Documentation of the gtk-decoration-layout property can be found here:
422 // https://developer.gnome.org/gtk3/stable/GtkSettings.html#GtkSettings--gtk-decoration-layout
423 if (aButtonLayout
.IsEmpty()) {
427 nsDependentCSubstring
layout(decorationLayout
, strlen(decorationLayout
));
430 size_t activeButtons
= 0;
431 for (const auto& part
: layout
.Split(':')) {
432 for (const auto& button
: part
.Split(',')) {
433 if (button
.EqualsLiteral("close")) {
434 aButtonLayout
[activeButtons
++] = {MOZ_GTK_HEADER_BAR_BUTTON_CLOSE
,
436 } else if (button
.EqualsLiteral("minimize")) {
437 aButtonLayout
[activeButtons
++] = {MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE
,
439 } else if (button
.EqualsLiteral("maximize")) {
440 aButtonLayout
[activeButtons
++] = {MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE
,
443 if (activeButtons
== aButtonLayout
.Length()) {
444 return activeButtons
;
449 return activeButtons
;
452 static void EnsureToolbarMetrics(void) {
453 if (!sToolbarMetrics
.initialized
) {
454 // Make sure we have clean cache after theme reset, etc.
455 memset(&sToolbarMetrics
, 0, sizeof(sToolbarMetrics
));
457 // Calculate titlebar button visibility and positions.
458 ButtonLayout aButtonLayout
[TOOLBAR_BUTTONS
];
459 size_t activeButtonNums
=
460 GetGtkHeaderBarButtonLayout(mozilla::Span(aButtonLayout
), nullptr);
462 for (size_t i
= 0; i
< activeButtonNums
; i
++) {
464 (aButtonLayout
[i
].mType
- MOZ_GTK_HEADER_BAR_BUTTON_CLOSE
);
465 ToolbarButtonGTKMetrics
* metrics
= sToolbarMetrics
.button
+ buttonIndex
;
466 metrics
->visible
= true;
469 metrics
->firstButton
= true;
472 if (i
== (activeButtonNums
- 1)) {
473 metrics
->lastButton
= true;
476 CalculateToolbarButtonMetrics(aButtonLayout
[i
].mType
, metrics
);
477 CalculateToolbarButtonSpacing(aButtonLayout
[i
].mType
, metrics
);
480 sToolbarMetrics
.initialized
= true;
484 const ToolbarButtonGTKMetrics
* GetToolbarButtonMetrics(
485 WidgetNodeType aAppearance
) {
486 EnsureToolbarMetrics();
488 int buttonIndex
= (aAppearance
- MOZ_GTK_HEADER_BAR_BUTTON_CLOSE
);
489 NS_ASSERTION(buttonIndex
>= 0 && buttonIndex
<= TOOLBAR_BUTTONS
,
490 "GetToolbarButtonMetrics(): Wrong titlebar button!");
491 return sToolbarMetrics
.button
+ buttonIndex
;
494 static gint
moz_gtk_window_paint(cairo_t
* cr
, GdkRectangle
* rect
,
495 GtkTextDirection direction
) {
496 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_WINDOW
, direction
);
498 gtk_style_context_save(style
);
499 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_BACKGROUND
);
500 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
501 gtk_style_context_restore(style
);
503 return MOZ_GTK_SUCCESS
;
506 static gint
moz_gtk_button_paint(cairo_t
* cr
, const GdkRectangle
* rect
,
507 GtkWidgetState
* state
, GtkReliefStyle relief
,
509 GtkTextDirection direction
) {
510 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
511 GtkStyleContext
* style
= gtk_widget_get_style_context(widget
);
512 gint x
= rect
->x
, y
= rect
->y
, width
= rect
->width
, height
= rect
->height
;
514 gtk_widget_set_direction(widget
, direction
);
516 gtk_style_context_save(style
);
517 StyleContextSetScale(style
, state
->scale
);
518 gtk_style_context_set_state(style
, state_flags
);
520 if (state
->isDefault
&& relief
== GTK_RELIEF_NORMAL
) {
521 /* handle default borders both outside and inside the button */
522 gint default_top
, default_left
, default_bottom
, default_right
;
523 moz_gtk_button_get_default_overflow(&default_top
, &default_left
,
524 &default_bottom
, &default_right
);
527 width
+= default_left
+ default_right
;
528 height
+= default_top
+ default_bottom
;
529 gtk_render_background(style
, cr
, x
, y
, width
, height
);
530 gtk_render_frame(style
, cr
, x
, y
, width
, height
);
531 moz_gtk_button_get_default_border(&default_top
, &default_left
,
532 &default_bottom
, &default_right
);
535 width
-= (default_left
+ default_right
);
536 height
-= (default_top
+ default_bottom
);
537 } else if (relief
!= GTK_RELIEF_NONE
|| state
->depressed
||
538 (state_flags
& GTK_STATE_FLAG_PRELIGHT
)) {
539 /* the following line can trigger an assertion (Crux theme)
540 file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
541 assertion `GDK_IS_WINDOW (window)' failed */
542 gtk_render_background(style
, cr
, x
, y
, width
, height
);
543 gtk_render_frame(style
, cr
, x
, y
, width
, height
);
546 if (state
->focused
) {
548 gtk_style_context_get_border(style
, state_flags
, &border
);
551 width
-= (border
.left
+ border
.right
);
552 height
-= (border
.top
+ border
.bottom
);
553 gtk_render_focus(style
, cr
, x
, y
, width
, height
);
555 gtk_style_context_restore(style
);
556 return MOZ_GTK_SUCCESS
;
559 static gint
moz_gtk_header_bar_button_paint(cairo_t
* cr
,
560 const GdkRectangle
* aRect
,
561 GtkWidgetState
* state
,
562 GtkReliefStyle relief
,
563 WidgetNodeType aIconWidgetType
,
564 GtkTextDirection direction
) {
565 WidgetNodeType buttonWidgetType
=
566 (aIconWidgetType
== MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE
)
567 ? MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE
570 GdkRectangle rect
= *aRect
;
571 // We need to inset our calculated margin because it also
572 // contains titlebar button spacing.
573 const ToolbarButtonGTKMetrics
* metrics
=
574 GetToolbarButtonMetrics(buttonWidgetType
);
575 Inset(&rect
, metrics
->buttonMargin
);
577 GtkWidget
* buttonWidget
= GetWidget(buttonWidgetType
);
578 moz_gtk_button_paint(cr
, &rect
, state
, relief
, buttonWidget
, direction
);
580 GtkWidget
* iconWidget
=
581 gtk_bin_get_child(GTK_BIN(GetWidget(aIconWidgetType
)));
582 cairo_surface_t
* surface
= GetWidgetIconSurface(iconWidget
, state
->scale
);
585 GtkStyleContext
* style
= gtk_widget_get_style_context(buttonWidget
);
586 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
588 gtk_style_context_save(style
);
589 StyleContextSetScale(style
, state
->scale
);
590 gtk_style_context_set_state(style
, state_flags
);
592 const ToolbarButtonGTKMetrics
* metrics
=
593 GetToolbarButtonMetrics(buttonWidgetType
);
595 /* This is available since Gtk+ 3.10 as well as GtkHeaderBar */
596 static auto sGtkRenderIconSurfacePtr
=
597 (void (*)(GtkStyleContext
*, cairo_t
*, cairo_surface_t
*, gdouble
,
598 gdouble
))dlsym(RTLD_DEFAULT
, "gtk_render_icon_surface");
600 sGtkRenderIconSurfacePtr(style
, cr
, surface
,
601 rect
.x
+ metrics
->iconXPosition
,
602 rect
.y
+ metrics
->iconYPosition
);
603 gtk_style_context_restore(style
);
606 return MOZ_GTK_SUCCESS
;
609 static gint
moz_gtk_toggle_paint(cairo_t
* cr
, GdkRectangle
* rect
,
610 GtkWidgetState
* state
, gboolean selected
,
611 gboolean inconsistent
, gboolean isradio
,
612 GtkTextDirection direction
) {
613 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
614 gint x
, y
, width
, height
;
615 GtkStyleContext
* style
;
617 // We need to call this before GetStyleContext, because otherwise we would
619 const ToggleGTKMetrics
* metrics
=
620 GetToggleMetrics(isradio
? MOZ_GTK_RADIOBUTTON
: MOZ_GTK_CHECKBUTTON
);
621 // Clamp the rect and paint it center aligned in the rect.
625 height
= rect
->height
;
627 if (rect
->width
< rect
->height
) {
628 y
= rect
->y
+ (rect
->height
- rect
->width
) / 2;
629 height
= rect
->width
;
632 if (rect
->height
< rect
->width
) {
633 x
= rect
->x
+ (rect
->width
- rect
->height
) / 2;
634 width
= rect
->height
;
639 static_cast<GtkStateFlags
>(state_flags
| checkbox_check_state
);
643 static_cast<GtkStateFlags
>(state_flags
| GTK_STATE_FLAG_INCONSISTENT
);
645 style
= GetStyleContext(isradio
? MOZ_GTK_RADIOBUTTON
: MOZ_GTK_CHECKBUTTON
,
646 state
->scale
, direction
, state_flags
);
648 if (gtk_check_version(3, 20, 0) == nullptr) {
649 gtk_render_background(style
, cr
, x
, y
, width
, height
);
650 gtk_render_frame(style
, cr
, x
, y
, width
, height
);
651 // Indicator is inset by the toggle's padding and border.
652 gint indicator_x
= x
+ metrics
->borderAndPadding
.left
;
653 gint indicator_y
= y
+ metrics
->borderAndPadding
.top
;
654 gint indicator_width
= metrics
->minSizeWithBorder
.width
-
655 metrics
->borderAndPadding
.left
-
656 metrics
->borderAndPadding
.right
;
657 gint indicator_height
= metrics
->minSizeWithBorder
.height
-
658 metrics
->borderAndPadding
.top
-
659 metrics
->borderAndPadding
.bottom
;
661 gtk_render_option(style
, cr
, indicator_x
, indicator_y
, indicator_width
,
664 gtk_render_check(style
, cr
, indicator_x
, indicator_y
, indicator_width
,
669 gtk_render_option(style
, cr
, x
, y
, width
, height
);
671 gtk_render_check(style
, cr
, x
, y
, width
, height
);
675 return MOZ_GTK_SUCCESS
;
678 static gint
calculate_button_inner_rect(GtkWidget
* button
,
679 const GdkRectangle
* rect
,
680 GdkRectangle
* inner_rect
,
681 GtkTextDirection direction
) {
682 GtkStyleContext
* style
;
684 GtkBorder padding
= {0, 0, 0, 0};
686 style
= gtk_widget_get_style_context(button
);
688 /* This mirrors gtkbutton's child positioning */
689 gtk_style_context_get_border(style
, gtk_style_context_get_state(style
),
691 gtk_style_context_get_padding(style
, gtk_style_context_get_state(style
),
694 inner_rect
->x
= rect
->x
+ border
.left
+ padding
.left
;
695 inner_rect
->y
= rect
->y
+ padding
.top
+ border
.top
;
697 MAX(1, rect
->width
- padding
.left
- padding
.right
- border
.left
* 2);
699 MAX(1, rect
->height
- padding
.top
- padding
.bottom
- border
.top
* 2);
701 return MOZ_GTK_SUCCESS
;
704 static gint
calculate_arrow_rect(GtkWidget
* arrow
, GdkRectangle
* rect
,
705 GdkRectangle
* arrow_rect
,
706 GtkTextDirection direction
) {
707 /* defined in gtkarrow.c */
708 gfloat arrow_scaling
= 0.7;
712 gfloat mxalign
, myalign
;
713 GtkMisc
* misc
= GTK_MISC(arrow
);
715 gtk_style_context_get_style(gtk_widget_get_style_context(arrow
),
716 "arrow_scaling", &arrow_scaling
, NULL
);
718 gtk_misc_get_padding(misc
, &mxpad
, &mypad
);
719 extent
= MIN((rect
->width
- mxpad
* 2), (rect
->height
- mypad
* 2)) *
722 gtk_misc_get_alignment(misc
, &mxalign
, &myalign
);
724 xalign
= direction
== GTK_TEXT_DIR_LTR
? mxalign
: 1.0 - mxalign
;
725 xpad
= mxpad
+ (rect
->width
- extent
) * xalign
;
727 arrow_rect
->x
= direction
== GTK_TEXT_DIR_LTR
? floor(rect
->x
+ xpad
)
728 : ceil(rect
->x
+ xpad
);
729 arrow_rect
->y
= floor(rect
->y
+ mypad
+ ((rect
->height
- extent
) * myalign
));
731 arrow_rect
->width
= arrow_rect
->height
= extent
;
733 return MOZ_GTK_SUCCESS
;
736 static MozGtkSize
GetMinContentBox(GtkStyleContext
* style
) {
737 GtkStateFlags state_flags
= gtk_style_context_get_state(style
);
739 gtk_style_context_get(style
, state_flags
, "min-width", &width
, "min-height",
741 return {width
, height
};
745 * Get minimum widget size as sum of margin, padding, border and
746 * min-width/min-height.
748 static void moz_gtk_get_widget_min_size(GtkStyleContext
* style
, int* width
,
750 GtkStateFlags state_flags
= gtk_style_context_get_state(style
);
751 gtk_style_context_get(style
, state_flags
, "min-height", height
, "min-width",
754 GtkBorder border
, padding
, margin
;
755 gtk_style_context_get_border(style
, state_flags
, &border
);
756 gtk_style_context_get_padding(style
, state_flags
, &padding
);
757 gtk_style_context_get_margin(style
, state_flags
, &margin
);
759 *width
+= border
.left
+ border
.right
+ margin
.left
+ margin
.right
+
760 padding
.left
+ padding
.right
;
761 *height
+= border
.top
+ border
.bottom
+ margin
.top
+ margin
.bottom
+
762 padding
.top
+ padding
.bottom
;
765 static MozGtkSize
GetMinMarginBox(GtkStyleContext
* style
) {
767 moz_gtk_get_widget_min_size(style
, &width
, &height
);
768 return {width
, height
};
771 static void Inset(GdkRectangle
* rect
, const GtkBorder
& aBorder
) {
772 rect
->x
+= aBorder
.left
;
773 rect
->y
+= aBorder
.top
;
774 rect
->width
-= aBorder
.left
+ aBorder
.right
;
775 rect
->height
-= aBorder
.top
+ aBorder
.bottom
;
778 // Inset a rectangle by the margins specified in a style context.
779 static void InsetByMargin(GdkRectangle
* rect
, GtkStyleContext
* style
) {
781 gtk_style_context_get_margin(style
, gtk_style_context_get_state(style
),
786 // Inset a rectangle by the border and padding specified in a style context.
787 static void InsetByBorderPadding(GdkRectangle
* rect
, GtkStyleContext
* style
) {
788 GtkStateFlags state
= gtk_style_context_get_state(style
);
789 GtkBorder padding
, border
;
791 gtk_style_context_get_padding(style
, state
, &padding
);
792 Inset(rect
, padding
);
793 gtk_style_context_get_border(style
, state
, &border
);
797 static gint
moz_gtk_scrollbar_button_paint(cairo_t
* cr
,
798 const GdkRectangle
* aRect
,
799 GtkWidgetState
* state
,
800 GtkScrollbarButtonFlags flags
,
801 GtkTextDirection direction
) {
802 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
803 GdkRectangle arrow_rect
;
805 GtkStyleContext
* style
;
806 gint arrow_displacement_x
, arrow_displacement_y
;
808 GtkWidget
* scrollbar
= GetWidget(flags
& MOZ_GTK_STEPPER_VERTICAL
809 ? MOZ_GTK_SCROLLBAR_VERTICAL
810 : MOZ_GTK_SCROLLBAR_HORIZONTAL
);
812 gtk_widget_set_direction(scrollbar
, direction
);
814 if (flags
& MOZ_GTK_STEPPER_VERTICAL
) {
815 arrow_angle
= (flags
& MOZ_GTK_STEPPER_DOWN
) ? ARROW_DOWN
: ARROW_UP
;
817 arrow_angle
= (flags
& MOZ_GTK_STEPPER_DOWN
) ? ARROW_RIGHT
: ARROW_LEFT
;
820 style
= gtk_widget_get_style_context(scrollbar
);
822 gtk_style_context_save(style
);
823 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_BUTTON
);
824 StyleContextSetScale(style
, state
->scale
);
825 gtk_style_context_set_state(style
, state_flags
);
826 if (arrow_angle
== ARROW_RIGHT
) {
827 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_RIGHT
);
828 } else if (arrow_angle
== ARROW_DOWN
) {
829 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_BOTTOM
);
830 } else if (arrow_angle
== ARROW_LEFT
) {
831 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_LEFT
);
833 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_TOP
);
836 GdkRectangle rect
= *aRect
;
837 if (gtk_check_version(3, 20, 0) == nullptr) {
838 // The "trough-border" is not used since GTK 3.20. The stepper margin
839 // box occupies the full width of the "contents" gadget content box.
840 InsetByMargin(&rect
, style
);
842 // Scrollbar button has to be inset by trough_border because its DOM
843 // element is filling width of vertical scrollbar's track (or height
844 // in case of horizontal scrollbars).
845 GtkOrientation orientation
= flags
& MOZ_GTK_STEPPER_VERTICAL
846 ? GTK_ORIENTATION_VERTICAL
847 : GTK_ORIENTATION_HORIZONTAL
;
849 const ScrollbarGTKMetrics
* metrics
= GetScrollbarMetrics(orientation
);
850 if (flags
& MOZ_GTK_STEPPER_VERTICAL
) {
851 rect
.x
+= metrics
->border
.track
.left
;
852 rect
.width
= metrics
->size
.thumb
.width
;
854 rect
.y
+= metrics
->border
.track
.top
;
855 rect
.height
= metrics
->size
.thumb
.height
;
859 gtk_render_background(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
860 gtk_render_frame(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
862 arrow_rect
.width
= rect
.width
/ 2;
863 arrow_rect
.height
= rect
.height
/ 2;
865 gfloat arrow_scaling
;
866 gtk_style_context_get_style(style
, "arrow-scaling", &arrow_scaling
, NULL
);
868 gdouble arrow_size
= MIN(rect
.width
, rect
.height
) * arrow_scaling
;
869 arrow_rect
.x
= rect
.x
+ (rect
.width
- arrow_size
) / 2;
870 arrow_rect
.y
= rect
.y
+ (rect
.height
- arrow_size
) / 2;
872 if (state_flags
& GTK_STATE_FLAG_ACTIVE
) {
873 gtk_style_context_get_style(style
, "arrow-displacement-x",
874 &arrow_displacement_x
, "arrow-displacement-y",
875 &arrow_displacement_y
, NULL
);
877 arrow_rect
.x
+= arrow_displacement_x
;
878 arrow_rect
.y
+= arrow_displacement_y
;
881 gtk_render_arrow(style
, cr
, arrow_angle
, arrow_rect
.x
, arrow_rect
.y
,
884 gtk_style_context_restore(style
);
886 return MOZ_GTK_SUCCESS
;
889 static void moz_gtk_update_scrollbar_style(GtkStyleContext
* style
,
890 WidgetNodeType widget
,
891 GtkTextDirection direction
) {
892 if (widget
== MOZ_GTK_SCROLLBAR_HORIZONTAL
) {
893 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_BOTTOM
);
895 if (direction
== GTK_TEXT_DIR_LTR
) {
896 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_RIGHT
);
897 gtk_style_context_remove_class(style
, GTK_STYLE_CLASS_LEFT
);
899 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_LEFT
);
900 gtk_style_context_remove_class(style
, GTK_STYLE_CLASS_RIGHT
);
905 static void moz_gtk_draw_styled_frame(GtkStyleContext
* style
, cairo_t
* cr
,
906 const GdkRectangle
* aRect
,
908 GdkRectangle rect
= *aRect
;
910 InsetByMargin(&rect
, style
);
912 gtk_render_background(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
913 gtk_render_frame(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
915 gtk_render_focus(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
919 static gint
moz_gtk_scrollbar_trough_paint(WidgetNodeType widget
, cairo_t
* cr
,
920 const GdkRectangle
* aRect
,
921 GtkWidgetState
* state
,
922 GtkTextDirection direction
) {
923 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
924 GdkRectangle rect
= *aRect
;
925 GtkStyleContext
* style
;
927 if (gtk_get_minor_version() >= 20) {
928 WidgetNodeType thumb
= widget
== MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL
929 ? MOZ_GTK_SCROLLBAR_THUMB_VERTICAL
930 : MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL
;
931 MozGtkSize thumbSize
= GetMinMarginBox(GetStyleContext(thumb
));
932 style
= GetStyleContext(widget
, state
->scale
, direction
, state_flags
);
933 MozGtkSize trackSize
= GetMinContentBox(style
);
934 trackSize
.Include(thumbSize
);
935 trackSize
+= GetMarginBorderPadding(style
);
936 // Gecko's trough |aRect| fills available breadth, but GTK's trough is
937 // centered in the contents_gadget. The centering here round left
938 // and up, like gtk_box_gadget_allocate_child().
939 if (widget
== MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL
) {
940 rect
.x
+= (rect
.width
- trackSize
.width
) / 2;
941 rect
.width
= trackSize
.width
;
943 rect
.y
+= (rect
.height
- trackSize
.height
) / 2;
944 rect
.height
= trackSize
.height
;
947 style
= GetStyleContext(widget
, state
->scale
, direction
, state_flags
);
950 moz_gtk_draw_styled_frame(style
, cr
, &rect
, state
->focused
);
952 return MOZ_GTK_SUCCESS
;
955 static gint
moz_gtk_scrollbar_paint(WidgetNodeType widget
, cairo_t
* cr
,
956 const GdkRectangle
* rect
,
957 GtkWidgetState
* state
,
958 GtkTextDirection direction
) {
959 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
960 GtkStyleContext
* style
=
961 GetStyleContext(widget
, state
->scale
, direction
, state_flags
);
963 moz_gtk_update_scrollbar_style(style
, widget
, direction
);
965 moz_gtk_draw_styled_frame(style
, cr
, rect
, state
->focused
);
967 style
= GetStyleContext((widget
== MOZ_GTK_SCROLLBAR_HORIZONTAL
)
968 ? MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL
969 : MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL
,
970 state
->scale
, direction
, state_flags
);
971 moz_gtk_draw_styled_frame(style
, cr
, rect
, state
->focused
);
973 return MOZ_GTK_SUCCESS
;
976 static gint
moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget
, cairo_t
* cr
,
977 const GdkRectangle
* aRect
,
978 GtkWidgetState
* state
,
979 GtkTextDirection direction
) {
980 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
981 GtkStyleContext
* style
=
982 GetStyleContext(widget
, state
->scale
, direction
, state_flags
);
984 GtkOrientation orientation
= (widget
== MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL
)
985 ? GTK_ORIENTATION_HORIZONTAL
986 : GTK_ORIENTATION_VERTICAL
;
988 GdkRectangle rect
= *aRect
;
990 const ScrollbarGTKMetrics
* metrics
=
991 (state
->depressed
|| state
->active
|| state
->inHover
)
992 ? GetActiveScrollbarMetrics(orientation
)
993 : GetScrollbarMetrics(orientation
);
994 Inset(&rect
, metrics
->margin
.thumb
);
996 gtk_render_slider(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
,
999 return MOZ_GTK_SUCCESS
;
1002 static gint
moz_gtk_inner_spin_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1003 GtkWidgetState
* state
,
1004 GtkTextDirection direction
) {
1005 GtkStyleContext
* style
=
1006 GetStyleContext(MOZ_GTK_SPINBUTTON
, state
->scale
, direction
,
1007 GetStateFlagsFromGtkWidgetState(state
));
1009 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1010 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1012 /* hard code these values */
1013 GdkRectangle arrow_rect
;
1014 arrow_rect
.width
= 6;
1015 arrow_rect
.height
= 6;
1017 // align spin to the left
1018 arrow_rect
.x
= rect
->x
;
1021 arrow_rect
.y
= rect
->y
+ (rect
->height
- arrow_rect
.height
) / 2 - 3;
1022 gtk_render_arrow(style
, cr
, ARROW_UP
, arrow_rect
.x
, arrow_rect
.y
,
1026 arrow_rect
.y
= rect
->y
+ (rect
->height
- arrow_rect
.height
) / 2 + 3;
1027 gtk_render_arrow(style
, cr
, ARROW_DOWN
, arrow_rect
.x
, arrow_rect
.y
,
1030 return MOZ_GTK_SUCCESS
;
1033 static gint
moz_gtk_spin_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1034 GtkWidgetState
* state
,
1035 GtkTextDirection direction
) {
1036 GtkStyleContext
* style
=
1037 GetStyleContext(MOZ_GTK_SPINBUTTON
, state
->scale
, direction
);
1038 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1039 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1040 return MOZ_GTK_SUCCESS
;
1043 static gint
moz_gtk_spin_updown_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1044 gboolean isDown
, GtkWidgetState
* state
,
1045 GtkTextDirection direction
) {
1046 GtkStyleContext
* style
=
1047 GetStyleContext(MOZ_GTK_SPINBUTTON
, state
->scale
, direction
,
1048 GetStateFlagsFromGtkWidgetState(state
));
1050 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1051 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1053 /* hard code these values */
1054 GdkRectangle arrow_rect
;
1055 arrow_rect
.width
= 6;
1056 arrow_rect
.height
= 6;
1057 arrow_rect
.x
= rect
->x
+ (rect
->width
- arrow_rect
.width
) / 2;
1058 arrow_rect
.y
= rect
->y
+ (rect
->height
- arrow_rect
.height
) / 2;
1059 arrow_rect
.y
+= isDown
? -1 : 1;
1061 gtk_render_arrow(style
, cr
, isDown
? ARROW_DOWN
: ARROW_UP
, arrow_rect
.x
,
1062 arrow_rect
.y
, arrow_rect
.width
);
1064 return MOZ_GTK_SUCCESS
;
1067 /* See gtk_range_draw() for reference.
1069 static gint
moz_gtk_scale_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1070 GtkWidgetState
* state
, GtkOrientation flags
,
1071 GtkTextDirection direction
) {
1072 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
1073 gint x
, y
, width
, height
, min_width
, min_height
;
1074 GtkStyleContext
* style
;
1077 moz_gtk_get_scale_metrics(flags
, &min_width
, &min_height
);
1079 WidgetNodeType widget
= (flags
== GTK_ORIENTATION_HORIZONTAL
)
1080 ? MOZ_GTK_SCALE_TROUGH_HORIZONTAL
1081 : MOZ_GTK_SCALE_TROUGH_VERTICAL
;
1082 style
= GetStyleContext(widget
, state
->scale
, direction
, state_flags
);
1083 gtk_style_context_get_margin(style
, state_flags
, &margin
);
1085 // Clamp the dimension perpendicular to the direction that the slider crosses
1086 // to the minimum size.
1087 if (flags
== GTK_ORIENTATION_HORIZONTAL
) {
1088 width
= rect
->width
- (margin
.left
+ margin
.right
);
1089 height
= min_height
- (margin
.top
+ margin
.bottom
);
1090 x
= rect
->x
+ margin
.left
;
1091 y
= rect
->y
+ (rect
->height
- height
) / 2;
1093 width
= min_width
- (margin
.left
+ margin
.right
);
1094 height
= rect
->height
- (margin
.top
+ margin
.bottom
);
1095 x
= rect
->x
+ (rect
->width
- width
) / 2;
1096 y
= rect
->y
+ margin
.top
;
1099 gtk_render_background(style
, cr
, x
, y
, width
, height
);
1100 gtk_render_frame(style
, cr
, x
, y
, width
, height
);
1103 gtk_render_focus(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1105 return MOZ_GTK_SUCCESS
;
1108 static gint
moz_gtk_scale_thumb_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1109 GtkWidgetState
* state
,
1110 GtkOrientation flags
,
1111 GtkTextDirection direction
) {
1112 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
1113 GtkStyleContext
* style
;
1114 gint thumb_width
, thumb_height
, x
, y
;
1116 /* determine the thumb size, and position the thumb in the center in the
1119 if (flags
== GTK_ORIENTATION_HORIZONTAL
) {
1120 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL
, &thumb_width
,
1123 y
= rect
->y
+ (rect
->height
- thumb_height
) / 2;
1125 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL
, &thumb_height
,
1127 x
= rect
->x
+ (rect
->width
- thumb_width
) / 2;
1131 WidgetNodeType widget
= (flags
== GTK_ORIENTATION_HORIZONTAL
)
1132 ? MOZ_GTK_SCALE_THUMB_HORIZONTAL
1133 : MOZ_GTK_SCALE_THUMB_VERTICAL
;
1134 style
= GetStyleContext(widget
, state
->scale
, direction
, state_flags
);
1135 gtk_render_slider(style
, cr
, x
, y
, thumb_width
, thumb_height
, flags
);
1137 return MOZ_GTK_SUCCESS
;
1140 static gint
moz_gtk_gripper_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1141 GtkWidgetState
* state
,
1142 GtkTextDirection direction
) {
1143 GtkStyleContext
* style
=
1144 GetStyleContext(MOZ_GTK_GRIPPER
, state
->scale
, direction
,
1145 GetStateFlagsFromGtkWidgetState(state
));
1146 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1147 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1148 return MOZ_GTK_SUCCESS
;
1151 static gint
moz_gtk_hpaned_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1152 GtkWidgetState
* state
) {
1153 GtkStyleContext
* style
=
1154 GetStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL
, state
->scale
,
1155 GTK_TEXT_DIR_LTR
, GetStateFlagsFromGtkWidgetState(state
));
1156 gtk_render_handle(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1157 return MOZ_GTK_SUCCESS
;
1160 static gint
moz_gtk_vpaned_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1161 GtkWidgetState
* state
) {
1162 GtkStyleContext
* style
=
1163 GetStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL
, state
->scale
,
1164 GTK_TEXT_DIR_LTR
, GetStateFlagsFromGtkWidgetState(state
));
1165 gtk_render_handle(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1166 return MOZ_GTK_SUCCESS
;
1169 // See gtk_entry_draw() for reference.
1170 static gint
moz_gtk_entry_paint(cairo_t
* cr
, const GdkRectangle
* aRect
,
1171 GtkWidgetState
* state
, GtkStyleContext
* style
,
1172 WidgetNodeType widget
) {
1173 // StyleAppearance::FocusOutline
1174 int draw_focus_outline_only
= state
->depressed
;
1175 GdkRectangle rect
= *aRect
;
1177 if (draw_focus_outline_only
) {
1178 // Inflate the given 'rect' with the focus outline size.
1180 moz_gtk_get_focus_outline_size(style
, &h
, &v
);
1182 rect
.width
+= 2 * h
;
1184 rect
.height
+= 2 * v
;
1186 gtk_render_background(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1189 // Paint the border, except for 'menulist-textfield' that isn't focused:
1190 if (widget
!= MOZ_GTK_DROPDOWN_ENTRY
|| state
->focused
) {
1191 gtk_render_frame(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1194 return MOZ_GTK_SUCCESS
;
1197 static gint
moz_gtk_text_view_paint(cairo_t
* cr
, GdkRectangle
* aRect
,
1198 GtkWidgetState
* state
,
1199 GtkTextDirection direction
) {
1200 // GtkTextView and GtkScrolledWindow do not set active and prelight flags.
1201 // The use of focus with MOZ_GTK_SCROLLED_WINDOW here is questionable
1202 // because a parent widget will not have focus when its child GtkTextView
1203 // has focus, but perhaps this may help identify a focused textarea with
1204 // some themes as GtkTextView backgrounds do not typically render
1205 // differently with focus.
1206 GtkStateFlags state_flags
= state
->disabled
? GTK_STATE_FLAG_INSENSITIVE
1207 : state
->focused
? GTK_STATE_FLAG_FOCUSED
1208 : GTK_STATE_FLAG_NORMAL
;
1210 GtkStyleContext
* style_frame
= GetStyleContext(
1211 MOZ_GTK_SCROLLED_WINDOW
, state
->scale
, direction
, state_flags
);
1212 gtk_render_frame(style_frame
, cr
, aRect
->x
, aRect
->y
, aRect
->width
,
1215 GdkRectangle rect
= *aRect
;
1216 InsetByBorderPadding(&rect
, style_frame
);
1218 GtkStyleContext
* style
=
1219 GetStyleContext(MOZ_GTK_TEXT_VIEW
, state
->scale
, direction
, state_flags
);
1220 gtk_render_background(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1221 // There is a separate "text" window, which usually provides the
1222 // background behind the text. However, this is transparent in Ambiance
1223 // for GTK 3.20, in which case the MOZ_GTK_TEXT_VIEW background is
1225 style
= GetStyleContext(MOZ_GTK_TEXT_VIEW_TEXT
, state
->scale
, direction
,
1227 gtk_render_background(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1229 return MOZ_GTK_SUCCESS
;
1232 static gint
moz_gtk_treeview_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1233 GtkWidgetState
* state
,
1234 GtkTextDirection direction
) {
1235 gint xthickness
, ythickness
;
1236 GtkStyleContext
* style
;
1237 GtkStyleContext
* style_tree
;
1238 GtkStateFlags state_flags
;
1241 /* only handle disabled and normal states, otherwise the whole background
1242 * area will be painted differently with other states */
1244 state
->disabled
? GTK_STATE_FLAG_INSENSITIVE
: GTK_STATE_FLAG_NORMAL
;
1246 style
= GetStyleContext(MOZ_GTK_SCROLLED_WINDOW
, state
->scale
, direction
);
1247 gtk_style_context_get_border(style
, state_flags
, &border
);
1248 xthickness
= border
.left
;
1249 ythickness
= border
.top
;
1251 style_tree
= GetStyleContext(MOZ_GTK_TREEVIEW_VIEW
, state
->scale
, direction
);
1252 gtk_render_background(style_tree
, cr
, rect
->x
+ xthickness
,
1253 rect
->y
+ ythickness
, rect
->width
- 2 * xthickness
,
1254 rect
->height
- 2 * ythickness
);
1256 style
= GetStyleContext(MOZ_GTK_SCROLLED_WINDOW
, state
->scale
, direction
);
1257 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1258 return MOZ_GTK_SUCCESS
;
1261 static gint
moz_gtk_tree_header_cell_paint(cairo_t
* cr
,
1262 const GdkRectangle
* aRect
,
1263 GtkWidgetState
* state
,
1265 GtkTextDirection direction
) {
1266 moz_gtk_button_paint(cr
, aRect
, state
, GTK_RELIEF_NORMAL
,
1267 GetWidget(MOZ_GTK_TREE_HEADER_CELL
), direction
);
1268 return MOZ_GTK_SUCCESS
;
1271 static gint
moz_gtk_tree_header_sort_arrow_paint(cairo_t
* cr
,
1273 GtkWidgetState
* state
,
1274 GtkArrowType arrow_type
,
1275 GtkTextDirection direction
) {
1276 GdkRectangle arrow_rect
;
1277 gdouble arrow_angle
;
1278 GtkStyleContext
* style
;
1280 /* hard code these values */
1281 arrow_rect
.width
= 11;
1282 arrow_rect
.height
= 11;
1283 arrow_rect
.x
= rect
->x
+ (rect
->width
- arrow_rect
.width
) / 2;
1284 arrow_rect
.y
= rect
->y
+ (rect
->height
- arrow_rect
.height
) / 2;
1285 style
= GetStyleContext(MOZ_GTK_TREE_HEADER_SORTARROW
, state
->scale
,
1286 direction
, GetStateFlagsFromGtkWidgetState(state
));
1287 switch (arrow_type
) {
1288 case GTK_ARROW_LEFT
:
1289 arrow_angle
= ARROW_LEFT
;
1291 case GTK_ARROW_RIGHT
:
1292 arrow_angle
= ARROW_RIGHT
;
1294 case GTK_ARROW_DOWN
:
1295 arrow_angle
= ARROW_DOWN
;
1298 arrow_angle
= ARROW_UP
;
1301 if (arrow_type
!= GTK_ARROW_NONE
)
1302 gtk_render_arrow(style
, cr
, arrow_angle
, arrow_rect
.x
, arrow_rect
.y
,
1304 return MOZ_GTK_SUCCESS
;
1307 /* See gtk_expander_paint() for reference.
1309 static gint
moz_gtk_treeview_expander_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1310 GtkWidgetState
* state
,
1311 GtkExpanderStyle expander_state
,
1312 GtkTextDirection direction
) {
1313 /* Because the frame we get is of the entire treeview, we can't get the
1314 * precise event state of one expander, thus rendering hover and active
1315 * feedback useless. */
1316 GtkStateFlags state_flags
=
1317 state
->disabled
? GTK_STATE_FLAG_INSENSITIVE
: GTK_STATE_FLAG_NORMAL
;
1321 static_cast<GtkStateFlags
>(state_flags
| GTK_STATE_FLAG_PRELIGHT
);
1322 if (state
->selected
)
1324 static_cast<GtkStateFlags
>(state_flags
| GTK_STATE_FLAG_SELECTED
);
1326 /* GTK_STATE_FLAG_ACTIVE controls expanded/colapsed state rendering
1327 * in gtk_render_expander()
1329 if (expander_state
== GTK_EXPANDER_EXPANDED
)
1331 static_cast<GtkStateFlags
>(state_flags
| checkbox_check_state
);
1334 static_cast<GtkStateFlags
>(state_flags
& ~(checkbox_check_state
));
1336 GtkStyleContext
* style
= GetStyleContext(
1337 MOZ_GTK_TREEVIEW_EXPANDER
, state
->scale
, direction
, state_flags
);
1338 gtk_render_expander(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1340 return MOZ_GTK_SUCCESS
;
1343 /* See gtk_separator_draw() for reference.
1345 static gint
moz_gtk_combo_box_paint(cairo_t
* cr
, const GdkRectangle
* aRect
,
1346 GtkWidgetState
* state
,
1347 GtkTextDirection direction
) {
1348 GdkRectangle arrow_rect
, real_arrow_rect
;
1349 gint separator_width
;
1350 gboolean wide_separators
;
1351 GtkStyleContext
* style
;
1352 GtkRequisition arrow_req
;
1354 GtkWidget
* comboBoxButton
= GetWidget(MOZ_GTK_COMBOBOX_BUTTON
);
1355 GtkWidget
* comboBoxArrow
= GetWidget(MOZ_GTK_COMBOBOX_ARROW
);
1357 /* Also sets the direction on gComboBoxButtonWidget, which is then
1358 * inherited by the separator and arrow */
1359 moz_gtk_button_paint(cr
, aRect
, state
, GTK_RELIEF_NORMAL
, comboBoxButton
,
1362 calculate_button_inner_rect(comboBoxButton
, aRect
, &arrow_rect
, direction
);
1363 /* Now arrow_rect contains the inner rect ; we want to correct the width
1364 * to what the arrow needs (see gtk_combo_box_size_allocate) */
1365 gtk_widget_get_preferred_size(comboBoxArrow
, NULL
, &arrow_req
);
1366 moz_gtk_sanity_preferred_size(&arrow_req
);
1368 if (direction
== GTK_TEXT_DIR_LTR
)
1369 arrow_rect
.x
+= arrow_rect
.width
- arrow_req
.width
;
1370 arrow_rect
.width
= arrow_req
.width
;
1372 calculate_arrow_rect(comboBoxArrow
, &arrow_rect
, &real_arrow_rect
, direction
);
1374 style
= GetStyleContext(MOZ_GTK_COMBOBOX_ARROW
, state
->scale
);
1375 gtk_render_arrow(style
, cr
, ARROW_DOWN
, real_arrow_rect
.x
, real_arrow_rect
.y
,
1376 real_arrow_rect
.width
);
1378 /* If there is no separator in the theme, there's nothing left to do. */
1379 GtkWidget
* widget
= GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR
);
1380 if (!widget
) return MOZ_GTK_SUCCESS
;
1381 style
= gtk_widget_get_style_context(widget
);
1382 StyleContextSetScale(style
, state
->scale
);
1383 gtk_style_context_get_style(style
, "wide-separators", &wide_separators
,
1384 "separator-width", &separator_width
, NULL
);
1386 if (wide_separators
) {
1387 if (direction
== GTK_TEXT_DIR_LTR
)
1388 arrow_rect
.x
-= separator_width
;
1390 arrow_rect
.x
+= arrow_rect
.width
;
1392 gtk_render_frame(style
, cr
, arrow_rect
.x
, arrow_rect
.y
, separator_width
,
1395 if (direction
== GTK_TEXT_DIR_LTR
) {
1397 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
1398 gtk_style_context_get_padding(style
, state_flags
, &padding
);
1399 arrow_rect
.x
-= padding
.left
;
1401 arrow_rect
.x
+= arrow_rect
.width
;
1403 gtk_render_line(style
, cr
, arrow_rect
.x
, arrow_rect
.y
, arrow_rect
.x
,
1404 arrow_rect
.y
+ arrow_rect
.height
);
1406 return MOZ_GTK_SUCCESS
;
1409 static gint
moz_gtk_arrow_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1410 GtkWidgetState
* state
, GtkArrowType arrow_type
,
1411 GtkTextDirection direction
) {
1412 GdkRectangle arrow_rect
;
1413 gdouble arrow_angle
;
1415 if (direction
== GTK_TEXT_DIR_RTL
) {
1416 if (arrow_type
== GTK_ARROW_LEFT
) {
1417 arrow_type
= GTK_ARROW_RIGHT
;
1418 } else if (arrow_type
== GTK_ARROW_RIGHT
) {
1419 arrow_type
= GTK_ARROW_LEFT
;
1422 switch (arrow_type
) {
1423 case GTK_ARROW_LEFT
:
1424 arrow_angle
= ARROW_LEFT
;
1426 case GTK_ARROW_RIGHT
:
1427 arrow_angle
= ARROW_RIGHT
;
1429 case GTK_ARROW_DOWN
:
1430 arrow_angle
= ARROW_DOWN
;
1433 arrow_angle
= ARROW_UP
;
1436 if (arrow_type
== GTK_ARROW_NONE
) return MOZ_GTK_SUCCESS
;
1438 calculate_arrow_rect(GetWidget(MOZ_GTK_BUTTON_ARROW
), rect
, &arrow_rect
,
1440 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
1441 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_BUTTON_ARROW
, state
->scale
,
1442 direction
, state_flags
);
1443 gtk_render_arrow(style
, cr
, arrow_angle
, arrow_rect
.x
, arrow_rect
.y
,
1445 return MOZ_GTK_SUCCESS
;
1448 static gint
moz_gtk_combo_box_entry_button_paint(cairo_t
* cr
,
1450 GtkWidgetState
* state
,
1451 gboolean input_focus
,
1452 GtkTextDirection direction
) {
1453 gint x_displacement
, y_displacement
;
1454 GdkRectangle arrow_rect
, real_arrow_rect
;
1455 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
1456 GtkStyleContext
* style
;
1458 GtkWidget
* comboBoxEntry
= GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON
);
1459 moz_gtk_button_paint(cr
, rect
, state
, GTK_RELIEF_NORMAL
, comboBoxEntry
,
1461 calculate_button_inner_rect(comboBoxEntry
, rect
, &arrow_rect
, direction
);
1463 if (state_flags
& GTK_STATE_FLAG_ACTIVE
) {
1464 style
= gtk_widget_get_style_context(comboBoxEntry
);
1465 StyleContextSetScale(style
, state
->scale
);
1466 gtk_style_context_get_style(style
, "child-displacement-x", &x_displacement
,
1467 "child-displacement-y", &y_displacement
, NULL
);
1468 arrow_rect
.x
+= x_displacement
;
1469 arrow_rect
.y
+= y_displacement
;
1472 calculate_arrow_rect(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_ARROW
), &arrow_rect
,
1473 &real_arrow_rect
, direction
);
1475 style
= GetStyleContext(MOZ_GTK_COMBOBOX_ENTRY_ARROW
, state
->scale
);
1476 gtk_render_arrow(style
, cr
, ARROW_DOWN
, real_arrow_rect
.x
, real_arrow_rect
.y
,
1477 real_arrow_rect
.width
);
1478 return MOZ_GTK_SUCCESS
;
1481 static gint
moz_gtk_container_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1482 GtkWidgetState
* state
,
1483 WidgetNodeType widget_type
,
1484 GtkTextDirection direction
) {
1485 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
1486 GtkStyleContext
* style
=
1487 GetStyleContext(widget_type
, state
->scale
, direction
, state_flags
);
1488 /* this is for drawing a prelight box */
1489 if (state_flags
& GTK_STATE_FLAG_PRELIGHT
) {
1490 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
,
1494 return MOZ_GTK_SUCCESS
;
1497 static gint
moz_gtk_toggle_label_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1498 GtkWidgetState
* state
, gboolean isradio
,
1499 GtkTextDirection direction
) {
1500 if (!state
->focused
) return MOZ_GTK_SUCCESS
;
1502 GtkStyleContext
* style
= GetStyleContext(
1503 isradio
? MOZ_GTK_RADIOBUTTON_CONTAINER
: MOZ_GTK_CHECKBUTTON_CONTAINER
,
1504 state
->scale
, direction
, GetStateFlagsFromGtkWidgetState(state
));
1505 gtk_render_focus(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1507 return MOZ_GTK_SUCCESS
;
1510 static gint
moz_gtk_toolbar_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1511 GtkWidgetState
* state
,
1512 GtkTextDirection direction
) {
1513 GtkStyleContext
* style
=
1514 GetStyleContext(MOZ_GTK_TOOLBAR
, state
->scale
, direction
);
1515 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1516 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1517 return MOZ_GTK_SUCCESS
;
1520 /* See _gtk_toolbar_paint_space_line() for reference.
1522 static gint
moz_gtk_toolbar_separator_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1523 GtkWidgetState
* state
,
1524 GtkTextDirection direction
) {
1525 gint separator_width
;
1527 gboolean wide_separators
;
1529 /* Defined as constants in GTK+ 2.10.14 */
1530 const double start_fraction
= 0.2;
1531 const double end_fraction
= 0.8;
1533 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_TOOLBAR
, state
->scale
);
1534 gtk_style_context_get_style(style
, "wide-separators", &wide_separators
,
1535 "separator-width", &separator_width
, NULL
);
1537 style
= GetStyleContext(MOZ_GTK_TOOLBAR_SEPARATOR
, state
->scale
, direction
);
1538 if (wide_separators
) {
1539 if (separator_width
> rect
->width
) separator_width
= rect
->width
;
1541 gtk_render_frame(style
, cr
, rect
->x
+ (rect
->width
- separator_width
) / 2,
1542 rect
->y
+ rect
->height
* start_fraction
, separator_width
,
1543 rect
->height
* (end_fraction
- start_fraction
));
1546 gtk_style_context_get_padding(style
, gtk_style_context_get_state(style
),
1549 paint_width
= padding
.left
;
1550 if (paint_width
> rect
->width
) paint_width
= rect
->width
;
1552 gtk_render_line(style
, cr
, rect
->x
+ (rect
->width
- paint_width
) / 2,
1553 rect
->y
+ rect
->height
* start_fraction
,
1554 rect
->x
+ (rect
->width
- paint_width
) / 2,
1555 rect
->y
+ rect
->height
* end_fraction
);
1557 return MOZ_GTK_SUCCESS
;
1560 static gint
moz_gtk_tooltip_paint(cairo_t
* cr
, const GdkRectangle
* aRect
,
1561 GtkWidgetState
* state
,
1562 GtkTextDirection direction
) {
1563 // Tooltip widget is made in GTK3 as following tree:
1566 // Icon (not supported by Firefox)
1568 // Each element can be fully styled by CSS of GTK theme.
1569 // We have to draw all elements with appropriate offset and right dimensions.
1572 GtkStyleContext
* style
=
1573 GetStyleContext(MOZ_GTK_TOOLTIP
, state
->scale
, direction
);
1574 GdkRectangle rect
= *aRect
;
1575 gtk_render_background(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1576 gtk_render_frame(style
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1578 // Horizontal Box drawing
1580 // The box element has hard-coded 6px margin-* GtkWidget properties, which
1581 // are added between the window dimensions and the CSS margin box of the
1582 // horizontal box. The frame of the tooltip window is drawn in the
1584 // For drawing Horizontal Box we have to inset drawing area by that 6px
1585 // plus its CSS margin.
1586 GtkStyleContext
* boxStyle
=
1587 GetStyleContext(MOZ_GTK_TOOLTIP_BOX
, state
->scale
, direction
);
1594 InsetByMargin(&rect
, boxStyle
);
1595 gtk_render_background(boxStyle
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1596 gtk_render_frame(boxStyle
, cr
, rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1599 InsetByBorderPadding(&rect
, boxStyle
);
1601 GtkStyleContext
* labelStyle
=
1602 GetStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL
, state
->scale
, direction
);
1603 moz_gtk_draw_styled_frame(labelStyle
, cr
, &rect
, false);
1605 return MOZ_GTK_SUCCESS
;
1608 static gint
moz_gtk_resizer_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1609 GtkWidgetState
* state
,
1610 GtkTextDirection direction
) {
1611 GtkStyleContext
* style
=
1612 GetStyleContext(MOZ_GTK_RESIZER
, state
->scale
, GTK_TEXT_DIR_LTR
,
1613 GetStateFlagsFromGtkWidgetState(state
));
1615 // Workaround unico not respecting the text direction for resizers.
1618 if (direction
== GTK_TEXT_DIR_RTL
) {
1620 cairo_matrix_init_translate(&mat
, 2 * rect
->x
+ rect
->width
, 0);
1621 cairo_matrix_scale(&mat
, -1, 1);
1622 cairo_transform(cr
, &mat
);
1625 gtk_render_handle(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1628 return MOZ_GTK_SUCCESS
;
1631 static gint
moz_gtk_frame_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1632 GtkWidgetState
* state
,
1633 GtkTextDirection direction
) {
1634 GtkStyleContext
* style
=
1635 GetStyleContext(MOZ_GTK_FRAME
, state
->scale
, direction
);
1636 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1637 return MOZ_GTK_SUCCESS
;
1640 static gint
moz_gtk_progressbar_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1641 GtkWidgetState
* state
,
1642 GtkTextDirection direction
) {
1643 GtkStyleContext
* style
=
1644 GetStyleContext(MOZ_GTK_PROGRESS_TROUGH
, state
->scale
, direction
);
1645 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1646 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1648 return MOZ_GTK_SUCCESS
;
1651 static gint
moz_gtk_progress_chunk_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1652 GtkWidgetState
* state
,
1653 GtkTextDirection direction
,
1654 WidgetNodeType widget
) {
1655 GtkStyleContext
* style
=
1656 GetStyleContext(MOZ_GTK_PROGRESS_CHUNK
, state
->scale
, direction
);
1658 if (widget
== MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
||
1659 widget
== MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
) {
1661 * The bar's size and the bar speed are set depending of the progress'
1662 * size. These could also be constant for all progress bars easily.
1665 (widget
== MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
);
1667 /* The size of the dimension we are going to use for the animation. */
1668 const gint progressSize
= vertical
? rect
->height
: rect
->width
;
1670 /* The bar is using a fifth of the element size, based on GtkProgressBar
1671 * activity-blocks property. */
1672 const gint barSize
= MAX(1, progressSize
/ 5);
1674 /* Represents the travel that has to be done for a complete cycle. */
1675 const gint travel
= 2 * (progressSize
- barSize
);
1677 /* period equals to travel / pixelsPerMillisecond
1678 * where pixelsPerMillisecond equals progressSize / 1000.0.
1679 * This is equivalent to 1600. */
1680 static const guint period
= 1600;
1681 const gint t
= PR_IntervalToMilliseconds(PR_IntervalNow()) % period
;
1682 const gint dx
= travel
* t
/ period
;
1685 rect
->y
+= (dx
< travel
/ 2) ? dx
: travel
- dx
;
1686 rect
->height
= barSize
;
1688 rect
->x
+= (dx
< travel
/ 2) ? dx
: travel
- dx
;
1689 rect
->width
= barSize
;
1693 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1694 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1696 return MOZ_GTK_SUCCESS
;
1699 static gint
moz_gtk_get_tab_thickness(GtkStyleContext
* style
) {
1700 if (!notebook_has_tab_gap
)
1701 return 0; /* tabs do not overdraw the tabpanel border with "no gap" style */
1704 gtk_style_context_get_border(style
, gtk_style_context_get_state(style
),
1706 if (border
.top
< 2) return 2; /* some themes don't set ythickness correctly */
1711 gint
moz_gtk_get_tab_thickness(WidgetNodeType aNodeType
) {
1712 GtkStyleContext
* style
= GetStyleContext(aNodeType
);
1713 int thickness
= moz_gtk_get_tab_thickness(style
);
1717 /* actual small tabs */
1718 static gint
moz_gtk_tab_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1719 GtkWidgetState
* state
, GtkTabFlags flags
,
1720 GtkTextDirection direction
,
1721 WidgetNodeType widget
) {
1722 /* When the tab isn't selected, we just draw a notebook extension.
1723 * When it is selected, we overwrite the adjacent border of the tabpanel
1724 * touching the tab with a pierced border (called "the gap") to make the
1725 * tab appear physically attached to the tabpanel; see details below. */
1727 GtkStyleContext
* style
;
1728 GdkRectangle tabRect
;
1729 GdkRectangle focusRect
;
1730 GdkRectangle backRect
;
1731 int initial_gap
= 0;
1732 bool isBottomTab
= (widget
== MOZ_GTK_TAB_BOTTOM
);
1734 style
= GetStyleContext(widget
, state
->scale
, direction
,
1735 GetStateFlagsFromGtkTabFlags(flags
));
1738 if (flags
& MOZ_GTK_TAB_FIRST
) {
1739 gtk_style_context_get_style(style
, "initial-gap", &initial_gap
, NULL
);
1740 tabRect
.width
-= initial_gap
;
1742 if (direction
!= GTK_TEXT_DIR_RTL
) {
1743 tabRect
.x
+= initial_gap
;
1747 focusRect
= backRect
= tabRect
;
1749 if (notebook_has_tab_gap
) {
1750 if ((flags
& MOZ_GTK_TAB_SELECTED
) == 0) {
1751 /* Only draw the tab */
1752 gtk_render_extension(style
, cr
, tabRect
.x
, tabRect
.y
, tabRect
.width
,
1754 isBottomTab
? GTK_POS_TOP
: GTK_POS_BOTTOM
);
1756 /* Draw the tab and the gap
1757 * We want the gap to be positioned exactly on the tabpanel top
1758 * border; since tabbox.css may set a negative margin so that the tab
1759 * frame rect already overlaps the tabpanel frame rect, we need to take
1760 * that into account when drawing. To that effect, nsNativeThemeGTK
1761 * passes us this negative margin (bmargin in the graphic below) in the
1762 * lowest bits of |flags|. We use it to set gap_voffset, the distance
1763 * between the top of the gap and the bottom of the tab (resp. the
1764 * bottom of the gap and the top of the tab when we draw a bottom tab),
1765 * while ensuring that the gap always touches the border of the tab,
1766 * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results
1767 * with big negative or positive margins.
1768 * Here is a graphical explanation in the case of top tabs:
1769 * ___________________________
1772 * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel
1774 * : | (-negative margin, : |
1775 * bottom : v passed in flags) : | gap_height
1776 * of -> :.............................: | (the size of the
1777 * the tab . part of the gap . | tabpanel top border)
1778 * . outside of the tab . v
1779 * ----------------------------------------------
1781 * To draw the gap, we use gtk_render_frame_gap(), see comment in
1782 * moz_gtk_tabpanels_paint(). This gap is made 3 * gap_height tall,
1783 * which should suffice to ensure that the only visible border is the
1784 * pierced one. If the tab is in the middle, we make the box_gap begin
1785 * a bit to the left of the tab and end a bit to the right, adjusting
1786 * the gap position so it still is under the tab, because we want the
1787 * rendering of a gap in the middle of a tabpanel. This is the role of
1788 * the gints gap_{l,r}_offset. On the contrary, if the tab is the
1789 * first, we align the start border of the box_gap with the start
1790 * border of the tab (left if LTR, right if RTL), by setting the
1791 * appropriate offset to 0.*/
1792 gint gap_loffset
, gap_roffset
, gap_voffset
, gap_height
;
1794 /* Get height needed by the gap */
1795 gap_height
= moz_gtk_get_tab_thickness(style
);
1797 /* Extract gap_voffset from the first bits of flags */
1798 gap_voffset
= flags
& MOZ_GTK_TAB_MARGIN_MASK
;
1799 if (gap_voffset
> gap_height
) gap_voffset
= gap_height
;
1801 /* Set gap_{l,r}_offset to appropriate values */
1802 gap_loffset
= gap_roffset
= 20; /* should be enough */
1803 if (flags
& MOZ_GTK_TAB_FIRST
) {
1804 if (direction
== GTK_TEXT_DIR_RTL
)
1805 gap_roffset
= initial_gap
;
1807 gap_loffset
= initial_gap
;
1810 GtkStyleContext
* panelStyle
=
1811 GetStyleContext(MOZ_GTK_TABPANELS
, state
->scale
, direction
);
1814 /* Draw the tab on bottom */
1815 focusRect
.y
+= gap_voffset
;
1816 focusRect
.height
-= gap_voffset
;
1818 gtk_render_extension(style
, cr
, tabRect
.x
, tabRect
.y
+ gap_voffset
,
1819 tabRect
.width
, tabRect
.height
- gap_voffset
,
1822 backRect
.y
+= (gap_voffset
- gap_height
);
1823 backRect
.height
= gap_height
;
1825 /* Draw the gap; erase with background color before painting in
1826 * case theme does not */
1827 gtk_render_background(panelStyle
, cr
, backRect
.x
, backRect
.y
,
1828 backRect
.width
, backRect
.height
);
1830 cairo_rectangle(cr
, backRect
.x
, backRect
.y
, backRect
.width
,
1834 gtk_render_frame_gap(panelStyle
, cr
, tabRect
.x
- gap_loffset
,
1835 tabRect
.y
+ gap_voffset
- 3 * gap_height
,
1836 tabRect
.width
+ gap_loffset
+ gap_roffset
,
1837 3 * gap_height
, GTK_POS_BOTTOM
, gap_loffset
,
1838 gap_loffset
+ tabRect
.width
);
1841 /* Draw the tab on top */
1842 focusRect
.height
-= gap_voffset
;
1843 gtk_render_extension(style
, cr
, tabRect
.x
, tabRect
.y
, tabRect
.width
,
1844 tabRect
.height
- gap_voffset
, GTK_POS_BOTTOM
);
1846 backRect
.y
+= (tabRect
.height
- gap_voffset
);
1847 backRect
.height
= gap_height
;
1849 /* Draw the gap; erase with background color before painting in
1850 * case theme does not */
1851 gtk_render_background(panelStyle
, cr
, backRect
.x
, backRect
.y
,
1852 backRect
.width
, backRect
.height
);
1855 cairo_rectangle(cr
, backRect
.x
, backRect
.y
, backRect
.width
,
1859 gtk_render_frame_gap(panelStyle
, cr
, tabRect
.x
- gap_loffset
,
1860 tabRect
.y
+ tabRect
.height
- gap_voffset
,
1861 tabRect
.width
+ gap_loffset
+ gap_roffset
,
1862 3 * gap_height
, GTK_POS_TOP
, gap_loffset
,
1863 gap_loffset
+ tabRect
.width
);
1868 gtk_render_background(style
, cr
, tabRect
.x
, tabRect
.y
, tabRect
.width
,
1870 gtk_render_frame(style
, cr
, tabRect
.x
, tabRect
.y
, tabRect
.width
,
1874 if (state
->focused
) {
1875 /* Paint the focus ring */
1877 gtk_style_context_get_padding(style
, GetStateFlagsFromGtkWidgetState(state
),
1880 focusRect
.x
+= padding
.left
;
1881 focusRect
.width
-= (padding
.left
+ padding
.right
);
1882 focusRect
.y
+= padding
.top
;
1883 focusRect
.height
-= (padding
.top
+ padding
.bottom
);
1885 gtk_render_focus(style
, cr
, focusRect
.x
, focusRect
.y
, focusRect
.width
,
1889 return MOZ_GTK_SUCCESS
;
1893 static gint
moz_gtk_tabpanels_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1894 GtkWidgetState
* state
,
1895 GtkTextDirection direction
) {
1896 GtkStyleContext
* style
=
1897 GetStyleContext(MOZ_GTK_TABPANELS
, state
->scale
, direction
);
1898 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1900 * The gap size is not needed in moz_gtk_tabpanels_paint because
1901 * the gap will be painted with the foreground tab in moz_gtk_tab_paint.
1903 * However, if moz_gtk_tabpanels_paint just uses gtk_render_frame(),
1904 * the theme will think that there are no tabs and may draw something
1905 * different.Hence the trick of using two clip regions, and drawing the
1906 * gap outside each clip region, to get the correct frame for
1907 * a tabpanel with tabs.
1911 cairo_rectangle(cr
, rect
->x
, rect
->y
, rect
->x
+ rect
->width
/ 2,
1912 rect
->y
+ rect
->height
);
1914 gtk_render_frame_gap(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
,
1915 GTK_POS_TOP
, rect
->width
- 1, rect
->width
);
1920 cairo_rectangle(cr
, rect
->x
+ rect
->width
/ 2, rect
->y
, rect
->x
+ rect
->width
,
1921 rect
->y
+ rect
->height
);
1923 gtk_render_frame_gap(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
,
1927 return MOZ_GTK_SUCCESS
;
1930 static gint
moz_gtk_tab_scroll_arrow_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1931 GtkWidgetState
* state
,
1932 GtkArrowType arrow_type
,
1933 GtkTextDirection direction
) {
1934 GtkStyleContext
* style
;
1935 gdouble arrow_angle
;
1936 gint arrow_size
= MIN(rect
->width
, rect
->height
);
1937 gint x
= rect
->x
+ (rect
->width
- arrow_size
) / 2;
1938 gint y
= rect
->y
+ (rect
->height
- arrow_size
) / 2;
1940 if (direction
== GTK_TEXT_DIR_RTL
) {
1942 (arrow_type
== GTK_ARROW_LEFT
) ? GTK_ARROW_RIGHT
: GTK_ARROW_LEFT
;
1944 switch (arrow_type
) {
1945 case GTK_ARROW_LEFT
:
1946 arrow_angle
= ARROW_LEFT
;
1948 case GTK_ARROW_RIGHT
:
1949 arrow_angle
= ARROW_RIGHT
;
1951 case GTK_ARROW_DOWN
:
1952 arrow_angle
= ARROW_DOWN
;
1955 arrow_angle
= ARROW_UP
;
1958 if (arrow_type
!= GTK_ARROW_NONE
) {
1959 style
= GetStyleContext(MOZ_GTK_TAB_SCROLLARROW
, state
->scale
, direction
,
1960 GetStateFlagsFromGtkWidgetState(state
));
1961 gtk_render_arrow(style
, cr
, arrow_angle
, x
, y
, arrow_size
);
1963 return MOZ_GTK_SUCCESS
;
1966 static gint
moz_gtk_menu_bar_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1967 GtkWidgetState
* state
,
1968 GtkTextDirection direction
) {
1969 GtkStyleContext
* style
;
1971 GtkWidget
* widget
= GetWidget(MOZ_GTK_MENUBAR
);
1972 gtk_widget_set_direction(widget
, direction
);
1974 style
= gtk_widget_get_style_context(widget
);
1975 gtk_style_context_save(style
);
1976 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_MENUBAR
);
1977 StyleContextSetScale(style
, state
->scale
);
1979 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1980 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
1981 gtk_style_context_restore(style
);
1983 return MOZ_GTK_SUCCESS
;
1986 static gint
moz_gtk_menu_popup_paint(cairo_t
* cr
, GdkRectangle
* rect
,
1987 GtkWidgetState
* state
,
1988 GtkTextDirection direction
) {
1989 GtkStyleContext
* style
;
1991 GtkWidget
* widget
= GetWidget(MOZ_GTK_MENUPOPUP
);
1992 gtk_widget_set_direction(widget
, direction
);
1994 // Draw a backing toplevel. This fixes themes that don't provide a menu
1995 // background, and depend on the GtkMenu's implementation window to provide
1997 moz_gtk_window_paint(cr
, rect
, direction
);
1999 style
= gtk_widget_get_style_context(widget
);
2000 gtk_style_context_save(style
);
2001 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_MENU
);
2002 StyleContextSetScale(style
, state
->scale
);
2004 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
2005 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
2006 gtk_style_context_restore(style
);
2008 return MOZ_GTK_SUCCESS
;
2011 // See gtk_menu_item_draw() for reference.
2012 static gint
moz_gtk_menu_separator_paint(cairo_t
* cr
, GdkRectangle
* rect
,
2013 GtkWidgetState
* state
,
2014 GtkTextDirection direction
) {
2015 GtkWidgetState defaultState
= {0};
2016 moz_gtk_menu_item_paint(MOZ_GTK_MENUSEPARATOR
, cr
, rect
, &defaultState
,
2019 if (gtk_get_minor_version() >= 20) return MOZ_GTK_SUCCESS
;
2021 GtkStyleContext
* style
;
2022 gboolean wide_separators
;
2023 gint separator_height
;
2027 style
= GetStyleContext(MOZ_GTK_MENUSEPARATOR
, state
->scale
, direction
);
2028 gtk_style_context_get_padding(style
, gtk_style_context_get_state(style
),
2035 gtk_style_context_save(style
);
2036 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_SEPARATOR
);
2038 gtk_style_context_get_style(style
, "wide-separators", &wide_separators
,
2039 "separator-height", &separator_height
, NULL
);
2041 if (wide_separators
) {
2042 gtk_render_frame(style
, cr
, x
+ padding
.left
, y
+ padding
.top
,
2043 w
- padding
.left
- padding
.right
, separator_height
);
2045 gtk_render_line(style
, cr
, x
+ padding
.left
, y
+ padding
.top
,
2046 x
+ w
- padding
.right
- 1, y
+ padding
.top
);
2049 gtk_style_context_restore(style
);
2051 return MOZ_GTK_SUCCESS
;
2054 // See gtk_menu_item_draw() for reference.
2055 static gint
moz_gtk_menu_item_paint(WidgetNodeType widget
, cairo_t
* cr
,
2056 GdkRectangle
* rect
, GtkWidgetState
* state
,
2057 GtkTextDirection direction
) {
2059 guint minorVersion
= gtk_get_minor_version();
2060 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
2062 // GTK versions prior to 3.8 render the background and frame only when not
2063 // a separator and in hover prelight.
2064 if (minorVersion
< 8 && (widget
== MOZ_GTK_MENUSEPARATOR
||
2065 !(state_flags
& GTK_STATE_FLAG_PRELIGHT
)))
2066 return MOZ_GTK_SUCCESS
;
2068 GtkStyleContext
* style
=
2069 GetStyleContext(widget
, state
->scale
, direction
, state_flags
);
2071 if (minorVersion
< 6) {
2072 // GTK+ 3.4 saves the style context and adds the menubar class to
2073 // menubar children, but does each of these only when drawing, not
2075 gtk_style_context_save(style
);
2076 if (widget
== MOZ_GTK_MENUBARITEM
) {
2077 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_MENUBAR
);
2086 gtk_render_background(style
, cr
, x
, y
, w
, h
);
2087 gtk_render_frame(style
, cr
, x
, y
, w
, h
);
2089 if (minorVersion
< 6) {
2090 gtk_style_context_restore(style
);
2093 return MOZ_GTK_SUCCESS
;
2096 static gint
moz_gtk_menu_arrow_paint(cairo_t
* cr
, GdkRectangle
* rect
,
2097 GtkWidgetState
* state
,
2098 GtkTextDirection direction
) {
2099 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
2100 GtkStyleContext
* style
=
2101 GetStyleContext(MOZ_GTK_MENUITEM
, state
->scale
, direction
, state_flags
);
2102 gtk_render_arrow(style
, cr
,
2103 (direction
== GTK_TEXT_DIR_LTR
) ? ARROW_RIGHT
: ARROW_LEFT
,
2104 rect
->x
, rect
->y
, rect
->width
);
2105 return MOZ_GTK_SUCCESS
;
2108 // For reference, see gtk_check_menu_item_size_allocate() in GTK versions after
2109 // 3.20 and gtk_real_check_menu_item_draw_indicator() in earlier versions.
2110 static gint
moz_gtk_check_menu_item_paint(WidgetNodeType widgetType
,
2111 cairo_t
* cr
, GdkRectangle
* rect
,
2112 GtkWidgetState
* state
,
2114 GtkTextDirection direction
) {
2115 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
2116 GtkStyleContext
* style
;
2117 gint indicator_size
, horizontal_padding
;
2120 moz_gtk_menu_item_paint(MOZ_GTK_MENUITEM
, cr
, rect
, state
, direction
);
2124 static_cast<GtkStateFlags
>(state_flags
| checkbox_check_state
);
2127 bool pre_3_20
= gtk_get_minor_version() < 20;
2129 style
= GetStyleContext(widgetType
, state
->scale
, direction
);
2130 gtk_style_context_get_style(style
, "indicator-size", &indicator_size
,
2131 "horizontal-padding", &horizontal_padding
, NULL
);
2134 gtk_style_context_get_padding(style
, state_flags
, &padding
);
2135 offset
= horizontal_padding
+ padding
.left
+ 2;
2137 GdkRectangle r
= {0};
2138 InsetByMargin(&r
, style
);
2139 InsetByBorderPadding(&r
, style
);
2143 bool isRadio
= (widgetType
== MOZ_GTK_RADIOMENUITEM
);
2144 WidgetNodeType indicatorType
= isRadio
? MOZ_GTK_RADIOMENUITEM_INDICATOR
2145 : MOZ_GTK_CHECKMENUITEM_INDICATOR
;
2146 const ToggleGTKMetrics
* metrics
= GetToggleMetrics(indicatorType
);
2147 style
= GetStyleContext(indicatorType
, state
->scale
, direction
, state_flags
);
2149 if (direction
== GTK_TEXT_DIR_RTL
) {
2150 x
= rect
->width
- indicator_size
- offset
;
2152 x
= rect
->x
+ offset
;
2154 y
= rect
->y
+ (rect
->height
- indicator_size
) / 2;
2156 gint indicator_width
, indicator_height
;
2157 indicator_width
= indicator_height
= indicator_size
;
2159 gtk_render_background(style
, cr
, x
, y
, indicator_size
, indicator_size
);
2160 gtk_render_frame(style
, cr
, x
, y
, indicator_size
, indicator_size
);
2161 x
= x
+ metrics
->borderAndPadding
.left
;
2162 y
= y
+ metrics
->borderAndPadding
.top
;
2163 indicator_width
= metrics
->minSizeWithBorder
.width
-
2164 metrics
->borderAndPadding
.left
-
2165 metrics
->borderAndPadding
.right
;
2166 indicator_height
= metrics
->minSizeWithBorder
.height
-
2167 metrics
->borderAndPadding
.top
-
2168 metrics
->borderAndPadding
.bottom
;
2172 gtk_render_option(style
, cr
, x
, y
, indicator_width
, indicator_height
);
2174 gtk_render_check(style
, cr
, x
, y
, indicator_width
, indicator_height
);
2177 return MOZ_GTK_SUCCESS
;
2180 static gint
moz_gtk_header_bar_paint(WidgetNodeType widgetType
, cairo_t
* cr
,
2182 GtkWidgetState
* state
) {
2183 GtkStateFlags state_flags
= GetStateFlagsFromGtkWidgetState(state
);
2184 GtkStyleContext
* style
=
2185 GetStyleContext(widgetType
, state
->scale
, GTK_TEXT_DIR_NONE
, state_flags
);
2187 // Some themes (Adwaita for instance) draws bold dark line at
2188 // titlebar bottom. It does not fit well with Firefox tabs so
2189 // draw with some extent to make the titlebar bottom part invisible.
2190 #define TITLEBAR_EXTENT 4
2192 // We don't need to draw window decoration for MOZ_GTK_HEADER_BAR_MAXIMIZED,
2193 // i.e. when main window is maximized.
2194 if (widgetType
== MOZ_GTK_HEADER_BAR
) {
2195 GtkStyleContext
* windowStyle
=
2196 GetStyleContext(MOZ_GTK_HEADERBAR_WINDOW
, state
->scale
);
2197 bool solidDecorations
=
2198 gtk_style_context_has_class(windowStyle
, "solid-csd");
2199 GtkStyleContext
* decorationStyle
=
2200 GetStyleContext(solidDecorations
? MOZ_GTK_WINDOW_DECORATION_SOLID
2201 : MOZ_GTK_WINDOW_DECORATION
,
2202 state
->scale
, GTK_TEXT_DIR_LTR
, state_flags
);
2204 gtk_render_background(decorationStyle
, cr
, rect
->x
, rect
->y
, rect
->width
,
2205 rect
->height
+ TITLEBAR_EXTENT
);
2206 gtk_render_frame(decorationStyle
, cr
, rect
->x
, rect
->y
, rect
->width
,
2207 rect
->height
+ TITLEBAR_EXTENT
);
2210 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
,
2211 rect
->height
+ TITLEBAR_EXTENT
);
2212 gtk_render_frame(style
, cr
, rect
->x
, rect
->y
, rect
->width
,
2213 rect
->height
+ TITLEBAR_EXTENT
);
2215 return MOZ_GTK_SUCCESS
;
2218 static GtkBorder
GetMarginBorderPadding(GtkStyleContext
* aStyle
) {
2219 gint left
= 0, top
= 0, right
= 0, bottom
= 0;
2220 moz_gtk_add_margin_border_padding(aStyle
, &left
, &top
, &right
, &bottom
);
2221 // narrowing conversions to gint16:
2224 result
.right
= right
;
2226 result
.bottom
= bottom
;
2230 gint
moz_gtk_get_widget_border(WidgetNodeType widget
, gint
* left
, gint
* top
,
2231 gint
* right
, gint
* bottom
,
2232 // NOTE: callers depend on direction being used
2233 // only for MOZ_GTK_DROPDOWN widgets.
2234 GtkTextDirection direction
) {
2236 GtkStyleContext
* style
;
2237 *left
= *top
= *right
= *bottom
= 0;
2240 case MOZ_GTK_BUTTON
:
2241 case MOZ_GTK_TOOLBAR_BUTTON
: {
2242 style
= GetStyleContext(MOZ_GTK_BUTTON
);
2244 *left
= *top
= *right
= *bottom
= gtk_container_get_border_width(
2245 GTK_CONTAINER(GetWidget(MOZ_GTK_BUTTON
)));
2247 if (widget
== MOZ_GTK_TOOLBAR_BUTTON
) {
2248 gtk_style_context_save(style
);
2249 gtk_style_context_add_class(style
, "image-button");
2252 moz_gtk_add_style_padding(style
, left
, top
, right
, bottom
);
2254 if (widget
== MOZ_GTK_TOOLBAR_BUTTON
) gtk_style_context_restore(style
);
2256 moz_gtk_add_style_border(style
, left
, top
, right
, bottom
);
2258 return MOZ_GTK_SUCCESS
;
2261 case MOZ_GTK_DROPDOWN_ENTRY
: {
2262 style
= GetStyleContext(widget
);
2264 // XXX: Subtract 1 pixel from the padding to account for the default
2265 // padding in forms.css. See bug 1187385.
2266 *left
= *top
= *right
= *bottom
= -1;
2267 moz_gtk_add_border_padding(style
, left
, top
, right
, bottom
);
2269 return MOZ_GTK_SUCCESS
;
2271 case MOZ_GTK_TEXT_VIEW
:
2272 case MOZ_GTK_TREEVIEW
: {
2273 style
= GetStyleContext(MOZ_GTK_SCROLLED_WINDOW
);
2274 moz_gtk_add_style_border(style
, left
, top
, right
, bottom
);
2275 return MOZ_GTK_SUCCESS
;
2277 case MOZ_GTK_TREE_HEADER_CELL
: {
2278 /* A Tree Header in GTK is just a different styled button
2279 * It must be placed in a TreeView for getting the correct style
2281 * That is why the following code is the same as for MOZ_GTK_BUTTON.
2283 *left
= *top
= *right
= *bottom
= gtk_container_get_border_width(
2284 GTK_CONTAINER(GetWidget(MOZ_GTK_TREE_HEADER_CELL
)));
2285 style
= GetStyleContext(MOZ_GTK_TREE_HEADER_CELL
);
2286 moz_gtk_add_border_padding(style
, left
, top
, right
, bottom
);
2287 return MOZ_GTK_SUCCESS
;
2289 case MOZ_GTK_TREE_HEADER_SORTARROW
:
2290 w
= GetWidget(MOZ_GTK_TREE_HEADER_SORTARROW
);
2292 case MOZ_GTK_DROPDOWN_ARROW
:
2293 w
= GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON
);
2295 case MOZ_GTK_DROPDOWN
: {
2296 /* We need to account for the arrow on the dropdown, so text
2297 * doesn't come too close to the arrow, or in some cases spill
2298 * into the arrow. */
2299 gboolean wide_separators
;
2300 gint separator_width
;
2301 GtkRequisition arrow_req
;
2304 *left
= *top
= *right
= *bottom
= gtk_container_get_border_width(
2305 GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_BUTTON
)));
2306 style
= GetStyleContext(MOZ_GTK_COMBOBOX_BUTTON
);
2307 moz_gtk_add_border_padding(style
, left
, top
, right
, bottom
);
2309 /* If there is no separator, don't try to count its width. */
2310 separator_width
= 0;
2311 GtkWidget
* comboBoxSeparator
= GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR
);
2312 if (comboBoxSeparator
) {
2313 style
= gtk_widget_get_style_context(comboBoxSeparator
);
2314 gtk_style_context_get_style(style
, "wide-separators", &wide_separators
,
2315 "separator-width", &separator_width
, NULL
);
2317 if (!wide_separators
) {
2318 gtk_style_context_get_border(
2319 style
, gtk_style_context_get_state(style
), &border
);
2320 separator_width
= border
.left
;
2324 gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ARROW
), NULL
,
2326 moz_gtk_sanity_preferred_size(&arrow_req
);
2328 if (direction
== GTK_TEXT_DIR_RTL
)
2329 *left
+= separator_width
+ arrow_req
.width
;
2331 *right
+= separator_width
+ arrow_req
.width
;
2333 return MOZ_GTK_SUCCESS
;
2335 case MOZ_GTK_TABPANELS
:
2336 w
= GetWidget(MOZ_GTK_TABPANELS
);
2338 case MOZ_GTK_PROGRESSBAR
:
2339 w
= GetWidget(MOZ_GTK_PROGRESSBAR
);
2341 case MOZ_GTK_SPINBUTTON_ENTRY
:
2342 case MOZ_GTK_SPINBUTTON_UP
:
2343 case MOZ_GTK_SPINBUTTON_DOWN
:
2344 w
= GetWidget(MOZ_GTK_SPINBUTTON
);
2346 case MOZ_GTK_SCALE_HORIZONTAL
:
2347 case MOZ_GTK_SCALE_VERTICAL
:
2348 w
= GetWidget(widget
);
2351 w
= GetWidget(MOZ_GTK_FRAME
);
2353 case MOZ_GTK_CHECKBUTTON_CONTAINER
:
2354 case MOZ_GTK_RADIOBUTTON_CONTAINER
: {
2355 w
= GetWidget(widget
);
2356 style
= gtk_widget_get_style_context(w
);
2358 *left
= *top
= *right
= *bottom
=
2359 gtk_container_get_border_width(GTK_CONTAINER(w
));
2360 moz_gtk_add_border_padding(style
, left
, top
, right
, bottom
);
2361 return MOZ_GTK_SUCCESS
;
2363 case MOZ_GTK_MENUPOPUP
:
2364 w
= GetWidget(MOZ_GTK_MENUPOPUP
);
2366 case MOZ_GTK_MENUBARITEM
:
2367 case MOZ_GTK_MENUITEM
:
2368 case MOZ_GTK_CHECKMENUITEM
:
2369 case MOZ_GTK_RADIOMENUITEM
: {
2370 // Bug 1274143 for MOZ_GTK_MENUBARITEM
2371 WidgetNodeType type
=
2372 widget
== MOZ_GTK_MENUBARITEM
? MOZ_GTK_MENUITEM
: widget
;
2373 style
= GetStyleContext(type
);
2375 if (gtk_get_minor_version() < 20) {
2376 moz_gtk_add_style_padding(style
, left
, top
, right
, bottom
);
2378 moz_gtk_add_margin_border_padding(style
, left
, top
, right
, bottom
);
2380 return MOZ_GTK_SUCCESS
;
2382 case MOZ_GTK_TOOLTIP
: {
2383 // In GTK 3 there are 6 pixels of additional margin around the box.
2384 // See details there:
2385 // https://github.com/GNOME/gtk/blob/5ea69a136bd7e4970b3a800390e20314665aaed2/gtk/ui/gtktooltipwindow.ui#L11
2386 *left
= *right
= *top
= *bottom
= 6;
2388 // We also need to add margin/padding/borders from Tooltip content.
2389 // Tooltip contains horizontal box, where icon and label is put.
2390 // We ignore icon as long as we don't have support for it.
2391 GtkStyleContext
* boxStyle
= GetStyleContext(MOZ_GTK_TOOLTIP_BOX
);
2392 moz_gtk_add_margin_border_padding(boxStyle
, left
, top
, right
, bottom
);
2394 GtkStyleContext
* labelStyle
= GetStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL
);
2395 moz_gtk_add_margin_border_padding(labelStyle
, left
, top
, right
, bottom
);
2397 return MOZ_GTK_SUCCESS
;
2399 case MOZ_GTK_HEADER_BAR_BUTTON_BOX
: {
2400 style
= GetStyleContext(MOZ_GTK_HEADER_BAR
);
2401 moz_gtk_add_border_padding(style
, left
, top
, right
, bottom
);
2403 bool leftButtonsPlacement
= false;
2404 GetGtkHeaderBarButtonLayout({}, &leftButtonsPlacement
);
2405 if (direction
== GTK_TEXT_DIR_RTL
) {
2406 leftButtonsPlacement
= !leftButtonsPlacement
;
2408 if (leftButtonsPlacement
) {
2413 return MOZ_GTK_SUCCESS
;
2415 /* These widgets have no borders, since they are not containers. */
2416 case MOZ_GTK_CHECKBUTTON_LABEL
:
2417 case MOZ_GTK_RADIOBUTTON_LABEL
:
2418 case MOZ_GTK_SPLITTER_HORIZONTAL
:
2419 case MOZ_GTK_SPLITTER_VERTICAL
:
2420 case MOZ_GTK_CHECKBUTTON
:
2421 case MOZ_GTK_RADIOBUTTON
:
2422 case MOZ_GTK_SCROLLBAR_BUTTON
:
2423 case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL
:
2424 case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL
:
2425 case MOZ_GTK_SCALE_THUMB_HORIZONTAL
:
2426 case MOZ_GTK_SCALE_THUMB_VERTICAL
:
2427 case MOZ_GTK_GRIPPER
:
2428 case MOZ_GTK_PROGRESS_CHUNK
:
2429 case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
:
2430 case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
:
2431 case MOZ_GTK_TREEVIEW_EXPANDER
:
2432 case MOZ_GTK_TOOLBAR_SEPARATOR
:
2433 case MOZ_GTK_MENUSEPARATOR
:
2434 case MOZ_GTK_HEADER_BAR
:
2435 case MOZ_GTK_HEADER_BAR_MAXIMIZED
:
2436 case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE
:
2437 case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE
:
2438 case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE
:
2439 case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE
:
2440 /* These widgets have no borders.*/
2441 case MOZ_GTK_INNER_SPIN_BUTTON
:
2442 case MOZ_GTK_SPINBUTTON
:
2443 case MOZ_GTK_WINDOW
:
2444 case MOZ_GTK_RESIZER
:
2445 case MOZ_GTK_MENUARROW
:
2446 case MOZ_GTK_TOOLBARBUTTON_ARROW
:
2447 case MOZ_GTK_TOOLBAR
:
2448 case MOZ_GTK_MENUBAR
:
2449 case MOZ_GTK_TAB_SCROLLARROW
:
2450 return MOZ_GTK_SUCCESS
;
2452 g_warning("Unsupported widget type: %d", widget
);
2453 return MOZ_GTK_UNKNOWN_WIDGET
;
2455 /* TODO - we're still missing some widget implementations */
2457 moz_gtk_add_style_border(gtk_widget_get_style_context(w
), left
, top
, right
,
2460 return MOZ_GTK_SUCCESS
;
2463 gint
moz_gtk_get_tab_border(gint
* left
, gint
* top
, gint
* right
, gint
* bottom
,
2464 GtkTextDirection direction
, GtkTabFlags flags
,
2465 WidgetNodeType widget
) {
2466 GtkStyleContext
* style
= GetStyleContext(widget
, 1, direction
,
2467 GetStateFlagsFromGtkTabFlags(flags
));
2469 *left
= *top
= *right
= *bottom
= 0;
2470 moz_gtk_add_style_padding(style
, left
, top
, right
, bottom
);
2472 // Gtk >= 3.20 does not use those styles
2473 if (gtk_check_version(3, 20, 0) != nullptr) {
2476 gtk_style_context_get_style(style
, "tab-curvature", &tab_curvature
, NULL
);
2477 *left
+= tab_curvature
;
2478 *right
+= tab_curvature
;
2480 if (flags
& MOZ_GTK_TAB_FIRST
) {
2481 int initial_gap
= 0;
2482 gtk_style_context_get_style(style
, "initial-gap", &initial_gap
, NULL
);
2483 if (direction
== GTK_TEXT_DIR_RTL
)
2484 *right
+= initial_gap
;
2486 *left
+= initial_gap
;
2491 gtk_style_context_get_margin(style
, gtk_style_context_get_state(style
),
2493 *left
+= margin
.left
;
2494 *right
+= margin
.right
;
2496 if (flags
& MOZ_GTK_TAB_FIRST
) {
2497 style
= GetStyleContext(MOZ_GTK_NOTEBOOK_HEADER
, direction
);
2498 gtk_style_context_get_margin(style
, gtk_style_context_get_state(style
),
2500 *left
+= margin
.left
;
2501 *right
+= margin
.right
;
2505 return MOZ_GTK_SUCCESS
;
2508 gint
moz_gtk_get_combo_box_entry_button_size(gint
* width
, gint
* height
) {
2510 * We get the requisition of the drop down button, which includes
2511 * all padding, border and focus line widths the button uses,
2512 * as well as the minimum arrow size and its padding
2514 GtkRequisition requisition
;
2516 gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON
), NULL
,
2518 moz_gtk_sanity_preferred_size(&requisition
);
2520 *width
= requisition
.width
;
2521 *height
= requisition
.height
;
2523 return MOZ_GTK_SUCCESS
;
2526 gint
moz_gtk_get_tab_scroll_arrow_size(gint
* width
, gint
* height
) {
2529 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_TABPANELS
);
2530 gtk_style_context_get_style(style
, "scroll-arrow-hlength", &arrow_size
, NULL
);
2532 *height
= *width
= arrow_size
;
2534 return MOZ_GTK_SUCCESS
;
2537 void moz_gtk_get_arrow_size(WidgetNodeType widgetType
, gint
* width
,
2540 switch (widgetType
) {
2541 case MOZ_GTK_DROPDOWN
:
2542 widget
= GetWidget(MOZ_GTK_COMBOBOX_ARROW
);
2545 widget
= GetWidget(MOZ_GTK_BUTTON_ARROW
);
2549 GtkRequisition requisition
;
2550 gtk_widget_get_preferred_size(widget
, NULL
, &requisition
);
2551 moz_gtk_sanity_preferred_size(&requisition
);
2553 *width
= requisition
.width
;
2554 *height
= requisition
.height
;
2557 gint
moz_gtk_get_toolbar_separator_width(gint
* size
) {
2558 gboolean wide_separators
;
2559 gint separator_width
;
2562 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_TOOLBAR
);
2563 gtk_style_context_get_style(style
, "space-size", size
, "wide-separators",
2564 &wide_separators
, "separator-width",
2565 &separator_width
, NULL
);
2566 /* Just in case... */
2567 gtk_style_context_get_border(style
, gtk_style_context_get_state(style
),
2569 *size
= MAX(*size
, (wide_separators
? separator_width
: border
.left
));
2570 return MOZ_GTK_SUCCESS
;
2573 gint
moz_gtk_get_expander_size(gint
* size
) {
2574 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_EXPANDER
);
2575 gtk_style_context_get_style(style
, "expander-size", size
, NULL
);
2576 return MOZ_GTK_SUCCESS
;
2579 gint
moz_gtk_get_treeview_expander_size(gint
* size
) {
2580 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_TREEVIEW
);
2581 gtk_style_context_get_style(style
, "expander-size", size
, NULL
);
2582 return MOZ_GTK_SUCCESS
;
2585 // See gtk_menu_item_draw() for reference.
2586 gint
moz_gtk_get_menu_separator_height(gint
* size
) {
2587 gboolean wide_separators
;
2588 gint separator_height
;
2590 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_MENUSEPARATOR
);
2591 gtk_style_context_get_padding(style
, gtk_style_context_get_state(style
),
2594 gtk_style_context_save(style
);
2595 gtk_style_context_add_class(style
, GTK_STYLE_CLASS_SEPARATOR
);
2597 gtk_style_context_get_style(style
, "wide-separators", &wide_separators
,
2598 "separator-height", &separator_height
, NULL
);
2600 gtk_style_context_restore(style
);
2602 *size
= padding
.top
+ padding
.bottom
;
2603 *size
+= (wide_separators
) ? separator_height
: 1;
2605 return MOZ_GTK_SUCCESS
;
2608 void moz_gtk_get_entry_min_height(gint
* min_content_height
,
2609 gint
* border_padding_height
) {
2610 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_ENTRY
);
2611 if (!gtk_check_version(3, 20, 0)) {
2612 gtk_style_context_get(style
, gtk_style_context_get_state(style
),
2613 "min-height", min_content_height
, nullptr);
2615 *min_content_height
= 0;
2620 gtk_style_context_get_border(style
, gtk_style_context_get_state(style
),
2622 gtk_style_context_get_padding(style
, gtk_style_context_get_state(style
),
2625 *border_padding_height
=
2626 (border
.top
+ border
.bottom
+ padding
.top
+ padding
.bottom
);
2629 void moz_gtk_get_scale_metrics(GtkOrientation orient
, gint
* scale_width
,
2630 gint
* scale_height
) {
2631 if (gtk_check_version(3, 20, 0) != nullptr) {
2632 WidgetNodeType widget
= (orient
== GTK_ORIENTATION_HORIZONTAL
)
2633 ? MOZ_GTK_SCALE_HORIZONTAL
2634 : MOZ_GTK_SCALE_VERTICAL
;
2636 gint thumb_length
, thumb_height
, trough_border
;
2637 moz_gtk_get_scalethumb_metrics(orient
, &thumb_length
, &thumb_height
);
2639 GtkStyleContext
* style
= GetStyleContext(widget
);
2640 gtk_style_context_get_style(style
, "trough-border", &trough_border
, NULL
);
2642 if (orient
== GTK_ORIENTATION_HORIZONTAL
) {
2643 *scale_width
= thumb_length
+ trough_border
* 2;
2644 *scale_height
= thumb_height
+ trough_border
* 2;
2646 *scale_width
= thumb_height
+ trough_border
* 2;
2647 *scale_height
= thumb_length
+ trough_border
* 2;
2650 WidgetNodeType widget
= (orient
== GTK_ORIENTATION_HORIZONTAL
)
2651 ? MOZ_GTK_SCALE_TROUGH_HORIZONTAL
2652 : MOZ_GTK_SCALE_TROUGH_VERTICAL
;
2653 moz_gtk_get_widget_min_size(GetStyleContext(widget
), scale_width
,
2658 gint
moz_gtk_get_scalethumb_metrics(GtkOrientation orient
, gint
* thumb_length
,
2659 gint
* thumb_height
) {
2660 if (gtk_check_version(3, 20, 0) != nullptr) {
2661 WidgetNodeType widget
= (orient
== GTK_ORIENTATION_HORIZONTAL
)
2662 ? MOZ_GTK_SCALE_HORIZONTAL
2663 : MOZ_GTK_SCALE_VERTICAL
;
2664 GtkStyleContext
* style
= GetStyleContext(widget
);
2665 gtk_style_context_get_style(style
, "slider_length", thumb_length
,
2666 "slider_width", thumb_height
, NULL
);
2668 WidgetNodeType widget
= (orient
== GTK_ORIENTATION_HORIZONTAL
)
2669 ? MOZ_GTK_SCALE_THUMB_HORIZONTAL
2670 : MOZ_GTK_SCALE_THUMB_VERTICAL
;
2671 GtkStyleContext
* style
= GetStyleContext(widget
);
2673 gint min_width
, min_height
;
2674 GtkStateFlags state
= gtk_style_context_get_state(style
);
2675 gtk_style_context_get(style
, state
, "min-width", &min_width
, "min-height",
2676 &min_height
, nullptr);
2678 gtk_style_context_get_margin(style
, state
, &margin
);
2679 gint margin_width
= margin
.left
+ margin
.right
;
2680 gint margin_height
= margin
.top
+ margin
.bottom
;
2682 // Negative margin of slider element also determines its minimal size
2683 // so use bigger of those two values.
2684 if (min_width
< -margin_width
) min_width
= -margin_width
;
2685 if (min_height
< -margin_height
) min_height
= -margin_height
;
2687 *thumb_length
= min_width
;
2688 *thumb_height
= min_height
;
2691 return MOZ_GTK_SUCCESS
;
2694 static MozGtkSize
SizeFromLengthAndBreadth(GtkOrientation aOrientation
,
2695 gint aLength
, gint aBreadth
) {
2696 return aOrientation
== GTK_ORIENTATION_HORIZONTAL
2697 ? MozGtkSize({aLength
, aBreadth
})
2698 : MozGtkSize({aBreadth
, aLength
});
2701 const ToggleGTKMetrics
* GetToggleMetrics(WidgetNodeType aWidgetType
) {
2702 ToggleGTKMetrics
* metrics
;
2704 switch (aWidgetType
) {
2705 case MOZ_GTK_RADIOBUTTON
:
2706 metrics
= &sRadioMetrics
;
2708 case MOZ_GTK_CHECKBUTTON
:
2709 metrics
= &sCheckboxMetrics
;
2711 case MOZ_GTK_RADIOMENUITEM_INDICATOR
:
2712 metrics
= &sMenuRadioMetrics
;
2714 case MOZ_GTK_CHECKMENUITEM_INDICATOR
:
2715 metrics
= &sMenuCheckboxMetrics
;
2718 MOZ_CRASH("Unsupported widget type for getting metrics");
2722 metrics
->initialized
= true;
2723 if (gtk_check_version(3, 20, 0) == nullptr) {
2724 GtkStyleContext
* style
= GetStyleContext(aWidgetType
);
2725 GtkStateFlags state_flags
= gtk_style_context_get_state(style
);
2726 gtk_style_context_get(style
, state_flags
, "min-height",
2727 &(metrics
->minSizeWithBorder
.height
), "min-width",
2728 &(metrics
->minSizeWithBorder
.width
), nullptr);
2729 // Fallback to indicator size if min dimensions are zero
2730 if (metrics
->minSizeWithBorder
.height
== 0 ||
2731 metrics
->minSizeWithBorder
.width
== 0) {
2732 gint indicator_size
;
2733 gtk_widget_style_get(GetWidget(MOZ_GTK_CHECKBUTTON_CONTAINER
),
2734 "indicator_size", &indicator_size
, nullptr);
2735 if (metrics
->minSizeWithBorder
.height
== 0) {
2736 metrics
->minSizeWithBorder
.height
= indicator_size
;
2738 if (metrics
->minSizeWithBorder
.width
== 0) {
2739 metrics
->minSizeWithBorder
.width
= indicator_size
;
2743 GtkBorder border
, padding
;
2744 gtk_style_context_get_border(style
, state_flags
, &border
);
2745 gtk_style_context_get_padding(style
, state_flags
, &padding
);
2746 metrics
->borderAndPadding
.left
= border
.left
+ padding
.left
;
2747 metrics
->borderAndPadding
.right
= border
.right
+ padding
.right
;
2748 metrics
->borderAndPadding
.top
= border
.top
+ padding
.top
;
2749 metrics
->borderAndPadding
.bottom
= border
.bottom
+ padding
.bottom
;
2750 metrics
->minSizeWithBorder
.width
+=
2751 metrics
->borderAndPadding
.left
+ metrics
->borderAndPadding
.right
;
2752 metrics
->minSizeWithBorder
.height
+=
2753 metrics
->borderAndPadding
.top
+ metrics
->borderAndPadding
.bottom
;
2755 gint indicator_size
, indicator_spacing
;
2756 gtk_widget_style_get(GetWidget(MOZ_GTK_CHECKBUTTON_CONTAINER
),
2757 "indicator_size", &indicator_size
, "indicator_spacing",
2758 &indicator_spacing
, nullptr);
2759 metrics
->minSizeWithBorder
.width
= metrics
->minSizeWithBorder
.height
=
2765 static void InitScrollbarMetrics(ScrollbarGTKMetrics
* aMetrics
,
2766 GtkOrientation aOrientation
,
2767 GtkStateFlags aStateFlags
) {
2768 WidgetNodeType scrollbar
= aOrientation
== GTK_ORIENTATION_HORIZONTAL
2769 ? MOZ_GTK_SCROLLBAR_HORIZONTAL
2770 : MOZ_GTK_SCROLLBAR_VERTICAL
;
2772 gboolean backward
, forward
, secondary_backward
, secondary_forward
;
2773 GtkStyleContext
* style
=
2774 GetStyleContext(scrollbar
, 1, GTK_TEXT_DIR_NONE
, aStateFlags
);
2775 gtk_style_context_get_style(
2776 style
, "has-backward-stepper", &backward
, "has-forward-stepper", &forward
,
2777 "has-secondary-backward-stepper", &secondary_backward
,
2778 "has-secondary-forward-stepper", &secondary_forward
, nullptr);
2780 backward
|| forward
|| secondary_backward
|| secondary_forward
;
2782 if (gtk_get_minor_version() < 20) {
2783 gint slider_width
, trough_border
, stepper_size
, min_slider_size
;
2785 gtk_style_context_get_style(style
, "slider-width", &slider_width
,
2786 "trough-border", &trough_border
, "stepper-size",
2787 &stepper_size
, "min-slider-length",
2788 &min_slider_size
, nullptr);
2790 aMetrics
->size
.thumb
=
2791 SizeFromLengthAndBreadth(aOrientation
, min_slider_size
, slider_width
);
2792 aMetrics
->size
.button
=
2793 SizeFromLengthAndBreadth(aOrientation
, stepper_size
, slider_width
);
2794 // overall scrollbar
2795 gint breadth
= slider_width
+ 2 * trough_border
;
2796 // Require room for the slider in the track if we don't have buttons.
2797 gint length
= hasButtons
? 0 : min_slider_size
+ 2 * trough_border
;
2798 aMetrics
->size
.scrollbar
=
2799 SizeFromLengthAndBreadth(aOrientation
, length
, breadth
);
2801 // Borders on the major axis are set on the outermost scrollbar
2802 // element to correctly position the buttons when
2803 // trough-under-steppers is true.
2804 // Borders on the minor axis are set on the track element so that it
2805 // receives mouse events, as in GTK.
2806 // Other borders have been zero-initialized.
2807 if (aOrientation
== GTK_ORIENTATION_HORIZONTAL
) {
2808 aMetrics
->border
.scrollbar
.left
= aMetrics
->border
.scrollbar
.right
=
2809 aMetrics
->border
.track
.top
= aMetrics
->border
.track
.bottom
=
2812 aMetrics
->border
.scrollbar
.top
= aMetrics
->border
.scrollbar
.bottom
=
2813 aMetrics
->border
.track
.left
= aMetrics
->border
.track
.right
=
2817 // We're done here for Gtk+ < 3.20...
2821 // GTK version > 3.20
2823 aMetrics
->border
.scrollbar
= GetMarginBorderPadding(style
);
2825 WidgetNodeType contents
, track
, thumb
;
2826 if (aOrientation
== GTK_ORIENTATION_HORIZONTAL
) {
2827 contents
= MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL
;
2828 track
= MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL
;
2829 thumb
= MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL
;
2831 contents
= MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL
;
2832 track
= MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL
;
2833 thumb
= MOZ_GTK_SCROLLBAR_THUMB_VERTICAL
;
2836 /* GetStyleContext() sets GtkStateFlags to the latest widget name
2837 * in css selector string. When we call:
2839 * GetStyleContext(thumb, GTK_STATE_FLAG_PRELIGHT)
2843 * "scrollbar contents trough slider:hover"
2845 * Some themes (Ubuntu Ambiance) styles trough/thumb by scrollbar,
2846 * the Gtk+ css rule looks like:
2848 * "scrollbar:hover contents trough slider"
2850 * So we need to apply GtkStateFlags to each widgets in style path.
2855 CreateStyleContextWithStates(thumb
, 1, GTK_TEXT_DIR_NONE
, aStateFlags
);
2856 aMetrics
->size
.thumb
= GetMinMarginBox(style
);
2857 gtk_style_context_get_margin(style
, gtk_style_context_get_state(style
),
2858 &aMetrics
->margin
.thumb
);
2859 g_object_unref(style
);
2863 CreateStyleContextWithStates(track
, 1, GTK_TEXT_DIR_NONE
, aStateFlags
);
2864 aMetrics
->border
.track
= GetMarginBorderPadding(style
);
2865 MozGtkSize trackMinSize
= GetMinContentBox(style
) + aMetrics
->border
.track
;
2866 MozGtkSize trackSizeForThumb
= aMetrics
->size
.thumb
+ aMetrics
->border
.track
;
2867 g_object_unref(style
);
2871 style
= CreateStyleContextWithStates(MOZ_GTK_SCROLLBAR_BUTTON
, 1,
2872 GTK_TEXT_DIR_NONE
, aStateFlags
);
2873 aMetrics
->size
.button
= GetMinMarginBox(style
);
2874 g_object_unref(style
);
2876 aMetrics
->size
.button
= {0, 0};
2878 if (aOrientation
== GTK_ORIENTATION_HORIZONTAL
) {
2879 aMetrics
->size
.button
.Rotate();
2880 // If the track is wider than necessary for the thumb, including when
2881 // the buttons will cause Gecko to expand the track to fill
2882 // available breadth, then add to the track border to prevent Gecko
2883 // from expanding the thumb to fill available breadth.
2884 gint extra
= std::max(trackMinSize
.height
, aMetrics
->size
.button
.height
) -
2885 trackSizeForThumb
.height
;
2887 // If extra is odd, then the thumb is 0.5 pixels above
2888 // center as in gtk_range_compute_slider_position().
2889 aMetrics
->border
.track
.top
+= extra
/ 2;
2890 aMetrics
->border
.track
.bottom
+= extra
- extra
/ 2;
2891 // Update size for change in border.
2892 trackSizeForThumb
.height
+= extra
;
2895 gint extra
= std::max(trackMinSize
.width
, aMetrics
->size
.button
.width
) -
2896 trackSizeForThumb
.width
;
2898 // If extra is odd, then the thumb is 0.5 pixels to the left
2899 // of center as in gtk_range_compute_slider_position().
2900 aMetrics
->border
.track
.left
+= extra
/ 2;
2901 aMetrics
->border
.track
.right
+= extra
- extra
/ 2;
2902 trackSizeForThumb
.width
+= extra
;
2907 CreateStyleContextWithStates(contents
, 1, GTK_TEXT_DIR_NONE
, aStateFlags
);
2908 GtkBorder contentsBorder
= GetMarginBorderPadding(style
);
2909 g_object_unref(style
);
2911 aMetrics
->size
.scrollbar
=
2912 trackSizeForThumb
+ contentsBorder
+ aMetrics
->border
.scrollbar
;
2915 const ScrollbarGTKMetrics
* GetScrollbarMetrics(GtkOrientation aOrientation
) {
2916 auto metrics
= &sScrollbarMetrics
[aOrientation
];
2917 if (!metrics
->initialized
) {
2918 InitScrollbarMetrics(metrics
, aOrientation
, GTK_STATE_FLAG_NORMAL
);
2920 // We calculate thumb margin here because it's composited from
2921 // thumb class margin + difference margin between active and inactive
2922 // scrollbars. It's a workaround which alows us to emulate
2923 // overlay scrollbars for some Gtk+ themes (Ubuntu/Ambiance),
2924 // when an inactive scrollbar thumb is smaller than the active one.
2925 const ScrollbarGTKMetrics
* metricsActive
=
2926 GetActiveScrollbarMetrics(aOrientation
);
2928 if (metrics
->size
.thumb
< metricsActive
->size
.thumb
) {
2929 metrics
->margin
.thumb
+=
2930 (metrics
->border
.scrollbar
+ metrics
->border
.track
) -
2931 (metricsActive
->border
.scrollbar
+ metricsActive
->border
.track
);
2934 metrics
->initialized
= true;
2939 const ScrollbarGTKMetrics
* GetActiveScrollbarMetrics(
2940 GtkOrientation aOrientation
) {
2941 auto metrics
= &sActiveScrollbarMetrics
[aOrientation
];
2942 if (!metrics
->initialized
) {
2943 InitScrollbarMetrics(metrics
, aOrientation
, GTK_STATE_FLAG_PRELIGHT
);
2944 metrics
->initialized
= true;
2950 * get_shadow_width() from gtkwindow.c is not public so we need
2953 void InitWindowDecorationSize(CSDWindowDecorationSize
* sWindowDecorationSize
,
2954 bool aPopupWindow
) {
2955 bool solidDecorations
= gtk_style_context_has_class(
2956 GetStyleContext(MOZ_GTK_HEADERBAR_WINDOW
, 1), "solid-csd");
2957 // solid-csd does not use frame extents, quit now.
2958 if (solidDecorations
) {
2959 sWindowDecorationSize
->decorationSize
= {0, 0, 0, 0};
2963 // Scale factor is applied later when decoration size is used for actual
2965 GtkStyleContext
* context
= GetStyleContext(MOZ_GTK_WINDOW_DECORATION
);
2967 /* Always sum border + padding */
2969 GtkStateFlags state
= gtk_style_context_get_state(context
);
2970 gtk_style_context_get_border(context
, state
,
2971 &sWindowDecorationSize
->decorationSize
);
2972 gtk_style_context_get_padding(context
, state
, &padding
);
2973 sWindowDecorationSize
->decorationSize
+= padding
;
2975 // Available on GTK 3.20+.
2976 static auto sGtkRenderBackgroundGetClip
= (void (*)(
2977 GtkStyleContext
*, gdouble
, gdouble
, gdouble
, gdouble
,
2978 GdkRectangle
*))dlsym(RTLD_DEFAULT
, "gtk_render_background_get_clip");
2980 if (!sGtkRenderBackgroundGetClip
) {
2985 sGtkRenderBackgroundGetClip(context
, 0, 0, 0, 0, &clip
);
2988 extents
.top
= -clip
.y
;
2989 extents
.right
= clip
.width
+ clip
.x
;
2990 extents
.bottom
= clip
.height
+ clip
.y
;
2991 extents
.left
= -clip
.x
;
2993 // Get shadow extents but combine with style margin; use the bigger value.
2994 // Margin is used for resize grip size - it's not present on
2996 if (!aPopupWindow
) {
2998 gtk_style_context_get_margin(context
, state
, &margin
);
3000 extents
.top
= MAX(extents
.top
, margin
.top
);
3001 extents
.right
= MAX(extents
.right
, margin
.right
);
3002 extents
.bottom
= MAX(extents
.bottom
, margin
.bottom
);
3003 extents
.left
= MAX(extents
.left
, margin
.left
);
3006 sWindowDecorationSize
->decorationSize
+= extents
;
3009 GtkBorder
GetCSDDecorationSize(bool aIsPopup
) {
3011 aIsPopup
? &sPopupWindowDecorationSize
: &sToplevelWindowDecorationSize
;
3012 if (!metrics
->initialized
) {
3013 InitWindowDecorationSize(metrics
, aIsPopup
);
3014 metrics
->initialized
= true;
3016 return metrics
->decorationSize
;
3019 /* cairo_t *cr argument has to be a system-cairo. */
3020 gint
moz_gtk_widget_paint(WidgetNodeType widget
, cairo_t
* cr
,
3021 GdkRectangle
* rect
, GtkWidgetState
* state
, gint flags
,
3022 GtkTextDirection direction
) {
3023 /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
3028 case MOZ_GTK_BUTTON
:
3029 case MOZ_GTK_TOOLBAR_BUTTON
:
3030 if (state
->depressed
) {
3031 return moz_gtk_button_paint(cr
, rect
, state
, (GtkReliefStyle
)flags
,
3032 GetWidget(MOZ_GTK_TOGGLE_BUTTON
),
3035 return moz_gtk_button_paint(cr
, rect
, state
, (GtkReliefStyle
)flags
,
3036 GetWidget(MOZ_GTK_BUTTON
), direction
);
3037 case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE
:
3038 case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE
:
3039 case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE
:
3040 case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE
:
3041 return moz_gtk_header_bar_button_paint(
3042 cr
, rect
, state
, (GtkReliefStyle
)flags
, widget
, direction
);
3043 case MOZ_GTK_CHECKBUTTON
:
3044 case MOZ_GTK_RADIOBUTTON
:
3045 return moz_gtk_toggle_paint(cr
, rect
, state
,
3046 !!(flags
& MOZ_GTK_WIDGET_CHECKED
),
3047 !!(flags
& MOZ_GTK_WIDGET_INCONSISTENT
),
3048 (widget
== MOZ_GTK_RADIOBUTTON
), direction
);
3049 case MOZ_GTK_SCROLLBAR_BUTTON
:
3050 return moz_gtk_scrollbar_button_paint(
3051 cr
, rect
, state
, (GtkScrollbarButtonFlags
)flags
, direction
);
3052 case MOZ_GTK_SCROLLBAR_HORIZONTAL
:
3053 case MOZ_GTK_SCROLLBAR_VERTICAL
: {
3054 if (flags
& MOZ_GTK_TRACK_OPAQUE
) {
3055 GtkStyleContext
* style
= GetStyleContext(MOZ_GTK_WINDOW
, direction
);
3056 gtk_render_background(style
, cr
, rect
->x
, rect
->y
, rect
->width
,
3059 if (gtk_check_version(3, 20, 0) == nullptr) {
3060 return moz_gtk_scrollbar_paint(widget
, cr
, rect
, state
, direction
);
3062 WidgetNodeType trough_widget
= (widget
== MOZ_GTK_SCROLLBAR_HORIZONTAL
)
3063 ? MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL
3064 : MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL
;
3065 return moz_gtk_scrollbar_trough_paint(trough_widget
, cr
, rect
, state
,
3068 case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL
:
3069 case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL
:
3070 if (gtk_check_version(3, 20, 0) == nullptr) {
3071 return moz_gtk_scrollbar_trough_paint(widget
, cr
, rect
, state
,
3075 case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL
:
3076 case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL
:
3077 return moz_gtk_scrollbar_thumb_paint(widget
, cr
, rect
, state
, direction
);
3078 case MOZ_GTK_SCALE_HORIZONTAL
:
3079 case MOZ_GTK_SCALE_VERTICAL
:
3080 return moz_gtk_scale_paint(cr
, rect
, state
, (GtkOrientation
)flags
,
3082 case MOZ_GTK_SCALE_THUMB_HORIZONTAL
:
3083 case MOZ_GTK_SCALE_THUMB_VERTICAL
:
3084 return moz_gtk_scale_thumb_paint(cr
, rect
, state
, (GtkOrientation
)flags
,
3086 case MOZ_GTK_INNER_SPIN_BUTTON
:
3087 return moz_gtk_inner_spin_paint(cr
, rect
, state
, direction
);
3088 case MOZ_GTK_SPINBUTTON
:
3089 return moz_gtk_spin_paint(cr
, rect
, state
, direction
);
3090 case MOZ_GTK_SPINBUTTON_UP
:
3091 case MOZ_GTK_SPINBUTTON_DOWN
:
3092 return moz_gtk_spin_updown_paint(
3093 cr
, rect
, (widget
== MOZ_GTK_SPINBUTTON_DOWN
), state
, direction
);
3094 case MOZ_GTK_SPINBUTTON_ENTRY
: {
3095 GtkStyleContext
* style
=
3096 GetStyleContext(MOZ_GTK_SPINBUTTON_ENTRY
, state
->scale
, direction
,
3097 GetStateFlagsFromGtkWidgetState(state
));
3098 return moz_gtk_entry_paint(cr
, rect
, state
, style
, widget
);
3100 case MOZ_GTK_GRIPPER
:
3101 return moz_gtk_gripper_paint(cr
, rect
, state
, direction
);
3102 case MOZ_GTK_TREEVIEW
:
3103 return moz_gtk_treeview_paint(cr
, rect
, state
, direction
);
3104 case MOZ_GTK_TREE_HEADER_CELL
:
3105 return moz_gtk_tree_header_cell_paint(cr
, rect
, state
, flags
, direction
);
3106 case MOZ_GTK_TREE_HEADER_SORTARROW
:
3107 return moz_gtk_tree_header_sort_arrow_paint(
3108 cr
, rect
, state
, (GtkArrowType
)flags
, direction
);
3109 case MOZ_GTK_TREEVIEW_EXPANDER
:
3110 return moz_gtk_treeview_expander_paint(
3111 cr
, rect
, state
, (GtkExpanderStyle
)flags
, direction
);
3113 case MOZ_GTK_DROPDOWN_ENTRY
: {
3114 GtkStyleContext
* style
=
3115 GetStyleContext(widget
, state
->scale
, direction
,
3116 GetStateFlagsFromGtkWidgetState(state
));
3117 gint ret
= moz_gtk_entry_paint(cr
, rect
, state
, style
, widget
);
3120 case MOZ_GTK_TEXT_VIEW
:
3121 return moz_gtk_text_view_paint(cr
, rect
, state
, direction
);
3122 case MOZ_GTK_DROPDOWN
:
3123 return moz_gtk_combo_box_paint(cr
, rect
, state
, direction
);
3124 case MOZ_GTK_DROPDOWN_ARROW
:
3125 return moz_gtk_combo_box_entry_button_paint(cr
, rect
, state
, flags
,
3127 case MOZ_GTK_CHECKBUTTON_CONTAINER
:
3128 case MOZ_GTK_RADIOBUTTON_CONTAINER
:
3129 return moz_gtk_container_paint(cr
, rect
, state
, widget
, direction
);
3130 case MOZ_GTK_CHECKBUTTON_LABEL
:
3131 case MOZ_GTK_RADIOBUTTON_LABEL
:
3132 return moz_gtk_toggle_label_paint(
3133 cr
, rect
, state
, (widget
== MOZ_GTK_RADIOBUTTON_LABEL
), direction
);
3134 case MOZ_GTK_TOOLBAR
:
3135 return moz_gtk_toolbar_paint(cr
, rect
, state
, direction
);
3136 case MOZ_GTK_TOOLBAR_SEPARATOR
:
3137 return moz_gtk_toolbar_separator_paint(cr
, rect
, state
, direction
);
3138 case MOZ_GTK_TOOLTIP
:
3139 return moz_gtk_tooltip_paint(cr
, rect
, state
, direction
);
3141 return moz_gtk_frame_paint(cr
, rect
, state
, direction
);
3142 case MOZ_GTK_RESIZER
:
3143 return moz_gtk_resizer_paint(cr
, rect
, state
, direction
);
3144 case MOZ_GTK_PROGRESSBAR
:
3145 return moz_gtk_progressbar_paint(cr
, rect
, state
, direction
);
3146 case MOZ_GTK_PROGRESS_CHUNK
:
3147 case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
:
3148 case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
:
3149 return moz_gtk_progress_chunk_paint(cr
, rect
, state
, direction
, widget
);
3150 case MOZ_GTK_TAB_TOP
:
3151 case MOZ_GTK_TAB_BOTTOM
:
3152 return moz_gtk_tab_paint(cr
, rect
, state
, (GtkTabFlags
)flags
, direction
,
3154 case MOZ_GTK_TABPANELS
:
3155 return moz_gtk_tabpanels_paint(cr
, rect
, state
, direction
);
3156 case MOZ_GTK_TAB_SCROLLARROW
:
3157 return moz_gtk_tab_scroll_arrow_paint(cr
, rect
, state
,
3158 (GtkArrowType
)flags
, direction
);
3159 case MOZ_GTK_MENUBAR
:
3160 return moz_gtk_menu_bar_paint(cr
, rect
, state
, direction
);
3161 case MOZ_GTK_MENUPOPUP
:
3162 return moz_gtk_menu_popup_paint(cr
, rect
, state
, direction
);
3163 case MOZ_GTK_MENUSEPARATOR
:
3164 return moz_gtk_menu_separator_paint(cr
, rect
, state
, direction
);
3165 case MOZ_GTK_MENUBARITEM
:
3166 case MOZ_GTK_MENUITEM
:
3167 return moz_gtk_menu_item_paint(widget
, cr
, rect
, state
, direction
);
3168 case MOZ_GTK_MENUARROW
:
3169 return moz_gtk_menu_arrow_paint(cr
, rect
, state
, direction
);
3170 case MOZ_GTK_TOOLBARBUTTON_ARROW
:
3171 return moz_gtk_arrow_paint(cr
, rect
, state
, (GtkArrowType
)flags
,
3173 case MOZ_GTK_CHECKMENUITEM
:
3174 case MOZ_GTK_RADIOMENUITEM
:
3175 return moz_gtk_check_menu_item_paint(widget
, cr
, rect
, state
,
3176 (gboolean
)flags
, direction
);
3177 case MOZ_GTK_SPLITTER_HORIZONTAL
:
3178 return moz_gtk_vpaned_paint(cr
, rect
, state
);
3179 case MOZ_GTK_SPLITTER_VERTICAL
:
3180 return moz_gtk_hpaned_paint(cr
, rect
, state
);
3181 case MOZ_GTK_WINDOW
:
3182 return moz_gtk_window_paint(cr
, rect
, direction
);
3183 case MOZ_GTK_HEADER_BAR
:
3184 case MOZ_GTK_HEADER_BAR_MAXIMIZED
:
3185 return moz_gtk_header_bar_paint(widget
, cr
, rect
, state
);
3187 g_warning("Unknown widget type: %d", widget
);
3190 return MOZ_GTK_UNKNOWN_WIDGET
;
3193 gint
moz_gtk_shutdown() {
3194 /* This will destroy all of our widgets */
3197 return MOZ_GTK_SUCCESS
;