/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * This file contains painting functions for each of the gtk2 widgets. * Adapted from the gtkdrawing.c, and gtk+2.0 source. */ #include #include #include #include "gdk/gdk.h" #include "gtkdrawing.h" #include "mozilla/Assertions.h" #include "mozilla/ScopeExit.h" #include "prinrval.h" #include "WidgetStyleCache.h" #include "nsString.h" #include "nsDebug.h" #include "WidgetUtilsGtk.h" #include #include static ToolbarGTKMetrics sToolbarMetrics; using mozilla::Span; #if 0 // It's used for debugging only to compare Gecko widget style with // the ones used by Gtk+ applications. static void style_path_print(GtkStyleContext *context) { const GtkWidgetPath* path = gtk_style_context_get_path(context); static auto sGtkWidgetPathToStringPtr = (char * (*)(const GtkWidgetPath *)) dlsym(RTLD_DEFAULT, "gtk_widget_path_to_string"); fprintf(stderr, "Style path:\n%s\n\n", sGtkWidgetPathToStringPtr(path)); } #endif void moz_gtk_init() { moz_gtk_refresh(); } void moz_gtk_refresh() { sToolbarMetrics.initialized = false; /* This will destroy all of our widgets */ ResetWidgetCache(); } size_t GetGtkHeaderBarButtonLayout(Span aButtonLayout, bool* aReversedButtonsPlacement) { gchar* decorationLayoutSetting = nullptr; GtkSettings* settings = gtk_settings_get_default(); g_object_get(settings, "gtk-decoration-layout", &decorationLayoutSetting, nullptr); auto free = mozilla::MakeScopeExit([&] { g_free(decorationLayoutSetting); }); // Use a default layout const gchar* decorationLayout = "menu:minimize,maximize,close"; if (decorationLayoutSetting) { decorationLayout = decorationLayoutSetting; } // "minimize,maximize,close:" layout means buttons are on the opposite // titlebar side. close button is always there. if (aReversedButtonsPlacement) { const char* closeButton = strstr(decorationLayout, "close"); const char* separator = strchr(decorationLayout, ':'); *aReversedButtonsPlacement = closeButton && separator && closeButton < separator; } // We check what position a button string is stored in decorationLayout. // // decorationLayout gets its value from the GNOME preference: // org.gnome.desktop.vm.preferences.button-layout via the // gtk-decoration-layout property. // // Documentation of the gtk-decoration-layout property can be found here: // https://developer.gnome.org/gtk3/stable/GtkSettings.html#GtkSettings--gtk-decoration-layout if (aButtonLayout.IsEmpty()) { return 0; } nsDependentCSubstring layout(decorationLayout, strlen(decorationLayout)); size_t activeButtons = 0; for (const auto& part : layout.Split(':')) { for (const auto& button : part.Split(',')) { if (button.EqualsLiteral("close")) { aButtonLayout[activeButtons++] = {ButtonLayout::Type::Close}; } else if (button.EqualsLiteral("minimize")) { aButtonLayout[activeButtons++] = {ButtonLayout::Type::Minimize}; } else if (button.EqualsLiteral("maximize")) { aButtonLayout[activeButtons++] = {ButtonLayout::Type::Maximize}; } if (activeButtons == aButtonLayout.Length()) { return activeButtons; } } } return activeButtons; } static void EnsureToolbarMetrics() { if (sToolbarMetrics.initialized) { return; } sToolbarMetrics = {}; // Account for the spacing property in the header bar. // Default to 6 pixels (gtk/gtkheaderbar.c) gint spacing = 6; g_object_get(GetWidget(MOZ_GTK_HEADER_BAR), "spacing", &spacing, nullptr); sToolbarMetrics.inlineSpacing += spacing; sToolbarMetrics.initialized = true; } gint moz_gtk_get_titlebar_button_spacing() { EnsureToolbarMetrics(); return sToolbarMetrics.inlineSpacing; } static void moz_gtk_window_decoration_paint(cairo_t* cr, const GtkDrawingParams& aParams) { if (mozilla::widget::GdkIsWaylandDisplay()) { // Doesn't seem to be needed. return; } GtkStyleContext* windowStyle = GetStyleContext(MOZ_GTK_HEADERBAR_WINDOW, aParams.image_scale); const bool solidDecorations = gtk_style_context_has_class(windowStyle, "solid-csd"); GtkStyleContext* decorationStyle = GetStyleContext(solidDecorations ? MOZ_GTK_WINDOW_DECORATION_SOLID : MOZ_GTK_WINDOW_DECORATION, aParams.image_scale, aParams.state); const auto& rect = aParams.rect; gtk_render_background(decorationStyle, cr, rect.x, rect.y, rect.width, rect.height); gtk_render_frame(decorationStyle, cr, rect.x, rect.y, rect.width, rect.height); } /* cairo_t *cr argument has to be a system-cairo. */ void moz_gtk_widget_paint(cairo_t* cr, const GtkDrawingParams* aParams) { /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086 */ cairo_new_path(cr); switch (aParams->widget) { case MOZ_GTK_WINDOW_DECORATION: return moz_gtk_window_decoration_paint(cr, *aParams); default: g_warning("Unknown widget type: %d", aParams->widget); return; } } gint moz_gtk_shutdown() { /* This will destroy all of our widgets */ ResetWidgetCache(); return MOZ_GTK_SUCCESS; }