Bug 1431337 Scale widget size to the current monitor, not the first one; r?stransky
We need to use scaling factor of the monitor on which application is actually positioned.
Previously we used ScreenHelperGTK::GetGTKMonitorScaleFactor() which use the first monitor.
This does not work on hidpi+normal dpi monitors setup.
We also need to listen to the changes of layout.css.devPixelsPerPx because we cannot
scale per monitor when this preference is set to positive number.
MozReview-Commit-ID: 4y1Dv5DWiNX
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -32,16 +32,17 @@
#include <gtk/gtkx.h>
#include "gfxContext.h"
#include "gfxPlatformGtk.h"
#include "gfxGdkNativeRenderer.h"
#include "mozilla/gfx/BorrowedContext.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/Preferences.h"
#ifdef MOZ_X11
# ifdef CAIRO_HAS_XLIB_SURFACE
# include "cairo-xlib.h"
# endif
# ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
# include "cairo-xlib-xrender.h"
# endif
@@ -54,32 +55,67 @@ using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::widget;
NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
nsIObserver)
static int gLastGdkError;
+static void UpdateDevPixelsPerPxCB(const char*, void* aClosure)
+{
+ static_cast<nsNativeThemeGTK*>(aClosure)->UpdateDevPixelsPerPx();
+}
+// Return scale factor of the monitor where the window is located
+// by the most part.
+static inline gint
+GetMonitorScaleFactor(nsIFrame* aFrame, double aDevPixelsPerPx)
+{
+ // When the layout.css.devPixelsPerPx is set the scale can be < 1,
+ // the real monitor scale cannot go under 1.
+ if (aDevPixelsPerPx > 0) {
+ return 1;
+ }
+
+ nsIWidget* rootWidget = aFrame->PresContext()->GetRootWidget();
+ if (rootWidget) {
+ // We need to use GetDefaultScale() despite it returns monitor scale
+ // factor multiplied by font scale factor because it is the only scale
+ // updated in nsPuppetWidget.
+ // Since we don't want to apply font scale factor for UI elements
+ // (because GTK does not do so) we need to remove that from returned value.
+ return rootWidget->GetDefaultScale().scale / gfxPlatformGtk::GetFontScaleFactor();
+ }
+ return 1.0;
+}
+
+NS_IMETHODIMP
+nsNativeThemeGTK::UpdateDevPixelsPerPx()
+{
+ mDevPixelsPerPx = Preferences::GetFloat("layout.css.devPixelsPerPx", -1);
+ return NS_OK;
+}
+
nsNativeThemeGTK::nsNativeThemeGTK()
{
if (moz_gtk_init() != MOZ_GTK_SUCCESS) {
memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes));
return;
}
// We have to call moz_gtk_shutdown before the event loop stops running.
nsCOMPtr<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
obsServ->AddObserver(this, "xpcom-shutdown", false);
-
+ Preferences::RegisterCallback(UpdateDevPixelsPerPxCB, "layout.css.devPixelsPerPx", this);
ThemeChanged();
}
nsNativeThemeGTK::~nsNativeThemeGTK() {
+ Preferences::UnregisterCallback(UpdateDevPixelsPerPxCB, "layout.css.devPixelsPerPx", this);
}
NS_IMETHODIMP
nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic,
const char16_t *aData)
{
if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
moz_gtk_shutdown();
@@ -1040,17 +1076,17 @@ nsNativeThemeGTK::GetExtraSizeForWidget(
} else {
aExtra->bottom = extra;
}
return false;
}
default:
return false;
}
- gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+ gint scale = GetMonitorScaleFactor(aFrame, mDevPixelsPerPx);
aExtra->top *= scale;
aExtra->right *= scale;
aExtra->bottom *= scale;
aExtra->left *= scale;
return true;
}
NS_IMETHODIMP
@@ -1068,17 +1104,17 @@ nsNativeThemeGTK::DrawWidgetBackground(g
&flags))
return NS_OK;
gfxContext* ctx = aContext;
nsPresContext *presContext = aFrame->PresContext();
gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
- gint scaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+ gint scaleFactor = GetMonitorScaleFactor(aFrame, mDevPixelsPerPx);
// Align to device pixels where sensible
// to provide crisper and faster drawing.
// Don't snap if it's a non-unit scale factor. We're going to have to take
// slow paths then in any case.
bool snapped = ctx->UserToDevicePixelSnapped(rect);
if (snapped) {
// Leave rect in device coords but make dirtyRect consistent.
@@ -1287,17 +1323,17 @@ nsNativeThemeGTK::GetWidgetBorder(nsDevi
break;
MOZ_FALLTHROUGH;
default:
{
GetCachedWidgetBorder(aFrame, aWidgetType, direction, aResult);
}
}
- gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+ gint scale = GetMonitorScaleFactor(aFrame, mDevPixelsPerPx);
aResult->top *= scale;
aResult->right *= scale;
aResult->bottom *= scale;
aResult->left *= scale;
return NS_OK;
}
bool
@@ -1345,17 +1381,17 @@ nsNativeThemeGTK::GetWidgetPadding(nsDev
if (aWidgetType == NS_THEME_MENUITEM)
moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding);
else
moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding);
aResult->left += horizontal_padding;
aResult->right += horizontal_padding;
- gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
+ gint scale = GetMonitorScaleFactor(aFrame, mDevPixelsPerPx);
aResult->top *= scale;
aResult->right *= scale;
aResult->bottom *= scale;
aResult->left *= scale;
return true;
}
}
@@ -1566,18 +1602,17 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
moz_gtk_get_arrow_size(MOZ_GTK_DROPDOWN,
&aResult->width, &aResult->height);
}
// else the minimum size is missing consideration of container
// descendants; the value returned here will not be helpful, but the
// box model may consider border and padding with child minimum sizes.
nsIntMargin border;
- nsNativeThemeGTK::GetWidgetBorder(aFrame->PresContext()->DeviceContext(),
- aFrame, aWidgetType, &border);
+ GetCachedWidgetBorder(aFrame, aWidgetType, GetTextDirection(aFrame), &border);
aResult->width += border.left + border.right;
aResult->height += border.top + border.bottom;
}
break;
#ifdef MOZ_WIDGET_GTK
case NS_THEME_NUMBER_INPUT:
case NS_THEME_TEXTFIELD:
{
@@ -1619,17 +1654,17 @@ nsNativeThemeGTK::GetMinimumWidgetSize(n
moz_gtk_get_treeview_expander_size(&expander_size);
aResult->width = aResult->height = expander_size;
*aIsOverridable = false;
}
break;
}
- *aResult = *aResult * ScreenHelperGTK::GetGTKMonitorScaleFactor();
+ *aResult = *aResult * GetMonitorScaleFactor(aFrame, mDevPixelsPerPx);
return NS_OK;
}
NS_IMETHODIMP
nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
nsAtom* aAttribute, bool* aShouldRepaint,
const nsAttrValue* aOldValue)
--- a/widget/gtk/nsNativeThemeGTK.h
+++ b/widget/gtk/nsNativeThemeGTK.h
@@ -63,17 +63,17 @@ public:
NS_IMETHOD_(bool) WidgetIsContainer(uint8_t aWidgetType) override;
NS_IMETHOD_(bool) ThemeDrawsFocusForWidget(uint8_t aWidgetType) override;
virtual bool ThemeNeedsComboboxDropmarker() override;
virtual Transparency GetWidgetTransparency(nsIFrame* aFrame,
uint8_t aWidgetType) override;
-
+ NS_IMETHOD UpdateDevPixelsPerPx();
nsNativeThemeGTK();
protected:
virtual ~nsNativeThemeGTK();
private:
GtkTextDirection GetTextDirection(nsIFrame* aFrame);
gint GetTabMarginPixels(nsIFrame* aFrame);
@@ -92,11 +92,12 @@ private:
// Because moz_gtk_get_widget_border can be slow, we cache its results
// by widget type. Each bit in mBorderCacheValid says whether the
// corresponding entry in mBorderCache is valid.
void GetCachedWidgetBorder(nsIFrame* aFrame, uint8_t aWidgetType,
GtkTextDirection aDirection, nsIntMargin* aResult);
uint8_t mBorderCacheValid[(MOZ_GTK_WIDGET_NODE_COUNT + 7) / 8];
nsIntMargin mBorderCache[MOZ_GTK_WIDGET_NODE_COUNT];
+ double mDevPixelsPerPx;
};
#endif