]> SALOME platform Git repositories - modules/gui.git/blobdiff - src/ViewerTools/ViewerTools_ScreenScaling.cxx
Salome HOME
[bos #32216][CEA] GUI ergonomic with HDPI screens.
[modules/gui.git] / src / ViewerTools / ViewerTools_ScreenScaling.cxx
diff --git a/src/ViewerTools/ViewerTools_ScreenScaling.cxx b/src/ViewerTools/ViewerTools_ScreenScaling.cxx
new file mode 100644 (file)
index 0000000..e4dd6d9
--- /dev/null
@@ -0,0 +1,167 @@
+// Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
+//
+// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "ViewerTools_ScreenScaling.h"
+
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QWheelEvent>
+
+#include "utilities.h"
+
+/*!
+ * Namespace   : ViewerTools_ScreenScaling
+ * Description : Tools for handle UI on screens with different scaling
+ */
+namespace ViewerTools_ScreenScaling
+{
+  // Returns pixel ratio for the current screen
+  double getPR()
+  {
+    auto getPixelRatio = []() -> double
+    {
+      // Returns the device pixel ratio for the device as a floating point number.
+      // Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" displays.
+      // Returns QT_SCALE_FACTOR if QT_SCALE_FACTOR was set.
+      // Returns 2 on HDPI if QApplication::setAttribute(Qt::AA_EnableHighDpiScaling) was set.
+      // Returns 2 * QT_SCALE_FACTOR if both of the features above were set.
+      // NOTE. QT_SCALE_FACTOR breaks UI in a set (-1.0, 0.0) and (0.0, 1.0).
+      // Negative values don't scale or break UI down, but end up with 1.0 pixel ratio.
+      const double pixelRatio = QApplication::desktop()->devicePixelRatioF();
+      MESSAGE("pixelRatio: " << pixelRatio);
+
+      // Keep commented block here for a test case where we use QT_SCALE_FACTOR value instead.
+      // QByteArray scaleFactorArr = qgetenv("QT_SCALE_FACTOR");
+      // const QString scaleFactorStr = QString::fromLocal8Bit(scaleFactorArr);
+
+      // bool isScale = false;
+      // const double scaleFactor = scaleFactorStr.toDouble(&isScale);
+      // if (isScale)
+      // {
+      //   MESSAGE("scaleFactor: " << scaleFactor);
+      //   return scaleFactor;
+      // }
+
+      return pixelRatio;
+    };
+
+    // TODO: check if we need to get it out of static to handle moving to another screen
+    static const double pixelRatio = getPixelRatio();
+    return pixelRatio;
+  }
+
+  // Check if we have pixel ratio != 1.0
+  bool isScaledPixelRatio()
+  {
+    auto isScaledPR = []() -> bool
+    {
+      // This an arbitrary value that seems to be meaningful for UI scaling.
+      // It's not clear if we need smaller values.
+      const double epsilon = 0.01;
+      const double pixelRatio = ViewerTools_ScreenScaling::getPR();
+
+      const bool isScaled = std::abs(pixelRatio - 1.0) > epsilon;
+      MESSAGE("isScaled: " << isScaled);
+
+      return isScaled;
+    };
+
+    static const bool isScaled = isScaledPR();
+    return isScaled;
+  }
+
+
+  // Returns a copy of the given event with the local coordinates
+  // updated with the current pixel ratio.
+  QEvent* getDpiAwareEvent(QEvent* e, bool toMultiply/*  = true */)
+  {
+    // Calculate a new position
+    auto getNewPos = [toMultiply](const QPointF& pos) -> QPointF
+    {
+      double pixelRatio = ViewerTools_ScreenScaling::getPR();
+      if (!toMultiply)
+      {
+        pixelRatio = 1.0 / pixelRatio;
+      }
+
+      const double x = pos.x() * pixelRatio;
+      const double y = pos.y() * pixelRatio;
+
+      // Commented because of bloated output for casual debug
+      // MESSAGE("New pos: " << x << ", " << y);
+
+      return QPointF(x, y);
+    };
+
+    // Scales mouse event
+    auto makeMouseEvent = [&getNewPos](QMouseEvent* e) -> QEvent*
+    {
+      const QPointF pos = e->localPos();
+      const QPointF newPos = getNewPos(pos);
+
+      const QPointF globalPos = e->globalPos();
+      const QPointF globalNewPos = getNewPos(globalPos);
+
+      // Commented because of bloated output for casual debug
+      // MESSAGE("type: " << e->type() << "; old: " << pos.x() << ", " << pos.y() << "; new: " << newPos.x() << ", " << newPos.y());
+
+      return new QMouseEvent(e->type(), newPos, globalNewPos, e->button(), e->buttons(), e->modifiers());
+    };
+
+    // Scales wheel event
+    auto makeWheelEvent = [&getNewPos](QWheelEvent* e) -> QEvent*
+    {
+      const QPointF pos = e->posF();
+
+      // Commented because of bloated output for casual debug
+      // MESSAGE("Old pos: " << pos.x() << ", " << pos.y());
+
+      return new QWheelEvent(
+        getNewPos(pos), e->globalPosF(), e->pixelDelta(), e->angleDelta(), e->buttons(), e->modifiers(), e->phase(), e->inverted(), e->source());
+    };
+
+    // Return on start if we don't have any scaling
+    if (!isScaledPixelRatio())
+      return e;
+
+    // Make a copy of the event with the new position
+    const int aType = e->type();
+    switch(aType)
+    {
+      case QEvent::MouseButtonPress:
+      case QEvent::MouseButtonRelease:
+      case QEvent::MouseMove:
+      case QEvent::MouseButtonDblClick:
+        return makeMouseEvent((QMouseEvent*)e);
+
+      case QEvent::Wheel:
+        return makeWheelEvent((QWheelEvent*)e);
+
+      default:
+        break;
+    }
+
+    return e;
+  }
+};