--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -18,16 +18,18 @@
#include <math.h>
static gboolean have_arrow_scaling;
static gboolean checkbox_check_state;
static gboolean notebook_has_tab_gap;
static gboolean is_initialized;
+static ScrollbarGTKMetrics sScrollbarMetrics[2];
+
#define ARROW_UP 0
#define ARROW_DOWN G_PI
#define ARROW_RIGHT G_PI_2
#define ARROW_LEFT (G_PI+G_PI_2)
#if !GTK_CHECK_VERSION(3,14,0)
#define GTK_STATE_FLAG_CHECKED (1 << 11)
#endif
@@ -430,17 +432,21 @@ calculate_arrow_rect(GtkWidget* arrow, G
arrow_rect->y = floor(rect->y + mypad +
((rect->height - extent) * myalign));
arrow_rect->width = arrow_rect->height = extent;
return MOZ_GTK_SUCCESS;
}
-void
+/**
+ * Get minimum widget size as sum of margin, padding, border and
+ * min-width/min-height.
+ */
+static void
moz_gtk_get_widget_min_size(WidgetNodeType aGtkWidgetType, int* width,
int* height)
{
GtkStyleContext* style = ClaimStyleContext(aGtkWidgetType);
GtkStateFlags state_flags = gtk_style_context_get_state(style);
gtk_style_context_get(style, state_flags,
"min-height", height,
"min-width", width,
@@ -453,16 +459,24 @@ moz_gtk_get_widget_min_size(WidgetNodeTy
ReleaseStyleContext(style);
*width += border.left + border.right + margin.left + margin.right +
padding.left + padding.right;
*height += border.top + border.bottom + margin.top + margin.bottom +
padding.top + padding.bottom;
}
+static MozGtkSize
+GetMinMarginBox(WidgetNodeType aNodeType)
+{
+ gint width, height;
+ moz_gtk_get_widget_min_size(aNodeType, &width, &height);
+ return {width, height};
+}
+
static void
Inset(GdkRectangle* rect, GtkBorder& aBorder)
{
MOZ_ASSERT(rect);
rect->x += aBorder.left;
rect->y += aBorder.top;
rect->width -= aBorder.left + aBorder.right;
rect->height -= aBorder.top + aBorder.bottom;
@@ -533,28 +547,32 @@ moz_gtk_scrollbar_button_paint(cairo_t *
}
GdkRectangle rect = *aRect;
if (gtk_check_version(3,20,0) == nullptr) {
// The "trough-border" is not used since GTK 3.20. The stepper margin
// box occupies the full width of the "contents" gadget content box.
InsetByMargin(&rect, style);
} else {
- // Scrollbar button has to be inset by trough_border because its DOM
- // element is filling width of vertical scrollbar's track (or height
- // in case of horizontal scrollbars).
- MozGtkScrollbarMetrics metrics;
- moz_gtk_get_scrollbar_metrics(&metrics);
- if (flags & MOZ_GTK_STEPPER_VERTICAL) {
- rect.x += metrics.trough_border;
- rect.width = metrics.slider_width;
- } else {
- rect.y += metrics.trough_border;
- rect.height = metrics.slider_width;
- }
+ // Scrollbar button has to be inset by trough_border because its DOM
+ // element is filling width of vertical scrollbar's track (or height
+ // in case of horizontal scrollbars).
+ GtkOrientation orientation = flags & MOZ_GTK_STEPPER_VERTICAL ?
+ GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL;
+ const auto& metrics = sScrollbarMetrics[orientation];
+ if (!metrics.initialized) {
+ NS_WARNING("Didn't measure before drawing?");
+ }
+ if (flags & MOZ_GTK_STEPPER_VERTICAL) {
+ rect.x += metrics.border.track.left;
+ rect.width = metrics.size.thumb.width;
+ } else {
+ rect.y += metrics.border.track.top;
+ rect.height = metrics.size.thumb.height;
+ }
}
gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height);
arrow_rect.width = rect.width / 2;
arrow_rect.height = rect.height / 2;
@@ -1996,16 +2014,30 @@ static void moz_gtk_add_margin_border_pa
gint* left, gint* top,
gint* right, gint* bottom)
{
moz_gtk_add_style_margin(style, left, top, right, bottom);
moz_gtk_add_style_border(style, left, top, right, bottom);
moz_gtk_add_style_padding(style, left, top, right, bottom);
}
+static GtkBorder
+GetMarginBorderPadding(GtkStyleContext* aStyle)
+{
+ gint left = 0, top = 0, right = 0, bottom = 0;
+ moz_gtk_add_margin_border_padding(aStyle, &left, &top, &right, &bottom);
+ // narrowing conversions to gint16:
+ GtkBorder result;
+ result.left = left;
+ result.right = right;
+ result.top = top;
+ result.bottom = bottom;
+ return result;
+}
+
gint
moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
gint* right, gint* bottom, GtkTextDirection direction,
gboolean inhtml)
{
GtkWidget* w;
GtkStyleContext* style;
*left = *top = *right = *bottom = 0;
@@ -2199,59 +2231,16 @@ moz_gtk_get_widget_border(WidgetNodeType
GtkStyleContext* labelStyle = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL);
moz_gtk_add_margin_border_padding(labelStyle,
left, top, right, bottom);
ReleaseStyleContext(labelStyle);
return MOZ_GTK_SUCCESS;
}
- case MOZ_GTK_SCROLLBAR_VERTICAL:
- case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
- {
- if (gtk_check_version(3,20,0) == nullptr) {
- style = ClaimStyleContext(widget);
- moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
- ReleaseStyleContext(style);
- if (widget == MOZ_GTK_SCROLLBAR_VERTICAL) {
- style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
- moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
- ReleaseStyleContext(style);
- }
- } else {
- MozGtkScrollbarMetrics metrics;
- moz_gtk_get_scrollbar_metrics(&metrics);
- /* Top and bottom border for whole vertical scrollbar, top and bottom
- * border for horizontal track - to correctly position thumb element */
- *top = *bottom = metrics.trough_border;
- }
- return MOZ_GTK_SUCCESS;
- }
- break;
-
- case MOZ_GTK_SCROLLBAR_HORIZONTAL:
- case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
- {
- if (gtk_check_version(3,20,0) == nullptr) {
- style = ClaimStyleContext(widget);
- moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
- ReleaseStyleContext(style);
- if (widget == MOZ_GTK_SCROLLBAR_HORIZONTAL) {
- style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL);
- moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
- ReleaseStyleContext(style);
- }
- } else {
- MozGtkScrollbarMetrics metrics;
- moz_gtk_get_scrollbar_metrics(&metrics);
- *left = *right = metrics.trough_border;
- }
- return MOZ_GTK_SUCCESS;
- }
- break;
/* These widgets have no borders, since they are not containers. */
case MOZ_GTK_CHECKBUTTON_LABEL:
case MOZ_GTK_RADIOBUTTON_LABEL:
case MOZ_GTK_SPLITTER_HORIZONTAL:
case MOZ_GTK_SPLITTER_VERTICAL:
case MOZ_GTK_CHECKBUTTON:
case MOZ_GTK_RADIOBUTTON:
@@ -2550,33 +2539,118 @@ moz_gtk_get_scalethumb_metrics(GtkOrient
*thumb_height = min_height;
ReleaseStyleContext(style);
}
return MOZ_GTK_SUCCESS;
}
-gint
-moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics)
+static MozGtkSize
+SizeFromLengthAndBreadth(GtkOrientation aOrientation,
+ gint aLength, gint aBreadth)
+{
+ return aOrientation == GTK_ORIENTATION_HORIZONTAL ?
+ MozGtkSize({aLength, aBreadth}) : MozGtkSize({aBreadth, aLength});
+}
+
+const ScrollbarGTKMetrics*
+GetScrollbarMetrics(GtkOrientation aOrientation)
{
- // For Gtk >= 3.20 scrollbar metrics are ignored
- MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr);
-
- GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_VERTICAL);
- gtk_style_context_get_style(style,
- "slider_width", &metrics->slider_width,
- "trough_border", &metrics->trough_border,
- "stepper_size", &metrics->stepper_size,
- "stepper_spacing", &metrics->stepper_spacing,
- "min-slider-length", &metrics->min_slider_size,
- nullptr);
+ auto metrics = &sScrollbarMetrics[aOrientation];
+ if (metrics->initialized)
+ return metrics;
+
+ metrics->initialized = true;
+
+ WidgetNodeType scrollbar = aOrientation == GTK_ORIENTATION_HORIZONTAL ?
+ MOZ_GTK_SCROLLBAR_HORIZONTAL : MOZ_GTK_SCROLLBAR_VERTICAL;
+
+ if (gtk_get_minor_version() < 20) {
+ gint slider_width, trough_border, stepper_size, min_slider_size;
+ gboolean backward, forward, secondary_backward, secondary_forward;
+
+ GtkStyleContext* style = ClaimStyleContext(scrollbar);
+ gtk_style_context_get_style(style,
+ "slider-width", &slider_width,
+ "trough-border", &trough_border,
+ "stepper-size", &stepper_size,
+ "min-slider-length", &min_slider_size,
+ "has-backward-stepper", &backward,
+ "has-forward-stepper", &forward,
+ "has-secondary-backward-stepper",
+ &secondary_backward,
+ "has-secondary-forward-stepper",
+ &secondary_forward, nullptr);
+ ReleaseStyleContext(style);
+
+ metrics->size.thumb =
+ SizeFromLengthAndBreadth(aOrientation, min_slider_size, slider_width);
+ metrics->size.button =
+ SizeFromLengthAndBreadth(aOrientation, stepper_size, slider_width);
+ // overall scrollbar
+ gint breadth = slider_width + 2 * trough_border;
+ // Require room for the slider in the track if we don't have buttons.
+ bool hasButtons = backward || forward ||
+ secondary_backward || secondary_forward;
+ gint length = hasButtons ? 0 : min_slider_size + 2 * trough_border;
+ metrics->size.scrollbar =
+ SizeFromLengthAndBreadth(aOrientation, length, breadth);
+
+ // Borders on the major axis are set on the outermost scrollbar
+ // element to correctly position the buttons when
+ // trough-under-steppers is true.
+ // Borders on the minor axis are set on the track element so that it
+ // receives mouse events, as in GTK.
+ // Other borders have been zero-initialized.
+ if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
+ metrics->border.scrollbar.left =
+ metrics->border.scrollbar.right =
+ metrics->border.track.top =
+ metrics->border.track.bottom = trough_border;
+ } else {
+ metrics->border.scrollbar.top =
+ metrics->border.scrollbar.bottom =
+ metrics->border.track.left =
+ metrics->border.track.right = trough_border;
+ }
+
+ return metrics;
+ }
+
+ // GTK version > 3.20
+ WidgetNodeType contents, track, thumb;
+ if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
+ contents = MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL;
+ track = MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL;
+ thumb = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
+ } else {
+ contents = MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL;
+ track = MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL;
+ thumb = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL;
+ }
+ // thumb
+ metrics->size.thumb = GetMinMarginBox(thumb);
+ // track
+ GtkStyleContext* style = ClaimStyleContext(track);
+ metrics->border.track = GetMarginBorderPadding(style);
ReleaseStyleContext(style);
-
- return MOZ_GTK_SUCCESS;
+ // button
+ metrics->size.button = GetMinMarginBox(MOZ_GTK_SCROLLBAR_BUTTON);
+ // scrollbar
+ style = ClaimStyleContext(scrollbar);
+ metrics->border.scrollbar = GetMarginBorderPadding(style);
+ ReleaseStyleContext(style);
+ style = ClaimStyleContext(contents);
+ GtkBorder contentsBorder = GetMarginBorderPadding(style);
+ ReleaseStyleContext(style);
+ metrics->size.scrollbar = metrics->size.thumb + metrics->border.track +
+ contentsBorder + metrics->border.scrollbar;
+
+ return metrics;
}
/* cairo_t *cr argument has to be a system-cairo. */
gint
moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
GdkRectangle* rect,
GtkWidgetState* state, gint flags,
GtkTextDirection direction)
@@ -2813,32 +2887,16 @@ moz_gtk_widget_paint(WidgetNodeType widg
return MOZ_GTK_UNKNOWN_WIDGET;
}
GtkWidget* moz_gtk_get_scrollbar_widget(void)
{
return GetWidget(MOZ_GTK_SCROLLBAR_HORIZONTAL);
}
-gboolean moz_gtk_has_scrollbar_buttons(void)
-{
- gboolean backward, forward, secondary_backward, secondary_forward;
- MOZ_ASSERT(is_initialized, "Forgot to call moz_gtk_init()");
- GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_SCROLLBAR_VERTICAL);
- gtk_style_context_get_style(style,
- "has-backward-stepper", &backward,
- "has-forward-stepper", &forward,
- "has-secondary-backward-stepper", &secondary_backward,
- "has-secondary-forward-stepper", &secondary_forward,
- NULL);
- ReleaseStyleContext(style);
-
- return backward | forward | secondary_forward | secondary_forward;
-}
-
gint
moz_gtk_shutdown()
{
/* This will destroy all of our widgets */
ResetWidgetCache();
is_initialized = FALSE;
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -1260,16 +1260,46 @@ nsNativeThemeGTK::NativeThemeToGtkTheme(
NS_IMETHODIMP
nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
uint8_t aWidgetType, nsIntMargin* aResult)
{
GtkTextDirection direction = GetTextDirection(aFrame);
aResult->top = aResult->left = aResult->right = aResult->bottom = 0;
switch (aWidgetType) {
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ {
+ GtkOrientation orientation =
+ aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ?
+ GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+ const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
+
+ const GtkBorder& border = metrics->border.scrollbar;
+ aResult->top = border.top;
+ aResult->right = border.right;
+ aResult->bottom = border.bottom;
+ aResult->left = border.left;
+ }
+ break;
+ case NS_THEME_SCROLLBARTRACK_HORIZONTAL:
+ case NS_THEME_SCROLLBARTRACK_VERTICAL:
+ {
+ GtkOrientation orientation =
+ aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL ?
+ GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+ const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
+
+ const GtkBorder& border = metrics->border.track;
+ aResult->top = border.top;
+ aResult->right = border.right;
+ aResult->bottom = border.bottom;
+ aResult->left = border.left;
+ }
+ break;
case NS_THEME_TOOLBOX:
// gtk has no toolbox equivalent. So, although we map toolbox to
// gtk's 'toolbar' for purposes of painting the widget background,
// we don't use the toolbar border for toolbox.
break;
case NS_THEME_DUALBUTTON:
// TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw
// around the entire button + dropdown, and also an inner border if you're
@@ -1414,43 +1444,32 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
{
aResult->width = aResult->height = 0;
*aIsOverridable = true;
switch (aWidgetType) {
case NS_THEME_SCROLLBARBUTTON_UP:
case NS_THEME_SCROLLBARBUTTON_DOWN:
{
- if (gtk_check_version(3,20,0) == nullptr) {
- moz_gtk_get_widget_min_size(MOZ_GTK_SCROLLBAR_BUTTON,
- &(aResult->width), &(aResult->height));
- } else {
- MozGtkScrollbarMetrics metrics;
- moz_gtk_get_scrollbar_metrics(&metrics);
+ const ScrollbarGTKMetrics* metrics =
+ GetScrollbarMetrics(GTK_ORIENTATION_VERTICAL);
- aResult->width = metrics.slider_width;
- aResult->height = metrics.stepper_size;
- }
-
+ aResult->width = metrics->size.button.width;
+ aResult->height = metrics->size.button.height;
*aIsOverridable = false;
}
break;
case NS_THEME_SCROLLBARBUTTON_LEFT:
case NS_THEME_SCROLLBARBUTTON_RIGHT:
{
- if (gtk_check_version(3,20,0) == nullptr) {
- moz_gtk_get_widget_min_size(MOZ_GTK_SCROLLBAR_BUTTON,
- &(aResult->width), &(aResult->height));
- } else {
- MozGtkScrollbarMetrics metrics;
- moz_gtk_get_scrollbar_metrics(&metrics);
+ const ScrollbarGTKMetrics* metrics =
+ GetScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL);
- aResult->width = metrics.stepper_size;
- aResult->height = metrics.slider_width;
- }
+ aResult->width = metrics->size.button.width;
+ aResult->height = metrics->size.button.height;
*aIsOverridable = false;
}
break;
case NS_THEME_SPLITTER:
{
gint metrics;
if (IsHorizontal(aFrame)) {
moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
@@ -1467,75 +1486,35 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
case NS_THEME_SCROLLBAR_HORIZONTAL:
case NS_THEME_SCROLLBAR_VERTICAL:
{
/* While we enforce a minimum size for the thumb, this is ignored
* for the some scrollbars if buttons are hidden (bug 513006) because
* the thumb isn't a direct child of the scrollbar, unlike the buttons
* or track. So add a minimum size to the track as well to prevent a
* 0-width scrollbar. */
- if (gtk_check_version(3,20,0) == nullptr) {
- // Thumb min dimensions to start with
- WidgetNodeType thumbType = aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ?
- MOZ_GTK_SCROLLBAR_THUMB_VERTICAL : MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL;
- moz_gtk_get_widget_min_size(thumbType, &(aResult->width), &(aResult->height));
-
- // Add scrollbar's borders
- nsIntMargin border;
- nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(),
- aFrame, aWidgetType, &border);
- aResult->width += border.left + border.right;
- aResult->height += border.top + border.bottom;
+ GtkOrientation orientation =
+ aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ?
+ GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+ const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
- // Add track's borders
- uint8_t trackType = aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ?
- NS_THEME_SCROLLBARTRACK_VERTICAL : NS_THEME_SCROLLBARTRACK_HORIZONTAL;
- nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(),
- aFrame, trackType, &border);
- aResult->width += border.left + border.right;
- aResult->height += border.top + border.bottom;
- } else {
- MozGtkScrollbarMetrics metrics;
- moz_gtk_get_scrollbar_metrics(&metrics);
-
- // Require room for the slider in the track if we don't have buttons.
- bool hasScrollbarButtons = moz_gtk_has_scrollbar_buttons();
-
- if (aWidgetType == NS_THEME_SCROLLBAR_VERTICAL) {
- aResult->width = metrics.slider_width + 2 * metrics.trough_border;
- if (!hasScrollbarButtons)
- aResult->height = metrics.min_slider_size + 2 * metrics.trough_border;
- } else {
- aResult->height = metrics.slider_width + 2 * metrics.trough_border;
- if (!hasScrollbarButtons)
- aResult->width = metrics.min_slider_size + 2 * metrics.trough_border;
- }
- *aIsOverridable = false;
- }
-
+ aResult->width = metrics->size.scrollbar.width;
+ aResult->height = metrics->size.scrollbar.height;
}
break;
case NS_THEME_SCROLLBARTHUMB_VERTICAL:
case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
{
- if (gtk_check_version(3,20,0) == nullptr) {
- moz_gtk_get_widget_min_size(NativeThemeToGtkTheme(aWidgetType, aFrame),
- &(aResult->width), &(aResult->height));
- } else {
- MozGtkScrollbarMetrics metrics;
- moz_gtk_get_scrollbar_metrics(&metrics);
+ GtkOrientation orientation =
+ aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL ?
+ GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+ const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation);
- if (aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL) {
- aResult->width = metrics.slider_width;
- aResult->height = metrics.min_slider_size;
- } else {
- aResult->height = metrics.slider_width;
- aResult->width = metrics.min_slider_size;
- }
- }
+ aResult->width = metrics->size.thumb.width;
+ aResult->height = metrics->size.thumb.height;
*aIsOverridable = false;
}
break;
case NS_THEME_RANGE_THUMB:
{
gint thumb_length, thumb_height;
if (IsRangeHorizontal(aFrame)) {