Bug 1850827 - Implement rounded bottom corners in GTK. r=rmader

The implementation is uglier than it needs to be. We basically need to
override the GTK styles for the window decorations with the desired
radius.

This is because of two reasons:

 * Adwaita on gtk3 doesn't provide a bottom corner radius.
 * Even if it did we couldn't reasonably query it, see comment 4.

So in order for stuff to look sensible we need to make sure that we and
GTK agree on what radius to use. Using the titlebar radius makes sense
here.

Differential Revision: https://phabricator.services.mozilla.com/D187343
This commit is contained in:
Emilio Cobos Álvarez 2023-09-05 18:07:08 +00:00
Родитель f0d8d07c60
Коммит 884175baf6
9 изменённых файлов: 125 добавлений и 30 удалений

Просмотреть файл

@ -294,19 +294,41 @@ menuitem.bookmark-item {
color: -moz-headerbarinactivetext;
}
:root[tabsintitlebar] #navigator-toolbox-background {
appearance: auto;
-moz-default-appearance: -moz-window-titlebar-maximized;
background-color: transparent;
color: CaptionText;
:root[tabsintitlebar] {
#navigator-toolbox-background {
appearance: auto;
-moz-default-appearance: -moz-window-titlebar-maximized;
background-color: transparent;
color: CaptionText;
}
#navigator-toolbox-background:-moz-window-inactive {
color: InactiveCaptionText;
}
/* When temporarily showing the menu bar, make it at least as tall as the tab
* bar such that the window controls don't appear to move up. */
#toolbar-menubar[autohide="true"] {
height: calc(var(--tab-min-height) + 2 * var(--tab-block-margin));
}
}
:root[tabsintitlebar] #navigator-toolbox-background:-moz-window-inactive {
color: InactiveCaptionText;
}
:root[tabsintitlebar][sizemode="normal"]:not([gtktiledwindow="true"]) {
#navigator-toolbox-background {
-moz-default-appearance: -moz-window-titlebar;
}
:root[tabsintitlebar][sizemode="normal"]:not([gtktiledwindow="true"]) #navigator-toolbox-background {
-moz-default-appearance: -moz-window-titlebar;
#navigator-toolbox:-moz-lwtheme, dialog::backdrop {
border-top-left-radius: env(-moz-gtk-csd-titlebar-radius);
border-top-right-radius: env(-moz-gtk-csd-titlebar-radius);
}
@media (-moz-gtk-csd-rounded-bottom-corners) {
body {
border-bottom-left-radius: env(-moz-gtk-csd-titlebar-radius);
border-bottom-right-radius: env(-moz-gtk-csd-titlebar-radius);
}
}
}
/*
@ -322,18 +344,6 @@ menuitem.bookmark-item {
color: inherit;
}
:root[tabsintitlebar][sizemode="normal"]:not([gtktiledwindow="true"]) #navigator-toolbox:-moz-lwtheme,
:root[tabsintitlebar][sizemode="normal"]:not([gtktiledwindow="true"]) dialog::backdrop {
border-top-left-radius: env(-moz-gtk-csd-titlebar-radius);
border-top-right-radius: env(-moz-gtk-csd-titlebar-radius);
}
/* When temporarily showing the menu bar, make it at least as tall as the tab
* bar such that the window controls don't appear to move up. */
:root[tabsintitlebar] #toolbar-menubar[autohide="true"] {
height: calc(var(--tab-min-height) + 2 * var(--tab-block-margin));
}
/* The button box must appear on top of the navigator-toolbox in order for
* click and hover mouse events to work properly for the button in the restored
* window state. Otherwise, elements in the navigator-toolbox, like the menubar,

Просмотреть файл

@ -15271,6 +15271,16 @@
value: true
mirror: always
# Whether we enable rounded bottom corners on GTK by default.
#
# The implementation is a bit hacky (see details in bug 1850827) so behind a
# pref for emergency purposes.
- name: widget.gtk.rounded-bottom-corners.enabled
type: bool
value: true
mirror: always
rust: true
# Whether selection colors for the non-system theme get passed from the system
# GTK theme.
- name: widget.gtk.alt-theme.selection

Просмотреть файл

@ -684,7 +684,7 @@ macro_rules! bool_pref_feature {
/// to support new types in these entries and (2) ensuring that either
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change.
pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [
pub static MEDIA_FEATURES: [QueryFeatureDescription; 61] = [
feature!(
atom!("width"),
AllowsRanges::Yes,
@ -992,9 +992,11 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [
),
lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme),
lnf_int_feature!(atom!("-moz-panel-animations"), PanelAnimations),
// media query for popover attribute
bool_pref_feature!(atom!("-moz-popover-enabled"), "dom.element.popover.enabled"),
// media query for MathML Core's implementation of mi
bool_pref_feature!(
atom!("-moz-gtk-csd-rounded-bottom-corners"),
"widget.gtk.rounded-bottom-corners.enabled"
),
bool_pref_feature!(
atom!("-moz-mathml-core-mi"),
"mathml.legacy_mathvariant_attribute.disabled"

Просмотреть файл

@ -40,6 +40,7 @@ GOBJECT_TRAITS(GdkPixbuf)
GOBJECT_TRAITS(GCancellable)
GOBJECT_TRAITS(GtkIMContext)
GOBJECT_TRAITS(GUnixFDList)
GOBJECT_TRAITS(GtkCssProvider)
#undef GOBJECT_TRAITS

Просмотреть файл

@ -524,6 +524,11 @@ static wl_region* moz_container_wayland_create_opaque_region(
wl_region_subtract(region, aX, aY, aCornerRadius, aCornerRadius);
wl_region_subtract(region, aX + aWidth - aCornerRadius, aY, aCornerRadius,
aCornerRadius);
wl_region_subtract(region, aX, aY + aHeight - aCornerRadius, aCornerRadius,
aCornerRadius);
wl_region_subtract(region, aX + aWidth - aCornerRadius,
aY + aHeight - aCornerRadius, aCornerRadius,
aCornerRadius);
}
return region;
}

Просмотреть файл

@ -187,6 +187,7 @@ nsLookAndFeel::nsLookAndFeel() {
}
nsLookAndFeel::~nsLookAndFeel() {
ClearRoundedCornerProvider();
if (mDBusSettingsProxy) {
g_signal_handlers_disconnect_by_func(
mDBusSettingsProxy, FuncToGpointer(settings_changed_signal_cb), this);
@ -1210,8 +1211,9 @@ void nsLookAndFeel::RestoreSystemTheme() {
"gtk-application-prefer-dark-theme",
mSystemTheme.mPreferDarkTheme, nullptr);
}
moz_gtk_refresh();
mSystemThemeOverridden = false;
UpdateRoundedBottomCornerStyles();
moz_gtk_refresh();
}
static bool AnyColorChannelIsDifferent(nscolor aColor) {
@ -1327,6 +1329,46 @@ void nsLookAndFeel::ConfigureAndInitializeAltTheme() {
// Right now we're using the opposite color-scheme theme, make sure to record
// it.
mSystemThemeOverridden = true;
UpdateRoundedBottomCornerStyles();
}
void nsLookAndFeel::ClearRoundedCornerProvider() {
if (mRoundedCornerProvider) {
gtk_style_context_remove_provider_for_screen(
gdk_screen_get_default(),
GTK_STYLE_PROVIDER(mRoundedCornerProvider.get()));
mRoundedCornerProvider = nullptr;
}
}
void nsLookAndFeel::UpdateRoundedBottomCornerStyles() {
ClearRoundedCornerProvider();
if (!StaticPrefs::widget_gtk_rounded_bottom_corners_enabled()) {
return;
}
int32_t radius = EffectiveTheme().mTitlebarRadius;
if (!radius) {
return;
}
mRoundedCornerProvider = dont_AddRef(gtk_css_provider_new());
nsPrintfCString string(
"window.csd decoration {"
"border-bottom-right-radius: %dpx;"
"border-bottom-left-radius: %dpx;"
"}\n",
radius, radius);
GUniquePtr<GError> error;
if (!gtk_css_provider_load_from_data(mRoundedCornerProvider.get(),
string.get(), string.Length(),
getter_Transfers(error))) {
NS_WARNING(nsPrintfCString("Failed to load provider: %s - %s\n",
string.get(), error ? error->message : nullptr)
.get());
}
gtk_style_context_add_provider_for_screen(
gdk_screen_get_default(),
GTK_STYLE_PROVIDER(mRoundedCornerProvider.get()),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
Maybe<ColorScheme> nsLookAndFeel::ComputeColorSchemeSetting() {
@ -1546,8 +1588,9 @@ void nsLookAndFeel::ConfigureFinalEffectiveTheme() {
"gtk-application-prefer-dark-theme",
mAltTheme.mPreferDarkTheme, nullptr);
}
moz_gtk_refresh();
mSystemThemeOverridden = true;
UpdateRoundedBottomCornerStyles();
moz_gtk_refresh();
}
}

Просмотреть файл

@ -16,6 +16,7 @@
enum WidgetNodeType : int;
struct _GtkStyle;
typedef struct _GDBusProxy GDBusProxy;
typedef struct _GtkCssProvider GtkCssProvider;
class nsLookAndFeel final : public nsXPLookAndFeel {
public:
@ -174,6 +175,11 @@ class nsLookAndFeel final : public nsXPLookAndFeel {
int32_t mCSDMinimizeButtonPosition = 0;
int32_t mCSDCloseButtonPosition = 0;
RefPtr<GtkCssProvider> mRoundedCornerProvider;
void UpdateRoundedBottomCornerStyles();
void ClearRoundedCornerProvider();
void EnsureInit() {
if (mInitialized) {
return;

Просмотреть файл

@ -6922,7 +6922,8 @@ LayoutDeviceIntCoord nsWindow::GetTitlebarRadius() {
// to draw transparent corners of default Gtk titlebar.
// Both implementations (cairo_region_t and wl_region) needs to be synced.
static void SubtractTitlebarCorners(cairo_region_t* aRegion, int aX, int aY,
int aWindowWidth, int aTitlebarRadius) {
int aWindowWidth, int aWindowHeight,
int aTitlebarRadius) {
if (!aTitlebarRadius) {
return;
}
@ -6935,9 +6936,23 @@ static void SubtractTitlebarCorners(cairo_region_t* aRegion, int aX, int aY,
aTitlebarRadius,
};
cairo_region_subtract_rectangle(aRegion, &rect);
rect = {
aX,
aY + aWindowHeight - aTitlebarRadius,
aTitlebarRadius,
aTitlebarRadius,
};
cairo_region_subtract_rectangle(aRegion, &rect);
rect = {
aX + aWindowWidth - aTitlebarRadius,
aY + aWindowHeight - aTitlebarRadius,
aTitlebarRadius,
aTitlebarRadius,
};
cairo_region_subtract_rectangle(aRegion, &rect);
}
void nsWindow::UpdateTopLevelOpaqueRegion(void) {
void nsWindow::UpdateTopLevelOpaqueRegion() {
if (!mCompositedScreen) {
return;
}
@ -6962,8 +6977,10 @@ void nsWindow::UpdateTopLevelOpaqueRegion(void) {
cairo_rectangle_int_t rect = {x, y, width, height};
cairo_region_union_rectangle(region, &rect);
// TODO: We actually could get a proper opaque region from layout, see
// nsIWidget::UpdateOpaqueRegion. This could simplify titlebar drawing.
int radius = DoDrawTilebarCorners() ? int(GetTitlebarRadius()) : 0;
SubtractTitlebarCorners(region, x, y, width, radius);
SubtractTitlebarCorners(region, x, y, width, height, radius);
gdk_window_set_opaque_region(window, region);

Просмотреть файл

@ -2258,6 +2258,7 @@ STATIC_ATOMS = [
Atom("_moz_gtk_csd_close_button", "-moz-gtk-csd-close-button"),
Atom("_moz_gtk_csd_close_button_position", "-moz-gtk-csd-close-button-position"),
Atom("_moz_gtk_csd_reversed_placement", "-moz-gtk-csd-reversed-placement"),
Atom("_moz_gtk_csd_rounded_bottom_corners", "-moz-gtk-csd-rounded-bottom-corners"),
Atom("_moz_content_prefers_color_scheme", "-moz-content-prefers-color-scheme"),
Atom("_moz_content_preferred_color_scheme", "-moz-content-preferred-color-scheme"),
Atom("_moz_system_dark_theme", "-moz-system-dark-theme"),