From e6d482c37f9fc91f95999251d1d9381a4477c42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 14 May 2025 09:45:06 +0000 Subject: [PATCH] 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 --- browser/themes/linux/browser.css | 93 ++++++++++-------- browser/themes/linux/jar.mn | 4 - widget/gtk/gtk3drawing.cpp | 159 ------------------------------- widget/gtk/nsNativeThemeGTK.cpp | 55 +---------- widget/nsNativeTheme.cpp | 9 +- 5 files changed, 54 insertions(+), 266 deletions(-) diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 195cf888a618..846c9eb0ac83 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -189,36 +189,24 @@ padding-inline: calc(env(-moz-gtk-csd-titlebar-button-spacing) / 2); > .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%; - background-position: center center; - background-repeat: no-repeat; - -moz-context-properties: stroke; - stroke: currentColor; - /* Roughly the Adwaita size */ - width: 24px; - height: 24px; - } - } + border-radius: 100%; + background-position: center center; + background-repeat: no-repeat; + -moz-context-properties: stroke; + stroke: currentColor; + /* Roughly the Adwaita size */ + width: 24px; + height: 24px; + /* Matches the libadwaita transition */ + transition: background-color .2s ease; } &: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 { - background-color: color-mix(in srgb, currentColor 20%, transparent); + background-color: color-mix(in srgb, currentColor 30%, transparent); } &:focus-visible > .toolbarbutton-icon { @@ -230,16 +218,32 @@ .titlebar-max { order: env(-moz-gtk-csd-maximize-button-position); > .toolbarbutton-icon { - -moz-default-appearance: -moz-window-button-maximize; - background-image: url(chrome://browser/skin/window-controls/maximize.svg); + background-image: image-set( + 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 { order: env(-moz-gtk-csd-maximize-button-position); > .toolbarbutton-icon { - -moz-default-appearance: -moz-window-button-restore; - background-image: url(chrome://browser/skin/window-controls/restore.svg); + background-image: image-set( + 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); > .toolbarbutton-icon { - -moz-default-appearance: -moz-window-button-close; - background-image: url(chrome://browser/skin/window-controls/close.svg); - } - - &:hover > .toolbarbutton-icon { - background-color: #d70022; - color: white; - } - - &:hover:active > .toolbarbutton-icon { - background-color: #ff0039; + background-image: image-set( + 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 + ); + } } @media not (-moz-gtk-csd-close-button) { @@ -276,8 +279,16 @@ order: env(-moz-gtk-csd-minimize-button-position); > .toolbarbutton-icon { - -moz-default-appearance: -moz-window-button-minimize; - background-image: url(chrome://browser/skin/window-controls/minimize.svg); + background-image: image-set( + 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) { diff --git a/browser/themes/linux/jar.mn b/browser/themes/linux/jar.mn index b89da964b7d6..8a12a401d3bc 100644 --- a/browser/themes/linux/jar.mn +++ b/browser/themes/linux/jar.mn @@ -17,7 +17,3 @@ browser.jar: skin/classic/browser/places/organizer.css (places/organizer.css) skin/classic/browser/preferences/alwaysAsk.png (preferences/alwaysAsk.png) 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) diff --git a/widget/gtk/gtk3drawing.cpp b/widget/gtk/gtk3drawing.cpp index 0bc8cf9538a6..1ecd60333158 100644 --- a/widget/gtk/gtk3drawing.cpp +++ b/widget/gtk/gtk3drawing.cpp @@ -326,155 +326,6 @@ static gint moz_gtk_window_decoration_paint(cairo_t* cr, 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, GtkWidgetState* state) { 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_HEADER_BAR: 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.*/ case MOZ_GTK_WINDOW_DECORATION: case MOZ_GTK_WINDOW_DECORATION_SOLID: @@ -901,12 +748,6 @@ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t* cr, cairo_new_path(cr); 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: return moz_gtk_treeview_paint(cr, rect, state, direction); case MOZ_GTK_FRAME: diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 8f881f63f810..5641829f1e57 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -184,13 +184,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, aState->isDefault = IsDefaultButton(aFrame); 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)) { // For these widget types, some element (either a child or parent) // actually has element focus, so we check the focused attribute @@ -199,11 +192,7 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, } if (aAppearance == StyleAppearance::MozWindowTitlebar || - aAppearance == StyleAppearance::MozWindowTitlebarMaximized || - aAppearance == StyleAppearance::MozWindowButtonClose || - aAppearance == StyleAppearance::MozWindowButtonMinimize || - aAppearance == StyleAppearance::MozWindowButtonMaximize || - aAppearance == StyleAppearance::MozWindowButtonRestore) { + aAppearance == StyleAppearance::MozWindowTitlebarMaximized) { aState->backdrop = aFrame->PresContext()->Document()->State().HasState( dom::DocumentState::WINDOW_INACTIVE); } @@ -243,18 +232,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, case StyleAppearance::MozWindowDecorations: aGtkWidgetType = MOZ_GTK_WINDOW_DECORATION; 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: return false; } @@ -730,10 +707,6 @@ bool nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext, } switch (aAppearance) { case StyleAppearance::Toolbarbutton: - case StyleAppearance::MozWindowButtonClose: - case StyleAppearance::MozWindowButtonMinimize: - case StyleAppearance::MozWindowButtonMaximize: - case StyleAppearance::MozWindowButtonRestore: aResult->SizeTo(0, 0, 0, 0); return true; default: @@ -811,28 +784,6 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize( moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &result.height); } } 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: break; } @@ -878,10 +829,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, // case StyleAppearance::Tabpanel: case StyleAppearance::Tabpanels: case StyleAppearance::Splitter: - case StyleAppearance::MozWindowButtonClose: - case StyleAppearance::MozWindowButtonMinimize: - case StyleAppearance::MozWindowButtonMaximize: - case StyleAppearance::MozWindowButtonRestore: case StyleAppearance::MozWindowTitlebar: case StyleAppearance::MozWindowTitlebarMaximized: case StyleAppearance::MozWindowDecorations: diff --git a/widget/nsNativeTheme.cpp b/widget/nsNativeTheme.cpp index 3b6d957613b9..ebe0e29d52ae 100644 --- a/widget/nsNativeTheme.cpp +++ b/widget/nsNativeTheme.cpp @@ -52,14 +52,7 @@ NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed) const bool isXULElement = frameContent->IsXULElement(); if (isXULElement) { if (aAppearance == StyleAppearance::Checkbox || - aAppearance == StyleAppearance::Radio || -#ifdef MOZ_WIDGET_GTK - aAppearance == StyleAppearance::MozWindowButtonClose || - aAppearance == StyleAppearance::MozWindowButtonMinimize || - aAppearance == StyleAppearance::MozWindowButtonRestore || - aAppearance == StyleAppearance::MozWindowButtonMaximize || -#endif - false) { + aAppearance == StyleAppearance::Radio) { aFrame = aFrame->GetParent(); frameContent = aFrame->GetContent(); }