Bug 1737676 - Use GTK menu radius on native context menus and panels. r=stransky

Differential Revision: https://phabricator.services.mozilla.com/D129439
This commit is contained in:
Emilio Cobos Álvarez 2021-10-28 10:52:31 +00:00
parent 6f11994c29
commit 5b31eea4fd
11 changed files with 121 additions and 50 deletions

View File

@ -75,8 +75,13 @@ fn get_titlebar_radius(device: &Device) -> VariableValue {
VariableValue::pixel(device.titlebar_radius())
}
static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 1] = [
fn get_menu_radius(device: &Device) -> VariableValue {
VariableValue::pixel(device.menu_radius())
}
static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 2] = [
make_variable!(atom!("-moz-gtk-csd-titlebar-radius"), get_titlebar_radius),
make_variable!(atom!("-moz-gtk-menu-radius"), get_menu_radius),
];
impl CssEnvironment {

View File

@ -375,6 +375,13 @@ impl Device {
}
}
/// Returns the gtk menu radius in CSS pixels.
pub fn menu_radius(&self) -> f32 {
unsafe {
bindings::Gecko_GetLookAndFeelInt(bindings::LookAndFeel_IntID::GtkMenuRadius as i32) as f32
}
}
/// Return whether the document is a chrome document.
#[inline]
pub fn is_chrome_document(&self) -> bool {

View File

@ -10,10 +10,10 @@
menupopup,
panel {
min-width: 1px;
--panel-padding: 0;
--panel-padding: max(env(-moz-gtk-menu-radius) - 1px, 0px) 0;
--panel-color: MenuText;
--panel-background: Menu;
--panel-border-radius: 0;
--panel-border-radius: env(-moz-gtk-menu-radius);
--panel-border-color: ThreeDShadow;
--panel-width: initial;
}

View File

@ -334,6 +334,9 @@ class LookAndFeel {
/** GTK titlebar radius */
TitlebarRadius,
/** GTK menu radius */
GtkMenuRadius,
/*
* Not an ID; used to define the range of valid IDs. Must be last.
*/

View File

@ -20,6 +20,13 @@ static_assert(GTK_STATE_FLAG_DIR_LTR == STATE_FLAG_DIR_LTR &&
GTK_STATE_FLAG_DIR_RTL == STATE_FLAG_DIR_RTL,
"incorrect direction state flags");
enum class CSDStyle {
Unknown,
Solid,
Normal,
};
static CSDStyle gCSDStyle = CSDStyle::Unknown;
static GtkWidget* sWidgetStorage[MOZ_GTK_WIDGET_NODE_COUNT];
static GtkStyleContext* sStyleStorage[MOZ_GTK_WIDGET_NODE_COUNT];
@ -70,6 +77,8 @@ static GtkWidget* CreateMenuBarWidget() {
static GtkWidget* CreateMenuPopupWidget() {
GtkWidget* widget = gtk_menu_new();
GtkStyleContext* style = gtk_widget_get_style_context(widget);
gtk_style_context_add_class(style, GTK_STYLE_CLASS_POPUP);
gtk_menu_attach_to_widget(GTK_MENU(widget), GetWidget(MOZ_GTK_WINDOW),
nullptr);
return widget;
@ -431,8 +440,7 @@ static GtkWidget* CreateNotebookWidget() {
return widget;
}
static void CreateHeaderBarWidget(WidgetNodeType aAppearance,
bool aIsSolidCSDStyleUsed) {
static void CreateHeaderBarWidget(WidgetNodeType aAppearance) {
sWidgetStorage[aAppearance] = gtk_header_bar_new();
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@ -452,7 +460,7 @@ static void CreateHeaderBarWidget(WidgetNodeType aAppearance,
// Headerbar has to be placed to window with csd or solid-csd style
// to properly draw the decorated.
gtk_style_context_add_class(style,
aIsSolidCSDStyleUsed ? "solid-csd" : "csd");
IsSolidCSDStyleUsed() ? "solid-csd" : "csd");
GtkWidget* fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
@ -597,6 +605,22 @@ static bool IsToolbarButtonEnabled(ButtonLayout* aButtonLayout,
return false;
}
bool IsSolidCSDStyleUsed() {
if (gCSDStyle == CSDStyle::Unknown) {
bool solid;
{
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_titlebar(GTK_WINDOW(window), gtk_header_bar_new());
gtk_widget_realize(window);
GtkStyleContext* windowStyle = gtk_widget_get_style_context(window);
solid = gtk_style_context_has_class(windowStyle, "solid-csd");
gtk_widget_destroy(window);
}
gCSDStyle = solid ? CSDStyle::Solid : CSDStyle::Normal;
}
return gCSDStyle == CSDStyle::Solid;
}
static void CreateHeaderBarButtons() {
GtkWidget* headerBar = sWidgetStorage[MOZ_GTK_HEADER_BAR];
MOZ_ASSERT(headerBar != nullptr, "We're missing header bar widget!");
@ -634,18 +658,8 @@ static void CreateHeaderBarButtons() {
}
static void CreateHeaderBar() {
const bool isSolidCSDStyleUsed = []() {
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_titlebar(GTK_WINDOW(window), gtk_header_bar_new());
gtk_widget_realize(window);
GtkStyleContext* windowStyle = gtk_widget_get_style_context(window);
bool ret = gtk_style_context_has_class(windowStyle, "solid-csd");
gtk_widget_destroy(window);
return ret;
}();
CreateHeaderBarWidget(MOZ_GTK_HEADER_BAR, isSolidCSDStyleUsed);
CreateHeaderBarWidget(MOZ_GTK_HEADER_BAR_MAXIMIZED, isSolidCSDStyleUsed);
CreateHeaderBarWidget(MOZ_GTK_HEADER_BAR);
CreateHeaderBarWidget(MOZ_GTK_HEADER_BAR_MAXIMIZED);
CreateHeaderBarButtons();
}
@ -1152,6 +1166,13 @@ static GtkStyleContext* GetCssNodeStyleInternal(WidgetNodeType aNodeType) {
false, "MOZ_GTK_HEADER_BAR_BUTTON_RESTORE is used as an icon only!");
return nullptr;
}
case MOZ_GTK_MENUPOPUP_DECORATION: {
GtkStyleContext* parentStyle =
CreateSubStyleWithClass(MOZ_GTK_MENUPOPUP, "csd");
style = CreateCSSNode("decoration", parentStyle);
g_object_unref(parentStyle);
break;
}
case MOZ_GTK_WINDOW_DECORATION: {
GtkStyleContext* parentStyle =
CreateSubStyleWithClass(MOZ_GTK_WINDOW, "csd");
@ -1309,12 +1330,14 @@ static GtkStyleContext* GetWidgetStyleInternal(WidgetNodeType aNodeType) {
return style;
}
void ResetWidgetCache(void) {
void ResetWidgetCache() {
for (int i = 0; i < MOZ_GTK_WIDGET_NODE_COUNT; i++) {
if (sStyleStorage[i]) g_object_unref(sStyleStorage[i]);
}
mozilla::PodArrayZero(sStyleStorage);
gCSDStyle = CSDStyle::Unknown;
/* This will destroy all of our widgets */
if (sWidgetStorage[MOZ_GTK_WINDOW]) {
gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);

View File

@ -50,7 +50,7 @@ GtkStyleContext* CreateStyleContextWithStates(
GtkTextDirection aDirection = GTK_TEXT_DIR_NONE,
GtkStateFlags aStateFlags = GTK_STATE_FLAG_NORMAL);
void ResetWidgetCache(void);
void ResetWidgetCache();
bool IsSolidCSDStyleUsed();

View File

@ -347,6 +347,8 @@ enum WidgetNodeType : int {
MOZ_GTK_WINDOW_DECORATION,
MOZ_GTK_WINDOW_DECORATION_SOLID,
MOZ_GTK_MENUPOPUP_DECORATION,
MOZ_GTK_WIDGET_NODE_COUNT
};

View File

@ -122,6 +122,20 @@ nsLookAndFeel::~nsLookAndFeel() {
gtk_settings_get_default(), FuncToGpointer(settings_changed_cb), nullptr);
}
#if 0
static void DumpStyleContext(GtkStyleContext* aStyle) {
static auto sGtkStyleContextToString =
reinterpret_cast<char* (*)(GtkStyleContext*, gint)>(
dlsym(RTLD_DEFAULT, "gtk_style_context_to_string"));
char* str = sGtkStyleContextToString(aStyle, ~0);
printf("%s\n", str);
g_free(str);
str = gtk_widget_path_to_string(gtk_style_context_get_path(aStyle));
printf("%s\n", str);
g_free(str);
}
#endif
// Modifies color |*aDest| as if a pattern of color |aSource| was painted with
// CAIRO_OPERATOR_OVER to a surface with color |*aDest|.
static void ApplyColorOver(const GdkRGBA& aSource, GdkRGBA* aDest) {
@ -837,6 +851,11 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
aResult = EffectiveTheme().mTitlebarRadius;
break;
}
case IntID::GtkMenuRadius: {
EnsureInit();
aResult = EffectiveTheme().mMenuRadius;
break;
}
case IntID::AllowOverlayScrollbarsOverlap: {
aResult = 1;
break;
@ -1406,6 +1425,35 @@ static nscolor GetBackgroundColor(
return NS_TRANSPARENT;
}
static int32_t GetBorderRadius(GtkStyleContext* aStyle) {
GValue value = G_VALUE_INIT;
// NOTE(emilio): In an ideal world, we'd query the two longhands
// (border-top-left-radius and border-top-right-radius) separately. However,
// that doesn't work (GTK rejects the query with:
//
// Style property "border-top-left-radius" is not gettable
//
// However! Getting border-radius does work, and it does return the
// border-top-left-radius as a gint:
//
// https://docs.gtk.org/gtk3/const.STYLE_PROPERTY_BORDER_RADIUS.html
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-20/gtk/gtkcssshorthandpropertyimpl.c#L961-977
//
// So we abuse this fact, and make the assumption here that the
// border-top-{left,right}-radius are the same, and roll with it.
gtk_style_context_get_property(aStyle, "border-radius", GTK_STATE_FLAG_NORMAL,
&value);
auto unset = MakeScopeExit([&] { g_value_unset(&value); });
auto type = G_VALUE_TYPE(&value);
if (type == G_TYPE_INT) {
return g_value_get_int(&value);
}
NS_WARNING(nsPrintfCString("Unknown value type %lu for titlebar radius", type)
.get());
return 0;
}
void nsLookAndFeel::PerThemeData::Init() {
mName = GetGtkTheme();
@ -1533,36 +1581,7 @@ void nsLookAndFeel::PerThemeData::Init() {
mTitlebarInactiveText = GDK_RGBA_TO_NS_RGBA(color);
mTitlebarInactiveBackground =
GetBackgroundColor(style, mTitlebarText, GTK_STATE_FLAG_BACKDROP);
GValue value = G_VALUE_INIT;
// NOTE(emilio): In an ideal world, we'd query the two longhands
// (border-top-left-radius and border-top-right-radius) separately. However,
// that doesn't work (GTK rejects the query with:
//
// Style property "border-top-left-radius" is not gettable
//
// However! Getting border-radius does work, and it does return the
// border-top-left-radius as a gint:
//
// https://docs.gtk.org/gtk3/const.STYLE_PROPERTY_BORDER_RADIUS.html
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-20/gtk/gtkcssshorthandpropertyimpl.c#L961-977
//
// So we abuse this fact, and make the assumption here that the
// border-top-{left,right}-radius are the same, and roll with it.
gtk_style_context_get_property(style, "border-radius",
GTK_STATE_FLAG_NORMAL, &value);
mTitlebarRadius = [&]() -> int {
auto type = G_VALUE_TYPE(&value);
if (type == G_TYPE_INT) {
return g_value_get_int(&value);
}
NS_WARNING(
nsPrintfCString("Unknown value type %lu for titlebar radius", type)
.get());
return 0;
}();
g_value_unset(&value);
mTitlebarRadius = IsSolidCSDStyleUsed() ? 0 : GetBorderRadius(style);
}
style = GetStyleContext(MOZ_GTK_MENUPOPUP);
@ -1585,6 +1604,14 @@ void nsLookAndFeel::PerThemeData::Init() {
"background");
return mMozWindowBackground;
}();
mMenuRadius = 0;
if (!IsSolidCSDStyleUsed()) {
mMenuRadius = GetBorderRadius(style);
if (!mMenuRadius) {
mMenuRadius =
GetBorderRadius(GetStyleContext(MOZ_GTK_MENUPOPUP_DECORATION));
}
}
style = GetStyleContext(MOZ_GTK_MENUITEM);
gtk_style_context_get_color(style, GTK_STATE_FLAG_PRELIGHT, &color);
@ -1825,6 +1852,7 @@ void nsLookAndFeel::PerThemeData::Init() {
NS_SUCCEEDED(rv) ? color : 0);
}
LOGLNF(" * titlebar-radius: %d\n", mTitlebarRadius);
LOGLNF(" * menu-radius: %d\n", mMenuRadius);
}
}

View File

@ -124,6 +124,7 @@ class nsLookAndFeel final : public nsXPLookAndFeel {
float mCaretRatio = 0.0f;
int32_t mTitlebarRadius = 0;
int32_t mMenuRadius = 0;
char16_t mInvisibleCharacter = 0;
bool mMenuSupportsDrag = false;

View File

@ -175,6 +175,7 @@ static const char sIntPrefs[][43] = {
"ui.systemHorizontalScrollbarHeight",
"ui.touchDeviceSupportPresent",
"ui.titlebarRadius",
"ui.GtkMenuRadius",
};
static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),

View File

@ -2231,6 +2231,7 @@ STATIC_ATOMS = [
Atom("_moz_gtk_csd_maximize_button", "-moz-gtk-csd-maximize-button"),
Atom("_moz_gtk_csd_close_button", "-moz-gtk-csd-close-button"),
Atom("_moz_gtk_csd_reversed_placement", "-moz-gtk-csd-reversed-placement"),
Atom("_moz_gtk_menu_radius", "-moz-gtk-menu-radius"),
Atom("_moz_proton", "-moz-proton"),
Atom("_moz_proton_places_tooltip", "-moz-proton-places-tooltip"),
Atom("_moz_system_dark_theme", "-moz-system-dark-theme"),