Bug 1493081 - Added nsIWidget::GetDesktopToDeviceScaleByScreen for scale factor lookup by window position; r=mattwoodrow
We've added nsIWidget::GetDesktopToDeviceScaleByScreen which will return scale factor of the newly placed window
according to its position on the display. This change is to move implementation to the nsIWidget derived classes.
We need that for GTK Wayland, because on the Wayland we cannot determine absolute position of the window, we
need to use parent's window scale factor. For other platforms the GetDesktopToDeviceScaleByScreen is implemented
in nsBaseWidget.
Differential Revision:
https://phabricator.services.mozilla.com/D7290
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -315,17 +315,16 @@ void nsView::DoResetWidgetBounds(bool aM
MOZ_ASSERT(mWindow, "Why was this called??");
// Hold this ref to make sure it stays alive.
nsCOMPtr<nsIWidget> widget = mWindow;
// Stash a copy of these and use them so we can handle this being deleted (say
// from sync painting/flushing from Show/Move/Resize on the widget).
LayoutDeviceIntRect newBounds;
- RefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext();
nsWindowType type = widget->WindowType();
LayoutDeviceIntRect curBounds = widget->GetClientBounds();
bool invisiblePopup = type == eWindowType_popup &&
((curBounds.IsEmpty() && mDimBounds.IsEmpty()) ||
mVis == nsViewVisibility_kHide);
@@ -355,17 +354,18 @@ void nsView::DoResetWidgetBounds(bool aM
bool changedSize = curBounds.Size() != newBounds.Size();
// Child views are never attached to top level widgets, this is safe.
// Coordinates are converted to desktop pixels for window Move/Resize APIs,
// because of the potential for device-pixel coordinate spaces for mixed
// hidpi/lodpi screens to overlap each other and result in bad placement
// (bug 814434).
- DesktopToLayoutDeviceScale scale = dx->GetDesktopToDeviceScale();
+
+ DesktopToLayoutDeviceScale scale = widget->GetDesktopToDeviceScaleByScreen();
DesktopRect deskRect = newBounds / scale;
if (changedPos) {
if (changedSize && !aMoveOnly) {
widget->ResizeClient(deskRect.X(), deskRect.Y(),
deskRect.Width(), deskRect.Height(),
aInvalidateChangedSize);
} else {
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -33,26 +33,26 @@ EXPORTS += [
EXPORTS.mozilla.widget += [
'AndroidCompositorWidget.h',
'AndroidUiThread.h',
]
SOURCES += [
'!GeneratedJNIWrappers.cpp',
+ 'EventDispatcher.cpp',
]
UNIFIED_SOURCES += [
'AndroidAlerts.cpp',
'AndroidBridge.cpp',
'AndroidCompositorWidget.cpp',
'AndroidContentController.cpp',
'AndroidUiThread.cpp',
'ANRReporter.cpp',
- 'EventDispatcher.cpp',
'GeckoEditableSupport.cpp',
'GfxInfo.cpp',
'nsAndroidProtocolHandler.cpp',
'nsAppShell.cpp',
'nsClipboard.cpp',
'nsDeviceContextAndroid.cpp',
'nsIdleServiceAndroid.cpp',
'nsLookAndFeel.cpp',
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -52,16 +52,17 @@
#include <X11/extensions/shape.h>
#include <gdk/gdkkeysyms-compat.h>
#endif /* MOZ_X11 */
#include <gdk/gdkkeysyms.h>
#if defined(MOZ_WAYLAND)
#include <gdk/gdkwayland.h>
+#include "nsView.h"
#endif
#include "nsGkAtoms.h"
#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
#define SN_API_NOT_YET_FROZEN
#include <startup-notification-1.0/libsn/sn.h>
#endif
@@ -836,16 +837,47 @@ nsWindow::GetDesktopToDeviceScale()
return DesktopToLayoutDeviceScale(GdkScaleFactor());
}
#endif
// In Gtk/X11, we manage windows using device pixels.
return DesktopToLayoutDeviceScale(1.0);
}
+DesktopToLayoutDeviceScale
+nsWindow::GetDesktopToDeviceScaleByScreen()
+{
+#ifdef MOZ_WAYLAND
+ GdkDisplay* gdkDisplay = gdk_display_get_default();
+ // In Wayland there's no way to get absolute position of the window and use it to
+ // determine the screen factor of the monitor on which the window is placed.
+ // The window is notified of the current scale factor but not at this point,
+ // so the GdkScaleFactor can return wrong value which can lead to wrong popup
+ // placement.
+ // We need to use parent's window scale factor for the new one.
+ if (GDK_IS_WAYLAND_DISPLAY(gdkDisplay)) {
+ nsView* view = nsView::GetViewFor(this);
+ if (view) {
+ nsView* parentView = view->GetParent();
+ if (parentView) {
+ nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr);
+ if (parentWidget) {
+ return DesktopToLayoutDeviceScale(parentWidget->RoundsWidgetCoordinatesTo());
+ } else {
+ NS_WARNING("Widget has no parent");
+ }
+ }
+ } else {
+ NS_WARNING("Cannot find widget view");
+ }
+ }
+#endif
+ return nsBaseWidget::GetDesktopToDeviceScale();
+}
+
void
nsWindow::SetParent(nsIWidget *aNewParent)
{
if (!mGdkWindow) {
MOZ_ASSERT_UNREACHABLE("The native window has already been destroyed");
return;
}
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -120,16 +120,17 @@ public:
nsNativeWidget aNativeParent,
const LayoutDeviceIntRect& aRect,
nsWidgetInitData* aInitData) override;
virtual void Destroy() override;
virtual nsIWidget *GetParent() override;
virtual float GetDPI() override;
virtual double GetDefaultScaleInternal() override;
mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override;
+ mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() override;
virtual void SetParent(nsIWidget* aNewParent) override;
virtual void SetModal(bool aModal) override;
virtual bool IsVisible() const override;
virtual void ConstrainPosition(bool aAllowSlop,
int32_t *aX,
int32_t *aY) override;
virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override;
virtual void Move(double aX, double aY) override;
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -19,16 +19,18 @@
#include "nsIFile.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIRollupListener.h"
#include "nsIObserver.h"
#include "nsIWidgetListener.h"
#include "nsPIDOMWindow.h"
#include "nsWeakReference.h"
+#include "nsView.h"
+#include "nsViewManager.h"
#include <algorithm>
#if defined(XP_WIN)
// Scroll capture constants
const uint32_t kScrollCaptureFillColor = 0xFFa0a0a0; // gray
const mozilla::gfx::SurfaceFormat kScrollCaptureFormat =
mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32;
#endif
@@ -233,16 +235,20 @@ public:
bool BoundsUseDesktopPixels() const {
return mWindowType <= eWindowType_popup;
}
// Default implementation, to be overridden by platforms where desktop coords
// are virtualized and may not correspond to device pixels on the screen.
mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override {
return mozilla::DesktopToLayoutDeviceScale(1.0);
}
+ mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() override {
+ return (nsView::GetViewFor(this)->GetViewManager()->GetDeviceContext())->GetDesktopToDeviceScale();
+ }
+
virtual void ConstrainPosition(bool aAllowSlop,
int32_t *aX,
int32_t *aY) override {}
virtual void MoveClient(double aX, double aY) override;
virtual void ResizeClient(double aWidth, double aHeight, bool aRepaint) override;
virtual void ResizeClient(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override;
virtual LayoutDeviceIntRect GetBounds() override;
virtual LayoutDeviceIntRect GetClientBounds() override;
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -573,16 +573,23 @@ class nsIWidget : public nsISupports
/**
* Return the scaling factor between device pixels and the platform-
* dependent "desktop pixels" used to manage window positions on a
* potentially multi-screen, mixed-resolution desktop.
*/
virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() = 0;
/**
+ * Return the scaling factor between device pixels and the platform-
+ * dependent "desktop pixels" by looking up the screen by the position
+ * of the widget.
+ */
+ virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScaleByScreen() = 0;
+
+ /**
* Return the default scale factor for the window. This is the
* default number of device pixels per CSS pixel to use. This should
* depend on OS/platform settings such as the Mac's "UI scale factor"
* or Windows' "font DPI". This will take into account Gecko preferences
* overriding the system setting.
*/
mozilla::CSSToLayoutDeviceScale GetDefaultScale();