1
0
Fork 0
firefox/widget/gtk/gtk3drawing.cpp
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

170 lines
5.5 KiB
C++

/* -*- 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 <gtk/gtk.h>
#include <gdk/gdkprivate.h>
#include <string.h>
#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 <math.h>
#include <dlfcn.h>
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<ButtonLayout> 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;
}