From 81bd7b1f5f6a63e5dcc68846c1695a64dc32d5f6 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Tue, 14 Sep 2021 20:11:00 -0700 Subject: [PATCH 1/4] Implement a system that resizes the ghost widget appropriately to avoid undefined behavior due to it being moved into the gap between screens caused by scaling. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Components/FancyDocking.cpp | 14 +++--- .../Components/FancyDockingGhostWidget.cpp | 50 ++++++++++++++++++- .../Components/FancyDockingGhostWidget.h | 9 ++++ 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp index f60a98f392..7a76781cd7 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDocking.cpp @@ -1615,14 +1615,16 @@ namespace AzQtComponents } // Handle snapping to the screen edges/other floating windows while dragging - QScreen* screen = Utilities::ScreenAtPoint(globalPos); + QScreen* screen = QApplication::screenAt(globalPos); - AdjustForSnapping(placeholder, screen); + if (screen) + { + AdjustForSnapping(placeholder, screen); + m_state.setPlaceholder(placeholder, screen); - m_state.setPlaceholder(placeholder, screen); - - m_ghostWidget->Enable(); - RepaintFloatingIndicators(); + m_ghostWidget->Enable(); + RepaintFloatingIndicators(); + } } return m_dropZoneState.dragging(); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp index 6894e5b011..04387875df 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -76,7 +77,32 @@ namespace AzQtComponents window->setScreen(screen); } - setGeometry(targetRect); + QPoint midPoint = targetRect.topLeft() + QPoint(targetRect.width() / 2, targetRect.height() / 2); + QScreen* pointScreen = QApplication::screenAt(midPoint); + QRect rect(targetRect); + + if (!pointScreen || pointScreen != screen) + { + if (midPoint.x() >= QCursor::pos().x()) + { + rect.setLeft(rect.left() - rect.width()); + rect.setTop(rect.top() - rect.height()); + m_paintMode = PaintMode::BOTTOMRIGHT; + } + else + { + rect.setRight(rect.right() + rect.width()); + rect.setTop(rect.top() - rect.height()); + m_paintMode = PaintMode::BOTTOMLEFT; + } + } + else + { + m_paintMode = PaintMode::FULL; + } + + setGeometry(rect); + setPixmapVisible(true); if (needsRepaint) { @@ -135,7 +161,27 @@ namespace AzQtComponents yOffset = widgetSize.height() - aspectRatioHeight; } - painter.drawPixmap(QRect(0, yOffset, widgetSize.width(), aspectRatioHeight), m_pixmap); + switch (m_paintMode) + { + case PaintMode::FULL: + { + painter.drawPixmap(QRect(0, yOffset, widgetSize.width(), aspectRatioHeight), m_pixmap); + } + break; + + case PaintMode::BOTTOMLEFT: + { + painter.drawPixmap(QRect(0, (widgetSize.height() + yOffset) / 2, widgetSize.width() / 2, aspectRatioHeight / 2), m_pixmap); + } + break; + + case PaintMode::BOTTOMRIGHT: + { + painter.drawPixmap(QRect(widgetSize.width() / 2, (widgetSize.height() + yOffset) / 2, widgetSize.width() / 2, aspectRatioHeight / 2), m_pixmap); + } + break; + } + if (m_clipToWidgets) { painter.restore(); diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h index cadcbd9c8a..6649c6d848 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h @@ -45,5 +45,14 @@ namespace AzQtComponents QPixmap m_pixmap; bool m_visible = false; // maintain our own flag, so that we're always ready to render ignoring Qt's widget caching system bool m_clipToWidgets = false; + + enum class PaintMode + { + FULL = 0, + BOTTOMLEFT, + BOTTOMRIGHT + }; + + PaintMode m_paintMode = PaintMode::FULL; }; } // namespace AzQtComponents From f111b67df608b504b48c71f8a4661ff6e0fed6ce Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 15 Sep 2021 14:44:27 -0700 Subject: [PATCH 2/4] Fix issue with one frame flicker in the transition between paint modes. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Components/FancyDockingGhostWidget.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp index 04387875df..5456356458 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp @@ -48,7 +48,7 @@ namespace AzQtComponents void FancyDockingGhostWidget::setPixmap(const QPixmap& pixmap, const QRect& targetRect, QScreen* screen) { - const bool needsRepaint = m_pixmap.cacheKey() != pixmap.cacheKey() || m_clipToWidgets; + bool needsRepaint = m_pixmap.cacheKey() != pixmap.cacheKey() || m_clipToWidgets; m_pixmap = pixmap; if (pixmap.isNull() || targetRect.isNull() || !screen) @@ -80,6 +80,7 @@ namespace AzQtComponents QPoint midPoint = targetRect.topLeft() + QPoint(targetRect.width() / 2, targetRect.height() / 2); QScreen* pointScreen = QApplication::screenAt(midPoint); QRect rect(targetRect); + PaintMode paintMode = PaintMode::FULL; if (!pointScreen || pointScreen != screen) { @@ -87,26 +88,28 @@ namespace AzQtComponents { rect.setLeft(rect.left() - rect.width()); rect.setTop(rect.top() - rect.height()); - m_paintMode = PaintMode::BOTTOMRIGHT; + paintMode = PaintMode::BOTTOMRIGHT; } else { rect.setRight(rect.right() + rect.width()); rect.setTop(rect.top() - rect.height()); - m_paintMode = PaintMode::BOTTOMLEFT; + paintMode = PaintMode::BOTTOMLEFT; } } - else + + if (m_paintMode != paintMode) { - m_paintMode = PaintMode::FULL; + needsRepaint = true; } setGeometry(rect); + m_paintMode = paintMode; setPixmapVisible(true); if (needsRepaint) { - update(); + repaint(); } } From 50e4d549f44ade8b615c73a33b76e975fb21347f Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 15 Sep 2021 14:51:36 -0700 Subject: [PATCH 3/4] Add comments Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzQtComponents/Components/FancyDockingGhostWidget.cpp | 8 ++++++++ .../AzQtComponents/Components/FancyDockingGhostWidget.h | 7 ++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp index 5456356458..d259b28636 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp @@ -80,6 +80,14 @@ namespace AzQtComponents QPoint midPoint = targetRect.topLeft() + QPoint(targetRect.width() / 2, targetRect.height() / 2); QScreen* pointScreen = QApplication::screenAt(midPoint); QRect rect(targetRect); + + // On environment with multiple screens with different scaling settings, the screen coordinate system may have gaps + // due to the screen real estate shrinking according to the scale. When that happens, if a widget is moved into the gap + // it will resize and translate with undefined behavior, causing a lot of jitter and flashes. + // To prevent this, whenever the widget would end up outside screen boundaries, we resize the widget to be twice its + // original size so that the center of the widget is back inside the screen boundaries, and set the ghost widget + // to paint the widget pixmap at half the previous size to make the process seamless. + // This makes the dragging a lot smoother in most situations. PaintMode paintMode = PaintMode::FULL; if (!pointScreen || pointScreen != screen) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h index 6649c6d848..5244bcdd0a 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.h @@ -46,11 +46,12 @@ namespace AzQtComponents bool m_visible = false; // maintain our own flag, so that we're always ready to render ignoring Qt's widget caching system bool m_clipToWidgets = false; + //! Determines the way the ghost widget pixmap should be painted on the widget. enum class PaintMode { - FULL = 0, - BOTTOMLEFT, - BOTTOMRIGHT + FULL = 0, //!< Paint the pixmap on the full widget + BOTTOMLEFT, //!< Paint the pixmap on the bottom left quarter of the widget, halving its size + BOTTOMRIGHT //!< Paint the pixmap on the bottom right quarter of the widget, halving its size }; PaintMode m_paintMode = PaintMode::FULL; From a9091b0a72572f05f5871b26a3231d678fc10bb8 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Wed, 15 Sep 2021 15:47:23 -0700 Subject: [PATCH 4/4] Minor changes to comments Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../AzQtComponents/Components/FancyDockingGhostWidget.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp index d259b28636..fd20f4d125 100644 --- a/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp +++ b/Code/Framework/AzQtComponents/AzQtComponents/Components/FancyDockingGhostWidget.cpp @@ -81,9 +81,8 @@ namespace AzQtComponents QScreen* pointScreen = QApplication::screenAt(midPoint); QRect rect(targetRect); - // On environment with multiple screens with different scaling settings, the screen coordinate system may have gaps - // due to the screen real estate shrinking according to the scale. When that happens, if a widget is moved into the gap - // it will resize and translate with undefined behavior, causing a lot of jitter and flashes. + // In environments with multiple screens the screen coordinate system may have gaps, especially when different scaling settings + // are involved. When that happens, if a widget is moved into the gap it will resize and translate with undefined behavior. // To prevent this, whenever the widget would end up outside screen boundaries, we resize the widget to be twice its // original size so that the center of the widget is back inside the screen boundaries, and set the ghost widget // to paint the widget pixmap at half the previous size to make the process seamless. @@ -117,6 +116,8 @@ namespace AzQtComponents setPixmapVisible(true); if (needsRepaint) { + // We use repaint instead of update since the latter has a delay of 1 frame, + // which would cause the ghost widget to flicker when changing paint mode. repaint(); } }