Bug 1454897 - Ubuntu/Ambiance - Render scrollbar thumb with different sizes in active/normal state, r=jhorak

This patch implements a workaround which alows us to emulate
overlay scrollbars for some Gtk+ themes (Ubuntu/Ambiance),
when an inactive scrollbar thumb is smaller than the active one.

We calculate thumb margin as thumb class margin + difference margin
between active and inactive scrollbars.

Also remove boolean parameter from GetScrollbarMetrics() and implement
GetActiveScrollbarMetrics() to get metrics for active scrollbar.

MozReview-Commit-ID: 2zje5OZskYw

--HG--
extra : rebase_source : dd50dc52d9d0942c9dcfbf72d217f3c6ee82c21a
This commit is contained in:
Martin Stransky 2018-04-24 12:27:38 +02:00
parent 681387a8ba
commit 659fc0cc75
3 changed files with 134 additions and 72 deletions

View File

@ -24,7 +24,7 @@ static gboolean checkbox_check_state;
static gboolean notebook_has_tab_gap;
static ScrollbarGTKMetrics sScrollbarMetrics[2];
static ScrollbarGTKMetrics sScrollbarMetricsActive[2];
static ScrollbarGTKMetrics sActiveScrollbarMetrics[2];
static ToggleGTKMetrics sCheckboxMetrics;
static ToggleGTKMetrics sRadioMetrics;
static ToolbarGTKMetrics sToolbarMetrics;
@ -38,6 +38,28 @@ static ToolbarGTKMetrics sToolbarMetrics;
#define GTK_STATE_FLAG_CHECKED (1 << 11)
#endif
static GtkBorder
operator-(const GtkBorder& first, const GtkBorder& second)
{
GtkBorder result;
result.left = first.left - second.left;
result.right = first.right - second.right;
result.top = first.top - second.top;
result.bottom = first.bottom - second.bottom;
return result;
}
static GtkBorder
operator+(const GtkBorder& first, const GtkBorder& second)
{
GtkBorder result;
result.left = first.left + second.left;
result.right = first.right + second.right;
result.top = first.top + second.top;
result.bottom = first.bottom + second.bottom;
return result;
}
static GtkBorder
operator+=(GtkBorder& first, const GtkBorder& second)
{
@ -193,8 +215,8 @@ moz_gtk_refresh()
sScrollbarMetrics[GTK_ORIENTATION_HORIZONTAL].initialized = false;
sScrollbarMetrics[GTK_ORIENTATION_VERTICAL].initialized = false;
sScrollbarMetricsActive[GTK_ORIENTATION_HORIZONTAL].initialized = false;
sScrollbarMetricsActive[GTK_ORIENTATION_VERTICAL].initialized = false;
sActiveScrollbarMetrics[GTK_ORIENTATION_HORIZONTAL].initialized = false;
sActiveScrollbarMetrics[GTK_ORIENTATION_VERTICAL].initialized = false;
sCheckboxMetrics.initialized = false;
sRadioMetrics.initialized = false;
sToolbarMetrics.initialized = false;
@ -1010,19 +1032,21 @@ moz_gtk_scrollbar_thumb_paint(WidgetNodeType widget,
GtkTextDirection direction)
{
GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
GtkStyleContext* style = GetStyleContext(widget, direction, state_flags);
GtkOrientation orientation = (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
GdkRectangle rect = *aRect;
GtkStyleContext* style = GetStyleContext(widget, direction, state_flags);
InsetByMargin(&rect, style);
gtk_render_slider(style, cr,
rect.x,
rect.y,
rect.width,
rect.height,
(widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ?
GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
const ScrollbarGTKMetrics* metrics =
(state->depressed || state->active || state->inHover) ?
GetActiveScrollbarMetrics(orientation) :
GetScrollbarMetrics(orientation);
Inset(&rect, metrics->margin.thumb);
gtk_render_slider(style, cr, rect.x, rect.y, rect.width, rect.height,
orientation);
return MOZ_GTK_SUCCESS;
}
@ -2896,23 +2920,17 @@ GetToggleMetrics(bool isRadio)
return metrics;
}
const ScrollbarGTKMetrics*
GetScrollbarMetrics(GtkOrientation aOrientation, bool aActive)
static void
InitScrollbarMetrics(ScrollbarGTKMetrics* aMetrics,
GtkOrientation aOrientation,
GtkStateFlags aStateFlags)
{
auto metrics = aActive ? &sScrollbarMetricsActive[aOrientation] :
&sScrollbarMetrics[aOrientation];
if (metrics->initialized)
return metrics;
metrics->initialized = true;
WidgetNodeType scrollbar = aOrientation == GTK_ORIENTATION_HORIZONTAL ?
MOZ_GTK_SCROLLBAR_HORIZONTAL : MOZ_GTK_SCROLLBAR_VERTICAL;
gboolean backward, forward, secondary_backward, secondary_forward;
GtkStyleContext* style = GetStyleContext(scrollbar, GTK_TEXT_DIR_NONE,
aActive ? GTK_STATE_FLAG_PRELIGHT :
GTK_STATE_FLAG_NORMAL);
aStateFlags);
gtk_style_context_get_style(style,
"has-backward-stepper", &backward,
"has-forward-stepper", &forward,
@ -2933,15 +2951,15 @@ GetScrollbarMetrics(GtkOrientation aOrientation, bool aActive)
"min-slider-length", &min_slider_size,
nullptr);
metrics->size.thumb =
aMetrics->size.thumb =
SizeFromLengthAndBreadth(aOrientation, min_slider_size, slider_width);
metrics->size.button =
aMetrics->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.
gint length = hasButtons ? 0 : min_slider_size + 2 * trough_border;
metrics->size.scrollbar =
aMetrics->size.scrollbar =
SizeFromLengthAndBreadth(aOrientation, length, breadth);
// Borders on the major axis are set on the outermost scrollbar
@ -2951,23 +2969,24 @@ GetScrollbarMetrics(GtkOrientation aOrientation, bool aActive)
// 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;
aMetrics->border.scrollbar.left =
aMetrics->border.scrollbar.right =
aMetrics->border.track.top =
aMetrics->border.track.bottom = trough_border;
} else {
metrics->border.scrollbar.top =
metrics->border.scrollbar.bottom =
metrics->border.track.left =
metrics->border.track.right = trough_border;
aMetrics->border.scrollbar.top =
aMetrics->border.scrollbar.bottom =
aMetrics->border.track.left =
aMetrics->border.track.right = trough_border;
}
return metrics;
// We're done here for Gtk+ < 3.20...
return;
}
// GTK version > 3.20
// scrollbar
metrics->border.scrollbar = GetMarginBorderPadding(style);
aMetrics->border.scrollbar = GetMarginBorderPadding(style);
WidgetNodeType contents, track, thumb;
if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
@ -2998,72 +3017,102 @@ GetScrollbarMetrics(GtkOrientation aOrientation, bool aActive)
*/
// thumb
style = CreateStyleContextWithStates(thumb, GTK_TEXT_DIR_NONE,
aActive ? GTK_STATE_FLAG_PRELIGHT :
GTK_STATE_FLAG_NORMAL);
metrics->size.thumb = GetMinMarginBox(style);
style = CreateStyleContextWithStates(thumb, GTK_TEXT_DIR_NONE, aStateFlags);
aMetrics->size.thumb = GetMinMarginBox(style);
gtk_style_context_get_margin(style, gtk_style_context_get_state(style),
&aMetrics->margin.thumb);
g_object_unref(style);
// track
style = CreateStyleContextWithStates(track, GTK_TEXT_DIR_NONE,
aActive ? GTK_STATE_FLAG_PRELIGHT :
GTK_STATE_FLAG_NORMAL);
metrics->border.track = GetMarginBorderPadding(style);
MozGtkSize trackMinSize = GetMinContentBox(style) + metrics->border.track;
MozGtkSize trackSizeForThumb = metrics->size.thumb + metrics->border.track;
style = CreateStyleContextWithStates(track, GTK_TEXT_DIR_NONE, aStateFlags);
aMetrics->border.track = GetMarginBorderPadding(style);
MozGtkSize trackMinSize = GetMinContentBox(style) + aMetrics->border.track;
MozGtkSize trackSizeForThumb = aMetrics->size.thumb + aMetrics->border.track;
g_object_unref(style);
// button
if (hasButtons) {
style = CreateStyleContextWithStates(MOZ_GTK_SCROLLBAR_BUTTON,
GTK_TEXT_DIR_NONE,
aActive ? GTK_STATE_FLAG_PRELIGHT :
GTK_STATE_FLAG_NORMAL);
metrics->size.button = GetMinMarginBox(style);
GTK_TEXT_DIR_NONE, aStateFlags);
aMetrics->size.button = GetMinMarginBox(style);
g_object_unref(style);
} else {
metrics->size.button = {0, 0};
aMetrics->size.button = {0, 0};
}
if (aOrientation == GTK_ORIENTATION_HORIZONTAL) {
metrics->size.button.Rotate();
aMetrics->size.button.Rotate();
// If the track is wider than necessary for the thumb, including when
// the buttons will cause Gecko to expand the track to fill
// available breadth, then add to the track border to prevent Gecko
// from expanding the thumb to fill available breadth.
gint extra =
std::max(trackMinSize.height,
metrics->size.button.height) - trackSizeForThumb.height;
aMetrics->size.button.height) - trackSizeForThumb.height;
if (extra > 0) {
// If extra is odd, then the thumb is 0.5 pixels above
// center as in gtk_range_compute_slider_position().
metrics->border.track.top += extra / 2;
metrics->border.track.bottom += extra - extra / 2;
aMetrics->border.track.top += extra / 2;
aMetrics->border.track.bottom += extra - extra / 2;
// Update size for change in border.
trackSizeForThumb.height += extra;
}
} else {
gint extra =
std::max(trackMinSize.width,
metrics->size.button.width) - trackSizeForThumb.width;
aMetrics->size.button.width) - trackSizeForThumb.width;
if (extra > 0) {
// If extra is odd, then the thumb is 0.5 pixels to the left
// of center as in gtk_range_compute_slider_position().
metrics->border.track.left += extra / 2;
metrics->border.track.right += extra - extra / 2;
aMetrics->border.track.left += extra / 2;
aMetrics->border.track.right += extra - extra / 2;
trackSizeForThumb.width += extra;
}
}
style = CreateStyleContextWithStates(contents, GTK_TEXT_DIR_NONE,
aActive ? GTK_STATE_FLAG_PRELIGHT :
GTK_STATE_FLAG_NORMAL);
aStateFlags);
GtkBorder contentsBorder = GetMarginBorderPadding(style);
g_object_unref(style);
metrics->size.scrollbar =
trackSizeForThumb + contentsBorder + metrics->border.scrollbar;
aMetrics->size.scrollbar =
trackSizeForThumb + contentsBorder + aMetrics->border.scrollbar;
}
return metrics;
const ScrollbarGTKMetrics*
GetScrollbarMetrics(GtkOrientation aOrientation)
{
auto metrics = &sScrollbarMetrics[aOrientation];
if (!metrics->initialized) {
InitScrollbarMetrics(metrics, aOrientation, GTK_STATE_FLAG_NORMAL);
// We calculate thumb margin here because it's composited from
// thumb class margin + difference margin between active and inactive
// scrollbars. It's a workaround which alows us to emulate
// overlay scrollbars for some Gtk+ themes (Ubuntu/Ambiance),
// when an inactive scrollbar thumb is smaller than the active one.
const ScrollbarGTKMetrics *metricsActive =
GetActiveScrollbarMetrics(aOrientation);
if (metrics->size.thumb < metricsActive->size.thumb) {
metrics->margin.thumb +=
(metrics->border.scrollbar + metrics->border.track) -
(metricsActive->border.scrollbar + metricsActive->border.track);
}
metrics->initialized = true;
}
return metrics;
}
const ScrollbarGTKMetrics*
GetActiveScrollbarMetrics(GtkOrientation aOrientation)
{
auto metrics = &sActiveScrollbarMetrics[aOrientation];
if (!metrics->initialized) {
InitScrollbarMetrics(metrics, aOrientation, GTK_STATE_FLAG_PRELIGHT);
metrics->initialized = true;
}
return metrics;
}
/*

View File

@ -82,6 +82,9 @@ typedef struct {
GtkBorder scrollbar;
GtkBorder track;
} border;
struct {
GtkBorder thumb;
} margin;
} ScrollbarGTKMetrics;
typedef struct {
@ -500,11 +503,17 @@ moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint*
/**
* Get the metrics in GTK pixels for a scrollbar.
* aOrientation: [IN] the scrollbar orientation
* aActive: [IN] Metricts for scrollbar with mouse pointer over it.
*
*/
const ScrollbarGTKMetrics*
GetScrollbarMetrics(GtkOrientation aOrientation, bool aActive = false);
GetScrollbarMetrics(GtkOrientation aOrientation);
/**
* Get the metrics in GTK pixels for a scrollbar which is active
* (selected by mouse pointer).
* aOrientation: [IN] the scrollbar orientation
*/
const ScrollbarGTKMetrics*
GetActiveScrollbarMetrics(GtkOrientation aOrientation);
/**
* Get the desired size of a dropdown arrow button

View File

@ -1288,7 +1288,8 @@ nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
GtkOrientation orientation =
aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ?
GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true);
const ScrollbarGTKMetrics* metrics =
GetActiveScrollbarMetrics(orientation);
const GtkBorder& border = metrics->border.scrollbar;
aResult->top = border.top;
@ -1303,7 +1304,8 @@ nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
GtkOrientation orientation =
aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL ?
GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true);
const ScrollbarGTKMetrics* metrics =
GetActiveScrollbarMetrics(orientation);
const GtkBorder& border = metrics->border.track;
aResult->top = border.top;
@ -1458,7 +1460,7 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
case NS_THEME_SCROLLBARBUTTON_DOWN:
{
const ScrollbarGTKMetrics* metrics =
GetScrollbarMetrics(GTK_ORIENTATION_VERTICAL, true);
GetActiveScrollbarMetrics(GTK_ORIENTATION_VERTICAL);
aResult->width = metrics->size.button.width;
aResult->height = metrics->size.button.height;
@ -1469,7 +1471,7 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
case NS_THEME_SCROLLBARBUTTON_RIGHT:
{
const ScrollbarGTKMetrics* metrics =
GetScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL, true);
GetActiveScrollbarMetrics(GTK_ORIENTATION_HORIZONTAL);
aResult->width = metrics->size.button.width;
aResult->height = metrics->size.button.height;
@ -1502,7 +1504,8 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
GtkOrientation orientation =
aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ?
GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true);
const ScrollbarGTKMetrics* metrics =
GetActiveScrollbarMetrics(orientation);
aResult->width = metrics->size.scrollbar.width;
aResult->height = metrics->size.scrollbar.height;
@ -1514,7 +1517,8 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
GtkOrientation orientation =
aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL ?
GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
const ScrollbarGTKMetrics* metrics = GetScrollbarMetrics(orientation, true);
const ScrollbarGTKMetrics* metrics =
GetActiveScrollbarMetrics(orientation);
aResult->width = metrics->size.thumb.width;
aResult->height = metrics->size.thumb.height;