1767 lines
65 KiB
Diff
1767 lines
65 KiB
Diff
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
|
|
index de61ac442b74..be15873ee2c4 100644
|
|
--- a/browser/app/profile/firefox.js
|
|
+++ b/browser/app/profile/firefox.js
|
|
@@ -455,11 +455,7 @@ pref("browser.tabs.loadDivertedInBackground", false);
|
|
pref("browser.tabs.loadBookmarksInBackground", false);
|
|
pref("browser.tabs.loadBookmarksInTabs", false);
|
|
pref("browser.tabs.tabClipWidth", 140);
|
|
-#ifdef UNIX_BUT_NOT_MAC
|
|
-pref("browser.tabs.drawInTitlebar", false);
|
|
-#else
|
|
pref("browser.tabs.drawInTitlebar", true);
|
|
-#endif
|
|
|
|
// Offer additional drag space to the user. The drag space
|
|
// will only be shown if browser.tabs.drawInTitlebar is true.
|
|
diff --git a/browser/base/content/browser-tabsintitlebar.js b/browser/base/content/browser-tabsintitlebar.js
|
|
index d9509029bbf1..b56f81c0c138 100644
|
|
--- a/browser/base/content/browser-tabsintitlebar.js
|
|
+++ b/browser/base/content/browser-tabsintitlebar.js
|
|
@@ -14,6 +14,11 @@ var TabsInTitlebar = {
|
|
this._readPref();
|
|
Services.prefs.addObserver(this._prefName, this);
|
|
|
|
+ // Always disable on unsupported GTK versions.
|
|
+ if (AppConstants.MOZ_WIDGET_TOOLKIT == "gtk3") {
|
|
+ this.allowedBy("gtk", window.matchMedia("(-moz-gtk-csd-available)"));
|
|
+ }
|
|
+
|
|
// We need to update the appearance of the titlebar when the menu changes
|
|
// from the active to the inactive state. We can't, however, rely on
|
|
// DOMMenuBarInactive, because the menu fires this event and then removes
|
|
diff --git a/browser/base/moz.build b/browser/base/moz.build
|
|
index a2bd4bcb4eca..f14553e4ba7f 100644
|
|
--- a/browser/base/moz.build
|
|
+++ b/browser/base/moz.build
|
|
@@ -56,7 +56,7 @@ DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR
|
|
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
|
|
DEFINES['CONTEXT_COPY_IMAGE_CONTENTS'] = 1
|
|
|
|
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
|
|
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa', 'gtk3'):
|
|
DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
|
|
|
|
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
|
|
diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css
|
|
index 65a3f0cca7b1..7bb448da620b 100644
|
|
--- a/browser/themes/linux/browser.css
|
|
+++ b/browser/themes/linux/browser.css
|
|
@@ -557,8 +557,12 @@ html|span.ac-emphasize-text-url {
|
|
color: -moz-menubartext;
|
|
}
|
|
|
|
+/* Support dragging the window using the toolbar when drawing our own
|
|
+ * decorations, or where the GTK theme allows. */
|
|
#toolbar-menubar:not([autohide="true"]):not(:-moz-lwtheme):-moz-system-metric(menubar-drag),
|
|
-#TabsToolbar:not(:-moz-lwtheme):-moz-system-metric(menubar-drag) {
|
|
+#TabsToolbar:not(:-moz-lwtheme):-moz-system-metric(menubar-drag),
|
|
+#main-window[tabsintitlebar] #toolbar-menubar:not([autohide="true"]),
|
|
+#main-window[tabsintitlebar] #TabsToolbar {
|
|
-moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-drag");
|
|
}
|
|
|
|
@@ -715,3 +719,85 @@ html|span.ac-emphasize-text-url {
|
|
.restore-tabs-button:hover:active:not([disabled="true"]) {
|
|
padding: 3px;
|
|
}
|
|
+
|
|
+@media not all and (-moz-gtk-csd-available) {
|
|
+ #main-window > #titlebar {
|
|
+ /* We need to hide the titlebar explicitly on versions of GTK without CSD. */
|
|
+ display: none;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Titlebar/CSD */
|
|
+@media (-moz-gtk-csd-available) {
|
|
+ #main-window[tabsintitlebar][sizemode="normal"] > #titlebar {
|
|
+ min-height: calc(var(--tab-min-height) + 12px);
|
|
+ }
|
|
+
|
|
+ #main-window[tabsintitlebar] #titlebar:-moz-lwtheme {
|
|
+ visibility: hidden;
|
|
+ }
|
|
+ #main-window[tabsintitlebar] #titlebar-content:-moz-lwtheme {
|
|
+ visibility: visible;
|
|
+ }
|
|
+
|
|
+ #main-window[tabsintitlebar][sizemode="normal"] > #titlebar {
|
|
+ -moz-appearance: -moz-window-titlebar;
|
|
+ }
|
|
+ #main-window[tabsintitlebar][sizemode="maximized"] > #titlebar {
|
|
+ -moz-appearance: -moz-window-titlebar-maximized;
|
|
+ }
|
|
+
|
|
+ /* 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,
|
|
+ * can swallow those events.
|
|
+ */
|
|
+ #titlebar-buttonbox {
|
|
+ z-index: 1;
|
|
+ }
|
|
+
|
|
+ /* titlebar command buttons */
|
|
+ /* Use full scale icons here as the Gtk+ does. */
|
|
+ @media (-moz-gtk-csd-minimize-button) {
|
|
+ #titlebar-min {
|
|
+ list-style-image: url("moz-icon://stock/window-minimize-symbolic");
|
|
+ -moz-appearance: -moz-window-button-minimize;
|
|
+ }
|
|
+ }
|
|
+ @media not all and (-moz-gtk-csd-minimize-button) {
|
|
+ #titlebar-min {
|
|
+ display: none;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @media (-moz-gtk-csd-maximize-button) {
|
|
+ #titlebar-max {
|
|
+ list-style-image: url("moz-icon://stock/window-maximize-symbolic");
|
|
+ -moz-appearance: -moz-window-button-maximize;
|
|
+ }
|
|
+ #main-window[sizemode="maximized"] #titlebar-max {
|
|
+ list-style-image: url("moz-icon://stock/window-restore-symbolic");
|
|
+ -moz-appearance: -moz-window-button-restore;
|
|
+ }
|
|
+ }
|
|
+ @media not all and (-moz-gtk-csd-maximize-button) {
|
|
+ #titlebar-max {
|
|
+ display: none;
|
|
+ }
|
|
+ #main-window[sizemode="maximized"] #titlebar-max {
|
|
+ display: none;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @media (-moz-gtk-csd-close-button) {
|
|
+ #titlebar-close {
|
|
+ list-style-image: url("moz-icon://stock/window-close-symbolic");
|
|
+ -moz-appearance: -moz-window-button-close;
|
|
+ }
|
|
+ }
|
|
+ @media not all and (-moz-gtk-csd-close-button) {
|
|
+ #titlebar-close {
|
|
+ display: none;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h
|
|
index 5d5ec3d405c9..bb2f652c41e0 100644
|
|
--- a/dom/base/nsGkAtomList.h
|
|
+++ b/dom/base/nsGkAtomList.h
|
|
@@ -2270,6 +2270,10 @@ GK_ATOM(touch_enabled, "touch-enabled")
|
|
GK_ATOM(menubar_drag, "menubar-drag")
|
|
GK_ATOM(swipe_animation_enabled, "swipe-animation-enabled")
|
|
GK_ATOM(physical_home_button, "physical-home-button")
|
|
+GK_ATOM(gtk_csd_available, "gtk-csd-available")
|
|
+GK_ATOM(gtk_csd_minimize_button, "gtk-csd-minimize-button")
|
|
+GK_ATOM(gtk_csd_maximize_button, "gtk-csd-maximize-button")
|
|
+GK_ATOM(gtk_csd_close_button, "gtk-csd-close-button")
|
|
|
|
// windows theme selector metrics
|
|
GK_ATOM(windows_classic, "windows-classic")
|
|
@@ -2306,6 +2310,10 @@ GK_ATOM(_moz_device_orientation, "-moz-device-orientation")
|
|
GK_ATOM(_moz_is_resource_document, "-moz-is-resource-document")
|
|
GK_ATOM(_moz_swipe_animation_enabled, "-moz-swipe-animation-enabled")
|
|
GK_ATOM(_moz_physical_home_button, "-moz-physical-home-button")
|
|
+GK_ATOM(_moz_gtk_csd_available, "-moz-gtk-csd-available")
|
|
+GK_ATOM(_moz_gtk_csd_minimize_button, "-moz-gtk-csd-minimize-button")
|
|
+GK_ATOM(_moz_gtk_csd_maximize_button, "-moz-gtk-csd-maximize-button")
|
|
+GK_ATOM(_moz_gtk_csd_close_button, "-moz-gtk-csd-close-button")
|
|
|
|
// application commands
|
|
GK_ATOM(Back, "Back")
|
|
diff --git a/gfx/src/nsThemeConstants.h b/gfx/src/nsThemeConstants.h
|
|
index d0ced405095d..1cd2361a0026 100644
|
|
--- a/gfx/src/nsThemeConstants.h
|
|
+++ b/gfx/src/nsThemeConstants.h
|
|
@@ -299,6 +299,7 @@ enum ThemeWidgetType : uint8_t {
|
|
NS_THEME_MAC_SOURCE_LIST,
|
|
NS_THEME_MAC_SOURCE_LIST_SELECTION,
|
|
NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION,
|
|
+ NS_THEME_GTK_WINDOW_DECORATION,
|
|
|
|
ThemeWidgetType_COUNT
|
|
};
|
|
diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp
|
|
index cbfb4d0f60aa..263372ca868e 100644
|
|
--- a/layout/style/nsCSSRuleProcessor.cpp
|
|
+++ b/layout/style/nsCSSRuleProcessor.cpp
|
|
@@ -1180,6 +1180,30 @@ nsCSSRuleProcessor::InitSystemMetrics()
|
|
sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button);
|
|
}
|
|
|
|
+ rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDAvailable,
|
|
+ &metricResult);
|
|
+ if (NS_SUCCEEDED(rv) && metricResult) {
|
|
+ sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_available);
|
|
+ }
|
|
+
|
|
+ rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDMinimizeButton,
|
|
+ &metricResult);
|
|
+ if (NS_SUCCEEDED(rv) && metricResult) {
|
|
+ sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_minimize_button);
|
|
+ }
|
|
+
|
|
+ rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDMaximizeButton,
|
|
+ &metricResult);
|
|
+ if (NS_SUCCEEDED(rv) && metricResult) {
|
|
+ sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_maximize_button);
|
|
+ }
|
|
+
|
|
+ rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDCloseButton,
|
|
+ &metricResult);
|
|
+ if (NS_SUCCEEDED(rv) && metricResult) {
|
|
+ sSystemMetrics->AppendElement(nsGkAtoms::gtk_csd_close_button);
|
|
+ }
|
|
+
|
|
#ifdef XP_WIN
|
|
if (NS_SUCCEEDED(
|
|
LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
|
|
diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp
|
|
index 7cf6a08e46c1..bf955d759e8b 100644
|
|
--- a/layout/style/nsMediaFeatures.cpp
|
|
+++ b/layout/style/nsMediaFeatures.cpp
|
|
@@ -788,6 +788,42 @@ nsMediaFeatures::features[] = {
|
|
GetSystemMetric
|
|
},
|
|
|
|
+ {
|
|
+ &nsGkAtoms::_moz_gtk_csd_available,
|
|
+ nsMediaFeature::eMinMaxNotAllowed,
|
|
+ nsMediaFeature::eBoolInteger,
|
|
+ nsMediaFeature::eNoRequirements,
|
|
+ { &nsGkAtoms::gtk_csd_available },
|
|
+ GetSystemMetric
|
|
+ },
|
|
+
|
|
+ {
|
|
+ &nsGkAtoms::_moz_gtk_csd_minimize_button,
|
|
+ nsMediaFeature::eMinMaxNotAllowed,
|
|
+ nsMediaFeature::eBoolInteger,
|
|
+ nsMediaFeature::eNoRequirements,
|
|
+ { &nsGkAtoms::gtk_csd_minimize_button },
|
|
+ GetSystemMetric
|
|
+ },
|
|
+
|
|
+ {
|
|
+ &nsGkAtoms::_moz_gtk_csd_maximize_button,
|
|
+ nsMediaFeature::eMinMaxNotAllowed,
|
|
+ nsMediaFeature::eBoolInteger,
|
|
+ nsMediaFeature::eNoRequirements,
|
|
+ { &nsGkAtoms::gtk_csd_maximize_button },
|
|
+ GetSystemMetric
|
|
+ },
|
|
+
|
|
+ {
|
|
+ &nsGkAtoms::_moz_gtk_csd_close_button,
|
|
+ nsMediaFeature::eMinMaxNotAllowed,
|
|
+ nsMediaFeature::eBoolInteger,
|
|
+ nsMediaFeature::eNoRequirements,
|
|
+ { &nsGkAtoms::gtk_csd_close_button },
|
|
+ GetSystemMetric
|
|
+ },
|
|
+
|
|
// Internal -moz-is-glyph media feature: applies only inside SVG glyphs.
|
|
// Internal because it is really only useful in the user agent anyway
|
|
// and therefore not worth standardizing.
|
|
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
|
|
index 0b77f1759021..b8dc9aeebfd8 100644
|
|
--- a/modules/libpref/init/all.js
|
|
+++ b/modules/libpref/init/all.js
|
|
@@ -4912,6 +4912,7 @@ pref("gfx.apitrace.enabled",false);
|
|
pref("gfx.xrender.enabled",false);
|
|
pref("widget.chrome.allow-gtk-dark-theme", false);
|
|
pref("widget.content.allow-gtk-dark-theme", false);
|
|
+pref("widget.allow-client-side-decoration", false);
|
|
#endif
|
|
#endif
|
|
|
|
diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build
|
|
index 8b5dc9e25d7e..14fb0fe87e9d 100644
|
|
--- a/toolkit/modules/moz.build
|
|
+++ b/toolkit/modules/moz.build
|
|
@@ -259,7 +259,7 @@ EXTRA_JS_MODULES.sessionstore += [
|
|
]
|
|
|
|
DEFINES['INSTALL_COMPACT_THEMES'] = 1
|
|
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
|
|
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa', 'gtk3'):
|
|
DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
|
|
|
|
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
|
|
diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h
|
|
index ff5da8017621..d31b411a9b1a 100644
|
|
--- a/widget/LookAndFeel.h
|
|
+++ b/widget/LookAndFeel.h
|
|
@@ -405,6 +405,30 @@ public:
|
|
eIntID_PhysicalHomeButton,
|
|
|
|
/*
|
|
+ * A boolean value indicating whether client-side decorations are
|
|
+ * supported by the user's GTK version.
|
|
+ */
|
|
+ eIntID_GTKCSDAvailable,
|
|
+
|
|
+ /*
|
|
+ * A boolean value indicating whether client-side decorations should
|
|
+ * contain a minimize button.
|
|
+ */
|
|
+ eIntID_GTKCSDMinimizeButton,
|
|
+
|
|
+ /*
|
|
+ * A boolean value indicating whether client-side decorations should
|
|
+ * contain a maximize button.
|
|
+ */
|
|
+ eIntID_GTKCSDMaximizeButton,
|
|
+
|
|
+ /*
|
|
+ * A boolean value indicating whether client-side decorations should
|
|
+ * contain a close button.
|
|
+ */
|
|
+ eIntID_GTKCSDCloseButton,
|
|
+
|
|
+ /*
|
|
* Controls whether overlay scrollbars display when the user moves
|
|
* the mouse in a scrollable frame.
|
|
*/
|
|
diff --git a/widget/gtk/WidgetStyleCache.cpp b/widget/gtk/WidgetStyleCache.cpp
|
|
index 39b7893df3bd..4c9e8523b5bf 100644
|
|
--- a/widget/gtk/WidgetStyleCache.cpp
|
|
+++ b/widget/gtk/WidgetStyleCache.cpp
|
|
@@ -26,10 +26,14 @@ static GtkStyleContext*
|
|
GetCssNodeStyleInternal(WidgetNodeType aNodeType);
|
|
|
|
static GtkWidget*
|
|
-CreateWindowWidget()
|
|
+CreateWindowWidget(WidgetNodeType type)
|
|
{
|
|
GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
|
|
gtk_widget_set_name(widget, "MozillaGtkWidget");
|
|
+ if (type == MOZ_GTK_WINDOW_CSD) {
|
|
+ GtkStyleContext* style = gtk_widget_get_style_context(widget);
|
|
+ gtk_style_context_add_class(style, "csd");
|
|
+ }
|
|
return widget;
|
|
}
|
|
|
|
@@ -101,7 +105,7 @@ CreateTooltipWidget()
|
|
{
|
|
MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr,
|
|
"CreateTooltipWidget should be used for Gtk < 3.20 only.");
|
|
- GtkWidget* widget = CreateWindowWidget();
|
|
+ GtkWidget* widget = CreateWindowWidget(MOZ_GTK_WINDOW);
|
|
GtkStyleContext* style = gtk_widget_get_style_context(widget);
|
|
gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
|
|
return widget;
|
|
@@ -529,11 +533,82 @@ CreateNotebookWidget()
|
|
}
|
|
|
|
static GtkWidget*
|
|
+CreateHeaderBar(bool aMaximized)
|
|
+{
|
|
+ MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
|
|
+ "GtkHeaderBar is only available on GTK 3.10+.");
|
|
+ if (gtk_check_version(3, 10, 0) != nullptr)
|
|
+ return nullptr;
|
|
+
|
|
+ static auto sGtkHeaderBarNewPtr = (GtkWidget* (*)())
|
|
+ dlsym(RTLD_DEFAULT, "gtk_header_bar_new");
|
|
+ static const char* MOZ_GTK_STYLE_CLASS_TITLEBAR = "titlebar";
|
|
+
|
|
+ GtkWidget* headerbar = sGtkHeaderBarNewPtr();
|
|
+ if (aMaximized) {
|
|
+ GtkWidget *window = gtk_window_new(GTK_WINDOW_POPUP);
|
|
+ gtk_widget_set_name(window, "MozillaMaximizedGtkWidget");
|
|
+ GtkStyleContext* style = gtk_widget_get_style_context(window);
|
|
+ gtk_style_context_add_class(style, "maximized");
|
|
+ GtkWidget *fixed = gtk_fixed_new();
|
|
+ gtk_container_add(GTK_CONTAINER(window), fixed);
|
|
+ gtk_container_add(GTK_CONTAINER(fixed), headerbar);
|
|
+ // Save the window container so we don't leak it.
|
|
+ sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED] = window;
|
|
+ } else {
|
|
+ AddToWindowContainer(headerbar);
|
|
+ }
|
|
+
|
|
+ // Emulate what create_titlebar() at gtkwindow.c does.
|
|
+ GtkStyleContext* style = gtk_widget_get_style_context(headerbar);
|
|
+ gtk_style_context_add_class(style, MOZ_GTK_STYLE_CLASS_TITLEBAR);
|
|
+ gtk_style_context_add_class(style, "default-decoration");
|
|
+
|
|
+ return headerbar;
|
|
+}
|
|
+
|
|
+// TODO - Also return style for buttons located at Maximized toolbar.
|
|
+static GtkWidget*
|
|
+CreateHeaderBarButton(WidgetNodeType aWidgetType)
|
|
+{
|
|
+ MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
|
|
+ "GtkHeaderBar is only available on GTK 3.10+.");
|
|
+
|
|
+ if (gtk_check_version(3, 10, 0) != nullptr)
|
|
+ return nullptr;
|
|
+
|
|
+ static const char* MOZ_GTK_STYLE_CLASS_TITLEBUTTON = "titlebutton";
|
|
+
|
|
+ GtkWidget* widget = gtk_button_new();
|
|
+ gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_HEADER_BAR)), widget);
|
|
+
|
|
+ GtkStyleContext* style = gtk_widget_get_style_context(widget);
|
|
+ gtk_style_context_add_class(style, MOZ_GTK_STYLE_CLASS_TITLEBUTTON);
|
|
+
|
|
+ switch (aWidgetType) {
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
|
|
+ gtk_style_context_add_class(style, "close");
|
|
+ break;
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
|
|
+ gtk_style_context_add_class(style, "minimize");
|
|
+ break;
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
|
|
+ gtk_style_context_add_class(style, "maximize");
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return widget;
|
|
+}
|
|
+
|
|
+static GtkWidget*
|
|
CreateWidget(WidgetNodeType aWidgetType)
|
|
{
|
|
switch (aWidgetType) {
|
|
case MOZ_GTK_WINDOW:
|
|
- return CreateWindowWidget();
|
|
+ case MOZ_GTK_WINDOW_CSD:
|
|
+ return CreateWindowWidget(aWidgetType);
|
|
case MOZ_GTK_WINDOW_CONTAINER:
|
|
return CreateWindowContainerWidget();
|
|
case MOZ_GTK_CHECKBUTTON_CONTAINER:
|
|
@@ -610,6 +685,13 @@ CreateWidget(WidgetNodeType aWidgetType)
|
|
return CreateComboBoxEntryButtonWidget();
|
|
case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
|
|
return CreateComboBoxEntryArrowWidget();
|
|
+ case MOZ_GTK_HEADER_BAR:
|
|
+ case MOZ_GTK_HEADER_BAR_MAXIMIZED:
|
|
+ return CreateHeaderBar(aWidgetType == MOZ_GTK_HEADER_BAR_MAXIMIZED);
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
|
|
+ return CreateHeaderBarButton(aWidgetType);
|
|
default:
|
|
/* Not implemented */
|
|
return nullptr;
|
|
@@ -1049,6 +1131,10 @@ GetCssNodeStyleInternal(WidgetNodeType aNodeType)
|
|
GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
|
|
return gtk_widget_get_style_context(widget);
|
|
}
|
|
+ case MOZ_GTK_WINDOW_DECORATION:
|
|
+ style = CreateChildCSSNode("decoration",
|
|
+ MOZ_GTK_WINDOW_CSD);
|
|
+ break;
|
|
default:
|
|
return GetWidgetRootStyle(aNodeType);
|
|
}
|
|
@@ -1214,6 +1300,8 @@ ResetWidgetCache(void)
|
|
/* This will destroy all of our widgets */
|
|
if (sWidgetStorage[MOZ_GTK_WINDOW])
|
|
gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);
|
|
+ if (sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED])
|
|
+ gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW_MAXIMIZED]);
|
|
|
|
/* Clear already freed arrays */
|
|
mozilla::PodArrayZero(sWidgetStorage);
|
|
diff --git a/widget/gtk/gtk3drawing.cpp b/widget/gtk/gtk3drawing.cpp
|
|
index 4c562b380095..ee2b8a04f63f 100644
|
|
--- a/widget/gtk/gtk3drawing.cpp
|
|
+++ b/widget/gtk/gtk3drawing.cpp
|
|
@@ -17,6 +17,7 @@
|
|
#include "WidgetStyleCache.h"
|
|
|
|
#include <math.h>
|
|
+#include <dlfcn.h>
|
|
|
|
static gboolean checkbox_check_state;
|
|
static gboolean notebook_has_tab_gap;
|
|
@@ -39,9 +40,25 @@ static gint
|
|
moz_gtk_menu_item_paint(WidgetNodeType widget, cairo_t *cr, GdkRectangle* rect,
|
|
GtkWidgetState* state, GtkTextDirection direction);
|
|
|
|
+static void
|
|
+moz_gtk_add_style_margin(GtkStyleContext* style,
|
|
+ gint* left, gint* top, gint* right, gint* bottom);
|
|
+static void
|
|
+moz_gtk_add_style_border(GtkStyleContext* style,
|
|
+ gint* left, gint* top, gint* right, gint* bottom);
|
|
+static void
|
|
+moz_gtk_add_style_padding(GtkStyleContext* style,
|
|
+ gint* left, gint* top, gint* right, gint* bottom);
|
|
+static void moz_gtk_add_margin_border_padding(GtkStyleContext *style,
|
|
+ gint* left, gint* top,
|
|
+ gint* right, gint* bottom);
|
|
+static void moz_gtk_add_border_padding(GtkStyleContext *style,
|
|
+ gint* left, gint* top,
|
|
+ gint* right, gint* bottom);
|
|
static GtkBorder
|
|
GetMarginBorderPadding(GtkStyleContext* aStyle);
|
|
|
|
+
|
|
// GetStateFlagsFromGtkWidgetState() can be safely used for the specific
|
|
// GtkWidgets that set both prelight and active flags. For other widgets,
|
|
// either the GtkStateFlags or Gecko's GtkWidgetState need to be carefully
|
|
@@ -233,6 +250,43 @@ moz_gtk_splitter_get_metrics(gint orientation, gint* size)
|
|
return MOZ_GTK_SUCCESS;
|
|
}
|
|
|
|
+void
|
|
+moz_gtk_get_window_border(gint* top, gint* right, gint* bottom, gint* left)
|
|
+{
|
|
+ MOZ_ASSERT(gtk_check_version(3, 20, 0) == nullptr,
|
|
+ "Window decorations are only supported on GTK 3.20+.");
|
|
+
|
|
+ GtkStyleContext* style = GetStyleContext(MOZ_GTK_WINDOW);
|
|
+
|
|
+ *top = *right = *bottom = *left = 0;
|
|
+ moz_gtk_add_border_padding(style, left, top, right, bottom);
|
|
+ GtkBorder windowMargin;
|
|
+ gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &windowMargin);
|
|
+
|
|
+ style = GetStyleContext(MOZ_GTK_WINDOW_DECORATION);
|
|
+
|
|
+ // Available on GTK 3.20+.
|
|
+ static auto sGtkRenderBackgroundGetClip =
|
|
+ (void (*)(GtkStyleContext*, gdouble, gdouble, gdouble, gdouble, GdkRectangle*))
|
|
+ dlsym(RTLD_DEFAULT, "gtk_render_background_get_clip");
|
|
+
|
|
+ GdkRectangle shadowClip;
|
|
+ sGtkRenderBackgroundGetClip(style, 0, 0, 0, 0, &shadowClip);
|
|
+
|
|
+ // Transfer returned inset rectangle to GtkBorder
|
|
+ GtkBorder shadowBorder = {
|
|
+ static_cast<gint16>(-shadowClip.x), // left
|
|
+ static_cast<gint16>(shadowClip.width + shadowClip.x), // right
|
|
+ static_cast<gint16>(-shadowClip.y), // top
|
|
+ static_cast<gint16>(shadowClip.height + shadowClip.y), // bottom
|
|
+ };
|
|
+
|
|
+ *left += MAX(windowMargin.left, shadowBorder.left);
|
|
+ *right += MAX(windowMargin.right, shadowBorder.right);
|
|
+ *top += MAX(windowMargin.top, shadowBorder.top);
|
|
+ *bottom += MAX(windowMargin.bottom, shadowBorder.bottom);
|
|
+}
|
|
+
|
|
static gint
|
|
moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect,
|
|
GtkTextDirection direction)
|
|
@@ -302,6 +356,24 @@ moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect,
|
|
}
|
|
|
|
static gint
|
|
+moz_gtk_header_bar_button_paint(cairo_t *cr, GdkRectangle* rect,
|
|
+ GtkWidgetState* state,
|
|
+ GtkReliefStyle relief, GtkWidget* widget,
|
|
+ GtkTextDirection direction)
|
|
+{
|
|
+ GtkBorder margin;
|
|
+ GtkStyleContext* style = gtk_widget_get_style_context(widget);
|
|
+ gtk_style_context_get_margin(style, GTK_STATE_FLAG_NORMAL, &margin);
|
|
+
|
|
+ rect->x += margin.left;
|
|
+ rect->y += margin.top;
|
|
+ rect->width -= margin.left + margin.right;
|
|
+ rect->height -= margin.top + margin.bottom;
|
|
+
|
|
+ return moz_gtk_button_paint(cr, rect, state, relief, widget, direction);
|
|
+}
|
|
+
|
|
+static gint
|
|
moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect,
|
|
GtkWidgetState* state,
|
|
gboolean selected, gboolean inconsistent,
|
|
@@ -1948,6 +2020,38 @@ moz_gtk_info_bar_paint(cairo_t *cr, GdkRectangle* rect,
|
|
return MOZ_GTK_SUCCESS;
|
|
}
|
|
|
|
+static gint
|
|
+moz_gtk_header_bar_paint(WidgetNodeType widgetType,
|
|
+ cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state)
|
|
+{
|
|
+ GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
|
|
+ GtkStyleContext *style;
|
|
+
|
|
+ style = GetStyleContext(widgetType, GTK_TEXT_DIR_LTR,
|
|
+ state_flags);
|
|
+ InsetByMargin(rect, style);
|
|
+ gtk_render_background(style, cr, rect->x, rect->y, rect->width,
|
|
+ rect->height);
|
|
+ gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
|
|
+
|
|
+ return MOZ_GTK_SUCCESS;
|
|
+}
|
|
+
|
|
+void
|
|
+moz_gtk_header_bar_paint(cairo_t *cr, GdkRectangle* rect)
|
|
+{
|
|
+ static GtkWidgetState state;
|
|
+ moz_gtk_header_bar_paint(MOZ_GTK_HEADER_BAR, cr, rect, &state);
|
|
+}
|
|
+
|
|
+void
|
|
+moz_gtk_get_header_bar_border(gint* top, gint* right, gint* bottom, gint* left)
|
|
+{
|
|
+ *left = *top = *right = *bottom = 0;
|
|
+ moz_gtk_add_border_padding(GetStyleContext(MOZ_GTK_HEADER_BAR),
|
|
+ left, top, right, bottom);
|
|
+}
|
|
+
|
|
static void
|
|
moz_gtk_add_style_margin(GtkStyleContext* style,
|
|
gint* left, gint* top, gint* right, gint* bottom)
|
|
@@ -1999,6 +2103,14 @@ static void moz_gtk_add_margin_border_padding(GtkStyleContext *style,
|
|
moz_gtk_add_style_padding(style, left, top, right, bottom);
|
|
}
|
|
|
|
+static void moz_gtk_add_border_padding(GtkStyleContext *style,
|
|
+ gint* left, gint* top,
|
|
+ gint* right, gint* bottom)
|
|
+{
|
|
+ moz_gtk_add_style_border(style, left, top, right, bottom);
|
|
+ moz_gtk_add_style_padding(style, left, top, right, bottom);
|
|
+}
|
|
+
|
|
static GtkBorder
|
|
GetMarginBorderPadding(GtkStyleContext* aStyle)
|
|
{
|
|
@@ -2054,8 +2166,7 @@ moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
|
|
// XXX: Subtract 1 pixel from the padding to account for the default
|
|
// padding in forms.css. See bug 1187385.
|
|
*left = *top = *right = *bottom = -1;
|
|
- moz_gtk_add_style_padding(style, left, top, right, bottom);
|
|
- moz_gtk_add_style_border(style, left, top, right, bottom);
|
|
+ moz_gtk_add_border_padding(style, left, top, right, bottom);
|
|
|
|
return MOZ_GTK_SUCCESS;
|
|
}
|
|
@@ -2076,10 +2187,8 @@ moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
|
|
*left = *top = *right = *bottom =
|
|
gtk_container_get_border_width(GTK_CONTAINER(
|
|
GetWidget(MOZ_GTK_TREE_HEADER_CELL)));
|
|
-
|
|
style = GetStyleContext(MOZ_GTK_TREE_HEADER_CELL);
|
|
- moz_gtk_add_style_border(style, left, top, right, bottom);
|
|
- moz_gtk_add_style_padding(style, left, top, right, bottom);
|
|
+ moz_gtk_add_border_padding(style, left, top, right, bottom);
|
|
return MOZ_GTK_SUCCESS;
|
|
}
|
|
case MOZ_GTK_TREE_HEADER_SORTARROW:
|
|
@@ -2105,8 +2214,7 @@ moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
|
|
gtk_container_get_border_width(GTK_CONTAINER(
|
|
GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
|
|
style = GetStyleContext(MOZ_GTK_COMBOBOX_BUTTON);
|
|
- moz_gtk_add_style_padding(style, left, top, right, bottom);
|
|
- moz_gtk_add_style_border(style, left, top, right, bottom);
|
|
+ moz_gtk_add_border_padding(style, left, top, right, bottom);
|
|
|
|
/* If there is no separator, don't try to count its width. */
|
|
separator_width = 0;
|
|
@@ -2160,10 +2268,8 @@ moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
|
|
style = gtk_widget_get_style_context(w);
|
|
|
|
*left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w));
|
|
- moz_gtk_add_style_border(style,
|
|
- left, top, right, bottom);
|
|
- moz_gtk_add_style_padding(style,
|
|
- left, top, right, bottom);
|
|
+ moz_gtk_add_border_padding(style,
|
|
+ left, top, right, bottom);
|
|
return MOZ_GTK_SUCCESS;
|
|
}
|
|
case MOZ_GTK_MENUPOPUP:
|
|
@@ -2210,6 +2316,21 @@ moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
|
|
|
|
return MOZ_GTK_SUCCESS;
|
|
}
|
|
+ case MOZ_GTK_HEADER_BAR:
|
|
+ case MOZ_GTK_HEADER_BAR_MAXIMIZED:
|
|
+ {
|
|
+ style = GetStyleContext(widget);
|
|
+ moz_gtk_add_border_padding(style, left, top, right, bottom);
|
|
+ return MOZ_GTK_SUCCESS;
|
|
+ }
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
|
|
+ {
|
|
+ style = GetStyleContext(widget);
|
|
+ moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
|
|
+ return MOZ_GTK_SUCCESS;
|
|
+ }
|
|
|
|
/* These widgets have no borders, since they are not containers. */
|
|
case MOZ_GTK_CHECKBUTTON_LABEL:
|
|
@@ -2646,6 +2767,36 @@ GetScrollbarMetrics(GtkOrientation aOrientation)
|
|
return metrics;
|
|
}
|
|
|
|
+void
|
|
+moz_gtk_window_decoration_paint(cairo_t *cr, GdkRectangle* rect)
|
|
+{
|
|
+ gint top, right, bottom, left;
|
|
+ moz_gtk_get_window_border(&top, &right, &bottom, &left);
|
|
+
|
|
+ cairo_save(cr);
|
|
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
|
|
+ cairo_rectangle(cr, rect->x, rect->y, left, rect->height);
|
|
+ cairo_fill(cr);
|
|
+ cairo_rectangle(cr, rect->x+rect->width-right, rect->y, right, rect->height);
|
|
+ cairo_fill(cr);
|
|
+ cairo_rectangle(cr, rect->x, rect->y, rect->width, top);
|
|
+ cairo_fill(cr);
|
|
+ cairo_rectangle(cr, rect->x, rect->height-bottom, rect->width, bottom);
|
|
+ cairo_fill(cr);
|
|
+ cairo_restore(cr);
|
|
+
|
|
+ GtkStyleContext* style = GetStyleContext(MOZ_GTK_WINDOW_DECORATION,
|
|
+ GTK_TEXT_DIR_NONE);
|
|
+ rect->x += left;
|
|
+ rect->y += top;
|
|
+ rect->width -= left + right;
|
|
+ rect->height -= top + bottom;
|
|
+
|
|
+ gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height);
|
|
+ gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height);
|
|
+}
|
|
+
|
|
+
|
|
/* cairo_t *cr argument has to be a system-cairo. */
|
|
gint
|
|
moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
|
|
@@ -2671,6 +2822,14 @@ moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
|
|
GetWidget(MOZ_GTK_BUTTON),
|
|
direction);
|
|
break;
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
|
|
+ case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
|
|
+ return moz_gtk_header_bar_button_paint(cr, rect, state,
|
|
+ (GtkReliefStyle) flags,
|
|
+ GetWidget(widget),
|
|
+ direction);
|
|
+ break;
|
|
case MOZ_GTK_CHECKBUTTON:
|
|
case MOZ_GTK_RADIOBUTTON:
|
|
return moz_gtk_toggle_paint(cr, rect, state,
|
|
@@ -2877,6 +3036,10 @@ moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
|
|
case MOZ_GTK_INFO_BAR:
|
|
return moz_gtk_info_bar_paint(cr, rect, state);
|
|
break;
|
|
+ case MOZ_GTK_HEADER_BAR:
|
|
+ case MOZ_GTK_HEADER_BAR_MAXIMIZED:
|
|
+ return moz_gtk_header_bar_paint(widget, cr, rect, state);
|
|
+ break;
|
|
default:
|
|
g_warning("Unknown widget type: %d", widget);
|
|
}
|
|
diff --git a/widget/gtk/gtkdrawing.h b/widget/gtk/gtkdrawing.h
|
|
index 42dbf8287499..c0a7eba5006a 100644
|
|
--- a/widget/gtk/gtkdrawing.h
|
|
+++ b/widget/gtk/gtkdrawing.h
|
|
@@ -268,8 +268,14 @@ typedef enum {
|
|
MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL,
|
|
/* Paints the background of a window, dialog or page. */
|
|
MOZ_GTK_WINDOW,
|
|
+ /* Used only as a container for MOZ_GTK_HEADER_BAR_MAXIMIZED. */
|
|
+ MOZ_GTK_WINDOW_MAXIMIZED,
|
|
/* Window container for all widgets */
|
|
MOZ_GTK_WINDOW_CONTAINER,
|
|
+ /* Window with the 'csd' style class. */
|
|
+ MOZ_GTK_WINDOW_CSD,
|
|
+ /* Client-side window decoration node. Available on GTK 3.20+. */
|
|
+ MOZ_GTK_WINDOW_DECORATION,
|
|
/* Paints a GtkInfoBar, for notifications. */
|
|
MOZ_GTK_INFO_BAR,
|
|
/* Used for widget tree construction. */
|
|
@@ -290,6 +296,14 @@ typedef enum {
|
|
MOZ_GTK_COMBOBOX_ENTRY_ARROW,
|
|
/* Used for scrolled window shell. */
|
|
MOZ_GTK_SCROLLED_WINDOW,
|
|
+ /* Paints a GtkHeaderBar */
|
|
+ MOZ_GTK_HEADER_BAR,
|
|
+ /* Paints a GtkHeaderBar in maximized state */
|
|
+ MOZ_GTK_HEADER_BAR_MAXIMIZED,
|
|
+ /* Paints a GtkHeaderBar title buttons */
|
|
+ MOZ_GTK_HEADER_BAR_BUTTON_CLOSE,
|
|
+ MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE,
|
|
+ MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE,
|
|
|
|
MOZ_GTK_WIDGET_NODE_COUNT
|
|
} WidgetNodeType;
|
|
@@ -542,6 +556,32 @@ gint moz_gtk_get_menu_separator_height(gint* size);
|
|
*/
|
|
gint moz_gtk_splitter_get_metrics(gint orientation, gint* size);
|
|
|
|
+#if (MOZ_WIDGET_GTK == 3)
|
|
+/**
|
|
+ * Gets the margins to be used for window decorations, typically the extra space
|
|
+ * required to draw a drop shadow (obtained from gtk_render_background_get_clip).
|
|
+ * Only available on GTK 3.20+.
|
|
+ */
|
|
+void moz_gtk_get_window_border(gint* top, gint* right, gint* bottom, gint* left);
|
|
+
|
|
+/**
|
|
+ * Draw window decorations, typically a shadow.
|
|
+ * Only available on GTK 3.20+.
|
|
+ */
|
|
+void moz_gtk_window_decoration_paint(cairo_t *cr, GdkRectangle* rect);
|
|
+
|
|
+/**
|
|
+ * Gets the border of window header bar, only available on GTK 3.20+.
|
|
+ */
|
|
+void moz_gtk_get_header_bar_border(gint* top, gint* right, gint* bottom, gint* left);
|
|
+
|
|
+/**
|
|
+ * Draw window header bar, only available on GTK 3.20+.
|
|
+ */
|
|
+void moz_gtk_header_bar_paint(cairo_t *cr, GdkRectangle* rect);
|
|
+
|
|
+#endif
|
|
+
|
|
/**
|
|
* Get the YTHICKNESS of a tab (notebook extension).
|
|
*/
|
|
diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c
|
|
index 7285b7f001ac..6e554c7f628e 100644
|
|
--- a/widget/gtk/mozgtk/mozgtk.c
|
|
+++ b/widget/gtk/mozgtk/mozgtk.c
|
|
@@ -580,6 +580,8 @@ STUB(gtk_style_context_set_state)
|
|
STUB(gtk_style_properties_lookup_property)
|
|
STUB(gtk_tree_view_column_get_button)
|
|
STUB(gtk_widget_get_preferred_size)
|
|
+STUB(gtk_widget_get_preferred_width)
|
|
+STUB(gtk_widget_get_preferred_height)
|
|
STUB(gtk_widget_get_state_flags)
|
|
STUB(gtk_widget_get_style_context)
|
|
STUB(gtk_widget_path_append_type)
|
|
@@ -589,6 +591,10 @@ STUB(gtk_widget_path_iter_add_class)
|
|
STUB(gtk_widget_path_get_object_type)
|
|
STUB(gtk_widget_path_new)
|
|
STUB(gtk_widget_path_unref)
|
|
+STUB(gtk_widget_set_margin_left)
|
|
+STUB(gtk_widget_set_margin_right)
|
|
+STUB(gtk_widget_set_margin_top)
|
|
+STUB(gtk_widget_set_margin_bottom)
|
|
STUB(gtk_widget_set_visual)
|
|
STUB(gtk_app_chooser_dialog_new_for_content_type)
|
|
STUB(gtk_app_chooser_get_type)
|
|
@@ -601,6 +607,7 @@ STUB(gtk_color_chooser_get_type)
|
|
STUB(gtk_color_chooser_set_rgba)
|
|
STUB(gtk_color_chooser_get_rgba)
|
|
STUB(gtk_color_chooser_set_use_alpha)
|
|
+STUB(gtk_window_get_size)
|
|
#endif
|
|
|
|
#ifdef GTK2_SYMBOLS
|
|
diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp
|
|
index e02bf10fe87f..a5950ac35799 100644
|
|
--- a/widget/gtk/nsLookAndFeel.cpp
|
|
+++ b/widget/gtk/nsLookAndFeel.cpp
|
|
@@ -642,6 +642,22 @@ nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
|
|
case eIntID_ContextMenuOffsetHorizontal:
|
|
aResult = 2;
|
|
break;
|
|
+ case eIntID_GTKCSDAvailable:
|
|
+ EnsureInit();
|
|
+ aResult = sCSDAvailable;
|
|
+ break;
|
|
+ case eIntID_GTKCSDMaximizeButton:
|
|
+ EnsureInit();
|
|
+ aResult = sCSDMaximizeButton;
|
|
+ break;
|
|
+ case eIntID_GTKCSDMinimizeButton:
|
|
+ EnsureInit();
|
|
+ aResult = sCSDMinimizeButton;
|
|
+ break;
|
|
+ case eIntID_GTKCSDCloseButton:
|
|
+ EnsureInit();
|
|
+ aResult = sCSDCloseButton;
|
|
+ break;
|
|
default:
|
|
aResult = 0;
|
|
res = NS_ERROR_FAILURE;
|
|
@@ -1048,6 +1064,40 @@ nsLookAndFeel::EnsureInit()
|
|
|
|
gtk_widget_destroy(window);
|
|
g_object_unref(labelWidget);
|
|
+
|
|
+ // Require GTK 3.20 for client-side decoration support.
|
|
+ // 3.20 exposes gtk_render_background_get_clip, which is required for
|
|
+ // calculating shadow metrics for decorated windows.
|
|
+ sCSDAvailable = gtk_check_version(3, 20, 0) == nullptr;
|
|
+ if (sCSDAvailable) {
|
|
+ sCSDAvailable =
|
|
+ mozilla::Preferences::GetBool("widget.allow-client-side-decoration",
|
|
+ false);
|
|
+ }
|
|
+
|
|
+ const gchar* decorationLayout = nullptr;
|
|
+ if (gtk_check_version(3, 12, 0) == nullptr) {
|
|
+ static auto sGtkHeaderBarGetDecorationLayoutPtr =
|
|
+ (const gchar* (*)(GtkWidget*))
|
|
+ dlsym(RTLD_DEFAULT, "gtk_header_bar_get_decoration_layout");
|
|
+
|
|
+ GtkWidget* headerBar = GetWidget(MOZ_GTK_HEADER_BAR);
|
|
+ decorationLayout = sGtkHeaderBarGetDecorationLayoutPtr(headerBar);
|
|
+ if (!decorationLayout) {
|
|
+ g_object_get(settings, "gtk-decoration-layout", &decorationLayout,
|
|
+ nullptr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (decorationLayout) {
|
|
+ sCSDCloseButton = (strstr(decorationLayout, "close") != nullptr);
|
|
+ sCSDMaximizeButton = (strstr(decorationLayout, "maximize") != nullptr);
|
|
+ sCSDMinimizeButton = (strstr(decorationLayout, "minimize") != nullptr);
|
|
+ } else {
|
|
+ sCSDCloseButton = true;
|
|
+ sCSDMaximizeButton = true;
|
|
+ sCSDMinimizeButton = true;
|
|
+ }
|
|
}
|
|
|
|
// virtual
|
|
diff --git a/widget/gtk/nsLookAndFeel.h b/widget/gtk/nsLookAndFeel.h
|
|
index 177d069f6567..6e14236e604a 100644
|
|
--- a/widget/gtk/nsLookAndFeel.h
|
|
+++ b/widget/gtk/nsLookAndFeel.h
|
|
@@ -32,6 +32,8 @@ public:
|
|
virtual char16_t GetPasswordCharacterImpl();
|
|
virtual bool GetEchoPasswordImpl();
|
|
|
|
+ bool IsCSDAvailable() const { return sCSDAvailable; }
|
|
+
|
|
protected:
|
|
|
|
// Cached fonts
|
|
@@ -82,6 +84,10 @@ protected:
|
|
char16_t sInvisibleCharacter;
|
|
float sCaretRatio;
|
|
bool sMenuSupportsDrag;
|
|
+ bool sCSDAvailable;
|
|
+ bool sCSDMaximizeButton;
|
|
+ bool sCSDMinimizeButton;
|
|
+ bool sCSDCloseButton;
|
|
bool mInitialized;
|
|
|
|
void EnsureInit();
|
|
diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp
|
|
index 211790096a3e..6ed35b97a0d7 100644
|
|
--- a/widget/gtk/nsNativeThemeGTK.cpp
|
|
+++ b/widget/gtk/nsNativeThemeGTK.cpp
|
|
@@ -23,6 +23,7 @@
|
|
#include "nsIDOMHTMLInputElement.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsAttrValueInlines.h"
|
|
+#include "nsWindow.h"
|
|
|
|
#include "mozilla/EventStates.h"
|
|
#include "mozilla/Services.h"
|
|
@@ -703,6 +704,24 @@ nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame,
|
|
case NS_THEME_GTK_INFO_BAR:
|
|
aGtkWidgetType = MOZ_GTK_INFO_BAR;
|
|
break;
|
|
+ case NS_THEME_WINDOW_TITLEBAR:
|
|
+ aGtkWidgetType = MOZ_GTK_HEADER_BAR;
|
|
+ break;
|
|
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
|
|
+ aGtkWidgetType = MOZ_GTK_HEADER_BAR_MAXIMIZED;
|
|
+ break;
|
|
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
|
|
+ aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE;
|
|
+ break;
|
|
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
|
|
+ aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE;
|
|
+ break;
|
|
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
|
|
+ aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
|
|
+ break;
|
|
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
|
|
+ aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE;
|
|
+ break;
|
|
default:
|
|
return false;
|
|
}
|
|
@@ -1627,6 +1646,10 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
|
|
case NS_THEME_MENULIST:
|
|
case NS_THEME_TOOLBARBUTTON:
|
|
case NS_THEME_TREEHEADERCELL:
|
|
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
|
|
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
|
|
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
|
|
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
|
|
{
|
|
if (aWidgetType == NS_THEME_MENULIST) {
|
|
// Include the arrow size.
|
|
@@ -1892,9 +1915,21 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
|
|
case NS_THEME_DIALOG:
|
|
#if (MOZ_WIDGET_GTK == 3)
|
|
case NS_THEME_GTK_INFO_BAR:
|
|
+ case NS_THEME_GTK_WINDOW_DECORATION:
|
|
#endif
|
|
return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
|
|
|
|
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
|
|
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
|
|
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
|
|
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
|
|
+ case NS_THEME_WINDOW_TITLEBAR:
|
|
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
|
|
+ // GtkHeaderBar is available on GTK 3.10+, which is used for styling
|
|
+ // title bars and title buttons.
|
|
+ return gtk_check_version(3, 10, 0) == nullptr &&
|
|
+ !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
|
|
+
|
|
case NS_THEME_MENULIST_BUTTON:
|
|
if (aFrame && aFrame->GetWritingMode().IsVertical()) {
|
|
return false;
|
|
@@ -1978,6 +2013,13 @@ nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
|
|
#else
|
|
return eTransparent;
|
|
#endif
|
|
+ case NS_THEME_GTK_WINDOW_DECORATION:
|
|
+ {
|
|
+ nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget());
|
|
+ if (window)
|
|
+ return window->IsComposited() ? eTransparent : eOpaque;
|
|
+ return eOpaque;
|
|
+ }
|
|
}
|
|
|
|
return eUnknownTransparency;
|
|
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
|
|
index 37b6aae4c3d0..f0c3343f20ae 100644
|
|
--- a/widget/gtk/nsWindow.cpp
|
|
+++ b/widget/gtk/nsWindow.cpp
|
|
@@ -85,6 +85,7 @@
|
|
#include "nsIPropertyBag2.h"
|
|
#include "GLContext.h"
|
|
#include "gfx2DGlue.h"
|
|
+#include "nsLookAndFeel.h"
|
|
|
|
#ifdef ACCESSIBILITY
|
|
#include "mozilla/a11y/Accessible.h"
|
|
@@ -139,6 +140,8 @@ using namespace mozilla::widget;
|
|
|
|
#include "mozilla/layers/APZCTreeManager.h"
|
|
|
|
+#include "gtkdrawing.h"
|
|
+
|
|
using namespace mozilla;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::widget;
|
|
@@ -185,6 +188,8 @@ static gboolean expose_event_cb (GtkWidget *widget,
|
|
#else
|
|
static gboolean expose_event_cb (GtkWidget *widget,
|
|
cairo_t *rect);
|
|
+static gboolean expose_event_decoration_draw_cb (GtkWidget *widget,
|
|
+ cairo_t *cr);
|
|
#endif
|
|
static gboolean configure_event_cb (GtkWidget *widget,
|
|
GdkEventConfigure *event);
|
|
@@ -230,7 +235,6 @@ static void screen_composited_changed_cb (GdkScreen* screen,
|
|
gpointer user_data);
|
|
static void widget_composited_changed_cb (GtkWidget* widget,
|
|
gpointer user_data);
|
|
-
|
|
#if (MOZ_WIDGET_GTK == 3)
|
|
static void scale_changed_cb (GtkWidget* widget,
|
|
GParamSpec* aPSpec,
|
|
@@ -439,6 +443,7 @@ nsWindow::nsWindow()
|
|
|
|
mContainer = nullptr;
|
|
mGdkWindow = nullptr;
|
|
+ mIsCSDEnabled = false;
|
|
mShell = nullptr;
|
|
mCompositorWidgetDelegate = nullptr;
|
|
mHasMappedToplevel = false;
|
|
@@ -480,6 +485,9 @@ nsWindow::nsWindow()
|
|
mLastScrollEventTime = GDK_CURRENT_TIME;
|
|
#endif
|
|
mPendingConfigures = 0;
|
|
+ mDrawWindowDecoration = false;
|
|
+ mDecorationSize = {0,0,0,0};
|
|
+ mCSDSupportLevel = CSD_SUPPORT_UNKNOWN;
|
|
}
|
|
|
|
nsWindow::~nsWindow()
|
|
@@ -1478,8 +1486,8 @@ LayoutDeviceIntRect
|
|
nsWindow::GetScreenBounds()
|
|
{
|
|
LayoutDeviceIntRect rect;
|
|
- if (mIsTopLevel && mContainer) {
|
|
- // use the point including window decorations
|
|
+ if (mIsTopLevel && mContainer && !IsClientDecorated()) {
|
|
+ // use the point including default Gtk+ window decorations
|
|
gint x, y;
|
|
gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)), &x, &y);
|
|
rect.MoveTo(GdkPointToDevicePixels({ x, y }));
|
|
@@ -1605,6 +1613,10 @@ nsWindow::SetCursor(nsCursor aCursor)
|
|
return;
|
|
|
|
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), newCursor);
|
|
+ if (IsClientDecorated()) {
|
|
+ gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mShell)),
|
|
+ newCursor);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -1661,6 +1673,10 @@ nsWindow::SetCursor(imgIContainer* aCursor,
|
|
if (cursor) {
|
|
if (mContainer) {
|
|
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)), cursor);
|
|
+ if (IsClientDecorated()) {
|
|
+ gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mShell)),
|
|
+ cursor);
|
|
+ }
|
|
rv = NS_OK;
|
|
}
|
|
#if (MOZ_WIDGET_GTK == 3)
|
|
@@ -2175,6 +2191,12 @@ nsWindow::OnExposeEvent(cairo_t *cr)
|
|
return TRUE;
|
|
}
|
|
|
|
+ // Clip upper part of the mContainer to get visible rounded corners
|
|
+ // of GtkHeaderBar which is renderd to mShell.
|
|
+ if (mIsCSDEnabled) {
|
|
+ ApplyCSDClipping();
|
|
+ }
|
|
+
|
|
// If this widget uses OMTC...
|
|
if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT ||
|
|
GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR) {
|
|
@@ -2585,6 +2607,53 @@ nsWindow::OnMotionNotifyEvent(GdkEventMotion *aEvent)
|
|
}
|
|
}
|
|
#endif /* MOZ_X11 */
|
|
+ // Client is decorated and we're getting the motion event for mShell
|
|
+ // window which draws the CSD decorations around mContainer.
|
|
+ if (IsClientDecorated()) {
|
|
+ if (aEvent->window == gtk_widget_get_window(mShell)) {
|
|
+ GdkWindowEdge edge;
|
|
+ LayoutDeviceIntPoint refPoint =
|
|
+ GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
|
|
+ if (CheckResizerEdge(refPoint, edge)) {
|
|
+ nsCursor cursor = eCursor_none;
|
|
+ switch (edge) {
|
|
+ case GDK_WINDOW_EDGE_NORTH:
|
|
+ cursor = eCursor_n_resize;
|
|
+ break;
|
|
+ case GDK_WINDOW_EDGE_NORTH_WEST:
|
|
+ cursor = eCursor_nw_resize;
|
|
+ break;
|
|
+ case GDK_WINDOW_EDGE_NORTH_EAST:
|
|
+ cursor = eCursor_ne_resize;
|
|
+ break;
|
|
+ case GDK_WINDOW_EDGE_WEST:
|
|
+ cursor = eCursor_w_resize;
|
|
+ break;
|
|
+ case GDK_WINDOW_EDGE_EAST:
|
|
+ cursor = eCursor_e_resize;
|
|
+ break;
|
|
+ case GDK_WINDOW_EDGE_SOUTH:
|
|
+ cursor = eCursor_s_resize;
|
|
+ break;
|
|
+ case GDK_WINDOW_EDGE_SOUTH_WEST:
|
|
+ cursor = eCursor_sw_resize;
|
|
+ break;
|
|
+ case GDK_WINDOW_EDGE_SOUTH_EAST:
|
|
+ cursor = eCursor_se_resize;
|
|
+ break;
|
|
+ }
|
|
+ SetCursor(cursor);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // We're not on resize handle - check if we need to reset cursor back.
|
|
+ if (mCursor == eCursor_n_resize || mCursor == eCursor_nw_resize ||
|
|
+ mCursor == eCursor_ne_resize || mCursor == eCursor_w_resize ||
|
|
+ mCursor == eCursor_e_resize || mCursor == eCursor_s_resize ||
|
|
+ mCursor == eCursor_sw_resize || mCursor == eCursor_se_resize) {
|
|
+ SetCursor(eCursor_standard);
|
|
+ }
|
|
+ }
|
|
|
|
WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
|
|
|
|
@@ -2755,6 +2824,20 @@ nsWindow::OnButtonPressEvent(GdkEventButton *aEvent)
|
|
if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false))
|
|
return;
|
|
|
|
+ if (IsClientDecorated() && aEvent->window == gtk_widget_get_window(mShell)) {
|
|
+ // Check to see if the event is within our window's resize region
|
|
+ GdkWindowEdge edge;
|
|
+ LayoutDeviceIntPoint refPoint =
|
|
+ GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
|
|
+ if (CheckResizerEdge(refPoint, edge)) {
|
|
+ gdk_window_begin_resize_drag(gtk_widget_get_window(mShell),
|
|
+ edge, aEvent->button,
|
|
+ aEvent->x_root, aEvent->y_root,
|
|
+ aEvent->time);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
gdouble pressure = 0;
|
|
gdk_event_get_axis ((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
|
|
mLastMotionPressure = pressure;
|
|
@@ -3340,6 +3423,8 @@ nsWindow::OnWindowStateEvent(GtkWidget *aWidget, GdkEventWindowState *aEvent)
|
|
#endif //ACCESSIBILITY
|
|
}
|
|
|
|
+ UpdateClientDecorations();
|
|
+
|
|
if (mWidgetListener) {
|
|
mWidgetListener->SizeModeChanged(mSizeState);
|
|
if (aEvent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
|
|
@@ -3404,6 +3489,7 @@ nsWindow::OnCompositedChanged()
|
|
presShell->ThemeChanged();
|
|
}
|
|
}
|
|
+ UpdateClientDecorations();
|
|
}
|
|
|
|
void
|
|
@@ -3592,7 +3678,8 @@ nsWindow::Create(nsIWidget* aParent,
|
|
GtkWindow *topLevelParent = nullptr;
|
|
nsWindow *parentnsWindow = nullptr;
|
|
GtkWidget *eventWidget = nullptr;
|
|
- bool shellHasCSD = false;
|
|
+ GtkWidget *drawWidget = nullptr;
|
|
+ bool drawToContainer = false;
|
|
|
|
if (aParent) {
|
|
parentnsWindow = static_cast<nsWindow*>(aParent);
|
|
@@ -3639,29 +3726,47 @@ nsWindow::Create(nsIWidget* aParent,
|
|
GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP;
|
|
mShell = gtk_window_new(type);
|
|
|
|
- bool useAlphaVisual = (mWindowType == eWindowType_popup &&
|
|
- aInitData->mSupportTranslucency);
|
|
+ bool useAlphaVisual = false;
|
|
+#if (MOZ_WIDGET_GTK == 3)
|
|
+ // When CSD is available we can emulate it for toplevel windows.
|
|
+ // Content is rendered to mContainer and transparent decorations to mShell.
|
|
+ if (GetCSDSupportLevel() != CSD_SUPPORT_NONE &&
|
|
+ mWindowType == eWindowType_toplevel) {
|
|
+ int32_t isCSDAvailable = false;
|
|
+ nsresult rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDAvailable,
|
|
+ &isCSDAvailable);
|
|
+ if (NS_SUCCEEDED(rv)) {
|
|
+ mIsCSDEnabled = useAlphaVisual = isCSDAvailable;
|
|
+ }
|
|
+ } else
|
|
+#endif
|
|
+ if (mWindowType == eWindowType_popup) {
|
|
+ useAlphaVisual = aInitData->mSupportTranslucency;
|
|
+ }
|
|
|
|
// mozilla.widget.use-argb-visuals is a hidden pref defaulting to false
|
|
// to allow experimentation
|
|
if (Preferences::GetBool("mozilla.widget.use-argb-visuals", false))
|
|
useAlphaVisual = true;
|
|
|
|
+ // An ARGB visual is only useful if we are on a compositing
|
|
+ // window manager.
|
|
+ GdkScreen *screen = gtk_widget_get_screen(mShell);
|
|
+ if (useAlphaVisual && !gdk_screen_is_composited(screen)) {
|
|
+ useAlphaVisual = false;
|
|
+ }
|
|
+
|
|
// We need to select an ARGB visual here instead of in
|
|
// SetTransparencyMode() because it has to be done before the
|
|
- // widget is realized. An ARGB visual is only useful if we
|
|
- // are on a compositing window manager.
|
|
+ // widget is realized.
|
|
if (useAlphaVisual) {
|
|
- GdkScreen *screen = gtk_widget_get_screen(mShell);
|
|
- if (gdk_screen_is_composited(screen)) {
|
|
#if (MOZ_WIDGET_GTK == 2)
|
|
- GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
|
|
- gtk_widget_set_colormap(mShell, colormap);
|
|
+ GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
|
|
+ gtk_widget_set_colormap(mShell, colormap);
|
|
#else
|
|
- GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
|
|
- gtk_widget_set_visual(mShell, visual);
|
|
+ GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
|
|
+ gtk_widget_set_visual(mShell, visual);
|
|
#endif
|
|
- }
|
|
}
|
|
|
|
// We only move a general managed toplevel window if someone has
|
|
@@ -3755,24 +3860,56 @@ nsWindow::Create(nsIWidget* aParent,
|
|
mContainer = MOZ_CONTAINER(container);
|
|
|
|
#if (MOZ_WIDGET_GTK == 3)
|
|
- // "csd" style is set when widget is realized so we need to call
|
|
- // it explicitly now.
|
|
- gtk_widget_realize(mShell);
|
|
-
|
|
- // We can't draw directly to top-level window when client side
|
|
- // decorations are enabled. We use container with GdkWindow instead.
|
|
- GtkStyleContext* style = gtk_widget_get_style_context(mShell);
|
|
- shellHasCSD = gtk_style_context_has_class(style, "csd");
|
|
-#endif
|
|
- if (!shellHasCSD) {
|
|
- // Use mShell's window for drawing and events.
|
|
- gtk_widget_set_has_window(container, FALSE);
|
|
- // Prevent GtkWindow from painting a background to flicker.
|
|
- gtk_widget_set_app_paintable(mShell, TRUE);
|
|
+ /* There are tree possible situations here:
|
|
+ *
|
|
+ * 1) We're running on Gtk+ < 3.20 without any decorations. Content
|
|
+ * is rendered to mShell window and we listen Gtk+ events on mShell.
|
|
+ * 2) We're running on Gtk+ > 3.20 and window decorations are drawn
|
|
+ * by default by Gtk+. Content is rendered to mContainer,
|
|
+ * we listen events on mContainer. mShell contains default Gtk+
|
|
+ * window decorations rendered by Gtk+.
|
|
+ * 3) We're running on Gtk+ > 3.20 and both window decorations and
|
|
+ * content is rendered by gecko. We emulate Gtk+ decoration rendering
|
|
+ * to mShell and we need to listen Gtk events on both mShell
|
|
+ * and mContainer.
|
|
+ */
|
|
+
|
|
+ // When client side decorations are enabled (rendered by us or by Gtk+)
|
|
+ // the decoration is rendered to mShell (toplevel) window and
|
|
+ // we draw our content to mContainer.
|
|
+ if (mIsCSDEnabled) {
|
|
+ drawToContainer = true;
|
|
+ } else {
|
|
+ // mIsCSDEnabled can be disabled by preference so look at actual
|
|
+ // toplevel window style to to detect active "csd" style.
|
|
+ // The "csd" style is set when widget is realized so we need to call
|
|
+ // it explicitly now.
|
|
+ gtk_widget_realize(mShell);
|
|
+
|
|
+ GtkStyleContext* style = gtk_widget_get_style_context(mShell);
|
|
+ drawToContainer = gtk_style_context_has_class(style, "csd");
|
|
}
|
|
- // Set up event widget
|
|
- eventWidget = shellHasCSD ? container : mShell;
|
|
+#endif
|
|
+ drawWidget = (drawToContainer) ? container : mShell;
|
|
+ // When we draw decorations on our own we need to handle resize events
|
|
+ // because Gtk+ does not provide resizers for undecorated windows.
|
|
+ // The CSD on mShell borders act as resize handlers
|
|
+ // so we need to listen there.
|
|
+ eventWidget = (drawToContainer && !mIsCSDEnabled) ? container : mShell;
|
|
+
|
|
gtk_widget_add_events(eventWidget, kEvents);
|
|
+ if (eventWidget != drawWidget) {
|
|
+ // CSD is rendered by us (not by Gtk+) so we also need to listen
|
|
+ // at mShell window for events.
|
|
+ gtk_widget_add_events(drawWidget, kEvents);
|
|
+ }
|
|
+
|
|
+ // Prevent GtkWindow from painting a background to flicker.
|
|
+ gtk_widget_set_app_paintable(drawWidget, TRUE);
|
|
+
|
|
+ // gtk_container_add() realizes the child widget so we need to
|
|
+ // set it now.
|
|
+ gtk_widget_set_has_window(container, drawToContainer);
|
|
|
|
gtk_container_add(GTK_CONTAINER(mShell), container);
|
|
gtk_widget_realize(container);
|
|
@@ -3782,7 +3919,7 @@ nsWindow::Create(nsIWidget* aParent,
|
|
gtk_widget_grab_focus(container);
|
|
|
|
// the drawing window
|
|
- mGdkWindow = gtk_widget_get_window(eventWidget);
|
|
+ mGdkWindow = gtk_widget_get_window(drawWidget);
|
|
|
|
if (mWindowType == eWindowType_popup) {
|
|
// gdk does not automatically set the cursor for "temporary"
|
|
@@ -3855,6 +3992,11 @@ nsWindow::Create(nsIWidget* aParent,
|
|
|
|
// label the drawing window with this object so we can find our way home
|
|
g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
|
|
+ if (mIsCSDEnabled) {
|
|
+ // label the CSD window with this object so we can find our way home
|
|
+ g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)),
|
|
+ "nsWindow", this);
|
|
+ }
|
|
|
|
if (mContainer)
|
|
g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
|
|
@@ -3892,6 +4034,10 @@ nsWindow::Create(nsIWidget* aParent,
|
|
g_signal_connect_after(default_settings,
|
|
"notify::gtk-font-name",
|
|
G_CALLBACK(theme_changed_cb), this);
|
|
+ if (mIsCSDEnabled) {
|
|
+ g_signal_connect(G_OBJECT(mShell), "draw",
|
|
+ G_CALLBACK(expose_event_decoration_draw_cb), nullptr);
|
|
+ }
|
|
}
|
|
|
|
if (mContainer) {
|
|
@@ -3942,7 +4088,7 @@ nsWindow::Create(nsIWidget* aParent,
|
|
G_CALLBACK(drag_data_received_event_cb), nullptr);
|
|
|
|
GtkWidget *widgets[] = { GTK_WIDGET(mContainer),
|
|
- !shellHasCSD ? mShell : nullptr };
|
|
+ !drawToContainer ? mShell : nullptr };
|
|
for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
|
|
// Visibility events are sent to the owning widget of the relevant
|
|
// window but do not propagate to parent widgets so connect on
|
|
@@ -3972,7 +4118,6 @@ nsWindow::Create(nsIWidget* aParent,
|
|
// Don't let GTK mess with the shapes of our GdkWindows
|
|
GTK_PRIVATE_SET_FLAG(eventWidget, GTK_HAS_SHAPE_MASK);
|
|
#endif
|
|
-
|
|
// These events are sent to the owning widget of the relevant window
|
|
// and propagate up to the first widget that handles the events, so we
|
|
// need only connect on mShell, if it exists, to catch events on its
|
|
@@ -4109,6 +4254,12 @@ nsWindow::NativeResize()
|
|
size.width, size.height));
|
|
|
|
if (mIsTopLevel) {
|
|
+ // When we draw decorations add extra space to draw shadows
|
|
+ // around the main window.
|
|
+ if (mDrawWindowDecoration) {
|
|
+ size.width += mDecorationSize.left + mDecorationSize.right;
|
|
+ size.height += mDecorationSize.top + mDecorationSize.bottom;
|
|
+ }
|
|
gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
|
|
}
|
|
else if (mContainer) {
|
|
@@ -4165,6 +4316,11 @@ nsWindow::NativeMoveResize()
|
|
if (mIsTopLevel) {
|
|
// x and y give the position of the window manager frame top-left.
|
|
gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y);
|
|
+
|
|
+ if (mDrawWindowDecoration) {
|
|
+ size.width += mDecorationSize.left + mDecorationSize.right;
|
|
+ size.height += mDecorationSize.top + mDecorationSize.bottom;
|
|
+ }
|
|
// This sets the client window size.
|
|
gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
|
|
}
|
|
@@ -5523,6 +5679,33 @@ expose_event_cb(GtkWidget *widget, cairo_t *cr)
|
|
|
|
return FALSE;
|
|
}
|
|
+
|
|
+/* static */
|
|
+gboolean
|
|
+expose_event_decoration_draw_cb(GtkWidget *widget, cairo_t *cr)
|
|
+{
|
|
+ GdkWindow* gdkWindow = gtk_widget_get_window(widget);
|
|
+ if (gtk_cairo_should_draw_window(cr, gdkWindow)) {
|
|
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
|
|
+ if (!window) {
|
|
+ NS_WARNING("Cannot get nsWindow from GtkWidget");
|
|
+ }
|
|
+ else if (window->IsClientDecorated()) {
|
|
+ cairo_save(cr);
|
|
+ gtk_cairo_transform_to_window(cr, widget, gdkWindow);
|
|
+
|
|
+ GdkRectangle rect = {0,0,0,0};
|
|
+ gtk_window_get_size(GTK_WINDOW(widget), &rect.width, &rect.height);
|
|
+ moz_gtk_window_decoration_paint(cr, &rect);
|
|
+
|
|
+ rect.height = 40;
|
|
+ moz_gtk_header_bar_paint(cr, &rect);
|
|
+ cairo_restore(cr);
|
|
+ }
|
|
+ }
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
#endif //MOZ_WIDGET_GTK == 2
|
|
|
|
static gboolean
|
|
@@ -6575,6 +6758,28 @@ nsWindow::ClearCachedResources()
|
|
}
|
|
}
|
|
|
|
+NS_IMETHODIMP
|
|
+nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &aMargins)
|
|
+{
|
|
+ SetDrawsInTitlebar(aMargins.top == 0);
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+void
|
|
+nsWindow::SetDrawsInTitlebar(bool aState)
|
|
+{
|
|
+ if (!mIsCSDEnabled || aState == mDrawWindowDecoration)
|
|
+ return;
|
|
+
|
|
+ if (mShell) {
|
|
+ gtk_window_set_decorated(GTK_WINDOW(mShell), !aState);
|
|
+ gtk_widget_set_app_paintable(mShell, aState);
|
|
+ }
|
|
+
|
|
+ mDrawWindowDecoration = aState;
|
|
+ UpdateClientDecorations();
|
|
+}
|
|
+
|
|
gint
|
|
nsWindow::GdkScaleFactor()
|
|
{
|
|
@@ -6845,6 +7050,157 @@ nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
|
|
}
|
|
#endif
|
|
|
|
+bool
|
|
+nsWindow::IsClientDecorated() const
|
|
+{
|
|
+ return mDrawWindowDecoration && mSizeState == nsSizeMode_Normal;
|
|
+}
|
|
+
|
|
+int
|
|
+nsWindow::GetClientResizerSize()
|
|
+{
|
|
+ if (!mShell)
|
|
+ return 0;
|
|
+
|
|
+ // GTK uses a default size of 20px as of 3.20.
|
|
+ gint size = 20;
|
|
+ gtk_widget_style_get(mShell, "decoration-resize-handle", &size, nullptr);
|
|
+
|
|
+ return GdkCoordToDevicePixels(size);
|
|
+}
|
|
+
|
|
+nsWindow::CSDSupportLevel
|
|
+nsWindow::GetCSDSupportLevel() {
|
|
+ if (mCSDSupportLevel != CSD_SUPPORT_UNKNOWN) {
|
|
+ return mCSDSupportLevel;
|
|
+ }
|
|
+ // TODO: MATE
|
|
+ const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
|
|
+ if (currentDesktop) {
|
|
+ if (strcmp(currentDesktop, "GNOME") == 0) {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_FULL;
|
|
+ } else if (strcmp(currentDesktop, "XFCE") == 0) {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_FULL;
|
|
+ } else if (strcmp(currentDesktop, "X-Cinnamon") == 0) {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_FULL;
|
|
+ } else if (strcmp(currentDesktop, "KDE") == 0) {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_FLAT;
|
|
+ } else if (strcmp(currentDesktop, "LXDE") == 0) {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_FLAT;
|
|
+ } else if (strcmp(currentDesktop, "openbox") == 0) {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_FLAT;
|
|
+ } else if (strcmp(currentDesktop, "i3") == 0) {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_NONE;
|
|
+ } else {
|
|
+ mCSDSupportLevel = CSD_SUPPORT_NONE;
|
|
+ }
|
|
+ }
|
|
+ return mCSDSupportLevel;
|
|
+}
|
|
+
|
|
+void
|
|
+nsWindow::UpdateClientDecorations()
|
|
+{
|
|
+ // When the CSD is not fully supported by window manager (ie. WM is not
|
|
+ // expecting that application is going to draw window shadows) we can't
|
|
+ // add shadows widths to the window margin. That would lead to completely
|
|
+ // opaque black border of the window.
|
|
+ if (!mDrawWindowDecoration || GetCSDSupportLevel() != CSD_SUPPORT_FULL)
|
|
+ return;
|
|
+
|
|
+ gint top = 0, right = 0, bottom = 0, left = 0;
|
|
+ if (mSizeState == nsSizeMode_Normal) {
|
|
+ moz_gtk_get_window_border(&top, &right, &bottom, &left);
|
|
+ }
|
|
+
|
|
+ static auto sGdkWindowSetShadowWidth =
|
|
+ (void (*)(GdkWindow*, gint, gint, gint, gint))
|
|
+ dlsym(RTLD_DEFAULT, "gdk_window_set_shadow_width");
|
|
+ sGdkWindowSetShadowWidth(gtk_widget_get_window(mShell),
|
|
+ left, right, top, bottom);
|
|
+
|
|
+ mDecorationSize.left = left;
|
|
+ mDecorationSize.right = right;
|
|
+ mDecorationSize.top = top;
|
|
+ mDecorationSize.bottom = bottom;
|
|
+
|
|
+ // Gtk+ doesn't like when we set mContainer margin bigger than actual
|
|
+ // mContainer window size. That happens when we're called early and the
|
|
+ // mShell/mContainer is not allocated/resized yet and has default 1x1 size.
|
|
+ // Just resize to some minimal value which will be changed
|
|
+ // by Gecko soon.
|
|
+ GtkAllocation allocation;
|
|
+ gtk_widget_get_allocation(GTK_WIDGET(mContainer), &allocation);
|
|
+ if (allocation.width < left + right || allocation.height < top + bottom) {
|
|
+ gtk_widget_get_preferred_width(GTK_WIDGET(mContainer), nullptr,
|
|
+ &allocation.width);
|
|
+ gtk_widget_get_preferred_height(GTK_WIDGET(mContainer), nullptr,
|
|
+ &allocation.height);
|
|
+ allocation.width += left + right + 1;
|
|
+ allocation.height += top + bottom + 1;
|
|
+ gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
|
|
+ }
|
|
+
|
|
+ gtk_widget_set_margin_left(GTK_WIDGET(mContainer), mDecorationSize.left);
|
|
+ gtk_widget_set_margin_right(GTK_WIDGET(mContainer), mDecorationSize.right);
|
|
+ gtk_widget_set_margin_top(GTK_WIDGET(mContainer), mDecorationSize.top);
|
|
+ gtk_widget_set_margin_bottom(GTK_WIDGET(mContainer), mDecorationSize.bottom);
|
|
+}
|
|
+
|
|
+void
|
|
+nsWindow::ApplyCSDClipping()
|
|
+{
|
|
+ if (IsClientDecorated()) {
|
|
+ gint top, right, bottom, left;
|
|
+ moz_gtk_get_header_bar_border(&top, &right, &bottom, &left);
|
|
+ cairo_rectangle_int_t rect = { 0, top, mBounds.width, mBounds.height};
|
|
+ cairo_region_t *region = cairo_region_create_rectangle(&rect);
|
|
+ gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
|
|
+ cairo_region_destroy(region);
|
|
+ } else {
|
|
+ gdk_window_shape_combine_region(mGdkWindow, nullptr, 0, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+bool
|
|
+nsWindow::CheckResizerEdge(LayoutDeviceIntPoint aPoint, GdkWindowEdge& aOutEdge)
|
|
+{
|
|
+ gint scale = GdkScaleFactor();
|
|
+ gint left = scale * mDecorationSize.left;
|
|
+ gint top = scale * mDecorationSize.top;
|
|
+ gint right = scale * mDecorationSize.right;
|
|
+ gint bottom = scale * mDecorationSize.bottom;
|
|
+
|
|
+ int resizerSize = GetClientResizerSize();
|
|
+ int topDist = aPoint.y;
|
|
+ int leftDist = aPoint.x;
|
|
+ int rightDist = mBounds.width - aPoint.x;
|
|
+ int bottomDist = mBounds.height - aPoint.y;
|
|
+
|
|
+ //TODO -> wrong sizes
|
|
+
|
|
+ if (leftDist <= resizerSize && topDist <= resizerSize) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_NORTH_WEST;
|
|
+ } else if (rightDist <= resizerSize && topDist <= resizerSize) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_NORTH_EAST;
|
|
+ } else if (leftDist <= resizerSize && bottomDist <= resizerSize) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_SOUTH_WEST;
|
|
+ } else if (rightDist <= resizerSize && bottomDist <= resizerSize) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_SOUTH_EAST;
|
|
+ } else if (topDist <= top) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_NORTH;
|
|
+ } else if (leftDist <= left) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_WEST;
|
|
+ } else if (rightDist <= right) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_EAST;
|
|
+ } else if (bottomDist <= bottom) {
|
|
+ aOutEdge = GDK_WINDOW_EDGE_SOUTH;
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
int32_t
|
|
nsWindow::RoundsWidgetCoordinatesTo()
|
|
{
|
|
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
|
|
index f7c07d57491b..d9a07316a52c 100644
|
|
--- a/widget/gtk/nsWindow.h
|
|
+++ b/widget/gtk/nsWindow.h
|
|
@@ -123,6 +123,7 @@ public:
|
|
double aHeight,
|
|
bool aRepaint) override;
|
|
virtual bool IsEnabled() const override;
|
|
+ bool IsComposited() const;
|
|
|
|
void SetZIndex(int32_t aZIndex) override;
|
|
virtual void SetSizeMode(nsSizeMode aMode) override;
|
|
@@ -351,6 +352,9 @@ public:
|
|
#endif
|
|
virtual void GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) override;
|
|
|
|
+ NS_IMETHOD SetNonClientMargins(LayoutDeviceIntMargin& aMargins) override;
|
|
+ void SetDrawsInTitlebar(bool aState) override;
|
|
+
|
|
// HiDPI scale conversion
|
|
gint GdkScaleFactor();
|
|
|
|
@@ -367,6 +371,9 @@ public:
|
|
LayoutDeviceIntRect GdkRectToDevicePixels(GdkRectangle rect);
|
|
|
|
virtual bool WidgetTypeSupportsAcceleration() override;
|
|
+
|
|
+ // Decorations
|
|
+ bool IsClientDecorated() const;
|
|
protected:
|
|
virtual ~nsWindow();
|
|
|
|
@@ -384,6 +391,16 @@ protected:
|
|
|
|
virtual void RegisterTouchWindow() override;
|
|
|
|
+ int GetClientResizerSize();
|
|
+
|
|
+ // Informs the window manager about the size of the shadows surrounding
|
|
+ // a client-side decorated window.
|
|
+ void UpdateClientDecorations();
|
|
+
|
|
+ // Returns true if the given point (in device pixels) is within a resizer
|
|
+ // region of the window. Only used when drawing decorations client side.
|
|
+ bool CheckResizerEdge(LayoutDeviceIntPoint aPoint, GdkWindowEdge& aOutEdge);
|
|
+
|
|
nsCOMPtr<nsIWidget> mParent;
|
|
// Is this a toplevel window?
|
|
bool mIsTopLevel;
|
|
@@ -432,12 +449,12 @@ private:
|
|
gint* aRootX, gint* aRootY);
|
|
void ClearCachedResources();
|
|
nsIWidgetListener* GetListener();
|
|
- bool IsComposited() const;
|
|
-
|
|
+ void ApplyCSDClipping();
|
|
|
|
GtkWidget *mShell;
|
|
MozContainer *mContainer;
|
|
GdkWindow *mGdkWindow;
|
|
+ bool mIsCSDEnabled;
|
|
PlatformCompositorWidgetDelegate* mCompositorWidgetDelegate;
|
|
|
|
|
|
@@ -536,6 +553,10 @@ private:
|
|
// leaving fullscreen
|
|
nsSizeMode mLastSizeMode;
|
|
|
|
+ // If true, draw our own window decorations (where supported).
|
|
+ bool mDrawWindowDecoration;
|
|
+ GtkBorder mDecorationSize;
|
|
+
|
|
static bool DragInProgress(void);
|
|
|
|
void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent);
|
|
@@ -567,6 +588,17 @@ private:
|
|
RefPtr<mozilla::widget::IMContextWrapper> mIMContext;
|
|
|
|
mozilla::UniquePtr<mozilla::CurrentX11TimeGetter> mCurrentTimeGetter;
|
|
+ typedef enum { CSD_SUPPORT_FULL, // CSD including shadows
|
|
+ CSD_SUPPORT_FLAT, // CSD without shadows
|
|
+ CSD_SUPPORT_NONE, // WM does not support CSD at all
|
|
+ CSD_SUPPORT_UNKNOWN
|
|
+ } CSDSupportLevel;
|
|
+ /**
|
|
+ * Get the support of Client Side Decoration by checking
|
|
+ * the XDG_CURRENT_DESKTOP environment variable.
|
|
+ */
|
|
+ CSDSupportLevel GetCSDSupportLevel();
|
|
+ CSDSupportLevel mCSDSupportLevel;
|
|
};
|
|
|
|
#endif /* __nsWindow_h__ */
|