170 lines
5.5 KiB
C++
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;
|
|
}
|