Bug 1964046 - Use HiDPI symbolic icons for the titlebar. r=stransky,desktop-theme-reviewers,dao

This looks better on the native theme, and on the built-in themes too.

It's a mixed improvement and regression for non-native themes:

 * On one hand, we use the system icons.
 * On the other, we don't use the theme fill (which usually would be
   used via context-fill / context-stroke).

So over-all I think it's worth it.

Differential Revision: https://phabricator.services.mozilla.com/D248603
This commit is contained in:
Emilio Cobos Álvarez
2025-05-14 09:45:06 +00:00
committed by ealvarez@mozilla.com
parent 3257301b11
commit e6d482c37f
5 changed files with 54 additions and 266 deletions

View File

@@ -189,18 +189,6 @@
padding-inline: calc(env(-moz-gtk-csd-titlebar-button-spacing) / 2); padding-inline: calc(env(-moz-gtk-csd-titlebar-button-spacing) / 2);
> .toolbarbutton-icon { > .toolbarbutton-icon {
appearance: auto;
}
/* stylelint-disable-next-line media-query-no-invalid */
@media -moz-pref("widget.gtk.non-native-titlebar-buttons.enabled") {
/* When using lightweight themes, use our own buttons since native ones might
* assume a native background in order to be visible. */
:root[lwtheme] & {
padding-inline: 3px;
> .toolbarbutton-icon {
appearance: none;
border-radius: 100%; border-radius: 100%;
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -209,16 +197,16 @@
/* Roughly the Adwaita size */ /* Roughly the Adwaita size */
width: 24px; width: 24px;
height: 24px; height: 24px;
} /* Matches the libadwaita transition */
} transition: background-color .2s ease;
} }
&:hover > .toolbarbutton-icon { &:hover > .toolbarbutton-icon {
background-color: color-mix(in srgb, currentColor 12%, transparent); background-color: color-mix(in srgb, currentColor 15%, transparent);
} }
&:hover:active > .toolbarbutton-icon { &:hover:active > .toolbarbutton-icon {
background-color: color-mix(in srgb, currentColor 20%, transparent); background-color: color-mix(in srgb, currentColor 30%, transparent);
} }
&:focus-visible > .toolbarbutton-icon { &:focus-visible > .toolbarbutton-icon {
@@ -230,16 +218,32 @@
.titlebar-max { .titlebar-max {
order: env(-moz-gtk-csd-maximize-button-position); order: env(-moz-gtk-csd-maximize-button-position);
> .toolbarbutton-icon { > .toolbarbutton-icon {
-moz-default-appearance: -moz-window-button-maximize; background-image: image-set(
background-image: url(chrome://browser/skin/window-controls/maximize.svg); url(moz-icon://stock/window-maximize-symbolic?dark=0),
url(moz-icon://stock/window-maximize-symbolic?dark=0&scale=2) 2x
);
@media (prefers-color-scheme: dark) {
background-image: image-set(
url(moz-icon://stock/window-maximize-symbolic?dark=1),
url(moz-icon://stock/window-maximize-symbolic?dark=1&scale=2) 2x
);
}
} }
} }
.titlebar-restore { .titlebar-restore {
order: env(-moz-gtk-csd-maximize-button-position); order: env(-moz-gtk-csd-maximize-button-position);
> .toolbarbutton-icon { > .toolbarbutton-icon {
-moz-default-appearance: -moz-window-button-restore; background-image: image-set(
background-image: url(chrome://browser/skin/window-controls/restore.svg); url(moz-icon://stock/window-restore-symbolic?dark=0),
url(moz-icon://stock/window-restore-symbolic?dark=0&scale=2) 2x
);
@media (prefers-color-scheme: dark) {
background-image: image-set(
url(moz-icon://stock/window-restore-symbolic?dark=1),
url(moz-icon://stock/window-restore-symbolic?dark=1&scale=2) 2x
);
}
} }
} }
@@ -254,17 +258,16 @@
order: env(-moz-gtk-csd-close-button-position); order: env(-moz-gtk-csd-close-button-position);
> .toolbarbutton-icon { > .toolbarbutton-icon {
-moz-default-appearance: -moz-window-button-close; background-image: image-set(
background-image: url(chrome://browser/skin/window-controls/close.svg); url(moz-icon://stock/window-close-symbolic?dark=0),
url(moz-icon://stock/window-close-symbolic?dark=0&scale=2) 2x
);
@media (prefers-color-scheme: dark) {
background-image: image-set(
url(moz-icon://stock/window-close-symbolic?dark=1),
url(moz-icon://stock/window-close-symbolic?dark=1&scale=2) 2x
);
} }
&:hover > .toolbarbutton-icon {
background-color: #d70022;
color: white;
}
&:hover:active > .toolbarbutton-icon {
background-color: #ff0039;
} }
@media not (-moz-gtk-csd-close-button) { @media not (-moz-gtk-csd-close-button) {
@@ -276,8 +279,16 @@
order: env(-moz-gtk-csd-minimize-button-position); order: env(-moz-gtk-csd-minimize-button-position);
> .toolbarbutton-icon { > .toolbarbutton-icon {
-moz-default-appearance: -moz-window-button-minimize; background-image: image-set(
background-image: url(chrome://browser/skin/window-controls/minimize.svg); url(moz-icon://stock/window-minimize-symbolic?dark=0),
url(moz-icon://stock/window-minimize-symbolic?dark=0&scale=2) 2x
);
@media (prefers-color-scheme: dark) {
background-image: image-set(
url(moz-icon://stock/window-minimize-symbolic?dark=1),
url(moz-icon://stock/window-minimize-symbolic?dark=1&scale=2) 2x
);
}
} }
@media not (-moz-gtk-csd-minimize-button) { @media not (-moz-gtk-csd-minimize-button) {

View File

@@ -17,7 +17,3 @@ browser.jar:
skin/classic/browser/places/organizer.css (places/organizer.css) skin/classic/browser/places/organizer.css (places/organizer.css)
skin/classic/browser/preferences/alwaysAsk.png (preferences/alwaysAsk.png) skin/classic/browser/preferences/alwaysAsk.png (preferences/alwaysAsk.png)
skin/classic/browser/preferences/applications.css (preferences/applications.css) skin/classic/browser/preferences/applications.css (preferences/applications.css)
skin/classic/browser/window-controls/close.svg (../windows/window-controls/close.svg)
skin/classic/browser/window-controls/maximize.svg (../windows/window-controls/maximize.svg)
skin/classic/browser/window-controls/minimize.svg (../windows/window-controls/minimize.svg)
skin/classic/browser/window-controls/restore.svg (../windows/window-controls/restore.svg)

View File

@@ -326,155 +326,6 @@ static gint moz_gtk_window_decoration_paint(cairo_t* cr,
return MOZ_GTK_SUCCESS; return MOZ_GTK_SUCCESS;
} }
gint moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left,
gint* border_bottom,
gint* border_right) {
GtkBorder* default_outside_border;
GtkStyleContext* style = GetStyleContext(MOZ_GTK_BUTTON);
gtk_style_context_get_style(style, "default-outside-border",
&default_outside_border, NULL);
if (default_outside_border) {
*border_top = default_outside_border->top;
*border_left = default_outside_border->left;
*border_bottom = default_outside_border->bottom;
*border_right = default_outside_border->right;
gtk_border_free(default_outside_border);
} else {
*border_top = *border_left = *border_bottom = *border_right = 0;
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_button_get_default_border(gint* border_top,
gint* border_left,
gint* border_bottom,
gint* border_right) {
GtkBorder* default_border;
GtkStyleContext* style = GetStyleContext(MOZ_GTK_BUTTON);
gtk_style_context_get_style(style, "default-border", &default_border, NULL);
if (default_border) {
*border_top = default_border->top;
*border_left = default_border->left;
*border_bottom = default_border->bottom;
*border_right = default_border->right;
gtk_border_free(default_border);
} else {
/* see gtkbutton.c */
*border_top = *border_left = *border_bottom = *border_right = 1;
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_button_paint(cairo_t* cr, const GdkRectangle* rect,
GtkWidgetState* state, GtkReliefStyle relief,
GtkWidget* widget,
GtkTextDirection direction) {
if (!widget) {
return MOZ_GTK_UNKNOWN_WIDGET;
}
GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
GtkStyleContext* style = gtk_widget_get_style_context(widget);
gint x = rect->x, y = rect->y, width = rect->width, height = rect->height;
gtk_widget_set_direction(widget, direction);
gtk_style_context_save(style);
StyleContextSetScale(style, state->image_scale);
gtk_style_context_set_state(style, state_flags);
if (state->isDefault && relief == GTK_RELIEF_NORMAL && !state->focused &&
!(state_flags & GTK_STATE_FLAG_PRELIGHT)) {
/* handle default borders both outside and inside the button */
gint default_top, default_left, default_bottom, default_right;
moz_gtk_button_get_default_overflow(&default_top, &default_left,
&default_bottom, &default_right);
x -= default_left;
y -= default_top;
width += default_left + default_right;
height += default_top + default_bottom;
gtk_render_background(style, cr, x, y, width, height);
gtk_render_frame(style, cr, x, y, width, height);
moz_gtk_button_get_default_border(&default_top, &default_left,
&default_bottom, &default_right);
x += default_left;
y += default_top;
width -= (default_left + default_right);
height -= (default_top + default_bottom);
} else if (relief != GTK_RELIEF_NONE || state->depressed ||
(state_flags & GTK_STATE_FLAG_PRELIGHT)) {
/* the following line can trigger an assertion (Crux theme)
file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
assertion `GDK_IS_WINDOW (window)' failed */
gtk_render_background(style, cr, x, y, width, height);
gtk_render_frame(style, cr, x, y, width, height);
}
if (state->focused) {
GtkBorder border;
gtk_style_context_get_border(style, state_flags, &border);
x += border.left;
y += border.top;
width -= (border.left + border.right);
height -= (border.top + border.bottom);
gtk_render_focus(style, cr, x, y, width, height);
}
gtk_style_context_restore(style);
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_header_bar_button_paint(cairo_t* cr, GdkRectangle* aRect,
GtkWidgetState* state,
GtkReliefStyle relief,
WidgetNodeType aIconWidgetType,
GtkTextDirection direction) {
GtkWidget* buttonWidget = GetWidget(aIconWidgetType);
if (!buttonWidget) {
return MOZ_GTK_UNKNOWN_WIDGET;
}
const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics(
aIconWidgetType == MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE
? MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE
: aIconWidgetType);
// Vertically center and clamp the rect to the desired size.
if (aRect->height > metrics->minSizeWithBorder.height) {
gint diff = aRect->height - metrics->minSizeWithBorder.height;
aRect->y += diff / 2;
aRect->height = metrics->minSizeWithBorder.height;
}
moz_gtk_button_paint(cr, aRect, state, relief, buttonWidget, direction);
GtkWidget* iconWidget =
gtk_bin_get_child(GTK_BIN(GetWidget(aIconWidgetType)));
if (!iconWidget) {
return MOZ_GTK_UNKNOWN_WIDGET;
}
cairo_surface_t* surface =
GetWidgetIconSurface(iconWidget, state->image_scale);
if (surface) {
GtkStyleContext* style = gtk_widget_get_style_context(buttonWidget);
GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
gtk_style_context_save(style);
StyleContextSetScale(style, state->image_scale);
gtk_style_context_set_state(style, state_flags);
/* This is available since Gtk+ 3.10 as well as GtkHeaderBar */
gtk_render_icon_surface(style, cr, surface,
aRect->x + metrics->iconXPosition,
aRect->y + metrics->iconYPosition);
gtk_style_context_restore(style);
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_hpaned_paint(cairo_t* cr, GdkRectangle* rect, static gint moz_gtk_hpaned_paint(cairo_t* cr, GdkRectangle* rect,
GtkWidgetState* state) { GtkWidgetState* state) {
GtkStyleContext* style = GtkStyleContext* style =
@@ -814,10 +665,6 @@ gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
case MOZ_GTK_SPLITTER_VERTICAL: case MOZ_GTK_SPLITTER_VERTICAL:
case MOZ_GTK_HEADER_BAR: case MOZ_GTK_HEADER_BAR:
case MOZ_GTK_HEADER_BAR_MAXIMIZED: case MOZ_GTK_HEADER_BAR_MAXIMIZED:
case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
/* These widgets have no borders.*/ /* These widgets have no borders.*/
case MOZ_GTK_WINDOW_DECORATION: case MOZ_GTK_WINDOW_DECORATION:
case MOZ_GTK_WINDOW_DECORATION_SOLID: case MOZ_GTK_WINDOW_DECORATION_SOLID:
@@ -901,12 +748,6 @@ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t* cr,
cairo_new_path(cr); cairo_new_path(cr);
switch (widget) { switch (widget) {
case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
return moz_gtk_header_bar_button_paint(
cr, rect, state, (GtkReliefStyle)flags, widget, direction);
case MOZ_GTK_TREEVIEW: case MOZ_GTK_TREEVIEW:
return moz_gtk_treeview_paint(cr, rect, state, direction); return moz_gtk_treeview_paint(cr, rect, state, direction);
case MOZ_GTK_FRAME: case MOZ_GTK_FRAME:

View File

@@ -184,13 +184,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance,
aState->isDefault = IsDefaultButton(aFrame); aState->isDefault = IsDefaultButton(aFrame);
aState->canDefault = FALSE; // XXX fix me aState->canDefault = FALSE; // XXX fix me
if (aAppearance == StyleAppearance::MozWindowButtonMinimize ||
aAppearance == StyleAppearance::MozWindowButtonRestore ||
aAppearance == StyleAppearance::MozWindowButtonMaximize ||
aAppearance == StyleAppearance::MozWindowButtonClose) {
aState->active &= aState->inHover;
}
if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) {
// For these widget types, some element (either a child or parent) // For these widget types, some element (either a child or parent)
// actually has element focus, so we check the focused attribute // actually has element focus, so we check the focused attribute
@@ -199,11 +192,7 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance,
} }
if (aAppearance == StyleAppearance::MozWindowTitlebar || if (aAppearance == StyleAppearance::MozWindowTitlebar ||
aAppearance == StyleAppearance::MozWindowTitlebarMaximized || aAppearance == StyleAppearance::MozWindowTitlebarMaximized) {
aAppearance == StyleAppearance::MozWindowButtonClose ||
aAppearance == StyleAppearance::MozWindowButtonMinimize ||
aAppearance == StyleAppearance::MozWindowButtonMaximize ||
aAppearance == StyleAppearance::MozWindowButtonRestore) {
aState->backdrop = aFrame->PresContext()->Document()->State().HasState( aState->backdrop = aFrame->PresContext()->Document()->State().HasState(
dom::DocumentState::WINDOW_INACTIVE); dom::DocumentState::WINDOW_INACTIVE);
} }
@@ -243,18 +232,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance,
case StyleAppearance::MozWindowDecorations: case StyleAppearance::MozWindowDecorations:
aGtkWidgetType = MOZ_GTK_WINDOW_DECORATION; aGtkWidgetType = MOZ_GTK_WINDOW_DECORATION;
break; break;
case StyleAppearance::MozWindowButtonClose:
aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE;
break;
case StyleAppearance::MozWindowButtonMinimize:
aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE;
break;
case StyleAppearance::MozWindowButtonMaximize:
aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
break;
case StyleAppearance::MozWindowButtonRestore:
aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE;
break;
default: default:
return false; return false;
} }
@@ -730,10 +707,6 @@ bool nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
} }
switch (aAppearance) { switch (aAppearance) {
case StyleAppearance::Toolbarbutton: case StyleAppearance::Toolbarbutton:
case StyleAppearance::MozWindowButtonClose:
case StyleAppearance::MozWindowButtonMinimize:
case StyleAppearance::MozWindowButtonMaximize:
case StyleAppearance::MozWindowButtonRestore:
aResult->SizeTo(0, 0, 0, 0); aResult->SizeTo(0, 0, 0, 0);
return true; return true;
default: default:
@@ -811,28 +784,6 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize(
moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &result.height); moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &result.height);
} }
} break; } break;
case StyleAppearance::MozWindowButtonClose: {
const ToolbarButtonGTKMetrics* metrics =
GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
result.width = metrics->minSizeWithBorder.width;
result.height = metrics->minSizeWithBorder.height;
break;
}
case StyleAppearance::MozWindowButtonMinimize: {
const ToolbarButtonGTKMetrics* metrics =
GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE);
result.width = metrics->minSizeWithBorder.width;
result.height = metrics->minSizeWithBorder.height;
break;
}
case StyleAppearance::MozWindowButtonMaximize:
case StyleAppearance::MozWindowButtonRestore: {
const ToolbarButtonGTKMetrics* metrics =
GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE);
result.width = metrics->minSizeWithBorder.width;
result.height = metrics->minSizeWithBorder.height;
break;
}
default: default:
break; break;
} }
@@ -878,10 +829,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
// case StyleAppearance::Tabpanel: // case StyleAppearance::Tabpanel:
case StyleAppearance::Tabpanels: case StyleAppearance::Tabpanels:
case StyleAppearance::Splitter: case StyleAppearance::Splitter:
case StyleAppearance::MozWindowButtonClose:
case StyleAppearance::MozWindowButtonMinimize:
case StyleAppearance::MozWindowButtonMaximize:
case StyleAppearance::MozWindowButtonRestore:
case StyleAppearance::MozWindowTitlebar: case StyleAppearance::MozWindowTitlebar:
case StyleAppearance::MozWindowTitlebarMaximized: case StyleAppearance::MozWindowTitlebarMaximized:
case StyleAppearance::MozWindowDecorations: case StyleAppearance::MozWindowDecorations:

View File

@@ -52,14 +52,7 @@ NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed)
const bool isXULElement = frameContent->IsXULElement(); const bool isXULElement = frameContent->IsXULElement();
if (isXULElement) { if (isXULElement) {
if (aAppearance == StyleAppearance::Checkbox || if (aAppearance == StyleAppearance::Checkbox ||
aAppearance == StyleAppearance::Radio || aAppearance == StyleAppearance::Radio) {
#ifdef MOZ_WIDGET_GTK
aAppearance == StyleAppearance::MozWindowButtonClose ||
aAppearance == StyleAppearance::MozWindowButtonMinimize ||
aAppearance == StyleAppearance::MozWindowButtonRestore ||
aAppearance == StyleAppearance::MozWindowButtonMaximize ||
#endif
false) {
aFrame = aFrame->GetParent(); aFrame = aFrame->GetParent();
frameContent = aFrame->GetContent(); frameContent = aFrame->GetContent();
} }