Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/gtk3drawing.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/. */
5
6
/*
7
 * This file contains painting functions for each of the gtk2 widgets.
8
 * Adapted from the gtkdrawing.c, and gtk+2.0 source.
9
 */
10
11
#include <gtk/gtk.h>
12
#include <gdk/gdkprivate.h>
13
#include <string.h>
14
#include "gtkdrawing.h"
15
#include "mozilla/Assertions.h"
16
#include "prinrval.h"
17
#include "WidgetStyleCache.h"
18
#include "nsDebug.h"
19
20
#include <math.h>
21
#include <dlfcn.h>
22
23
static gboolean checkbox_check_state;
24
static gboolean notebook_has_tab_gap;
25
26
static ScrollbarGTKMetrics sScrollbarMetrics[2];
27
static ScrollbarGTKMetrics sActiveScrollbarMetrics[2];
28
static ToggleGTKMetrics sCheckboxMetrics;
29
static ToggleGTKMetrics sRadioMetrics;
30
static ToolbarGTKMetrics sToolbarMetrics;
31
32
0
#define ARROW_UP      0
33
0
#define ARROW_DOWN    G_PI
34
0
#define ARROW_RIGHT   G_PI_2
35
0
#define ARROW_LEFT    (G_PI+G_PI_2)
36
37
#if !GTK_CHECK_VERSION(3,14,0)
38
#define GTK_STATE_FLAG_CHECKED (1 << 11)
39
#endif
40
41
static GtkBorder
42
operator-(const GtkBorder& first, const GtkBorder& second)
43
0
{
44
0
    GtkBorder result;
45
0
    result.left = first.left - second.left;
46
0
    result.right = first.right - second.right;
47
0
    result.top = first.top - second.top;
48
0
    result.bottom = first.bottom - second.bottom;
49
0
    return result;
50
0
}
51
52
static GtkBorder
53
operator+(const GtkBorder& first, const GtkBorder& second)
54
0
{
55
0
    GtkBorder result;
56
0
    result.left = first.left + second.left;
57
0
    result.right = first.right + second.right;
58
0
    result.top = first.top + second.top;
59
0
    result.bottom = first.bottom + second.bottom;
60
0
    return result;
61
0
}
62
63
static GtkBorder
64
operator+=(GtkBorder& first, const GtkBorder& second)
65
0
{
66
0
    first.left += second.left;
67
0
    first.right += second.right;
68
0
    first.top += second.top;
69
0
    first.bottom += second.bottom;
70
0
    return first;
71
0
}
72
73
static gint
74
moz_gtk_get_tab_thickness(GtkStyleContext *style);
75
76
static gint
77
moz_gtk_menu_item_paint(WidgetNodeType widget, cairo_t *cr, GdkRectangle* rect,
78
                        GtkWidgetState* state, GtkTextDirection direction);
79
80
static GtkBorder
81
GetMarginBorderPadding(GtkStyleContext* aStyle);
82
83
static void
84
Inset(GdkRectangle* rect, const GtkBorder& aBorder);
85
86
static void
87
InsetByMargin(GdkRectangle* rect, GtkStyleContext* style);
88
89
static void
90
moz_gtk_add_style_margin(GtkStyleContext* style,
91
                         gint* left, gint* top, gint* right, gint* bottom)
92
0
{
93
0
    GtkBorder margin;
94
0
95
0
    gtk_style_context_get_margin(style, gtk_style_context_get_state(style),
96
0
                                 &margin);
97
0
    *left += margin.left;
98
0
    *right += margin.right;
99
0
    *top += margin.top;
100
0
    *bottom += margin.bottom;
101
0
}
102
103
static void
104
moz_gtk_add_style_border(GtkStyleContext* style,
105
                         gint* left, gint* top, gint* right, gint* bottom)
106
0
{
107
0
    GtkBorder border;
108
0
109
0
    gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border);
110
0
111
0
    *left += border.left;
112
0
    *right += border.right;
113
0
    *top += border.top;
114
0
    *bottom += border.bottom;
115
0
}
116
117
static void
118
moz_gtk_add_style_padding(GtkStyleContext* style,
119
                          gint* left, gint* top, gint* right, gint* bottom)
120
0
{
121
0
    GtkBorder padding;
122
0
123
0
    gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding);
124
0
125
0
    *left += padding.left;
126
0
    *right += padding.right;
127
0
    *top += padding.top;
128
0
    *bottom += padding.bottom;
129
0
}
130
131
static void
132
moz_gtk_add_margin_border_padding(GtkStyleContext *style,
133
                                  gint* left, gint* top,
134
                                  gint* right, gint* bottom)
135
0
{
136
0
    moz_gtk_add_style_margin(style, left, top, right, bottom);
137
0
    moz_gtk_add_style_border(style, left, top, right, bottom);
138
0
    moz_gtk_add_style_padding(style, left, top, right, bottom);
139
0
}
140
141
static void
142
moz_gtk_add_border_padding(GtkStyleContext *style,
143
                           gint* left, gint* top,
144
                           gint* right, gint* bottom)
145
0
{
146
0
    moz_gtk_add_style_border(style, left, top, right, bottom);
147
0
    moz_gtk_add_style_padding(style, left, top, right, bottom);
148
0
}
149
150
// GetStateFlagsFromGtkWidgetState() can be safely used for the specific
151
// GtkWidgets that set both prelight and active flags.  For other widgets,
152
// either the GtkStateFlags or Gecko's GtkWidgetState need to be carefully
153
// adjusted to match GTK behavior.  Although GTK sets insensitive and focus
154
// flags in the generic GtkWidget base class, GTK adds prelight and active
155
// flags only to widgets that are expected to demonstrate prelight or active
156
// states.  This contrasts with HTML where any element may have :active and
157
// :hover states, and so Gecko's GtkStateFlags do not necessarily map to GTK
158
// flags.  Failure to restrict the flags in the same way as GTK can cause
159
// generic CSS selectors from some themes to unintentionally match elements
160
// that are not expected to change appearance on hover or mouse-down.
161
static GtkStateFlags
162
GetStateFlagsFromGtkWidgetState(GtkWidgetState* state)
163
0
{
164
0
    GtkStateFlags stateFlags = GTK_STATE_FLAG_NORMAL;
165
0
166
0
    if (state->disabled)
167
0
        stateFlags = GTK_STATE_FLAG_INSENSITIVE;
168
0
    else {
169
0
        if (state->depressed || state->active)
170
0
            stateFlags = static_cast<GtkStateFlags>(stateFlags|GTK_STATE_FLAG_ACTIVE);
171
0
        if (state->inHover)
172
0
            stateFlags = static_cast<GtkStateFlags>(stateFlags|GTK_STATE_FLAG_PRELIGHT);
173
0
        if (state->focused)
174
0
            stateFlags = static_cast<GtkStateFlags>(stateFlags|GTK_STATE_FLAG_FOCUSED);
175
0
        if (state->backdrop)
176
0
            stateFlags = static_cast<GtkStateFlags>(stateFlags|GTK_STATE_FLAG_BACKDROP);
177
0
    }
178
0
179
0
    return stateFlags;
180
0
}
181
182
static GtkStateFlags
183
GetStateFlagsFromGtkTabFlags(GtkTabFlags flags)
184
0
{
185
0
    return ((flags & MOZ_GTK_TAB_SELECTED) == 0) ?
186
0
            GTK_STATE_FLAG_NORMAL : GTK_STATE_FLAG_ACTIVE;
187
0
}
188
189
gint
190
moz_gtk_init()
191
0
{
192
0
    if (gtk_major_version > 3 ||
193
0
       (gtk_major_version == 3 && gtk_minor_version >= 14))
194
0
        checkbox_check_state = GTK_STATE_FLAG_CHECKED;
195
0
    else
196
0
        checkbox_check_state = GTK_STATE_FLAG_ACTIVE;
197
0
198
0
    moz_gtk_refresh();
199
0
200
0
    return MOZ_GTK_SUCCESS;
201
0
}
202
203
void
204
moz_gtk_refresh()
205
0
{
206
0
    if (gtk_check_version(3, 12, 0) == nullptr &&
207
0
        gtk_check_version(3, 20, 0) != nullptr)
208
0
    {
209
0
        // Deprecated for Gtk >= 3.20+
210
0
        GtkStyleContext *style = GetStyleContext(MOZ_GTK_TAB_TOP);
211
0
        gtk_style_context_get_style(style,
212
0
                                    "has-tab-gap", &notebook_has_tab_gap, NULL);
213
0
    }
214
0
    else {
215
0
        notebook_has_tab_gap = true;
216
0
    }
217
0
218
0
    sScrollbarMetrics[GTK_ORIENTATION_HORIZONTAL].initialized = false;
219
0
    sScrollbarMetrics[GTK_ORIENTATION_VERTICAL].initialized = false;
220
0
    sActiveScrollbarMetrics[GTK_ORIENTATION_HORIZONTAL].initialized = false;
221
0
    sActiveScrollbarMetrics[GTK_ORIENTATION_VERTICAL].initialized = false;
222
0
    sCheckboxMetrics.initialized = false;
223
0
    sRadioMetrics.initialized = false;
224
0
    sToolbarMetrics.initialized = false;
225
0
226
0
    /* This will destroy all of our widgets */
227
0
    ResetWidgetCache();
228
0
}
229
230
gint
231
moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing)
232
0
{
233
0
    gtk_widget_style_get(GetWidget(MOZ_GTK_CHECKBUTTON_CONTAINER),
234
0
                         "indicator_size", indicator_size,
235
0
                         "indicator_spacing", indicator_spacing,
236
0
                         NULL);
237
0
    return MOZ_GTK_SUCCESS;
238
0
}
239
240
gint
241
moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing)
242
0
{
243
0
    gtk_widget_style_get(GetWidget(MOZ_GTK_RADIOBUTTON_CONTAINER),
244
0
                         "indicator_size", indicator_size,
245
0
                         "indicator_spacing", indicator_spacing,
246
0
                          NULL);
247
0
    return MOZ_GTK_SUCCESS;
248
0
}
249
250
static gint
251
moz_gtk_get_focus_outline_size(GtkStyleContext* style,
252
                               gint* focus_h_width, gint* focus_v_width)
253
0
{
254
0
    GtkBorder border;
255
0
    GtkBorder padding;
256
0
    gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border);
257
0
    gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding);
258
0
    *focus_h_width = border.left + padding.left;
259
0
    *focus_v_width = border.top + padding.top;
260
0
    return MOZ_GTK_SUCCESS;
261
0
}
262
263
gint
264
moz_gtk_get_focus_outline_size(gint* focus_h_width, gint* focus_v_width)
265
0
{
266
0
    GtkStyleContext *style = GetStyleContext(MOZ_GTK_ENTRY);
267
0
    moz_gtk_get_focus_outline_size(style, focus_h_width, focus_v_width);
268
0
    return MOZ_GTK_SUCCESS;
269
0
}
270
271
gint
272
moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding)
273
0
{
274
0
    GtkStyleContext *style = GetStyleContext(MOZ_GTK_MENUITEM);
275
0
    gtk_style_context_get_style(style,
276
0
                                "horizontal-padding", horizontal_padding,
277
0
                                nullptr);
278
0
    return MOZ_GTK_SUCCESS;
279
0
}
280
281
gint
282
moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding)
283
0
{
284
0
    GtkStyleContext *style = GetStyleContext(MOZ_GTK_CHECKMENUITEM);
285
0
    gtk_style_context_get_style(style,
286
0
                                "horizontal-padding", horizontal_padding,
287
0
                                nullptr);
288
0
    return MOZ_GTK_SUCCESS;
289
0
}
290
291
gint
292
moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left,
293
                                    gint* border_bottom, gint* border_right)
294
0
{
295
0
    GtkBorder* default_outside_border;
296
0
297
0
    GtkStyleContext *style = GetStyleContext(MOZ_GTK_BUTTON);
298
0
    gtk_style_context_get_style(style,
299
0
                                "default-outside-border", &default_outside_border,
300
0
                                NULL);
301
0
302
0
    if (default_outside_border) {
303
0
        *border_top = default_outside_border->top;
304
0
        *border_left = default_outside_border->left;
305
0
        *border_bottom = default_outside_border->bottom;
306
0
        *border_right = default_outside_border->right;
307
0
        gtk_border_free(default_outside_border);
308
0
    } else {
309
0
        *border_top = *border_left = *border_bottom = *border_right = 0;
310
0
    }
311
0
    return MOZ_GTK_SUCCESS;
312
0
}
313
314
static gint
315
moz_gtk_button_get_default_border(gint* border_top, gint* border_left,
316
                                  gint* border_bottom, gint* border_right)
317
0
{
318
0
    GtkBorder* default_border;
319
0
320
0
    GtkStyleContext *style = GetStyleContext(MOZ_GTK_BUTTON);
321
0
    gtk_style_context_get_style(style,
322
0
                                "default-border", &default_border,
323
0
                                NULL);
324
0
325
0
    if (default_border) {
326
0
        *border_top = default_border->top;
327
0
        *border_left = default_border->left;
328
0
        *border_bottom = default_border->bottom;
329
0
        *border_right = default_border->right;
330
0
        gtk_border_free(default_border);
331
0
    } else {
332
0
        /* see gtkbutton.c */
333
0
        *border_top = *border_left = *border_bottom = *border_right = 1;
334
0
    }
335
0
    return MOZ_GTK_SUCCESS;
336
0
}
337
338
gint
339
moz_gtk_splitter_get_metrics(gint orientation, gint* size)
340
0
{
341
0
    GtkStyleContext *style;
342
0
    if (orientation == GTK_ORIENTATION_HORIZONTAL) {
343
0
        style = GetStyleContext(MOZ_GTK_SPLITTER_HORIZONTAL);
344
0
    } else {
345
0
        style = GetStyleContext(MOZ_GTK_SPLITTER_VERTICAL);
346
0
    }
347
0
    gtk_style_context_get_style(style, "handle_size", size, NULL);
348
0
    return MOZ_GTK_SUCCESS;
349
0
}
350
351
static void
352
CalculateToolbarButtonMetrics(WidgetNodeType aWidgetType,
353
                              ToolbarButtonGTKMetrics* aMetrics)
354
0
{
355
0
    gint iconWidth, iconHeight;
356
0
    if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &iconWidth, &iconHeight)) {
357
0
        NS_WARNING("Failed to get Gtk+ icon size for titlebar button!");
358
0
359
0
        // Use some reasonable fallback size
360
0
        iconWidth = 16;
361
0
        iconHeight = 16;
362
0
    }
363
0
364
0
    GtkStyleContext* style = GetStyleContext(aWidgetType);
365
0
    gint width = 0, height = 0;
366
0
    if (gtk_check_version(3, 20, 0) == nullptr) {
367
0
        gtk_style_context_get(style,  gtk_style_context_get_state(style),
368
0
                              "min-width", &width,
369
0
                              "min-height", &height, NULL);
370
0
    }
371
0
372
0
    // Cover cases when min-width/min-height is not set, it's invalid
373
0
    // or we're running on Gtk+ < 3.20.
374
0
    if (width < iconWidth)
375
0
        width = iconWidth;
376
0
    if (height < iconHeight)
377
0
        height = iconHeight;
378
0
379
0
    gint left = 0, top = 0, right = 0, bottom = 0;
380
0
    moz_gtk_add_border_padding(style, &left, &top, &right, &bottom);
381
0
382
0
    // Button size is calculated as min-width/height + border/padding.
383
0
    width += left + right;
384
0
    height += top + bottom;
385
0
386
0
    // Place icon at button center.
387
0
    aMetrics->iconXPosition = (width - iconWidth) / 2;
388
0
    aMetrics->iconYPosition = (height - iconHeight) / 2;
389
0
390
0
    aMetrics->minSizeWithBorderMargin.width = width;
391
0
    aMetrics->minSizeWithBorderMargin.height = height;
392
0
}
393
394
// We support LTR layout only here for now.
395
static void
396
CalculateToolbarButtonSpacing(WidgetNodeType aWidgetType,
397
                              ToolbarButtonGTKMetrics* aMetrics)
398
0
{
399
0
    GtkStyleContext* style = GetStyleContext(aWidgetType);
400
0
    gtk_style_context_get_margin(style, gtk_style_context_get_state(style),
401
0
                                 &aMetrics->buttonMargin);
402
0
403
0
    // Get titlebar spacing, a default one is 6 pixels (gtk/gtkheaderbar.c)
404
0
    gint buttonSpacing = 6;
405
0
    g_object_get(GetWidget(MOZ_GTK_HEADER_BAR),
406
0
                 "spacing", &buttonSpacing, nullptr);
407
0
408
0
    // We apply spacing as a margin equaly to both adjacent buttons.
409
0
    buttonSpacing /= 2;
410
0
411
0
    if (!aMetrics->firstButton) {
412
0
      aMetrics->buttonMargin.left += buttonSpacing;
413
0
    }
414
0
    if (!aMetrics->lastButton) {
415
0
      aMetrics->buttonMargin.right += buttonSpacing;
416
0
    }
417
0
418
0
    aMetrics->iconXPosition += aMetrics->buttonMargin.left;
419
0
    aMetrics->iconYPosition += aMetrics->buttonMargin.top;
420
0
421
0
    aMetrics->minSizeWithBorderMargin.width +=
422
0
        aMetrics->buttonMargin.right + aMetrics->buttonMargin.left;
423
0
    aMetrics->minSizeWithBorderMargin.height +=
424
0
        aMetrics->buttonMargin.top + aMetrics->buttonMargin.bottom;
425
0
}
426
427
int
428
GetGtkHeaderBarButtonLayout(WidgetNodeType* aButtonLayout, int aMaxButtonNums)
429
0
{
430
0
  NS_ASSERTION(aMaxButtonNums >= TOOLBAR_BUTTONS,
431
0
               "Requested number of buttons is higher than storage capacity!");
432
0
433
0
  const gchar* decorationLayout = nullptr;
434
0
  GtkSettings *settings =
435
0
      gtk_settings_get_for_screen(gdk_screen_get_default());
436
0
  g_object_get(settings, "gtk-decoration-layout", &decorationLayout, nullptr);
437
0
438
0
  // Use a default layout
439
0
  if (!decorationLayout) {
440
0
      decorationLayout = "minimize,maximize,close";
441
0
  }
442
0
443
0
  // We support only default button order now:
444
0
  // minimize/maximize/close
445
0
  int activeButtonNums = 0;
446
0
  if (strstr(decorationLayout, "minimize") != nullptr) {
447
0
      aButtonLayout[activeButtonNums++] = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE;
448
0
  }
449
0
  if (strstr(decorationLayout, "maximize") != nullptr) {
450
0
      aButtonLayout[activeButtonNums++] = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
451
0
  }
452
0
  if (strstr(decorationLayout, "close") != nullptr) {
453
0
      aButtonLayout[activeButtonNums++] = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE;
454
0
  }
455
0
456
0
  return activeButtonNums;
457
0
}
458
459
static void
460
EnsureToolbarMetrics(void)
461
0
{
462
0
  if (!sToolbarMetrics.initialized) {
463
0
      // Make sure we have clean cache after theme reset, etc.
464
0
      memset(&sToolbarMetrics, 0, sizeof(sToolbarMetrics));
465
0
466
0
      // We're running on old Gtk+ version. Leave the cache empty
467
0
      // which means all buttons are disabled.
468
0
      if (gtk_check_version(3, 10, 0) != nullptr) {
469
0
          sToolbarMetrics.initialized = true;
470
0
          return;
471
0
      }
472
0
473
0
      // Calculate titlebar button visibility and positions.
474
0
      WidgetNodeType aButtonLayout[TOOLBAR_BUTTONS];
475
0
      int activeButtonNums =
476
0
          GetGtkHeaderBarButtonLayout(aButtonLayout, TOOLBAR_BUTTONS);
477
0
478
0
      for (int i = 0; i < activeButtonNums; i++) {
479
0
          int buttonIndex = (aButtonLayout[i] - MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
480
0
          ToolbarButtonGTKMetrics* metrics = sToolbarMetrics.button + buttonIndex;
481
0
          metrics->visible = true;
482
0
          // Mark first button
483
0
          if (!i) {
484
0
              metrics->firstButton = true;
485
0
          }
486
0
          // Mark last button.
487
0
          if (i == (activeButtonNums-1)) {
488
0
              metrics->lastButton = true;
489
0
          }
490
0
491
0
          CalculateToolbarButtonMetrics(aButtonLayout[i], metrics);
492
0
          CalculateToolbarButtonSpacing(aButtonLayout[i], metrics);
493
0
      }
494
0
495
0
      sToolbarMetrics.initialized = true;
496
0
  }
497
0
}
498
499
const ToolbarButtonGTKMetrics*
500
GetToolbarButtonMetrics(WidgetNodeType aWidgetType)
501
0
{
502
0
    EnsureToolbarMetrics();
503
0
504
0
    int buttonIndex = (aWidgetType - MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
505
0
    NS_ASSERTION(buttonIndex >= 0 &&
506
0
                 buttonIndex <= TOOLBAR_BUTTONS,
507
0
                 "GetToolbarButtonMetrics(): Wrong titlebar button!");
508
0
    return sToolbarMetrics.button + buttonIndex;
509
0
}
510
511
static gint
512
moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect,
513
                     GtkTextDirection direction)
514
0
{
515
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_WINDOW, direction);
516
0
517
0
    gtk_style_context_save(style);
518
0
    gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
519
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
520
0
    gtk_style_context_restore(style);
521
0
522
0
    return MOZ_GTK_SUCCESS;
523
0
}
524
525
static gint
526
moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect,
527
                     GtkWidgetState* state,
528
                     GtkReliefStyle relief, GtkWidget* widget,
529
                     GtkTextDirection direction)
530
0
{
531
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
532
0
    GtkStyleContext* style = gtk_widget_get_style_context(widget);
533
0
    gint x = rect->x, y=rect->y, width=rect->width, height=rect->height;
534
0
535
0
    gtk_widget_set_direction(widget, direction);
536
0
537
0
    gtk_style_context_save(style);
538
0
    gtk_style_context_set_state(style, state_flags);
539
0
540
0
    if (state->isDefault && relief == GTK_RELIEF_NORMAL) {
541
0
        /* handle default borders both outside and inside the button */
542
0
        gint default_top, default_left, default_bottom, default_right;
543
0
        moz_gtk_button_get_default_overflow(&default_top, &default_left,
544
0
                                            &default_bottom, &default_right);
545
0
        x -= default_left;
546
0
        y -= default_top;
547
0
        width += default_left + default_right;
548
0
        height += default_top + default_bottom;
549
0
        gtk_render_background(style, cr, x, y, width, height);
550
0
        gtk_render_frame(style, cr, x, y, width, height);
551
0
        moz_gtk_button_get_default_border(&default_top, &default_left,
552
0
                                          &default_bottom, &default_right);
553
0
        x += default_left;
554
0
        y += default_top;
555
0
        width -= (default_left + default_right);
556
0
        height -= (default_top + default_bottom);
557
0
    } else if (relief != GTK_RELIEF_NONE || state->depressed ||
558
0
        (state_flags & GTK_STATE_FLAG_PRELIGHT)) {
559
0
        /* the following line can trigger an assertion (Crux theme)
560
0
           file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
561
0
           assertion `GDK_IS_WINDOW (window)' failed */
562
0
        gtk_render_background(style, cr, x, y, width, height);
563
0
        gtk_render_frame(style, cr, x, y, width, height);
564
0
    }
565
0
566
0
    if (state->focused) {
567
0
        GtkBorder border;
568
0
        gtk_style_context_get_border(style, state_flags, &border);
569
0
        x += border.left;
570
0
        y += border.top;
571
0
        width -= (border.left + border.right);
572
0
        height -= (border.top + border.bottom);
573
0
        gtk_render_focus(style, cr, x, y, width, height);
574
0
    }
575
0
    gtk_style_context_restore(style);
576
0
    return MOZ_GTK_SUCCESS;
577
0
}
578
579
static gint
580
moz_gtk_header_bar_button_paint(cairo_t *cr, GdkRectangle* rect,
581
                                GtkWidgetState* state,
582
                                GtkReliefStyle relief,
583
                                WidgetNodeType aIconWidgetType,
584
                                GtkTextDirection direction)
585
0
{
586
0
    WidgetNodeType buttonWidgetType =
587
0
      (aIconWidgetType == MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE) ?
588
0
      MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE : aIconWidgetType;
589
0
590
0
    // We need to inset our calculated margin because it also
591
0
    // contains titlebar button spacing.
592
0
    const ToolbarButtonGTKMetrics* metrics =
593
0
        GetToolbarButtonMetrics(buttonWidgetType);
594
0
    Inset(rect, metrics->buttonMargin);
595
0
596
0
    GtkWidget *buttonWidget = GetWidget(buttonWidgetType);
597
0
    moz_gtk_button_paint(cr, rect, state, relief, buttonWidget, direction);
598
0
599
0
    GtkWidget* iconWidget =
600
0
        gtk_bin_get_child(GTK_BIN(GetWidget(aIconWidgetType)));
601
0
    cairo_surface_t *surface = GetWidgetIconSurface(iconWidget, state->scale);
602
0
603
0
    if (surface) {
604
0
        GtkStyleContext* style = gtk_widget_get_style_context(buttonWidget);
605
0
        GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
606
0
607
0
        gtk_style_context_save(style);
608
0
        gtk_style_context_set_state(style, state_flags);
609
0
610
0
        const ToolbarButtonGTKMetrics *metrics =
611
0
            GetToolbarButtonMetrics(buttonWidgetType);
612
0
613
0
        /* This is available since Gtk+ 3.10 as well as GtkHeaderBar */
614
0
        static auto sGtkRenderIconSurfacePtr =
615
0
          (void (*)(GtkStyleContext *, cairo_t *, cairo_surface_t *, gdouble, gdouble))
616
0
        dlsym(RTLD_DEFAULT, "gtk_render_icon_surface");
617
0
618
0
        sGtkRenderIconSurfacePtr(style, cr, surface,
619
0
                                 metrics->iconXPosition, metrics->iconYPosition);
620
0
        gtk_style_context_restore(style);
621
0
    }
622
0
623
0
    return MOZ_GTK_SUCCESS;
624
0
}
625
626
static gint
627
moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect,
628
                     GtkWidgetState* state,
629
                     gboolean selected, gboolean inconsistent,
630
                     gboolean isradio, GtkTextDirection direction)
631
0
{
632
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
633
0
    gint x, y, width, height;
634
0
    GtkStyleContext *style;
635
0
636
0
    const ToggleGTKMetrics* metrics = GetToggleMetrics(isradio);
637
0
638
0
    // Clamp the rect and paint it center aligned in the rect.
639
0
    x = rect->x;
640
0
    y = rect->y;
641
0
    width = rect->width;
642
0
    height = rect->height;
643
0
644
0
    if (rect->width < rect->height) {
645
0
      y = rect->y + (rect->height - rect->width) / 2;
646
0
      height = rect->width;
647
0
    }
648
0
649
0
    if (rect->height < rect->width) {
650
0
      x = rect->x + (rect->width - rect->height) / 2;
651
0
      width = rect->height;
652
0
    }
653
0
654
0
    if (selected)
655
0
        state_flags = static_cast<GtkStateFlags>(state_flags|checkbox_check_state);
656
0
657
0
    if (inconsistent)
658
0
        state_flags = static_cast<GtkStateFlags>(state_flags|GTK_STATE_FLAG_INCONSISTENT);
659
0
660
0
    style = GetStyleContext(isradio ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON,
661
0
                            direction, state_flags);
662
0
663
0
    if (gtk_check_version(3, 20, 0) == nullptr) {
664
0
        gtk_render_background(style, cr, x, y, width, height);
665
0
        gtk_render_frame(style, cr, x, y, width, height);
666
0
        // Indicator is inset by the toggle's padding and border.
667
0
        gint indicator_x = x + metrics->borderAndPadding.left;
668
0
        gint indicator_y = y + metrics->borderAndPadding.top;
669
0
        gint indicator_width = metrics->minSizeWithBorder.width -
670
0
            metrics->borderAndPadding.left - metrics->borderAndPadding.right;
671
0
        gint indicator_height = metrics->minSizeWithBorder.height -
672
0
            metrics->borderAndPadding.top - metrics->borderAndPadding.bottom;
673
0
        if (isradio) {
674
0
            gtk_render_option(style, cr, indicator_x, indicator_y,
675
0
                              indicator_width, indicator_height);
676
0
        } else {
677
0
            gtk_render_check(style, cr, indicator_x, indicator_y,
678
0
                             indicator_width, indicator_height);
679
0
        }
680
0
    } else {
681
0
        if (isradio) {
682
0
            gtk_render_option(style, cr, x, y, width, height);
683
0
        } else {
684
0
            gtk_render_check(style, cr, x, y, width, height);
685
0
        }
686
0
    }
687
0
688
0
    return MOZ_GTK_SUCCESS;
689
0
}
690
691
static gint
692
calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect,
693
                            GdkRectangle* inner_rect,
694
                            GtkTextDirection direction)
695
0
{
696
0
    GtkStyleContext* style;
697
0
    GtkBorder border;
698
0
    GtkBorder padding = {0, 0, 0, 0};
699
0
700
0
    style = gtk_widget_get_style_context(button);
701
0
702
0
    /* This mirrors gtkbutton's child positioning */
703
0
    gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border);
704
0
    gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding);
705
0
706
0
    inner_rect->x = rect->x + border.left + padding.left;
707
0
    inner_rect->y = rect->y + padding.top + border.top;
708
0
    inner_rect->width = MAX(1, rect->width - padding.left -
709
0
       padding.right - border.left * 2);
710
0
    inner_rect->height = MAX(1, rect->height - padding.top -
711
0
       padding.bottom - border.top * 2);
712
0
713
0
    return MOZ_GTK_SUCCESS;
714
0
}
715
716
717
static gint
718
calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect,
719
                     GdkRectangle* arrow_rect, GtkTextDirection direction)
720
0
{
721
0
    /* defined in gtkarrow.c */
722
0
    gfloat arrow_scaling = 0.7;
723
0
    gfloat xalign, xpad;
724
0
    gint extent;
725
0
    gint mxpad, mypad;
726
0
    gfloat mxalign, myalign;
727
0
    GtkMisc* misc = GTK_MISC(arrow);
728
0
729
0
    gtk_style_context_get_style(gtk_widget_get_style_context(arrow),
730
0
                                "arrow_scaling", &arrow_scaling, NULL);
731
0
732
0
    gtk_misc_get_padding(misc, &mxpad, &mypad);
733
0
    extent = MIN((rect->width - mxpad * 2),
734
0
                 (rect->height - mypad * 2)) * arrow_scaling;
735
0
736
0
    gtk_misc_get_alignment(misc, &mxalign, &myalign);
737
0
738
0
    xalign = direction == GTK_TEXT_DIR_LTR ? mxalign : 1.0 - mxalign;
739
0
    xpad = mxpad + (rect->width - extent) * xalign;
740
0
741
0
    arrow_rect->x = direction == GTK_TEXT_DIR_LTR ?
742
0
                        floor(rect->x + xpad) : ceil(rect->x + xpad);
743
0
    arrow_rect->y = floor(rect->y + mypad +
744
0
                          ((rect->height - extent) * myalign));
745
0
746
0
    arrow_rect->width = arrow_rect->height = extent;
747
0
748
0
    return MOZ_GTK_SUCCESS;
749
0
}
750
751
static MozGtkSize
752
GetMinContentBox(GtkStyleContext* style)
753
0
{
754
0
    GtkStateFlags state_flags = gtk_style_context_get_state(style);
755
0
    gint width, height;
756
0
    gtk_style_context_get(style, state_flags,
757
0
                          "min-width", &width,
758
0
                          "min-height", &height,
759
0
                          nullptr);
760
0
    return {width, height};
761
0
}
762
763
/**
764
 * Get minimum widget size as sum of margin, padding, border and
765
 * min-width/min-height.
766
 */
767
static void
768
moz_gtk_get_widget_min_size(GtkStyleContext* style, int* width,
769
                            int* height)
770
0
{
771
0
  GtkStateFlags state_flags = gtk_style_context_get_state(style);
772
0
  gtk_style_context_get(style, state_flags,
773
0
                        "min-height", height,
774
0
                        "min-width", width,
775
0
                        nullptr);
776
0
777
0
  GtkBorder border, padding, margin;
778
0
  gtk_style_context_get_border(style, state_flags, &border);
779
0
  gtk_style_context_get_padding(style, state_flags, &padding);
780
0
  gtk_style_context_get_margin(style, state_flags,  &margin);
781
0
782
0
  *width += border.left + border.right + margin.left + margin.right +
783
0
            padding.left + padding.right;
784
0
  *height += border.top + border.bottom + margin.top + margin.bottom +
785
0
             padding.top + padding.bottom;
786
0
}
787
788
static MozGtkSize
789
GetMinMarginBox(GtkStyleContext* style)
790
0
{
791
0
    gint width, height;
792
0
    moz_gtk_get_widget_min_size(style, &width, &height);
793
0
    return {width, height};
794
0
}
795
796
static void
797
Inset(GdkRectangle* rect, const GtkBorder& aBorder)
798
0
{
799
0
    MOZ_ASSERT(rect);
800
0
    rect->x += aBorder.left;
801
0
    rect->y += aBorder.top;
802
0
    rect->width -= aBorder.left + aBorder.right;
803
0
    rect->height -= aBorder.top + aBorder.bottom;
804
0
}
805
806
// Inset a rectangle by the margins specified in a style context.
807
static void
808
InsetByMargin(GdkRectangle* rect, GtkStyleContext* style)
809
0
{
810
0
    MOZ_ASSERT(rect);
811
0
    GtkBorder margin;
812
0
813
0
    gtk_style_context_get_margin(style, gtk_style_context_get_state(style),
814
0
                                 &margin);
815
0
    Inset(rect, margin);
816
0
}
817
818
// Inset a rectangle by the border and padding specified in a style context.
819
static void
820
InsetByBorderPadding(GdkRectangle* rect, GtkStyleContext* style)
821
0
{
822
0
    GtkStateFlags state = gtk_style_context_get_state(style);
823
0
    GtkBorder padding, border;
824
0
825
0
    gtk_style_context_get_padding(style, state, &padding);
826
0
    Inset(rect, padding);
827
0
    gtk_style_context_get_border(style, state, &border);
828
0
    Inset(rect, border);
829
0
}
830
831
static gint
832
moz_gtk_scrollbar_button_paint(cairo_t *cr, const GdkRectangle* aRect,
833
                               GtkWidgetState* state,
834
                               GtkScrollbarButtonFlags flags,
835
                               GtkTextDirection direction)
836
0
{
837
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
838
0
    GdkRectangle arrow_rect;
839
0
    gdouble arrow_angle;
840
0
    GtkStyleContext* style;
841
0
    gint arrow_displacement_x, arrow_displacement_y;
842
0
843
0
    GtkWidget *scrollbar =
844
0
        GetWidget(flags & MOZ_GTK_STEPPER_VERTICAL ?
845
0
                  MOZ_GTK_SCROLLBAR_VERTICAL : MOZ_GTK_SCROLLBAR_HORIZONTAL);
846
0
847
0
    gtk_widget_set_direction(scrollbar, direction);
848
0
849
0
    if (flags & MOZ_GTK_STEPPER_VERTICAL) {
850
0
        arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_DOWN : ARROW_UP;
851
0
    } else {
852
0
        arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_RIGHT : ARROW_LEFT;
853
0
    }
854
0
855
0
    style = gtk_widget_get_style_context(scrollbar);
856
0
857
0
    gtk_style_context_save(style);
858
0
    gtk_style_context_add_class(style, GTK_STYLE_CLASS_BUTTON);
859
0
    gtk_style_context_set_state(style, state_flags);
860
0
    if (arrow_angle == ARROW_RIGHT) {
861
0
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_RIGHT);
862
0
    } else if (arrow_angle == ARROW_DOWN) {
863
0
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_BOTTOM);
864
0
    } else if (arrow_angle == ARROW_LEFT) {
865
0
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_LEFT);
866
0
    } else {
867
0
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOP);
868
0
    }
869
0
870
0
    GdkRectangle rect = *aRect;
871
0
    if (gtk_check_version(3,20,0) == nullptr) {
872
0
      // The "trough-border" is not used since GTK 3.20.  The stepper margin
873
0
      // box occupies the full width of the "contents" gadget content box.
874
0
      InsetByMargin(&rect, style);
875
0
    } else {
876
0
        // Scrollbar button has to be inset by trough_border because its DOM
877
0
        // element is filling width of vertical scrollbar's track (or height
878
0
        // in case of horizontal scrollbars).
879
0
        GtkOrientation orientation = flags & MOZ_GTK_STEPPER_VERTICAL ?
880
0
            GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL;
881
0
882
0
        const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
883
0
        if (flags & MOZ_GTK_STEPPER_VERTICAL) {
884
0
            rect.x += metrics->border.track.left;
885
0
            rect.width = metrics->size.thumb.width;
886
0
        } else {
887
0
            rect.y += metrics->border.track.top;
888
0
            rect.height = metrics->size.thumb.height;
889
0
        }
890
0
    }
891
0
892
0
    gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
893
0
    gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height);
894
0
895
0
    arrow_rect.width = rect.width / 2;
896
0
    arrow_rect.height = rect.height / 2;
897
0
898
0
    gfloat arrow_scaling;
899
0
    gtk_style_context_get_style(style, "arrow-scaling", &arrow_scaling, NULL);
900
0
901
0
    gdouble arrow_size = MIN(rect.width, rect.height) * arrow_scaling;
902
0
    arrow_rect.x = rect.x + (rect.width - arrow_size) / 2;
903
0
    arrow_rect.y = rect.y + (rect.height - arrow_size) / 2;
904
0
905
0
    if (state_flags & GTK_STATE_FLAG_ACTIVE) {
906
0
        gtk_style_context_get_style(style,
907
0
                                    "arrow-displacement-x", &arrow_displacement_x,
908
0
                                    "arrow-displacement-y", &arrow_displacement_y,
909
0
                                    NULL);
910
0
911
0
        arrow_rect.x += arrow_displacement_x;
912
0
        arrow_rect.y += arrow_displacement_y;
913
0
    }
914
0
915
0
    gtk_render_arrow(style, cr, arrow_angle,
916
0
                     arrow_rect.x,
917
0
                     arrow_rect.y,
918
0
                     arrow_size);
919
0
920
0
    gtk_style_context_restore(style);
921
0
922
0
    return MOZ_GTK_SUCCESS;
923
0
}
924
925
static void
926
moz_gtk_update_scrollbar_style(GtkStyleContext* style,
927
                               WidgetNodeType widget,
928
                               GtkTextDirection direction)
929
0
{
930
0
    if (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) {
931
0
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_BOTTOM);
932
0
    } else {
933
0
        if (direction == GTK_TEXT_DIR_LTR) {
934
0
            gtk_style_context_add_class(style, GTK_STYLE_CLASS_RIGHT);
935
0
            gtk_style_context_remove_class(style, GTK_STYLE_CLASS_LEFT);
936
0
        } else {
937
0
            gtk_style_context_add_class(style, GTK_STYLE_CLASS_LEFT);
938
0
            gtk_style_context_remove_class(style, GTK_STYLE_CLASS_RIGHT);
939
0
        }
940
0
    }
941
0
}
942
943
static void
944
moz_gtk_draw_styled_frame(GtkStyleContext* style, cairo_t *cr,
945
                          const GdkRectangle* aRect, bool drawFocus)
946
0
{
947
0
    GdkRectangle rect = *aRect;
948
0
    if (gtk_check_version(3, 6, 0) == nullptr) {
949
0
        InsetByMargin(&rect, style);
950
0
    }
951
0
    gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
952
0
    gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height);
953
0
    if (drawFocus) {
954
0
        gtk_render_focus(style, cr,
955
0
                         rect.x, rect.y, rect.width, rect.height);
956
0
    }
957
0
}
958
959
static gint
960
moz_gtk_scrollbar_trough_paint(WidgetNodeType widget,
961
                               cairo_t *cr, const GdkRectangle* aRect,
962
                               GtkWidgetState* state,
963
                               GtkTextDirection direction)
964
0
{
965
0
    GdkRectangle rect = *aRect;
966
0
    GtkStyleContext* style;
967
0
968
0
    if (gtk_get_minor_version() >= 20) {
969
0
        WidgetNodeType thumb = widget == MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL ?
970
0
            MOZ_GTK_SCROLLBAR_THUMB_VERTICAL :
971
0
            MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
972
0
        MozGtkSize thumbSize = GetMinMarginBox(GetStyleContext(thumb));
973
0
        style = GetStyleContext(widget, direction);
974
0
        MozGtkSize trackSize = GetMinContentBox(style);
975
0
        trackSize.Include(thumbSize);
976
0
        trackSize += GetMarginBorderPadding(style);
977
0
        // Gecko's trough |aRect| fills available breadth, but GTK's trough is
978
0
        // centered in the contents_gadget.  The centering here round left
979
0
        // and up, like gtk_box_gadget_allocate_child().
980
0
        if (widget == MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL) {
981
0
            rect.x += (rect.width - trackSize.width)/2;
982
0
            rect.width = trackSize.width;
983
0
        } else {
984
0
            rect.y += (rect.height - trackSize.height)/2;
985
0
            rect.height = trackSize.height;
986
0
        }
987
0
    } else {
988
0
        style = GetStyleContext(widget, direction);
989
0
    }
990
0
991
0
    moz_gtk_draw_styled_frame(style, cr, &rect, state->focused);
992
0
993
0
    return MOZ_GTK_SUCCESS;
994
0
}
995
996
static gint
997
moz_gtk_scrollbar_paint(WidgetNodeType widget,
998
                        cairo_t *cr, const GdkRectangle* rect,
999
                        GtkWidgetState* state,
1000
                        GtkTextDirection direction)
1001
0
{
1002
0
    GtkStyleContext* style = GetStyleContext(widget, direction);
1003
0
    moz_gtk_update_scrollbar_style(style, widget, direction);
1004
0
1005
0
    moz_gtk_draw_styled_frame(style, cr, rect, state->focused);
1006
0
1007
0
    style = GetStyleContext((widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) ?
1008
0
                            MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL :
1009
0
                            MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL,
1010
0
                            direction);
1011
0
    moz_gtk_draw_styled_frame(style, cr, rect, state->focused);
1012
0
1013
0
    return MOZ_GTK_SUCCESS;
1014
0
}
1015
1016
static gint
1017
moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget,
1018
                              cairo_t *cr, const GdkRectangle* aRect,
1019
                              GtkWidgetState* state,
1020
                              GtkTextDirection direction)
1021
0
{
1022
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1023
0
    GtkStyleContext* style = GetStyleContext(widget, direction, state_flags);
1024
0
1025
0
    GtkOrientation orientation = (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
1026
0
        GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1027
0
1028
0
    GdkRectangle rect = *aRect;
1029
0
1030
0
    const ScrollbarGTKMetrics* metrics =
1031
0
        (state->depressed || state->active || state->inHover) ?
1032
0
        GetActiveScrollbarMetrics(orientation) :
1033
0
        GetScrollbarMetrics(orientation);
1034
0
    Inset(&rect, metrics->margin.thumb);
1035
0
1036
0
    gtk_render_slider(style, cr, rect.x, rect.y, rect.width, rect.height,
1037
0
                      orientation);
1038
0
1039
0
    return MOZ_GTK_SUCCESS;
1040
0
}
1041
1042
static gint
1043
moz_gtk_inner_spin_paint(cairo_t *cr, GdkRectangle* rect,
1044
                         GtkWidgetState* state,
1045
                         GtkTextDirection direction)
1046
0
{
1047
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_SPINBUTTON, direction,
1048
0
                                 GetStateFlagsFromGtkWidgetState(state));
1049
0
1050
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1051
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1052
0
1053
0
    /* hard code these values */
1054
0
    GdkRectangle arrow_rect;
1055
0
    arrow_rect.width = 6;
1056
0
    arrow_rect.height = 6;
1057
0
1058
0
    // align spin to the left
1059
0
    arrow_rect.x = rect->x;
1060
0
1061
0
    // up button
1062
0
    arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2 - 3;
1063
0
    gtk_render_arrow(style, cr, ARROW_UP,
1064
0
                    arrow_rect.x, arrow_rect.y,
1065
0
                    arrow_rect.width);
1066
0
1067
0
    // down button
1068
0
    arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2 + 3;
1069
0
    gtk_render_arrow(style, cr, ARROW_DOWN,
1070
0
                    arrow_rect.x, arrow_rect.y,
1071
0
                    arrow_rect.width);
1072
0
1073
0
    return MOZ_GTK_SUCCESS;
1074
0
}
1075
1076
static gint
1077
moz_gtk_spin_paint(cairo_t *cr, GdkRectangle* rect,
1078
                   GtkTextDirection direction)
1079
0
{
1080
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_SPINBUTTON, direction);
1081
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1082
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1083
0
    return MOZ_GTK_SUCCESS;
1084
0
}
1085
1086
static gint
1087
moz_gtk_spin_updown_paint(cairo_t *cr, GdkRectangle* rect,
1088
                          gboolean isDown, GtkWidgetState* state,
1089
                          GtkTextDirection direction)
1090
0
{
1091
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_SPINBUTTON, direction,
1092
0
                                 GetStateFlagsFromGtkWidgetState(state));
1093
0
1094
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1095
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1096
0
1097
0
    /* hard code these values */
1098
0
    GdkRectangle arrow_rect;
1099
0
    arrow_rect.width = 6;
1100
0
    arrow_rect.height = 6;
1101
0
    arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1102
0
    arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1103
0
    arrow_rect.y += isDown ? -1 : 1;
1104
0
1105
0
    gtk_render_arrow(style, cr,
1106
0
                    isDown ? ARROW_DOWN : ARROW_UP,
1107
0
                    arrow_rect.x, arrow_rect.y,
1108
0
                    arrow_rect.width);
1109
0
1110
0
    return MOZ_GTK_SUCCESS;
1111
0
}
1112
1113
/* See gtk_range_draw() for reference.
1114
*/
1115
static gint
1116
moz_gtk_scale_paint(cairo_t *cr, GdkRectangle* rect,
1117
                    GtkWidgetState* state,
1118
                    GtkOrientation flags, GtkTextDirection direction)
1119
0
{
1120
0
  GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1121
0
  gint x, y, width, height, min_width, min_height;
1122
0
  GtkStyleContext* style;
1123
0
  GtkBorder margin;
1124
0
1125
0
  moz_gtk_get_scale_metrics(flags, &min_width, &min_height);
1126
0
1127
0
  WidgetNodeType widget = (flags == GTK_ORIENTATION_HORIZONTAL) ?
1128
0
                          MOZ_GTK_SCALE_TROUGH_HORIZONTAL :
1129
0
                          MOZ_GTK_SCALE_TROUGH_VERTICAL;
1130
0
  style = GetStyleContext(widget, direction, state_flags);
1131
0
  gtk_style_context_get_margin(style, state_flags, &margin);
1132
0
1133
0
  // Clamp the dimension perpendicular to the direction that the slider crosses
1134
0
  // to the minimum size.
1135
0
  if (flags == GTK_ORIENTATION_HORIZONTAL) {
1136
0
    width = rect->width - (margin.left + margin.right);
1137
0
    height = min_height - (margin.top + margin.bottom);
1138
0
    x = rect->x + margin.left;
1139
0
    y = rect->y + (rect->height - height)/2;
1140
0
  } else {
1141
0
    width = min_width - (margin.left + margin.right);
1142
0
    height = rect->height - (margin.top + margin.bottom);
1143
0
    x = rect->x + (rect->width - width)/2;
1144
0
    y = rect->y + margin.top;
1145
0
  }
1146
0
1147
0
  gtk_render_background(style, cr, x, y, width, height);
1148
0
  gtk_render_frame(style, cr, x, y, width, height);
1149
0
1150
0
  if (state->focused)
1151
0
    gtk_render_focus(style, cr,
1152
0
                    rect->x, rect->y, rect->width, rect->height);
1153
0
1154
0
  return MOZ_GTK_SUCCESS;
1155
0
}
1156
1157
static gint
1158
moz_gtk_scale_thumb_paint(cairo_t *cr, GdkRectangle* rect,
1159
                          GtkWidgetState* state,
1160
                          GtkOrientation flags, GtkTextDirection direction)
1161
0
{
1162
0
  GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1163
0
  GtkStyleContext* style;
1164
0
  gint thumb_width, thumb_height, x, y;
1165
0
1166
0
  /* determine the thumb size, and position the thumb in the center in the opposite axis
1167
0
  */
1168
0
  if (flags == GTK_ORIENTATION_HORIZONTAL) {
1169
0
    moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_width, &thumb_height);
1170
0
    x = rect->x;
1171
0
    y = rect->y + (rect->height - thumb_height) / 2;
1172
0
  }
1173
0
  else {
1174
0
    moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_width);
1175
0
    x = rect->x + (rect->width - thumb_width) / 2;
1176
0
    y = rect->y;
1177
0
  }
1178
0
1179
0
  WidgetNodeType widget = (flags == GTK_ORIENTATION_HORIZONTAL) ?
1180
0
                          MOZ_GTK_SCALE_THUMB_HORIZONTAL :
1181
0
                          MOZ_GTK_SCALE_THUMB_VERTICAL;
1182
0
  style = GetStyleContext(widget, direction, state_flags);
1183
0
  gtk_render_slider(style, cr, x, y, thumb_width, thumb_height, flags);
1184
0
1185
0
  return MOZ_GTK_SUCCESS;
1186
0
}
1187
1188
static gint
1189
moz_gtk_gripper_paint(cairo_t *cr, GdkRectangle* rect,
1190
                      GtkWidgetState* state,
1191
                      GtkTextDirection direction)
1192
0
{
1193
0
    GtkStyleContext* style =
1194
0
            GetStyleContext(MOZ_GTK_GRIPPER, direction,
1195
0
                            GetStateFlagsFromGtkWidgetState(state));
1196
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1197
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1198
0
    return MOZ_GTK_SUCCESS;
1199
0
}
1200
1201
static gint
1202
moz_gtk_hpaned_paint(cairo_t *cr, GdkRectangle* rect,
1203
                     GtkWidgetState* state)
1204
0
{
1205
0
    GtkStyleContext* style =
1206
0
        GetStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL,
1207
0
                        GTK_TEXT_DIR_LTR,
1208
0
                        GetStateFlagsFromGtkWidgetState(state));
1209
0
    gtk_render_handle(style, cr,
1210
0
                      rect->x, rect->y, rect->width, rect->height);
1211
0
    return MOZ_GTK_SUCCESS;
1212
0
}
1213
1214
static gint
1215
moz_gtk_vpaned_paint(cairo_t *cr, GdkRectangle* rect,
1216
                     GtkWidgetState* state)
1217
0
{
1218
0
    GtkStyleContext* style =
1219
0
        GetStyleContext(MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL,
1220
0
                        GTK_TEXT_DIR_LTR,
1221
0
                        GetStateFlagsFromGtkWidgetState(state));
1222
0
    gtk_render_handle(style, cr,
1223
0
                      rect->x, rect->y, rect->width, rect->height);
1224
0
    return MOZ_GTK_SUCCESS;
1225
0
}
1226
1227
// See gtk_entry_draw() for reference.
1228
static gint
1229
moz_gtk_entry_paint(cairo_t *cr, GdkRectangle* rect,
1230
                    GtkWidgetState* state,
1231
                    GtkStyleContext* style)
1232
0
{
1233
0
    gint x = rect->x, y = rect->y, width = rect->width, height = rect->height;
1234
0
    int draw_focus_outline_only = state->depressed; // StyleAppearance::FocusOutline
1235
0
1236
0
    if (draw_focus_outline_only) {
1237
0
        // Inflate the given 'rect' with the focus outline size.
1238
0
        gint h, v;
1239
0
        moz_gtk_get_focus_outline_size(style, &h, &v);
1240
0
        rect->x -= h;
1241
0
        rect->width += 2 * h;
1242
0
        rect->y -= v;
1243
0
        rect->height += 2 * v;
1244
0
        width = rect->width;
1245
0
        height = rect->height;
1246
0
    } else {
1247
0
        gtk_render_background(style, cr, x, y, width, height);
1248
0
    }
1249
0
    gtk_render_frame(style, cr, x, y, width, height);
1250
0
1251
0
    return MOZ_GTK_SUCCESS;
1252
0
}
1253
1254
static gint
1255
moz_gtk_text_view_paint(cairo_t *cr, GdkRectangle* aRect,
1256
                        GtkWidgetState* state,
1257
                        GtkTextDirection direction)
1258
0
{
1259
0
    // GtkTextView and GtkScrolledWindow do not set active and prelight flags.
1260
0
    // The use of focus with MOZ_GTK_SCROLLED_WINDOW here is questionable
1261
0
    // because a parent widget will not have focus when its child GtkTextView
1262
0
    // has focus, but perhaps this may help identify a focused textarea with
1263
0
    // some themes as GtkTextView backgrounds do not typically render
1264
0
    // differently with focus.
1265
0
    GtkStateFlags state_flags =
1266
0
        state->disabled ? GTK_STATE_FLAG_INSENSITIVE :
1267
0
        state->focused ? GTK_STATE_FLAG_FOCUSED :
1268
0
        GTK_STATE_FLAG_NORMAL;
1269
0
1270
0
    GtkStyleContext* style_frame =
1271
0
        GetStyleContext(MOZ_GTK_SCROLLED_WINDOW, direction, state_flags);
1272
0
    gtk_render_frame(style_frame, cr,
1273
0
                     aRect->x, aRect->y, aRect->width, aRect->height);
1274
0
1275
0
    GdkRectangle rect = *aRect;
1276
0
    InsetByBorderPadding(&rect, style_frame);
1277
0
1278
0
    GtkStyleContext* style =
1279
0
        GetStyleContext(MOZ_GTK_TEXT_VIEW, direction, state_flags);
1280
0
    gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
1281
0
    // There is a separate "text" window, which usually provides the
1282
0
    // background behind the text.  However, this is transparent in Ambiance
1283
0
    // for GTK 3.20, in which case the MOZ_GTK_TEXT_VIEW background is
1284
0
    // visible.
1285
0
    style = GetStyleContext(MOZ_GTK_TEXT_VIEW_TEXT, direction, state_flags);
1286
0
    gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
1287
0
1288
0
    return MOZ_GTK_SUCCESS;
1289
0
}
1290
1291
static gint
1292
moz_gtk_treeview_paint(cairo_t *cr, GdkRectangle* rect,
1293
                       GtkWidgetState* state,
1294
                       GtkTextDirection direction)
1295
0
{
1296
0
    gint xthickness, ythickness;
1297
0
    GtkStyleContext *style;
1298
0
    GtkStyleContext *style_tree;
1299
0
    GtkStateFlags state_flags;
1300
0
    GtkBorder border;
1301
0
1302
0
    /* only handle disabled and normal states, otherwise the whole background
1303
0
     * area will be painted differently with other states */
1304
0
    state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL;
1305
0
1306
0
    style = GetStyleContext(MOZ_GTK_SCROLLED_WINDOW, direction);
1307
0
    gtk_style_context_get_border(style, state_flags, &border);
1308
0
    xthickness = border.left;
1309
0
    ythickness = border.top;
1310
0
1311
0
    style_tree = GetStyleContext(MOZ_GTK_TREEVIEW_VIEW, direction);
1312
0
    gtk_render_background(style_tree, cr,
1313
0
                          rect->x + xthickness, rect->y + ythickness,
1314
0
                          rect->width - 2 * xthickness,
1315
0
                          rect->height - 2 * ythickness);
1316
0
1317
0
    style = GetStyleContext(MOZ_GTK_SCROLLED_WINDOW, direction);
1318
0
    gtk_render_frame(style, cr,
1319
0
                     rect->x, rect->y, rect->width, rect->height);
1320
0
    return MOZ_GTK_SUCCESS;
1321
0
}
1322
1323
static gint
1324
moz_gtk_tree_header_cell_paint(cairo_t *cr, GdkRectangle* rect,
1325
                               GtkWidgetState* state,
1326
                               gboolean isSorted, GtkTextDirection direction)
1327
0
{
1328
0
    moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
1329
0
                         GetWidget(MOZ_GTK_TREE_HEADER_CELL), direction);
1330
0
    return MOZ_GTK_SUCCESS;
1331
0
}
1332
1333
static gint
1334
moz_gtk_tree_header_sort_arrow_paint(cairo_t *cr, GdkRectangle* rect,
1335
                                     GtkWidgetState* state, GtkArrowType arrow_type,
1336
                                     GtkTextDirection direction)
1337
0
{
1338
0
    GdkRectangle arrow_rect;
1339
0
    gdouble arrow_angle;
1340
0
    GtkStyleContext* style;
1341
0
1342
0
    /* hard code these values */
1343
0
    arrow_rect.width = 11;
1344
0
    arrow_rect.height = 11;
1345
0
    arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2;
1346
0
    arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2;
1347
0
    style = GetStyleContext(MOZ_GTK_TREE_HEADER_SORTARROW, direction,
1348
0
                            GetStateFlagsFromGtkWidgetState(state));
1349
0
    switch (arrow_type) {
1350
0
    case GTK_ARROW_LEFT:
1351
0
        arrow_angle = ARROW_LEFT;
1352
0
        break;
1353
0
    case GTK_ARROW_RIGHT:
1354
0
        arrow_angle = ARROW_RIGHT;
1355
0
        break;
1356
0
    case GTK_ARROW_DOWN:
1357
0
        arrow_angle = ARROW_DOWN;
1358
0
        break;
1359
0
    default:
1360
0
        arrow_angle = ARROW_UP;
1361
0
        break;
1362
0
    }
1363
0
    if (arrow_type != GTK_ARROW_NONE)
1364
0
        gtk_render_arrow(style, cr, arrow_angle,
1365
0
                         arrow_rect.x, arrow_rect.y,
1366
0
                         arrow_rect.width);
1367
0
    return MOZ_GTK_SUCCESS;
1368
0
}
1369
1370
/* See gtk_expander_paint() for reference.
1371
 */
1372
static gint
1373
moz_gtk_treeview_expander_paint(cairo_t *cr, GdkRectangle* rect,
1374
                                GtkWidgetState* state,
1375
                                GtkExpanderStyle expander_state,
1376
                                GtkTextDirection direction)
1377
0
{
1378
0
    /* Because the frame we get is of the entire treeview, we can't get the precise
1379
0
     * event state of one expander, thus rendering hover and active feedback useless. */
1380
0
    GtkStateFlags state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE :
1381
0
                                                  GTK_STATE_FLAG_NORMAL;
1382
0
1383
0
    if (state->inHover)
1384
0
        state_flags =
1385
0
            static_cast<GtkStateFlags>(state_flags|GTK_STATE_FLAG_PRELIGHT);
1386
0
    if (state->selected)
1387
0
        state_flags =
1388
0
            static_cast<GtkStateFlags>(state_flags|GTK_STATE_FLAG_SELECTED);
1389
0
1390
0
    /* GTK_STATE_FLAG_ACTIVE controls expanded/colapsed state rendering
1391
0
     * in gtk_render_expander()
1392
0
     */
1393
0
    if (expander_state == GTK_EXPANDER_EXPANDED)
1394
0
        state_flags = static_cast<GtkStateFlags>(state_flags|checkbox_check_state);
1395
0
    else
1396
0
        state_flags = static_cast<GtkStateFlags>(state_flags&~(checkbox_check_state));
1397
0
1398
0
    GtkStyleContext *style = GetStyleContext(MOZ_GTK_TREEVIEW_EXPANDER,
1399
0
                                             direction, state_flags);
1400
0
    gtk_render_expander(style, cr,
1401
0
                        rect->x,
1402
0
                        rect->y,
1403
0
                        rect->width,
1404
0
                        rect->height);
1405
0
1406
0
    return MOZ_GTK_SUCCESS;
1407
0
}
1408
1409
/* See gtk_separator_draw() for reference.
1410
*/
1411
static gint
1412
moz_gtk_combo_box_paint(cairo_t *cr, GdkRectangle* rect,
1413
                        GtkWidgetState* state,
1414
                        GtkTextDirection direction)
1415
0
{
1416
0
    GdkRectangle arrow_rect, real_arrow_rect;
1417
0
    gint separator_width;
1418
0
    gboolean wide_separators;
1419
0
    GtkStyleContext* style;
1420
0
    GtkRequisition arrow_req;
1421
0
1422
0
    GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
1423
0
    GtkWidget* comboBoxArrow = GetWidget(MOZ_GTK_COMBOBOX_ARROW);
1424
0
1425
0
    /* Also sets the direction on gComboBoxButtonWidget, which is then
1426
0
     * inherited by the separator and arrow */
1427
0
    moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
1428
0
                         comboBoxButton, direction);
1429
0
1430
0
    calculate_button_inner_rect(comboBoxButton, rect, &arrow_rect, direction);
1431
0
    /* Now arrow_rect contains the inner rect ; we want to correct the width
1432
0
     * to what the arrow needs (see gtk_combo_box_size_allocate) */
1433
0
    gtk_widget_get_preferred_size(comboBoxArrow, NULL, &arrow_req);
1434
0
1435
0
    if (direction == GTK_TEXT_DIR_LTR)
1436
0
        arrow_rect.x += arrow_rect.width - arrow_req.width;
1437
0
    arrow_rect.width = arrow_req.width;
1438
0
1439
0
    calculate_arrow_rect(comboBoxArrow,
1440
0
                         &arrow_rect, &real_arrow_rect, direction);
1441
0
1442
0
    style = GetStyleContext(MOZ_GTK_COMBOBOX_ARROW);
1443
0
    gtk_render_arrow(style, cr, ARROW_DOWN,
1444
0
                     real_arrow_rect.x, real_arrow_rect.y,
1445
0
                     real_arrow_rect.width);
1446
0
1447
0
    /* If there is no separator in the theme, there's nothing left to do. */
1448
0
    GtkWidget* widget = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR);
1449
0
    if (!widget)
1450
0
        return MOZ_GTK_SUCCESS;
1451
0
    style = gtk_widget_get_style_context(widget);
1452
0
    gtk_style_context_get_style(style,
1453
0
                                "wide-separators", &wide_separators,
1454
0
                                "separator-width", &separator_width,
1455
0
                                NULL);
1456
0
1457
0
    if (wide_separators) {
1458
0
        if (direction == GTK_TEXT_DIR_LTR)
1459
0
            arrow_rect.x -= separator_width;
1460
0
        else
1461
0
            arrow_rect.x += arrow_rect.width;
1462
0
1463
0
        gtk_render_frame(style, cr, arrow_rect.x, arrow_rect.y, separator_width, arrow_rect.height);
1464
0
    } else {
1465
0
        if (direction == GTK_TEXT_DIR_LTR) {
1466
0
            GtkBorder padding;
1467
0
            GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1468
0
            gtk_style_context_get_padding(style, state_flags, &padding);
1469
0
            arrow_rect.x -= padding.left;
1470
0
        }
1471
0
        else
1472
0
            arrow_rect.x += arrow_rect.width;
1473
0
1474
0
        gtk_render_line(style, cr,
1475
0
                        arrow_rect.x, arrow_rect.y,
1476
0
                        arrow_rect.x, arrow_rect.y + arrow_rect.height);
1477
0
    }
1478
0
    return MOZ_GTK_SUCCESS;
1479
0
}
1480
1481
static gint
1482
moz_gtk_arrow_paint(cairo_t *cr, GdkRectangle* rect,
1483
                    GtkWidgetState* state,
1484
                    GtkArrowType arrow_type, GtkTextDirection direction)
1485
0
{
1486
0
    GdkRectangle arrow_rect;
1487
0
    gdouble arrow_angle;
1488
0
1489
0
    if (direction == GTK_TEXT_DIR_RTL) {
1490
0
        if (arrow_type == GTK_ARROW_LEFT) {
1491
0
            arrow_type = GTK_ARROW_RIGHT;
1492
0
        } else if (arrow_type == GTK_ARROW_RIGHT) {
1493
0
            arrow_type = GTK_ARROW_LEFT;
1494
0
        }
1495
0
    }
1496
0
    switch (arrow_type) {
1497
0
    case GTK_ARROW_LEFT:
1498
0
        arrow_angle = ARROW_LEFT;
1499
0
        break;
1500
0
    case GTK_ARROW_RIGHT:
1501
0
        arrow_angle = ARROW_RIGHT;
1502
0
        break;
1503
0
    case GTK_ARROW_DOWN:
1504
0
        arrow_angle = ARROW_DOWN;
1505
0
        break;
1506
0
    default:
1507
0
        arrow_angle = ARROW_UP;
1508
0
        break;
1509
0
    }
1510
0
    if (arrow_type == GTK_ARROW_NONE)
1511
0
        return MOZ_GTK_SUCCESS;
1512
0
1513
0
    calculate_arrow_rect(GetWidget(MOZ_GTK_BUTTON_ARROW), rect, &arrow_rect,
1514
0
                         direction);
1515
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1516
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_BUTTON_ARROW,
1517
0
                                             direction, state_flags);
1518
0
    gtk_render_arrow(style, cr, arrow_angle,
1519
0
                     arrow_rect.x, arrow_rect.y, arrow_rect.width);
1520
0
    return MOZ_GTK_SUCCESS;
1521
0
}
1522
1523
static gint
1524
moz_gtk_combo_box_entry_button_paint(cairo_t *cr, GdkRectangle* rect,
1525
                                     GtkWidgetState* state,
1526
                                     gboolean input_focus,
1527
                                     GtkTextDirection direction)
1528
0
{
1529
0
    gint x_displacement, y_displacement;
1530
0
    GdkRectangle arrow_rect, real_arrow_rect;
1531
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1532
0
    GtkStyleContext* style;
1533
0
1534
0
    GtkWidget* comboBoxEntry = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
1535
0
    moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL,
1536
0
                         comboBoxEntry, direction);
1537
0
    calculate_button_inner_rect(comboBoxEntry, rect, &arrow_rect, direction);
1538
0
1539
0
    if (state_flags & GTK_STATE_FLAG_ACTIVE) {
1540
0
        style = gtk_widget_get_style_context(comboBoxEntry);
1541
0
        gtk_style_context_get_style(style,
1542
0
                                    "child-displacement-x", &x_displacement,
1543
0
                                    "child-displacement-y", &y_displacement,
1544
0
                                    NULL);
1545
0
        arrow_rect.x += x_displacement;
1546
0
        arrow_rect.y += y_displacement;
1547
0
    }
1548
0
1549
0
    calculate_arrow_rect(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_ARROW),
1550
0
                         &arrow_rect, &real_arrow_rect, direction);
1551
0
1552
0
    style = GetStyleContext(MOZ_GTK_COMBOBOX_ENTRY_ARROW);
1553
0
    gtk_render_arrow(style, cr, ARROW_DOWN,
1554
0
                    real_arrow_rect.x, real_arrow_rect.y,
1555
0
                    real_arrow_rect.width);
1556
0
    return MOZ_GTK_SUCCESS;
1557
0
}
1558
1559
static gint
1560
moz_gtk_container_paint(cairo_t *cr, GdkRectangle* rect,
1561
                        GtkWidgetState* state,
1562
                        WidgetNodeType  widget_type,
1563
                        GtkTextDirection direction)
1564
0
{
1565
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
1566
0
    GtkStyleContext* style = GetStyleContext(widget_type, direction,
1567
0
                                             state_flags);
1568
0
    /* this is for drawing a prelight box */
1569
0
    if (state_flags & GTK_STATE_FLAG_PRELIGHT) {
1570
0
        gtk_render_background(style, cr,
1571
0
                              rect->x, rect->y, rect->width, rect->height);
1572
0
    }
1573
0
1574
0
    return MOZ_GTK_SUCCESS;
1575
0
}
1576
1577
static gint
1578
moz_gtk_toggle_label_paint(cairo_t *cr, GdkRectangle* rect,
1579
                           GtkWidgetState* state,
1580
                           gboolean isradio, GtkTextDirection direction)
1581
0
{
1582
0
    if (!state->focused)
1583
0
        return MOZ_GTK_SUCCESS;
1584
0
1585
0
    GtkStyleContext *style =
1586
0
        GetStyleContext(isradio ? MOZ_GTK_RADIOBUTTON_CONTAINER :
1587
0
                                  MOZ_GTK_CHECKBUTTON_CONTAINER,
1588
0
                        direction,
1589
0
                        GetStateFlagsFromGtkWidgetState(state));
1590
0
    gtk_render_focus(style, cr,
1591
0
                    rect->x, rect->y, rect->width, rect->height);
1592
0
1593
0
    return MOZ_GTK_SUCCESS;
1594
0
}
1595
1596
static gint
1597
moz_gtk_toolbar_paint(cairo_t *cr, GdkRectangle* rect,
1598
                      GtkTextDirection direction)
1599
0
{
1600
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_TOOLBAR, direction);
1601
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1602
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1603
0
    return MOZ_GTK_SUCCESS;
1604
0
}
1605
1606
/* See _gtk_toolbar_paint_space_line() for reference.
1607
*/
1608
static gint
1609
moz_gtk_toolbar_separator_paint(cairo_t *cr, GdkRectangle* rect,
1610
                                GtkTextDirection direction)
1611
0
{
1612
0
    gint     separator_width;
1613
0
    gint     paint_width;
1614
0
    gboolean wide_separators;
1615
0
1616
0
    /* Defined as constants in GTK+ 2.10.14 */
1617
0
    const double start_fraction = 0.2;
1618
0
    const double end_fraction = 0.8;
1619
0
1620
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_TOOLBAR);
1621
0
    gtk_style_context_get_style(style,
1622
0
                                "wide-separators", &wide_separators,
1623
0
                                "separator-width", &separator_width,
1624
0
                                NULL);
1625
0
1626
0
    style = GetStyleContext(MOZ_GTK_TOOLBAR_SEPARATOR, direction);
1627
0
    if (wide_separators) {
1628
0
        if (separator_width > rect->width)
1629
0
            separator_width = rect->width;
1630
0
1631
0
        gtk_render_frame(style, cr,
1632
0
                          rect->x + (rect->width - separator_width) / 2,
1633
0
                          rect->y + rect->height * start_fraction,
1634
0
                          separator_width,
1635
0
                          rect->height * (end_fraction - start_fraction));
1636
0
    } else {
1637
0
        GtkBorder padding;
1638
0
        gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding);
1639
0
1640
0
        paint_width = padding.left;
1641
0
        if (paint_width > rect->width)
1642
0
            paint_width = rect->width;
1643
0
1644
0
        gtk_render_line(style, cr,
1645
0
                        rect->x + (rect->width - paint_width) / 2,
1646
0
                        rect->y + rect->height * start_fraction,
1647
0
                        rect->x + (rect->width - paint_width) / 2,
1648
0
                        rect->y + rect->height * end_fraction);
1649
0
    }
1650
0
    return MOZ_GTK_SUCCESS;
1651
0
}
1652
1653
static gint
1654
moz_gtk_tooltip_paint(cairo_t *cr, const GdkRectangle* aRect,
1655
                      GtkTextDirection direction)
1656
0
{
1657
0
    // Tooltip widget is made in GTK3 as following tree:
1658
0
    // Tooltip window
1659
0
    //   Horizontal Box
1660
0
    //     Icon (not supported by Firefox)
1661
0
    //     Label
1662
0
    // Each element can be fully styled by CSS of GTK theme.
1663
0
    // We have to draw all elements with appropriate offset and right dimensions.
1664
0
1665
0
    // Tooltip drawing
1666
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_TOOLTIP, direction);
1667
0
    GdkRectangle rect = *aRect;
1668
0
    gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
1669
0
    gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height);
1670
0
1671
0
    // Horizontal Box drawing
1672
0
    //
1673
0
    // The box element has hard-coded 6px margin-* GtkWidget properties, which
1674
0
    // are added between the window dimensions and the CSS margin box of the
1675
0
    // horizontal box.  The frame of the tooltip window is drawn in the
1676
0
    // 6px margin.
1677
0
    // For drawing Horizontal Box we have to inset drawing area by that 6px
1678
0
    // plus its CSS margin.
1679
0
    GtkStyleContext* boxStyle = GetStyleContext(MOZ_GTK_TOOLTIP_BOX, direction);
1680
0
1681
0
    rect.x += 6;
1682
0
    rect.y += 6;
1683
0
    rect.width -= 12;
1684
0
    rect.height -= 12;
1685
0
1686
0
    InsetByMargin(&rect, boxStyle);
1687
0
    gtk_render_background(boxStyle, cr, rect.x, rect.y, rect.width, rect.height);
1688
0
    gtk_render_frame(boxStyle, cr, rect.x, rect.y, rect.width, rect.height);
1689
0
1690
0
    // Label drawing
1691
0
    InsetByBorderPadding(&rect, boxStyle);
1692
0
1693
0
    GtkStyleContext* labelStyle =
1694
0
        GetStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL, direction);
1695
0
    moz_gtk_draw_styled_frame(labelStyle, cr, &rect, false);
1696
0
1697
0
    return MOZ_GTK_SUCCESS;
1698
0
}
1699
1700
static gint
1701
moz_gtk_resizer_paint(cairo_t *cr, GdkRectangle* rect,
1702
                      GtkWidgetState* state,
1703
                      GtkTextDirection direction)
1704
0
{
1705
0
    GtkStyleContext* style =
1706
0
        GetStyleContext(MOZ_GTK_RESIZER, GTK_TEXT_DIR_LTR,
1707
0
                        GetStateFlagsFromGtkWidgetState(state));
1708
0
1709
0
    // Workaround unico not respecting the text direction for resizers.
1710
0
    // See bug 1174248.
1711
0
    cairo_save(cr);
1712
0
    if (direction == GTK_TEXT_DIR_RTL) {
1713
0
      cairo_matrix_t mat;
1714
0
      cairo_matrix_init_translate(&mat, 2 * rect->x + rect->width, 0);
1715
0
      cairo_matrix_scale(&mat, -1, 1);
1716
0
      cairo_transform(cr, &mat);
1717
0
    }
1718
0
1719
0
    gtk_render_handle(style, cr, rect->x, rect->y, rect->width, rect->height);
1720
0
    cairo_restore(cr);
1721
0
1722
0
    return MOZ_GTK_SUCCESS;
1723
0
}
1724
1725
static gint
1726
moz_gtk_frame_paint(cairo_t *cr, GdkRectangle* rect,
1727
                    GtkTextDirection direction)
1728
0
{
1729
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_FRAME, direction);
1730
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1731
0
    return MOZ_GTK_SUCCESS;
1732
0
}
1733
1734
static gint
1735
moz_gtk_progressbar_paint(cairo_t *cr, GdkRectangle* rect,
1736
                          GtkTextDirection direction)
1737
0
{
1738
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_PROGRESS_TROUGH,
1739
0
                                             direction);
1740
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1741
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1742
0
1743
0
    return MOZ_GTK_SUCCESS;
1744
0
}
1745
1746
static gint
1747
moz_gtk_progress_chunk_paint(cairo_t *cr, GdkRectangle* rect,
1748
                             GtkTextDirection direction,
1749
                             WidgetNodeType widget)
1750
0
{
1751
0
    GtkStyleContext* style =
1752
0
        GetStyleContext(MOZ_GTK_PROGRESS_CHUNK, direction);
1753
0
1754
0
    if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE ||
1755
0
        widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) {
1756
0
      /**
1757
0
       * The bar's size and the bar speed are set depending of the progress'
1758
0
       * size. These could also be constant for all progress bars easily.
1759
0
       */
1760
0
      gboolean vertical = (widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE);
1761
0
1762
0
      /* The size of the dimension we are going to use for the animation. */
1763
0
      const gint progressSize = vertical ? rect->height : rect->width;
1764
0
1765
0
      /* The bar is using a fifth of the element size, based on GtkProgressBar
1766
0
       * activity-blocks property. */
1767
0
      const gint barSize = MAX(1, progressSize / 5);
1768
0
1769
0
      /* Represents the travel that has to be done for a complete cycle. */
1770
0
      const gint travel = 2 * (progressSize - barSize);
1771
0
1772
0
      /* period equals to travel / pixelsPerMillisecond
1773
0
       * where pixelsPerMillisecond equals progressSize / 1000.0.
1774
0
       * This is equivalent to 1600. */
1775
0
      static const guint period = 1600;
1776
0
      const gint t = PR_IntervalToMilliseconds(PR_IntervalNow()) % period;
1777
0
      const gint dx = travel * t / period;
1778
0
1779
0
      if (vertical) {
1780
0
        rect->y += (dx < travel / 2) ? dx : travel - dx;
1781
0
        rect->height = barSize;
1782
0
      } else {
1783
0
        rect->x += (dx < travel / 2) ? dx : travel - dx;
1784
0
        rect->width = barSize;
1785
0
      }
1786
0
    }
1787
0
1788
0
    // gtk_render_activity was used to render progress chunks on GTK versions
1789
0
    // before 3.13.7, see bug 1173907.
1790
0
    if (!gtk_check_version(3, 13, 7)) {
1791
0
      gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
1792
0
      gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
1793
0
    } else {
1794
0
      gtk_render_activity(style, cr, rect->x, rect->y, rect->width, rect->height);
1795
0
    }
1796
0
1797
0
    return MOZ_GTK_SUCCESS;
1798
0
}
1799
1800
static gint
1801
moz_gtk_get_tab_thickness(GtkStyleContext *style)
1802
0
{
1803
0
    if (!notebook_has_tab_gap)
1804
0
      return 0; /* tabs do not overdraw the tabpanel border with "no gap" style */
1805
0
1806
0
    GtkBorder border;
1807
0
    gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border);
1808
0
    if (border.top < 2)
1809
0
        return 2; /* some themes don't set ythickness correctly */
1810
0
1811
0
    return border.top;
1812
0
}
1813
1814
gint
1815
moz_gtk_get_tab_thickness(WidgetNodeType aNodeType)
1816
0
{
1817
0
    GtkStyleContext *style = GetStyleContext(aNodeType);
1818
0
    int thickness = moz_gtk_get_tab_thickness(style);
1819
0
    return thickness;
1820
0
}
1821
1822
/* actual small tabs */
1823
static gint
1824
moz_gtk_tab_paint(cairo_t *cr, GdkRectangle* rect,
1825
                  GtkWidgetState* state,
1826
                  GtkTabFlags flags, GtkTextDirection direction,
1827
                  WidgetNodeType widget)
1828
0
{
1829
0
    /* When the tab isn't selected, we just draw a notebook extension.
1830
0
     * When it is selected, we overwrite the adjacent border of the tabpanel
1831
0
     * touching the tab with a pierced border (called "the gap") to make the
1832
0
     * tab appear physically attached to the tabpanel; see details below. */
1833
0
1834
0
    GtkStyleContext* style;
1835
0
    GdkRectangle tabRect;
1836
0
    GdkRectangle focusRect;
1837
0
    GdkRectangle backRect;
1838
0
    int initial_gap = 0;
1839
0
    bool isBottomTab = (widget == MOZ_GTK_TAB_BOTTOM);
1840
0
1841
0
    style = GetStyleContext(widget, direction,
1842
0
                            GetStateFlagsFromGtkTabFlags(flags));
1843
0
    tabRect = *rect;
1844
0
1845
0
    if (flags & MOZ_GTK_TAB_FIRST) {
1846
0
        gtk_style_context_get_style(style, "initial-gap", &initial_gap, NULL);
1847
0
        tabRect.width -= initial_gap;
1848
0
1849
0
        if (direction != GTK_TEXT_DIR_RTL) {
1850
0
            tabRect.x += initial_gap;
1851
0
        }
1852
0
    }
1853
0
1854
0
    focusRect = backRect = tabRect;
1855
0
1856
0
    if (notebook_has_tab_gap) {
1857
0
        if ((flags & MOZ_GTK_TAB_SELECTED) == 0) {
1858
0
            /* Only draw the tab */
1859
0
            gtk_render_extension(style, cr,
1860
0
                                 tabRect.x, tabRect.y, tabRect.width, tabRect.height,
1861
0
                                 isBottomTab ? GTK_POS_TOP : GTK_POS_BOTTOM );
1862
0
        } else {
1863
0
            /* Draw the tab and the gap
1864
0
             * We want the gap to be positioned exactly on the tabpanel top
1865
0
             * border; since tabbox.css may set a negative margin so that the tab
1866
0
             * frame rect already overlaps the tabpanel frame rect, we need to take
1867
0
             * that into account when drawing. To that effect, nsNativeThemeGTK
1868
0
             * passes us this negative margin (bmargin in the graphic below) in the
1869
0
             * lowest bits of |flags|.  We use it to set gap_voffset, the distance
1870
0
             * between the top of the gap and the bottom of the tab (resp. the
1871
0
             * bottom of the gap and the top of the tab when we draw a bottom tab),
1872
0
             * while ensuring that the gap always touches the border of the tab,
1873
0
             * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results
1874
0
             * with big negative or positive margins.
1875
0
             * Here is a graphical explanation in the case of top tabs:
1876
0
             *             ___________________________
1877
0
             *            /                           \
1878
0
             *           |            T A B            |
1879
0
             * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel
1880
0
             *           :    ^       bmargin          :  ^
1881
0
             *           :    | (-negative margin,     :  |
1882
0
             *  bottom   :    v  passed in flags)      :  |       gap_height
1883
0
             *    of  -> :.............................:  |    (the size of the
1884
0
             * the tab   .       part of the gap       .  |  tabpanel top border)
1885
0
             *           .      outside of the tab     .  v
1886
0
             * ----------------------------------------------
1887
0
             *
1888
0
             * To draw the gap, we use gtk_render_frame_gap(), see comment in
1889
0
             * moz_gtk_tabpanels_paint(). This gap is made 3 * gap_height tall,
1890
0
             * which should suffice to ensure that the only visible border is the
1891
0
             * pierced one.  If the tab is in the middle, we make the box_gap begin
1892
0
             * a bit to the left of the tab and end a bit to the right, adjusting
1893
0
             * the gap position so it still is under the tab, because we want the
1894
0
             * rendering of a gap in the middle of a tabpanel.  This is the role of
1895
0
             * the gints gap_{l,r}_offset. On the contrary, if the tab is the
1896
0
             * first, we align the start border of the box_gap with the start
1897
0
             * border of the tab (left if LTR, right if RTL), by setting the
1898
0
             * appropriate offset to 0.*/
1899
0
            gint gap_loffset, gap_roffset, gap_voffset, gap_height;
1900
0
1901
0
            /* Get height needed by the gap */
1902
0
            gap_height = moz_gtk_get_tab_thickness(style);
1903
0
1904
0
            /* Extract gap_voffset from the first bits of flags */
1905
0
            gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK;
1906
0
            if (gap_voffset > gap_height)
1907
0
                gap_voffset = gap_height;
1908
0
1909
0
            /* Set gap_{l,r}_offset to appropriate values */
1910
0
            gap_loffset = gap_roffset = 20; /* should be enough */
1911
0
            if (flags & MOZ_GTK_TAB_FIRST) {
1912
0
                if (direction == GTK_TEXT_DIR_RTL)
1913
0
                    gap_roffset = initial_gap;
1914
0
                else
1915
0
                    gap_loffset = initial_gap;
1916
0
            }
1917
0
1918
0
            GtkStyleContext* panelStyle =
1919
0
                GetStyleContext(MOZ_GTK_TABPANELS, direction);
1920
0
1921
0
            if (isBottomTab) {
1922
0
                /* Draw the tab on bottom */
1923
0
                focusRect.y += gap_voffset;
1924
0
                focusRect.height -= gap_voffset;
1925
0
1926
0
                gtk_render_extension(style, cr,
1927
0
                                     tabRect.x, tabRect.y + gap_voffset, tabRect.width,
1928
0
                                     tabRect.height - gap_voffset, GTK_POS_TOP);
1929
0
1930
0
                backRect.y += (gap_voffset - gap_height);
1931
0
                backRect.height = gap_height;
1932
0
1933
0
                /* Draw the gap; erase with background color before painting in
1934
0
                 * case theme does not */
1935
0
                gtk_render_background(panelStyle, cr, backRect.x, backRect.y,
1936
0
                                     backRect.width, backRect.height);
1937
0
                cairo_save(cr);
1938
0
                cairo_rectangle(cr, backRect.x, backRect.y, backRect.width, backRect.height);
1939
0
                cairo_clip(cr);
1940
0
1941
0
                gtk_render_frame_gap(panelStyle, cr,
1942
0
                                     tabRect.x - gap_loffset,
1943
0
                                     tabRect.y + gap_voffset - 3 * gap_height,
1944
0
                                     tabRect.width + gap_loffset + gap_roffset,
1945
0
                                     3 * gap_height, GTK_POS_BOTTOM,
1946
0
                                     gap_loffset, gap_loffset + tabRect.width);
1947
0
                cairo_restore(cr);
1948
0
            } else {
1949
0
                /* Draw the tab on top */
1950
0
                focusRect.height -= gap_voffset;
1951
0
                gtk_render_extension(style, cr,
1952
0
                                     tabRect.x, tabRect.y, tabRect.width,
1953
0
                                     tabRect.height - gap_voffset, GTK_POS_BOTTOM);
1954
0
1955
0
                backRect.y += (tabRect.height - gap_voffset);
1956
0
                backRect.height = gap_height;
1957
0
1958
0
                /* Draw the gap; erase with background color before painting in
1959
0
                 * case theme does not */
1960
0
                gtk_render_background(panelStyle, cr, backRect.x, backRect.y,
1961
0
                                      backRect.width, backRect.height);
1962
0
1963
0
                cairo_save(cr);
1964
0
                cairo_rectangle(cr, backRect.x, backRect.y, backRect.width, backRect.height);
1965
0
                cairo_clip(cr);
1966
0
1967
0
                gtk_render_frame_gap(panelStyle, cr,
1968
0
                                     tabRect.x - gap_loffset,
1969
0
                                     tabRect.y + tabRect.height - gap_voffset,
1970
0
                                     tabRect.width + gap_loffset + gap_roffset,
1971
0
                                     3 * gap_height, GTK_POS_TOP,
1972
0
                                     gap_loffset, gap_loffset + tabRect.width);
1973
0
                cairo_restore(cr);
1974
0
            }
1975
0
        }
1976
0
    } else {
1977
0
        gtk_render_background(style, cr, tabRect.x, tabRect.y, tabRect.width, tabRect.height);
1978
0
        gtk_render_frame(style, cr, tabRect.x, tabRect.y, tabRect.width, tabRect.height);
1979
0
    }
1980
0
1981
0
    if (state->focused) {
1982
0
      /* Paint the focus ring */
1983
0
      GtkBorder padding;
1984
0
      gtk_style_context_get_padding(style, GetStateFlagsFromGtkWidgetState(state), &padding);
1985
0
1986
0
      focusRect.x += padding.left;
1987
0
      focusRect.width -= (padding.left + padding.right);
1988
0
      focusRect.y += padding.top;
1989
0
      focusRect.height -= (padding.top + padding.bottom);
1990
0
1991
0
      gtk_render_focus(style, cr,
1992
0
                      focusRect.x, focusRect.y, focusRect.width, focusRect.height);
1993
0
    }
1994
0
1995
0
    return MOZ_GTK_SUCCESS;
1996
0
}
1997
1998
/* tab area*/
1999
static gint
2000
moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect,
2001
                        GtkTextDirection direction)
2002
0
{
2003
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_TABPANELS, direction);
2004
0
    gtk_render_background(style, cr, rect->x, rect->y,
2005
0
                          rect->width, rect->height);
2006
0
    /*
2007
0
     * The gap size is not needed in moz_gtk_tabpanels_paint because
2008
0
     * the gap will be painted with the foreground tab in moz_gtk_tab_paint.
2009
0
     *
2010
0
     * However, if moz_gtk_tabpanels_paint just uses gtk_render_frame(),
2011
0
     * the theme will think that there are no tabs and may draw something
2012
0
     * different.Hence the trick of using two clip regions, and drawing the
2013
0
     * gap outside each clip region, to get the correct frame for
2014
0
     * a tabpanel with tabs.
2015
0
     */
2016
0
    /* left side */
2017
0
    cairo_save(cr);
2018
0
    cairo_rectangle(cr, rect->x, rect->y,
2019
0
                    rect->x + rect->width / 2,
2020
0
                    rect->y + rect->height);
2021
0
    cairo_clip(cr);
2022
0
    gtk_render_frame_gap(style, cr,
2023
0
                         rect->x, rect->y,
2024
0
                         rect->width, rect->height,
2025
0
                         GTK_POS_TOP, rect->width - 1, rect->width);
2026
0
    cairo_restore(cr);
2027
0
2028
0
    /* right side */
2029
0
    cairo_save(cr);
2030
0
    cairo_rectangle(cr, rect->x + rect->width / 2, rect->y,
2031
0
                    rect->x + rect->width,
2032
0
                    rect->y + rect->height);
2033
0
    cairo_clip(cr);
2034
0
    gtk_render_frame_gap(style, cr,
2035
0
                         rect->x, rect->y,
2036
0
                         rect->width, rect->height,
2037
0
                         GTK_POS_TOP, 0, 1);
2038
0
    cairo_restore(cr);
2039
0
2040
0
    return MOZ_GTK_SUCCESS;
2041
0
}
2042
2043
static gint
2044
moz_gtk_tab_scroll_arrow_paint(cairo_t *cr, GdkRectangle* rect,
2045
                               GtkWidgetState* state,
2046
                               GtkArrowType arrow_type,
2047
                               GtkTextDirection direction)
2048
0
{
2049
0
    GtkStyleContext* style;
2050
0
    gdouble arrow_angle;
2051
0
    gint arrow_size = MIN(rect->width, rect->height);
2052
0
    gint x = rect->x + (rect->width - arrow_size) / 2;
2053
0
    gint y = rect->y + (rect->height - arrow_size) / 2;
2054
0
2055
0
    if (direction == GTK_TEXT_DIR_RTL) {
2056
0
        arrow_type = (arrow_type == GTK_ARROW_LEFT) ?
2057
0
                         GTK_ARROW_RIGHT : GTK_ARROW_LEFT;
2058
0
    }
2059
0
    switch (arrow_type) {
2060
0
    case GTK_ARROW_LEFT:
2061
0
        arrow_angle = ARROW_LEFT;
2062
0
        break;
2063
0
    case GTK_ARROW_RIGHT:
2064
0
        arrow_angle = ARROW_RIGHT;
2065
0
        break;
2066
0
    case GTK_ARROW_DOWN:
2067
0
        arrow_angle = ARROW_DOWN;
2068
0
        break;
2069
0
    default:
2070
0
        arrow_angle = ARROW_UP;
2071
0
        break;
2072
0
    }
2073
0
    if (arrow_type != GTK_ARROW_NONE)  {
2074
0
        style = GetStyleContext(MOZ_GTK_TAB_SCROLLARROW, direction,
2075
0
                                GetStateFlagsFromGtkWidgetState(state));
2076
0
        gtk_render_arrow(style, cr, arrow_angle,
2077
0
                         x, y, arrow_size);
2078
0
    }
2079
0
    return MOZ_GTK_SUCCESS;
2080
0
}
2081
2082
static gint
2083
moz_gtk_menu_bar_paint(cairo_t *cr, GdkRectangle* rect,
2084
                       GtkTextDirection direction)
2085
0
{
2086
0
    GtkStyleContext* style;
2087
0
2088
0
    GtkWidget* widget = GetWidget(MOZ_GTK_MENUBAR);
2089
0
    gtk_widget_set_direction(widget, direction);
2090
0
2091
0
    style = gtk_widget_get_style_context(widget);
2092
0
    gtk_style_context_save(style);
2093
0
    gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR);
2094
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
2095
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
2096
0
    gtk_style_context_restore(style);
2097
0
2098
0
    return MOZ_GTK_SUCCESS;
2099
0
}
2100
2101
static gint
2102
moz_gtk_menu_popup_paint(cairo_t *cr, GdkRectangle* rect,
2103
                         GtkTextDirection direction)
2104
0
{
2105
0
    GtkStyleContext* style;
2106
0
2107
0
    GtkWidget* widget = GetWidget(MOZ_GTK_MENUPOPUP);
2108
0
    gtk_widget_set_direction(widget, direction);
2109
0
2110
0
    // Draw a backing toplevel. This fixes themes that don't provide a menu
2111
0
    // background, and depend on the GtkMenu's implementation window to provide it.
2112
0
    moz_gtk_window_paint(cr, rect, direction);
2113
0
2114
0
    style = gtk_widget_get_style_context(widget);
2115
0
    gtk_style_context_save(style);
2116
0
    gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENU);
2117
0
2118
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
2119
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
2120
0
    gtk_style_context_restore(style);
2121
0
2122
0
    return MOZ_GTK_SUCCESS;
2123
0
}
2124
2125
// See gtk_menu_item_draw() for reference.
2126
static gint
2127
moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect,
2128
                             GtkTextDirection direction)
2129
0
{
2130
0
    GtkWidgetState defaultState = { 0 };
2131
0
    moz_gtk_menu_item_paint(MOZ_GTK_MENUSEPARATOR, cr, rect,
2132
0
                            &defaultState, direction);
2133
0
2134
0
    if (gtk_get_minor_version() >= 20)
2135
0
        return MOZ_GTK_SUCCESS;
2136
0
2137
0
    GtkStyleContext* style;
2138
0
    gboolean wide_separators;
2139
0
    gint separator_height;
2140
0
    gint x, y, w;
2141
0
    GtkBorder padding;
2142
0
2143
0
    style = GetStyleContext(MOZ_GTK_MENUSEPARATOR, direction);
2144
0
    gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding);
2145
0
2146
0
    x = rect->x;
2147
0
    y = rect->y;
2148
0
    w = rect->width;
2149
0
2150
0
    gtk_style_context_save(style);
2151
0
    gtk_style_context_add_class(style, GTK_STYLE_CLASS_SEPARATOR);
2152
0
2153
0
    gtk_style_context_get_style(style,
2154
0
                                "wide-separators",    &wide_separators,
2155
0
                                "separator-height",   &separator_height,
2156
0
                                NULL);
2157
0
2158
0
    if (wide_separators) {
2159
0
      gtk_render_frame(style, cr,
2160
0
                       x + padding.left,
2161
0
                       y + padding.top,
2162
0
                       w - padding.left - padding.right,
2163
0
                       separator_height);
2164
0
    } else {
2165
0
      gtk_render_line(style, cr,
2166
0
                      x + padding.left,
2167
0
                      y + padding.top,
2168
0
                      x + w - padding.right - 1,
2169
0
                      y + padding.top);
2170
0
    }
2171
0
2172
0
    gtk_style_context_restore(style);
2173
0
2174
0
    return MOZ_GTK_SUCCESS;
2175
0
}
2176
2177
// See gtk_menu_item_draw() for reference.
2178
static gint
2179
moz_gtk_menu_item_paint(WidgetNodeType widget, cairo_t *cr, GdkRectangle* rect,
2180
                        GtkWidgetState* state, GtkTextDirection direction)
2181
0
{
2182
0
    gint x, y, w, h;
2183
0
    guint minorVersion = gtk_get_minor_version();
2184
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
2185
0
2186
0
    // GTK versions prior to 3.8 render the background and frame only when not
2187
0
    // a separator and in hover prelight.
2188
0
    if (minorVersion < 8 && (widget == MOZ_GTK_MENUSEPARATOR ||
2189
0
                             !(state_flags & GTK_STATE_FLAG_PRELIGHT)))
2190
0
        return MOZ_GTK_SUCCESS;
2191
0
2192
0
    GtkStyleContext* style = GetStyleContext(widget, direction, state_flags);
2193
0
2194
0
    if (minorVersion < 6) {
2195
0
        // GTK+ 3.4 saves the style context and adds the menubar class to
2196
0
        // menubar children, but does each of these only when drawing, not
2197
0
        // during layout.
2198
0
        gtk_style_context_save(style);
2199
0
        if (widget == MOZ_GTK_MENUBARITEM) {
2200
0
            gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR);
2201
0
        }
2202
0
    }
2203
0
2204
0
    x = rect->x;
2205
0
    y = rect->y;
2206
0
    w = rect->width;
2207
0
    h = rect->height;
2208
0
2209
0
    gtk_render_background(style, cr, x, y, w, h);
2210
0
    gtk_render_frame(style, cr, x, y, w, h);
2211
0
2212
0
    if (minorVersion < 6) {
2213
0
        gtk_style_context_restore(style);
2214
0
    }
2215
0
2216
0
    return MOZ_GTK_SUCCESS;
2217
0
}
2218
2219
static gint
2220
moz_gtk_menu_arrow_paint(cairo_t *cr, GdkRectangle* rect,
2221
                         GtkWidgetState* state,
2222
                         GtkTextDirection direction)
2223
0
{
2224
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
2225
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_MENUITEM,
2226
0
                                             direction, state_flags);
2227
0
    gtk_render_arrow(style, cr,
2228
0
                    (direction == GTK_TEXT_DIR_LTR) ? ARROW_RIGHT : ARROW_LEFT,
2229
0
                    rect->x, rect->y, rect->width);
2230
0
    return MOZ_GTK_SUCCESS;
2231
0
}
2232
2233
// For reference, see gtk_check_menu_item_size_allocate() in GTK versions after
2234
// 3.20 and gtk_real_check_menu_item_draw_indicator() in earlier versions.
2235
static gint
2236
moz_gtk_check_menu_item_paint(WidgetNodeType widgetType,
2237
                              cairo_t *cr, GdkRectangle* rect,
2238
                              GtkWidgetState* state,
2239
                              gboolean checked, GtkTextDirection direction)
2240
0
{
2241
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
2242
0
    GtkStyleContext* style;
2243
0
    gint indicator_size, horizontal_padding;
2244
0
    gint x, y;
2245
0
2246
0
    moz_gtk_menu_item_paint(MOZ_GTK_MENUITEM, cr, rect, state, direction);
2247
0
2248
0
    if (checked) {
2249
0
      state_flags = static_cast<GtkStateFlags>(state_flags|checkbox_check_state);
2250
0
    }
2251
0
2252
0
    bool pre_3_20 = gtk_get_minor_version() < 20;
2253
0
    gint offset;
2254
0
    style = GetStyleContext(widgetType, direction);
2255
0
    gtk_style_context_get_style(style,
2256
0
                                "indicator-size", &indicator_size,
2257
0
                                "horizontal-padding", &horizontal_padding,
2258
0
                                NULL);
2259
0
    if (pre_3_20) {
2260
0
        GtkBorder padding;
2261
0
        gtk_style_context_get_padding(style, state_flags, &padding);
2262
0
        offset = horizontal_padding + padding.left + 2;
2263
0
    } else {
2264
0
        GdkRectangle r = { 0 };
2265
0
        InsetByMargin(&r, style);
2266
0
        InsetByBorderPadding(&r, style);
2267
0
        offset = r.x;
2268
0
    }
2269
0
2270
0
    bool isRadio = (widgetType == MOZ_GTK_RADIOMENUITEM);
2271
0
    WidgetNodeType indicatorType = isRadio ? MOZ_GTK_RADIOMENUITEM_INDICATOR
2272
0
                                           : MOZ_GTK_CHECKMENUITEM_INDICATOR;
2273
0
    style = GetStyleContext(indicatorType, direction, state_flags);
2274
0
2275
0
    if (direction == GTK_TEXT_DIR_RTL) {
2276
0
        x = rect->width - indicator_size - offset;
2277
0
    }
2278
0
    else {
2279
0
        x = rect->x + offset;
2280
0
    }
2281
0
    y = rect->y + (rect->height - indicator_size) / 2;
2282
0
2283
0
    if (!pre_3_20) {
2284
0
        gtk_render_background(style, cr, x, y, indicator_size, indicator_size);
2285
0
        gtk_render_frame(style, cr, x, y, indicator_size, indicator_size);
2286
0
    }
2287
0
2288
0
    if (isRadio) {
2289
0
      gtk_render_option(style, cr, x, y, indicator_size, indicator_size);
2290
0
    } else {
2291
0
      gtk_render_check(style, cr, x, y, indicator_size, indicator_size);
2292
0
    }
2293
0
2294
0
    return MOZ_GTK_SUCCESS;
2295
0
}
2296
2297
static gint
2298
moz_gtk_info_bar_paint(cairo_t *cr, GdkRectangle* rect,
2299
                       GtkWidgetState* state)
2300
0
{
2301
0
    GtkStyleContext *style =
2302
0
        GetStyleContext(MOZ_GTK_INFO_BAR, GTK_TEXT_DIR_LTR,
2303
0
                        GetStateFlagsFromGtkWidgetState(state));
2304
0
    gtk_render_background(style, cr, rect->x, rect->y, rect->width,
2305
0
                          rect->height);
2306
0
    gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
2307
0
2308
0
    return MOZ_GTK_SUCCESS;
2309
0
}
2310
2311
static gint
2312
moz_gtk_header_bar_paint(WidgetNodeType widgetType,
2313
                         cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state)
2314
0
{
2315
0
    GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
2316
0
    GtkStyleContext *style =
2317
0
        GetStyleContext(widgetType, GTK_TEXT_DIR_NONE, state_flags);
2318
0
2319
0
    InsetByMargin(rect, style);
2320
0
2321
0
    // Some themes (Adwaita for instance) draws bold dark line at
2322
0
    // titlebar bottom. It does not fit well with Firefox tabs so
2323
0
    // draw with some extent to make the titlebar bottom part invisible.
2324
0
    #define TITLEBAR_EXTENT 4
2325
0
2326
0
    // We don't need to draw window decoration for MOZ_GTK_HEADER_BAR_MAXIMIZED,
2327
0
    // i.e. when main window is maximized.
2328
0
    if (widgetType == MOZ_GTK_HEADER_BAR) {
2329
0
        GtkStyleContext* windowStyle = GetStyleContext(MOZ_GTK_WINDOW);
2330
0
        bool solidDecorations =
2331
0
            gtk_style_context_has_class(windowStyle, "solid-csd");
2332
0
        GtkStyleContext *decorationStyle =
2333
0
            GetStyleContext(solidDecorations ? MOZ_GTK_WINDOW_DECORATION_SOLID:
2334
0
                                               MOZ_GTK_WINDOW_DECORATION,
2335
0
                            GTK_TEXT_DIR_LTR,
2336
0
                            state_flags);
2337
0
2338
0
        gtk_render_background(decorationStyle, cr, rect->x, rect->y,
2339
0
                              rect->width, rect->height + TITLEBAR_EXTENT);
2340
0
        gtk_render_frame(decorationStyle, cr, rect->x, rect->y,
2341
0
                         rect->width, rect->height + TITLEBAR_EXTENT);
2342
0
    }
2343
0
2344
0
    gtk_render_background(style, cr, rect->x, rect->y,
2345
0
                          rect->width, rect->height + TITLEBAR_EXTENT);
2346
0
    gtk_render_frame(style, cr, rect->x, rect->y,
2347
0
                     rect->width, rect->height + TITLEBAR_EXTENT);
2348
0
2349
0
    return MOZ_GTK_SUCCESS;
2350
0
}
2351
2352
static GtkBorder
2353
GetMarginBorderPadding(GtkStyleContext* aStyle)
2354
0
{
2355
0
    gint left = 0, top = 0, right = 0, bottom = 0;
2356
0
    moz_gtk_add_margin_border_padding(aStyle, &left, &top, &right, &bottom);
2357
0
    // narrowing conversions to gint16:
2358
0
    GtkBorder result;
2359
0
    result.left = left;
2360
0
    result.right = right;
2361
0
    result.top = top;
2362
0
    result.bottom = bottom;
2363
0
    return result;
2364
0
}
2365
2366
gint
2367
moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
2368
                          gint* right, gint* bottom,
2369
                          // NOTE: callers depend on direction being used
2370
                          // only for MOZ_GTK_DROPDOWN widgets.
2371
                          GtkTextDirection direction)
2372
0
{
2373
0
    GtkWidget* w;
2374
0
    GtkStyleContext* style;
2375
0
    *left = *top = *right = *bottom = 0;
2376
0
2377
0
    switch (widget) {
2378
0
    case MOZ_GTK_BUTTON:
2379
0
    case MOZ_GTK_TOOLBAR_BUTTON:
2380
0
        {
2381
0
            style = GetStyleContext(MOZ_GTK_BUTTON);
2382
0
2383
0
            *left = *top = *right = *bottom =
2384
0
                gtk_container_get_border_width(GTK_CONTAINER(GetWidget(MOZ_GTK_BUTTON)));
2385
0
2386
0
            if (widget == MOZ_GTK_TOOLBAR_BUTTON) {
2387
0
                gtk_style_context_save(style);
2388
0
                gtk_style_context_add_class(style, "image-button");
2389
0
            }
2390
0
2391
0
            moz_gtk_add_style_padding(style, left, top, right, bottom);
2392
0
2393
0
            if (widget == MOZ_GTK_TOOLBAR_BUTTON)
2394
0
                gtk_style_context_restore(style);
2395
0
2396
0
            moz_gtk_add_style_border(style, left, top, right, bottom);
2397
0
2398
0
            return MOZ_GTK_SUCCESS;
2399
0
        }
2400
0
    case MOZ_GTK_ENTRY:
2401
0
        {
2402
0
            style = GetStyleContext(MOZ_GTK_ENTRY);
2403
0
2404
0
            // XXX: Subtract 1 pixel from the padding to account for the default
2405
0
            // padding in forms.css. See bug 1187385.
2406
0
            *left = *top = *right = *bottom = -1;
2407
0
            moz_gtk_add_border_padding(style, left, top, right, bottom);
2408
0
2409
0
            return MOZ_GTK_SUCCESS;
2410
0
        }
2411
0
    case MOZ_GTK_TEXT_VIEW:
2412
0
    case MOZ_GTK_TREEVIEW:
2413
0
        {
2414
0
            style = GetStyleContext(MOZ_GTK_SCROLLED_WINDOW);
2415
0
            moz_gtk_add_style_border(style, left, top, right, bottom);
2416
0
            return MOZ_GTK_SUCCESS;
2417
0
        }
2418
0
    case MOZ_GTK_TREE_HEADER_CELL:
2419
0
        {
2420
0
            /* A Tree Header in GTK is just a different styled button
2421
0
             * It must be placed in a TreeView for getting the correct style
2422
0
             * assigned.
2423
0
             * That is why the following code is the same as for MOZ_GTK_BUTTON.
2424
0
             * */
2425
0
            *left = *top = *right = *bottom =
2426
0
                gtk_container_get_border_width(GTK_CONTAINER(
2427
0
                                               GetWidget(MOZ_GTK_TREE_HEADER_CELL)));
2428
0
            style = GetStyleContext(MOZ_GTK_TREE_HEADER_CELL);
2429
0
            moz_gtk_add_border_padding(style, left, top, right, bottom);
2430
0
            return MOZ_GTK_SUCCESS;
2431
0
        }
2432
0
    case MOZ_GTK_TREE_HEADER_SORTARROW:
2433
0
        w = GetWidget(MOZ_GTK_TREE_HEADER_SORTARROW);
2434
0
        break;
2435
0
    case MOZ_GTK_DROPDOWN_ENTRY:
2436
0
        w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA);
2437
0
        break;
2438
0
    case MOZ_GTK_DROPDOWN_ARROW:
2439
0
        w = GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
2440
0
        break;
2441
0
    case MOZ_GTK_DROPDOWN:
2442
0
        {
2443
0
            /* We need to account for the arrow on the dropdown, so text
2444
0
             * doesn't come too close to the arrow, or in some cases spill
2445
0
             * into the arrow. */
2446
0
            gboolean wide_separators;
2447
0
            gint separator_width;
2448
0
            GtkRequisition arrow_req;
2449
0
            GtkBorder border;
2450
0
2451
0
            *left = *top = *right = *bottom =
2452
0
                gtk_container_get_border_width(GTK_CONTAINER(
2453
0
                                               GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
2454
0
            style = GetStyleContext(MOZ_GTK_COMBOBOX_BUTTON);
2455
0
            moz_gtk_add_border_padding(style, left, top, right, bottom);
2456
0
2457
0
            /* If there is no separator, don't try to count its width. */
2458
0
            separator_width = 0;
2459
0
            GtkWidget* comboBoxSeparator = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR);
2460
0
            if (comboBoxSeparator) {
2461
0
                style = gtk_widget_get_style_context(comboBoxSeparator);
2462
0
                gtk_style_context_get_style(style,
2463
0
                                            "wide-separators", &wide_separators,
2464
0
                                            "separator-width", &separator_width,
2465
0
                                            NULL);
2466
0
2467
0
                if (!wide_separators) {
2468
0
                    gtk_style_context_get_border(style, gtk_style_context_get_state(style),
2469
0
                                                 &border);
2470
0
                    separator_width = border.left;
2471
0
                }
2472
0
            }
2473
0
2474
0
            gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ARROW),
2475
0
                                          NULL, &arrow_req);
2476
0
2477
0
            if (direction == GTK_TEXT_DIR_RTL)
2478
0
                *left += separator_width + arrow_req.width;
2479
0
            else
2480
0
                *right += separator_width + arrow_req.width;
2481
0
2482
0
            return MOZ_GTK_SUCCESS;
2483
0
        }
2484
0
    case MOZ_GTK_TABPANELS:
2485
0
        w = GetWidget(MOZ_GTK_TABPANELS);
2486
0
        break;
2487
0
    case MOZ_GTK_PROGRESSBAR:
2488
0
        w = GetWidget(MOZ_GTK_PROGRESSBAR);
2489
0
        break;
2490
0
    case MOZ_GTK_SPINBUTTON_ENTRY:
2491
0
    case MOZ_GTK_SPINBUTTON_UP:
2492
0
    case MOZ_GTK_SPINBUTTON_DOWN:
2493
0
        w = GetWidget(MOZ_GTK_SPINBUTTON);
2494
0
        break;
2495
0
    case MOZ_GTK_SCALE_HORIZONTAL:
2496
0
    case MOZ_GTK_SCALE_VERTICAL:
2497
0
        w = GetWidget(widget);
2498
0
        break;
2499
0
    case MOZ_GTK_FRAME:
2500
0
        w = GetWidget(MOZ_GTK_FRAME);
2501
0
        break;
2502
0
    case MOZ_GTK_CHECKBUTTON_CONTAINER:
2503
0
    case MOZ_GTK_RADIOBUTTON_CONTAINER:
2504
0
        {
2505
0
            w = GetWidget(widget);
2506
0
            style = gtk_widget_get_style_context(w);
2507
0
2508
0
            *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w));
2509
0
            moz_gtk_add_border_padding(style,
2510
0
                                       left, top, right, bottom);
2511
0
            return MOZ_GTK_SUCCESS;
2512
0
        }
2513
0
    case MOZ_GTK_MENUPOPUP:
2514
0
        w = GetWidget(MOZ_GTK_MENUPOPUP);
2515
0
        break;
2516
0
    case MOZ_GTK_MENUBARITEM:
2517
0
    case MOZ_GTK_MENUITEM:
2518
0
    case MOZ_GTK_CHECKMENUITEM:
2519
0
    case MOZ_GTK_RADIOMENUITEM:
2520
0
        {
2521
0
            // Bug 1274143 for MOZ_GTK_MENUBARITEM
2522
0
            WidgetNodeType type =
2523
0
                widget == MOZ_GTK_MENUBARITEM ? MOZ_GTK_MENUITEM : widget;
2524
0
            style = GetStyleContext(type);
2525
0
2526
0
            if (gtk_get_minor_version() < 20) {
2527
0
                moz_gtk_add_style_padding(style, left, top, right, bottom);
2528
0
            } else {
2529
0
                moz_gtk_add_margin_border_padding(style,
2530
0
                                                  left, top, right, bottom);
2531
0
            }
2532
0
            return MOZ_GTK_SUCCESS;
2533
0
        }
2534
0
    case MOZ_GTK_INFO_BAR:
2535
0
        w = GetWidget(MOZ_GTK_INFO_BAR);
2536
0
        break;
2537
0
    case MOZ_GTK_TOOLTIP:
2538
0
        {
2539
0
            // In GTK 3 there are 6 pixels of additional margin around the box.
2540
0
            // See details there:
2541
0
            // https://github.com/GNOME/gtk/blob/5ea69a136bd7e4970b3a800390e20314665aaed2/gtk/ui/gtktooltipwindow.ui#L11
2542
0
            *left = *right = *top = *bottom = 6;
2543
0
2544
0
            // We also need to add margin/padding/borders from Tooltip content.
2545
0
            // Tooltip contains horizontal box, where icon and label is put.
2546
0
            // We ignore icon as long as we don't have support for it.
2547
0
            GtkStyleContext* boxStyle = GetStyleContext(MOZ_GTK_TOOLTIP_BOX);
2548
0
            moz_gtk_add_margin_border_padding(boxStyle,
2549
0
                                              left, top, right, bottom);
2550
0
2551
0
            GtkStyleContext* labelStyle = GetStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL);
2552
0
            moz_gtk_add_margin_border_padding(labelStyle,
2553
0
                                              left, top, right, bottom);
2554
0
2555
0
            return MOZ_GTK_SUCCESS;
2556
0
        }
2557
0
    case MOZ_GTK_HEADER_BAR:
2558
0
    case MOZ_GTK_HEADER_BAR_MAXIMIZED:
2559
0
        {
2560
0
            style = GetStyleContext(widget);
2561
0
            moz_gtk_add_border_padding(style, left, top, right, bottom);
2562
0
            *top = *bottom = 0;
2563
0
            return MOZ_GTK_SUCCESS;
2564
0
        }
2565
0
    /* These widgets have no borders, since they are not containers. */
2566
0
    case MOZ_GTK_CHECKBUTTON_LABEL:
2567
0
    case MOZ_GTK_RADIOBUTTON_LABEL:
2568
0
    case MOZ_GTK_SPLITTER_HORIZONTAL:
2569
0
    case MOZ_GTK_SPLITTER_VERTICAL:
2570
0
    case MOZ_GTK_CHECKBUTTON:
2571
0
    case MOZ_GTK_RADIOBUTTON:
2572
0
    case MOZ_GTK_SCROLLBAR_BUTTON:
2573
0
    case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
2574
0
    case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
2575
0
    case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
2576
0
    case MOZ_GTK_SCALE_THUMB_VERTICAL:
2577
0
    case MOZ_GTK_GRIPPER:
2578
0
    case MOZ_GTK_PROGRESS_CHUNK:
2579
0
    case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
2580
0
    case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
2581
0
    case MOZ_GTK_TREEVIEW_EXPANDER:
2582
0
    case MOZ_GTK_TOOLBAR_SEPARATOR:
2583
0
    case MOZ_GTK_MENUSEPARATOR:
2584
0
    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
2585
0
    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
2586
0
    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
2587
0
    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
2588
0
    /* These widgets have no borders.*/
2589
0
    case MOZ_GTK_INNER_SPIN_BUTTON:
2590
0
    case MOZ_GTK_SPINBUTTON:
2591
0
    case MOZ_GTK_WINDOW:
2592
0
    case MOZ_GTK_RESIZER:
2593
0
    case MOZ_GTK_MENUARROW:
2594
0
    case MOZ_GTK_TOOLBARBUTTON_ARROW:
2595
0
    case MOZ_GTK_TOOLBAR:
2596
0
    case MOZ_GTK_MENUBAR:
2597
0
    case MOZ_GTK_TAB_SCROLLARROW:
2598
0
        return MOZ_GTK_SUCCESS;
2599
0
    default:
2600
0
        g_warning("Unsupported widget type: %d", widget);
2601
0
        return MOZ_GTK_UNKNOWN_WIDGET;
2602
0
    }
2603
0
    /* TODO - we're still missing some widget implementations */
2604
0
    if (w) {
2605
0
      moz_gtk_add_style_border(gtk_widget_get_style_context(w),
2606
0
                               left, top, right, bottom);
2607
0
    }
2608
0
    return MOZ_GTK_SUCCESS;
2609
0
}
2610
2611
gint
2612
moz_gtk_get_tab_border(gint* left, gint* top, gint* right, gint* bottom,
2613
                       GtkTextDirection direction, GtkTabFlags flags,
2614
                       WidgetNodeType widget)
2615
0
{
2616
0
    GtkStyleContext* style = GetStyleContext(widget, direction,
2617
0
                              GetStateFlagsFromGtkTabFlags(flags));
2618
0
2619
0
    *left = *top = *right = *bottom = 0;
2620
0
    moz_gtk_add_style_padding(style, left, top, right, bottom);
2621
0
2622
0
    // Gtk >= 3.20 does not use those styles
2623
0
    if (gtk_check_version(3, 20, 0) != nullptr) {
2624
0
        int tab_curvature;
2625
0
2626
0
        gtk_style_context_get_style(style, "tab-curvature", &tab_curvature, NULL);
2627
0
        *left += tab_curvature;
2628
0
        *right += tab_curvature;
2629
0
2630
0
        if (flags & MOZ_GTK_TAB_FIRST) {
2631
0
            int initial_gap = 0;
2632
0
            gtk_style_context_get_style(style, "initial-gap", &initial_gap, NULL);
2633
0
            if (direction == GTK_TEXT_DIR_RTL)
2634
0
                *right += initial_gap;
2635
0
            else
2636
0
                *left += initial_gap;
2637
0
        }
2638
0
    } else {
2639
0
        GtkBorder margin;
2640
0
2641
0
        gtk_style_context_get_margin(style, gtk_style_context_get_state(style), &margin);
2642
0
        *left += margin.left;
2643
0
        *right += margin.right;
2644
0
2645
0
        if (flags & MOZ_GTK_TAB_FIRST) {
2646
0
            style = GetStyleContext(MOZ_GTK_NOTEBOOK_HEADER, direction);
2647
0
            gtk_style_context_get_margin(style, gtk_style_context_get_state(style), &margin);
2648
0
            *left += margin.left;
2649
0
            *right += margin.right;
2650
0
        }
2651
0
    }
2652
0
2653
0
    return MOZ_GTK_SUCCESS;
2654
0
}
2655
2656
gint
2657
moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height)
2658
0
{
2659
0
    /*
2660
0
     * We get the requisition of the drop down button, which includes
2661
0
     * all padding, border and focus line widths the button uses,
2662
0
     * as well as the minimum arrow size and its padding
2663
0
     * */
2664
0
    GtkRequisition requisition;
2665
0
2666
0
    gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON),
2667
0
                                  NULL, &requisition);
2668
0
    *width = requisition.width;
2669
0
    *height = requisition.height;
2670
0
2671
0
    return MOZ_GTK_SUCCESS;
2672
0
}
2673
2674
gint
2675
moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height)
2676
0
{
2677
0
    gint arrow_size;
2678
0
2679
0
    GtkStyleContext *style = GetStyleContext(MOZ_GTK_TABPANELS);
2680
0
    gtk_style_context_get_style(style,
2681
0
                                "scroll-arrow-hlength", &arrow_size,
2682
0
                                NULL);
2683
0
2684
0
    *height = *width = arrow_size;
2685
0
2686
0
    return MOZ_GTK_SUCCESS;
2687
0
}
2688
2689
void
2690
moz_gtk_get_arrow_size(WidgetNodeType widgetType, gint* width, gint* height)
2691
0
{
2692
0
    GtkWidget* widget;
2693
0
    switch (widgetType) {
2694
0
        case MOZ_GTK_DROPDOWN:
2695
0
            widget = GetWidget(MOZ_GTK_COMBOBOX_ARROW);
2696
0
            break;
2697
0
        default:
2698
0
            widget = GetWidget(MOZ_GTK_BUTTON_ARROW);
2699
0
            break;
2700
0
    }
2701
0
2702
0
    GtkRequisition requisition;
2703
0
    gtk_widget_get_preferred_size(widget, NULL, &requisition);
2704
0
    *width = requisition.width;
2705
0
    *height = requisition.height;
2706
0
}
2707
2708
gint
2709
moz_gtk_get_toolbar_separator_width(gint* size)
2710
0
{
2711
0
    gboolean wide_separators;
2712
0
    gint separator_width;
2713
0
    GtkBorder border;
2714
0
2715
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_TOOLBAR);
2716
0
    gtk_style_context_get_style(style,
2717
0
                                "space-size", size,
2718
0
                                "wide-separators",  &wide_separators,
2719
0
                                "separator-width", &separator_width,
2720
0
                                NULL);
2721
0
    /* Just in case... */
2722
0
    gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border);
2723
0
    *size = MAX(*size, (wide_separators ? separator_width : border.left));
2724
0
    return MOZ_GTK_SUCCESS;
2725
0
}
2726
2727
gint
2728
moz_gtk_get_expander_size(gint* size)
2729
0
{
2730
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_EXPANDER);
2731
0
    gtk_style_context_get_style(style,
2732
0
                                "expander-size", size,
2733
0
                                NULL);
2734
0
    return MOZ_GTK_SUCCESS;
2735
0
}
2736
2737
gint
2738
moz_gtk_get_treeview_expander_size(gint* size)
2739
0
{
2740
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_TREEVIEW);
2741
0
    gtk_style_context_get_style(style, "expander-size", size, NULL);
2742
0
    return MOZ_GTK_SUCCESS;
2743
0
}
2744
2745
// See gtk_menu_item_draw() for reference.
2746
gint
2747
moz_gtk_get_menu_separator_height(gint *size)
2748
0
{
2749
0
    gboolean  wide_separators;
2750
0
    gint      separator_height;
2751
0
    GtkBorder padding;
2752
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_MENUSEPARATOR);
2753
0
    gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding);
2754
0
2755
0
    gtk_style_context_save(style);
2756
0
    gtk_style_context_add_class(style, GTK_STYLE_CLASS_SEPARATOR);
2757
0
2758
0
    gtk_style_context_get_style(style,
2759
0
                                "wide-separators",  &wide_separators,
2760
0
                                "separator-height", &separator_height,
2761
0
                                NULL);
2762
0
2763
0
    gtk_style_context_restore(style);
2764
0
2765
0
    *size = padding.top + padding.bottom;
2766
0
    *size += (wide_separators) ? separator_height : 1;
2767
0
2768
0
    return MOZ_GTK_SUCCESS;
2769
0
}
2770
2771
void
2772
moz_gtk_get_entry_min_height(gint* height)
2773
0
{
2774
0
    GtkStyleContext* style = GetStyleContext(MOZ_GTK_ENTRY);
2775
0
    if (!gtk_check_version(3, 20, 0)) {
2776
0
        gtk_style_context_get(style, gtk_style_context_get_state(style),
2777
0
                              "min-height", height,
2778
0
                              nullptr);
2779
0
    } else {
2780
0
        *height = 0;
2781
0
    }
2782
0
2783
0
    GtkBorder border;
2784
0
    GtkBorder padding;
2785
0
    gtk_style_context_get_border(style, gtk_style_context_get_state(style), &border);
2786
0
    gtk_style_context_get_padding(style, gtk_style_context_get_state(style), &padding);
2787
0
2788
0
    *height += (border.top + border.bottom + padding.top + padding.bottom);
2789
0
}
2790
2791
void
2792
moz_gtk_get_scale_metrics(GtkOrientation orient, gint* scale_width,
2793
                          gint* scale_height)
2794
0
{
2795
0
  if (gtk_check_version(3, 20, 0) != nullptr) {
2796
0
      WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ?
2797
0
                               MOZ_GTK_SCALE_HORIZONTAL :
2798
0
                               MOZ_GTK_SCALE_VERTICAL;
2799
0
2800
0
      gint thumb_length, thumb_height, trough_border;
2801
0
      moz_gtk_get_scalethumb_metrics(orient, &thumb_length, &thumb_height);
2802
0
2803
0
      GtkStyleContext* style = GetStyleContext(widget);
2804
0
      gtk_style_context_get_style(style, "trough-border", &trough_border, NULL);
2805
0
2806
0
      if (orient == GTK_ORIENTATION_HORIZONTAL) {
2807
0
          *scale_width = thumb_length + trough_border * 2;
2808
0
          *scale_height = thumb_height + trough_border * 2;
2809
0
      } else {
2810
0
          *scale_width = thumb_height + trough_border * 2;
2811
0
          *scale_height = thumb_length + trough_border * 2;
2812
0
      }
2813
0
  } else {
2814
0
      WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ?
2815
0
                               MOZ_GTK_SCALE_TROUGH_HORIZONTAL :
2816
0
                               MOZ_GTK_SCALE_TROUGH_VERTICAL;
2817
0
      moz_gtk_get_widget_min_size(GetStyleContext(widget),
2818
0
                                  scale_width, scale_height);
2819
0
  }
2820
0
}
2821
2822
gint
2823
moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height)
2824
0
{
2825
0
2826
0
  if (gtk_check_version(3, 20, 0) != nullptr) {
2827
0
      WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ?
2828
0
                               MOZ_GTK_SCALE_HORIZONTAL:
2829
0
                               MOZ_GTK_SCALE_VERTICAL;
2830
0
      GtkStyleContext* style = GetStyleContext(widget);
2831
0
      gtk_style_context_get_style(style,
2832
0
                                  "slider_length", thumb_length,
2833
0
                                  "slider_width", thumb_height,
2834
0
                                  NULL);
2835
0
  } else {
2836
0
      WidgetNodeType widget = (orient == GTK_ORIENTATION_HORIZONTAL) ?
2837
0
                               MOZ_GTK_SCALE_THUMB_HORIZONTAL:
2838
0
                               MOZ_GTK_SCALE_THUMB_VERTICAL;
2839
0
      GtkStyleContext* style = GetStyleContext(widget);
2840
0
2841
0
      gint min_width, min_height;
2842
0
      GtkStateFlags state = gtk_style_context_get_state(style);
2843
0
      gtk_style_context_get(style, state,
2844
0
                            "min-width", &min_width,
2845
0
                            "min-height", &min_height,
2846
0
                             nullptr);
2847
0
      GtkBorder margin;
2848
0
      gtk_style_context_get_margin(style, state, &margin);
2849
0
      gint margin_width = margin.left + margin.right;
2850
0
      gint margin_height = margin.top + margin.bottom;
2851
0
2852
0
      // Negative margin of slider element also determines its minimal size
2853
0
      // so use bigger of those two values.
2854
0
      if (min_width < -margin_width)
2855
0
          min_width = -margin_width;
2856
0
      if (min_height < -margin_height)
2857
0
          min_height = -margin_height;
2858
0
2859
0
      *thumb_length = min_width;
2860
0
      *thumb_height = min_height;
2861
0
  }
2862
0
2863
0
  return MOZ_GTK_SUCCESS;
2864
0
}
2865
2866
static MozGtkSize
2867
SizeFromLengthAndBreadth(GtkOrientation aOrientation,
2868
                         gint aLength, gint aBreadth)
2869
0
{
2870
0
    return aOrientation == GTK_ORIENTATION_HORIZONTAL ?
2871
0
        MozGtkSize({aLength, aBreadth}) : MozGtkSize({aBreadth, aLength});
2872
0
}
2873
2874
const ToggleGTKMetrics*
2875
GetToggleMetrics(bool isRadio)
2876
0
{
2877
0
    ToggleGTKMetrics* metrics;
2878
0
    if (isRadio) {
2879
0
        metrics = &sRadioMetrics;
2880
0
    } else {
2881
0
        metrics = &sCheckboxMetrics;
2882
0
    }
2883
0
    if (metrics->initialized)
2884
0
        return metrics;
2885
0
2886
0
    metrics->initialized = true;
2887
0
    if (gtk_check_version(3,20,0) == nullptr) {
2888
0
        GtkStyleContext* style;
2889
0
        if (isRadio) {
2890
0
            style = GetStyleContext(MOZ_GTK_RADIOBUTTON);
2891
0
        } else {
2892
0
            style = GetStyleContext(MOZ_GTK_CHECKBUTTON);
2893
0
        }
2894
0
        GtkStateFlags state_flags = gtk_style_context_get_state(style);
2895
0
        gtk_style_context_get(style, state_flags,
2896
0
                              "min-height",&(metrics->minSizeWithBorder.height),
2897
0
                              "min-width", &(metrics->minSizeWithBorder.width),
2898
0
                              nullptr);
2899
0
        // Fallback to indicator size if min dimensions are zero
2900
0
        if (metrics->minSizeWithBorder.height == 0 ||
2901
0
            metrics->minSizeWithBorder.width == 0) {
2902
0
            gint indicator_size;
2903
0
            gtk_widget_style_get(GetWidget(MOZ_GTK_CHECKBUTTON_CONTAINER),
2904
0
                                 "indicator_size", &indicator_size, nullptr);
2905
0
            if (metrics->minSizeWithBorder.height == 0) {
2906
0
                metrics->minSizeWithBorder.height = indicator_size;
2907
0
            }
2908
0
            if (metrics->minSizeWithBorder.width == 0) {
2909
0
                metrics->minSizeWithBorder.width = indicator_size;
2910
0
            }
2911
0
        }
2912
0
2913
0
        GtkBorder border, padding;
2914
0
        gtk_style_context_get_border(style, state_flags, &border);
2915
0
        gtk_style_context_get_padding(style, state_flags, &padding);
2916
0
        metrics->borderAndPadding.left = border.left + padding.left;
2917
0
        metrics->borderAndPadding.right = border.right + padding.right;
2918
0
        metrics->borderAndPadding.top = border.top + padding.top;
2919
0
        metrics->borderAndPadding.bottom = border.bottom + padding.bottom;
2920
0
        metrics->minSizeWithBorder.width += metrics->borderAndPadding.left +
2921
0
                                            metrics->borderAndPadding.right;
2922
0
        metrics->minSizeWithBorder.height += metrics->borderAndPadding.top +
2923
0
                                             metrics->borderAndPadding.bottom;
2924
0
    } else {
2925
0
        gint indicator_size, indicator_spacing;
2926
0
        gtk_widget_style_get(GetWidget(MOZ_GTK_CHECKBUTTON_CONTAINER),
2927
0
                             "indicator_size", &indicator_size,
2928
0
                             "indicator_spacing", &indicator_spacing,
2929
0
                             nullptr);
2930
0
        metrics->minSizeWithBorder.width =
2931
0
            metrics->minSizeWithBorder.height = indicator_size;
2932
0
    }
2933
0
    return metrics;
2934
0
}
2935
2936
static void
2937
InitScrollbarMetrics(ScrollbarGTKMetrics* aMetrics,
2938
                     GtkOrientation aOrientation,
2939
                     GtkStateFlags aStateFlags)
2940
0
{
2941
0
    WidgetNodeType scrollbar = aOrientation == GTK_ORIENTATION_HORIZONTAL ?
2942
0
        MOZ_GTK_SCROLLBAR_HORIZONTAL : MOZ_GTK_SCROLLBAR_VERTICAL;
2943
0
2944
0
    gboolean backward, forward, secondary_backward, secondary_forward;
2945
0
    GtkStyleContext* style = GetStyleContext(scrollbar, GTK_TEXT_DIR_NONE,
2946
0
                                             aStateFlags);
2947
0
    gtk_style_context_get_style(style,
2948
0
                                "has-backward-stepper", &backward,
2949
0
                                "has-forward-stepper", &forward,
2950
0
                                "has-secondary-backward-stepper",
2951
0
                                &secondary_backward,
2952
0
                                "has-secondary-forward-stepper",
2953
0
                                &secondary_forward, nullptr);
2954
0
    bool hasButtons =
2955
0
        backward || forward || secondary_backward || secondary_forward;
2956
0
2957
0
    if (gtk_get_minor_version() < 20) {
2958
0
        gint slider_width, trough_border, stepper_size, min_slider_size;
2959
0
2960
0
        gtk_style_context_get_style(style,
2961
0
                                    "slider-width", &slider_width,
2962
0
                                    "trough-border", &trough_border,
2963
0
                                    "stepper-size", &stepper_size,
2964
0
                                    "min-slider-length", &min_slider_size,
2965
0
                                    nullptr);
2966
0
2967
0
        aMetrics->size.thumb =
2968
0
            SizeFromLengthAndBreadth(aOrientation, min_slider_size, slider_width);
2969
0
        aMetrics->size.button =
2970
0
            SizeFromLengthAndBreadth(aOrientation, stepper_size, slider_width);
2971
0
        // overall scrollbar
2972
0
        gint breadth = slider_width + 2 * trough_border;
2973
0
        // Require room for the slider in the track if we don't have buttons.
2974
0
        gint length = hasButtons ? 0 : min_slider_size + 2 * trough_border;
2975
0
        aMetrics->size.scrollbar =
2976
0
            SizeFromLengthAndBreadth(aOrientation, length, breadth);
2977
0
2978
0
        // Borders on the major axis are set on the outermost scrollbar
2979
0
        // element to correctly position the buttons when
2980
0
        // trough-under-steppers is true.
2981
0
        // Borders on the minor axis are set on the track element so that it
2982
0
        // receives mouse events, as in GTK.
2983
0
        // Other borders have been zero-initialized.
2984
0
        if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
2985
0
            aMetrics->border.scrollbar.left =
2986
0
                aMetrics->border.scrollbar.right =
2987
0
                aMetrics->border.track.top =
2988
0
                aMetrics->border.track.bottom = trough_border;
2989
0
        } else {
2990
0
            aMetrics->border.scrollbar.top =
2991
0
                aMetrics->border.scrollbar.bottom =
2992
0
                aMetrics->border.track.left =
2993
0
                aMetrics->border.track.right = trough_border;
2994
0
        }
2995
0
2996
0
        // We're done here for Gtk+ < 3.20...
2997
0
        return;
2998
0
    }
2999
0
3000
0
    // GTK version > 3.20
3001
0
    // scrollbar
3002
0
    aMetrics->border.scrollbar = GetMarginBorderPadding(style);
3003
0
3004
0
    WidgetNodeType contents, track, thumb;
3005
0
    if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
3006
0
        contents = MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL;
3007
0
        track = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL;
3008
0
        thumb = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
3009
0
    } else {
3010
0
        contents = MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL;
3011
0
        track = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL;
3012
0
        thumb = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
3013
0
    }
3014
0
3015
0
    /* GetStyleContext() sets GtkStateFlags to the latest widget name
3016
0
     * in css selector string. When we call:
3017
0
     *
3018
0
     *     GetStyleContext(thumb, GTK_STATE_FLAG_PRELIGHT)
3019
0
     *
3020
0
     * we get:
3021
0
     *
3022
0
     *    "scrollbar contents trough slider:hover"
3023
0
     *
3024
0
     * Some themes (Ubuntu Ambiance) styles trough/thumb by scrollbar,
3025
0
     * the Gtk+ css rule looks like:
3026
0
     *
3027
0
     *    "scrollbar:hover contents trough slider"
3028
0
     *
3029
0
     *  So we need to apply GtkStateFlags to each widgets in style path.
3030
0
     */
3031
0
3032
0
    // thumb
3033
0
    style = CreateStyleContextWithStates(thumb, GTK_TEXT_DIR_NONE, aStateFlags);
3034
0
    aMetrics->size.thumb = GetMinMarginBox(style);
3035
0
    gtk_style_context_get_margin(style, gtk_style_context_get_state(style),
3036
0
                                 &aMetrics->margin.thumb);
3037
0
    g_object_unref(style);
3038
0
3039
0
    // track
3040
0
    style = CreateStyleContextWithStates(track, GTK_TEXT_DIR_NONE, aStateFlags);
3041
0
    aMetrics->border.track = GetMarginBorderPadding(style);
3042
0
    MozGtkSize trackMinSize = GetMinContentBox(style) + aMetrics->border.track;
3043
0
    MozGtkSize trackSizeForThumb = aMetrics->size.thumb + aMetrics->border.track;
3044
0
    g_object_unref(style);
3045
0
3046
0
    // button
3047
0
    if (hasButtons) {
3048
0
        style = CreateStyleContextWithStates(MOZ_GTK_SCROLLBAR_BUTTON,
3049
0
                                             GTK_TEXT_DIR_NONE, aStateFlags);
3050
0
        aMetrics->size.button = GetMinMarginBox(style);
3051
0
        g_object_unref(style);
3052
0
    } else {
3053
0
        aMetrics->size.button = {0, 0};
3054
0
    }
3055
0
    if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
3056
0
        aMetrics->size.button.Rotate();
3057
0
        // If the track is wider than necessary for the thumb, including when
3058
0
        // the buttons will cause Gecko to expand the track to fill
3059
0
        // available breadth, then add to the track border to prevent Gecko
3060
0
        // from expanding the thumb to fill available breadth.
3061
0
        gint extra =
3062
0
            std::max(trackMinSize.height,
3063
0
                     aMetrics->size.button.height) - trackSizeForThumb.height;
3064
0
        if (extra > 0) {
3065
0
            // If extra is odd, then the thumb is 0.5 pixels above
3066
0
            // center as in gtk_range_compute_slider_position().
3067
0
            aMetrics->border.track.top += extra / 2;
3068
0
            aMetrics->border.track.bottom += extra - extra / 2;
3069
0
            // Update size for change in border.
3070
0
            trackSizeForThumb.height += extra;
3071
0
        }
3072
0
    } else {
3073
0
        gint extra =
3074
0
            std::max(trackMinSize.width,
3075
0
                     aMetrics->size.button.width) - trackSizeForThumb.width;
3076
0
        if (extra > 0) {
3077
0
            // If extra is odd, then the thumb is 0.5 pixels to the left
3078
0
            // of center as in gtk_range_compute_slider_position().
3079
0
            aMetrics->border.track.left += extra / 2;
3080
0
            aMetrics->border.track.right += extra - extra / 2;
3081
0
            trackSizeForThumb.width += extra;
3082
0
        }
3083
0
    }
3084
0
3085
0
    style = CreateStyleContextWithStates(contents, GTK_TEXT_DIR_NONE,
3086
0
                                         aStateFlags);
3087
0
    GtkBorder contentsBorder = GetMarginBorderPadding(style);
3088
0
    g_object_unref(style);
3089
0
3090
0
    aMetrics->size.scrollbar =
3091
0
        trackSizeForThumb + contentsBorder + aMetrics->border.scrollbar;
3092
0
}
3093
3094
const ScrollbarGTKMetrics*
3095
GetScrollbarMetrics(GtkOrientation aOrientation)
3096
0
{
3097
0
  auto metrics = &sScrollbarMetrics[aOrientation];
3098
0
  if (!metrics->initialized) {
3099
0
      InitScrollbarMetrics(metrics, aOrientation, GTK_STATE_FLAG_NORMAL);
3100
0
3101
0
      // We calculate thumb margin here because it's composited from
3102
0
      // thumb class margin + difference margin between active and inactive
3103
0
      // scrollbars. It's a workaround which alows us to emulate
3104
0
      // overlay scrollbars for some Gtk+ themes (Ubuntu/Ambiance),
3105
0
      // when an inactive scrollbar thumb is smaller than the active one.
3106
0
      const ScrollbarGTKMetrics *metricsActive =
3107
0
          GetActiveScrollbarMetrics(aOrientation);
3108
0
3109
0
      if (metrics->size.thumb < metricsActive->size.thumb) {
3110
0
          metrics->margin.thumb +=
3111
0
              (metrics->border.scrollbar + metrics->border.track) -
3112
0
              (metricsActive->border.scrollbar + metricsActive->border.track);
3113
0
      }
3114
0
3115
0
      metrics->initialized = true;
3116
0
  }
3117
0
  return metrics;
3118
0
}
3119
3120
const ScrollbarGTKMetrics*
3121
GetActiveScrollbarMetrics(GtkOrientation aOrientation)
3122
0
{
3123
0
  auto metrics = &sActiveScrollbarMetrics[aOrientation];
3124
0
  if (!metrics->initialized) {
3125
0
      InitScrollbarMetrics(metrics, aOrientation, GTK_STATE_FLAG_PRELIGHT);
3126
0
      metrics->initialized = true;
3127
0
  }
3128
0
  return metrics;
3129
0
}
3130
3131
/*
3132
 * get_shadow_width() from gtkwindow.c is not public so we need
3133
 * to implement it.
3134
 */
3135
bool
3136
GetCSDDecorationSize(GtkWindow *aGtkWindow, GtkBorder* aDecorationSize)
3137
0
{
3138
0
    // Available on GTK 3.20+.
3139
0
    static auto sGtkRenderBackgroundGetClip =
3140
0
        (void (*)(GtkStyleContext*, gdouble, gdouble, gdouble, gdouble, GdkRectangle*))
3141
0
        dlsym(RTLD_DEFAULT, "gtk_render_background_get_clip");
3142
0
3143
0
    if (!sGtkRenderBackgroundGetClip) {
3144
0
        *aDecorationSize = {0,0,0,0};
3145
0
        return false;
3146
0
    }
3147
0
3148
0
    GtkStyleContext* context = gtk_widget_get_style_context(GTK_WIDGET(aGtkWindow));
3149
0
    bool solidDecorations = gtk_style_context_has_class(context, "solid-csd");
3150
0
    context = GetStyleContext(solidDecorations ?
3151
0
                              MOZ_GTK_WINDOW_DECORATION_SOLID :
3152
0
                              MOZ_GTK_WINDOW_DECORATION);
3153
0
3154
0
    /* Always sum border + padding */
3155
0
    GtkBorder padding;
3156
0
    GtkStateFlags state = gtk_style_context_get_state(context);
3157
0
    gtk_style_context_get_border(context, state, aDecorationSize);
3158
0
    gtk_style_context_get_padding(context, state, &padding);
3159
0
    *aDecorationSize += padding;
3160
0
3161
0
3162
0
    GtkBorder margin;
3163
0
    gtk_style_context_get_margin(context, state, &margin);
3164
0
3165
0
    /* Get shadow extents but combine with style margin; use the bigger value.
3166
0
     */
3167
0
    GdkRectangle clip;
3168
0
    sGtkRenderBackgroundGetClip(context, 0, 0, 0, 0, &clip);
3169
0
3170
0
    GtkBorder extents;
3171
0
    extents.top = -clip.y;
3172
0
    extents.right = clip.width + clip.x;
3173
0
    extents.bottom = clip.height + clip.y;
3174
0
    extents.left = -clip.x;
3175
0
3176
0
    // Margin is used for resize grip size - it's not present on
3177
0
    // popup windows.
3178
0
    if (gtk_window_get_window_type(aGtkWindow) != GTK_WINDOW_POPUP) {
3179
0
        extents.top = MAX(extents.top, margin.top);
3180
0
        extents.right = MAX(extents.right, margin.right);
3181
0
        extents.bottom = MAX(extents.bottom, margin.bottom);
3182
0
        extents.left = MAX(extents.left, margin.left);
3183
0
    }
3184
0
3185
0
    *aDecorationSize += extents;
3186
0
    return true;
3187
0
}
3188
3189
/* cairo_t *cr argument has to be a system-cairo. */
3190
gint
3191
moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
3192
                     GdkRectangle* rect,
3193
                     GtkWidgetState* state, gint flags,
3194
                     GtkTextDirection direction)
3195
0
{
3196
0
    /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
3197
0
     */
3198
0
    cairo_new_path(cr);
3199
0
3200
0
    switch (widget) {
3201
0
    case MOZ_GTK_BUTTON:
3202
0
    case MOZ_GTK_TOOLBAR_BUTTON:
3203
0
        if (state->depressed) {
3204
0
            return moz_gtk_button_paint(cr, rect, state,
3205
0
                                        (GtkReliefStyle) flags,
3206
0
                                        GetWidget(MOZ_GTK_TOGGLE_BUTTON),
3207
0
                                        direction);
3208
0
        }
3209
0
        return moz_gtk_button_paint(cr, rect, state,
3210
0
                                    (GtkReliefStyle) flags,
3211
0
                                    GetWidget(MOZ_GTK_BUTTON),
3212
0
                                    direction);
3213
0
        break;
3214
0
    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
3215
0
    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
3216
0
    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
3217
0
    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
3218
0
        return moz_gtk_header_bar_button_paint(cr, rect, state,
3219
0
                                               (GtkReliefStyle) flags,
3220
0
                                               widget,
3221
0
                                               direction);
3222
0
        break;
3223
0
    case MOZ_GTK_CHECKBUTTON:
3224
0
    case MOZ_GTK_RADIOBUTTON:
3225
0
        return moz_gtk_toggle_paint(cr, rect, state,
3226
0
                                    !!(flags & MOZ_GTK_WIDGET_CHECKED),
3227
0
                                    !!(flags & MOZ_GTK_WIDGET_INCONSISTENT),
3228
0
                                    (widget == MOZ_GTK_RADIOBUTTON),
3229
0
                                    direction);
3230
0
        break;
3231
0
    case MOZ_GTK_SCROLLBAR_BUTTON:
3232
0
        return moz_gtk_scrollbar_button_paint(cr, rect, state,
3233
0
                                              (GtkScrollbarButtonFlags) flags,
3234
0
                                              direction);
3235
0
        break;
3236
0
    case MOZ_GTK_SCROLLBAR_HORIZONTAL:
3237
0
    case MOZ_GTK_SCROLLBAR_VERTICAL:
3238
0
        if (flags & MOZ_GTK_TRACK_OPAQUE) {
3239
0
            GtkStyleContext* style =
3240
0
                GetStyleContext(MOZ_GTK_WINDOW, direction);
3241
0
            gtk_render_background(style, cr,
3242
0
                                  rect->x, rect->y, rect->width, rect->height);
3243
0
        }
3244
0
        if (gtk_check_version(3,20,0) == nullptr) {
3245
0
          return moz_gtk_scrollbar_paint(widget, cr, rect, state, direction);
3246
0
        } else {
3247
0
          WidgetNodeType trough_widget = (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) ?
3248
0
              MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL : MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL;
3249
0
          return moz_gtk_scrollbar_trough_paint(trough_widget, cr, rect,
3250
0
                                                state, direction);
3251
0
        }
3252
0
        break;
3253
0
    case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
3254
0
    case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
3255
0
        if (gtk_check_version(3,20,0) == nullptr) {
3256
0
          return moz_gtk_scrollbar_trough_paint(widget, cr, rect,
3257
0
                                                state, direction);
3258
0
        }
3259
0
        break;
3260
0
    case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
3261
0
    case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
3262
0
        return moz_gtk_scrollbar_thumb_paint(widget, cr, rect,
3263
0
                                             state, direction);
3264
0
        break;
3265
0
    case MOZ_GTK_SCALE_HORIZONTAL:
3266
0
    case MOZ_GTK_SCALE_VERTICAL:
3267
0
        return moz_gtk_scale_paint(cr, rect, state,
3268
0
                                   (GtkOrientation) flags, direction);
3269
0
        break;
3270
0
    case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
3271
0
    case MOZ_GTK_SCALE_THUMB_VERTICAL:
3272
0
        return moz_gtk_scale_thumb_paint(cr, rect, state,
3273
0
                                         (GtkOrientation) flags, direction);
3274
0
        break;
3275
0
    case MOZ_GTK_INNER_SPIN_BUTTON:
3276
0
        return moz_gtk_inner_spin_paint(cr, rect, state, direction);
3277
0
        break;
3278
0
    case MOZ_GTK_SPINBUTTON:
3279
0
        return moz_gtk_spin_paint(cr, rect, direction);
3280
0
        break;
3281
0
    case MOZ_GTK_SPINBUTTON_UP:
3282
0
    case MOZ_GTK_SPINBUTTON_DOWN:
3283
0
        return moz_gtk_spin_updown_paint(cr, rect,
3284
0
                                         (widget == MOZ_GTK_SPINBUTTON_DOWN),
3285
0
                                         state, direction);
3286
0
        break;
3287
0
    case MOZ_GTK_SPINBUTTON_ENTRY:
3288
0
        {
3289
0
            GtkStyleContext* style =
3290
0
                GetStyleContext(MOZ_GTK_SPINBUTTON_ENTRY, direction,
3291
0
                                GetStateFlagsFromGtkWidgetState(state));
3292
0
            gint ret = moz_gtk_entry_paint(cr, rect, state, style);
3293
0
            return ret;
3294
0
        }
3295
0
        break;
3296
0
    case MOZ_GTK_GRIPPER:
3297
0
        return moz_gtk_gripper_paint(cr, rect, state,
3298
0
                                     direction);
3299
0
        break;
3300
0
    case MOZ_GTK_TREEVIEW:
3301
0
        return moz_gtk_treeview_paint(cr, rect, state,
3302
0
                                      direction);
3303
0
        break;
3304
0
    case MOZ_GTK_TREE_HEADER_CELL:
3305
0
        return moz_gtk_tree_header_cell_paint(cr, rect, state,
3306
0
                                              flags, direction);
3307
0
        break;
3308
0
    case MOZ_GTK_TREE_HEADER_SORTARROW:
3309
0
        return moz_gtk_tree_header_sort_arrow_paint(cr, rect,
3310
0
                                                    state,
3311
0
                                                    (GtkArrowType) flags,
3312
0
                                                    direction);
3313
0
        break;
3314
0
    case MOZ_GTK_TREEVIEW_EXPANDER:
3315
0
        return moz_gtk_treeview_expander_paint(cr, rect, state,
3316
0
                                               (GtkExpanderStyle) flags, direction);
3317
0
        break;
3318
0
    case MOZ_GTK_ENTRY:
3319
0
        {
3320
0
            GtkStyleContext* style =
3321
0
                GetStyleContext(MOZ_GTK_ENTRY, direction,
3322
0
                                GetStateFlagsFromGtkWidgetState(state));
3323
0
            gint ret = moz_gtk_entry_paint(cr, rect, state, style);
3324
0
            return ret;
3325
0
        }
3326
0
    case MOZ_GTK_TEXT_VIEW:
3327
0
        return moz_gtk_text_view_paint(cr, rect, state, direction);
3328
0
        break;
3329
0
    case MOZ_GTK_DROPDOWN:
3330
0
        return moz_gtk_combo_box_paint(cr, rect, state, direction);
3331
0
        break;
3332
0
    case MOZ_GTK_DROPDOWN_ARROW:
3333
0
        return moz_gtk_combo_box_entry_button_paint(cr, rect,
3334
0
                                                    state, flags, direction);
3335
0
        break;
3336
0
    case MOZ_GTK_DROPDOWN_ENTRY:
3337
0
        {
3338
0
            GtkStyleContext* style =
3339
0
                GetStyleContext(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA, direction,
3340
0
                                GetStateFlagsFromGtkWidgetState(state));
3341
0
            gint ret = moz_gtk_entry_paint(cr, rect, state, style);
3342
0
            return ret;
3343
0
        }
3344
0
        break;
3345
0
    case MOZ_GTK_CHECKBUTTON_CONTAINER:
3346
0
    case MOZ_GTK_RADIOBUTTON_CONTAINER:
3347
0
        return moz_gtk_container_paint(cr, rect, state, widget, direction);
3348
0
        break;
3349
0
    case MOZ_GTK_CHECKBUTTON_LABEL:
3350
0
    case MOZ_GTK_RADIOBUTTON_LABEL:
3351
0
        return moz_gtk_toggle_label_paint(cr, rect, state,
3352
0
                                          (widget == MOZ_GTK_RADIOBUTTON_LABEL),
3353
0
                                          direction);
3354
0
        break;
3355
0
    case MOZ_GTK_TOOLBAR:
3356
0
        return moz_gtk_toolbar_paint(cr, rect, direction);
3357
0
        break;
3358
0
    case MOZ_GTK_TOOLBAR_SEPARATOR:
3359
0
        return moz_gtk_toolbar_separator_paint(cr, rect,
3360
0
                                               direction);
3361
0
        break;
3362
0
    case MOZ_GTK_TOOLTIP:
3363
0
        return moz_gtk_tooltip_paint(cr, rect, direction);
3364
0
        break;
3365
0
    case MOZ_GTK_FRAME:
3366
0
        return moz_gtk_frame_paint(cr, rect, direction);
3367
0
        break;
3368
0
    case MOZ_GTK_RESIZER:
3369
0
        return moz_gtk_resizer_paint(cr, rect, state,
3370
0
                                     direction);
3371
0
        break;
3372
0
    case MOZ_GTK_PROGRESSBAR:
3373
0
        return moz_gtk_progressbar_paint(cr, rect, direction);
3374
0
        break;
3375
0
    case MOZ_GTK_PROGRESS_CHUNK:
3376
0
    case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE:
3377
0
    case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE:
3378
0
        return moz_gtk_progress_chunk_paint(cr, rect,
3379
0
                                            direction, widget);
3380
0
        break;
3381
0
    case MOZ_GTK_TAB_TOP:
3382
0
    case MOZ_GTK_TAB_BOTTOM:
3383
0
        return moz_gtk_tab_paint(cr, rect, state,
3384
0
                                 (GtkTabFlags) flags, direction, widget);
3385
0
        break;
3386
0
    case MOZ_GTK_TABPANELS:
3387
0
        return moz_gtk_tabpanels_paint(cr, rect, direction);
3388
0
        break;
3389
0
    case MOZ_GTK_TAB_SCROLLARROW:
3390
0
        return moz_gtk_tab_scroll_arrow_paint(cr, rect, state,
3391
0
                                              (GtkArrowType) flags, direction);
3392
0
        break;
3393
0
    case MOZ_GTK_MENUBAR:
3394
0
        return moz_gtk_menu_bar_paint(cr, rect, direction);
3395
0
        break;
3396
0
    case MOZ_GTK_MENUPOPUP:
3397
0
        return moz_gtk_menu_popup_paint(cr, rect, direction);
3398
0
        break;
3399
0
    case MOZ_GTK_MENUSEPARATOR:
3400
0
        return moz_gtk_menu_separator_paint(cr, rect,
3401
0
                                            direction);
3402
0
        break;
3403
0
    case MOZ_GTK_MENUBARITEM:
3404
0
    case MOZ_GTK_MENUITEM:
3405
0
        return moz_gtk_menu_item_paint(widget, cr, rect, state, direction);
3406
0
        break;
3407
0
    case MOZ_GTK_MENUARROW:
3408
0
        return moz_gtk_menu_arrow_paint(cr, rect, state,
3409
0
                                        direction);
3410
0
        break;
3411
0
    case MOZ_GTK_TOOLBARBUTTON_ARROW:
3412
0
        return moz_gtk_arrow_paint(cr, rect, state,
3413
0
                                   (GtkArrowType) flags, direction);
3414
0
        break;
3415
0
    case MOZ_GTK_CHECKMENUITEM:
3416
0
    case MOZ_GTK_RADIOMENUITEM:
3417
0
        return moz_gtk_check_menu_item_paint(widget, cr, rect, state,
3418
0
                                             (gboolean) flags, direction);
3419
0
        break;
3420
0
    case MOZ_GTK_SPLITTER_HORIZONTAL:
3421
0
        return moz_gtk_vpaned_paint(cr, rect, state);
3422
0
        break;
3423
0
    case MOZ_GTK_SPLITTER_VERTICAL:
3424
0
        return moz_gtk_hpaned_paint(cr, rect, state);
3425
0
        break;
3426
0
    case MOZ_GTK_WINDOW:
3427
0
        return moz_gtk_window_paint(cr, rect, direction);
3428
0
        break;
3429
0
    case MOZ_GTK_INFO_BAR:
3430
0
        return moz_gtk_info_bar_paint(cr, rect, state);
3431
0
        break;
3432
0
    case MOZ_GTK_HEADER_BAR:
3433
0
    case MOZ_GTK_HEADER_BAR_MAXIMIZED:
3434
0
        return moz_gtk_header_bar_paint(widget, cr, rect, state);
3435
0
        break;
3436
0
    default:
3437
0
        g_warning("Unknown widget type: %d", widget);
3438
0
    }
3439
0
3440
0
    return MOZ_GTK_UNKNOWN_WIDGET;
3441
0
}
3442
3443
gint
3444
moz_gtk_shutdown()
3445
0
{
3446
0
    /* This will destroy all of our widgets */
3447
0
    ResetWidgetCache();
3448
0
3449
0
    return MOZ_GTK_SUCCESS;
3450
0
}