File indexing completed on 2024-05-12 08:34:14
0001 /* SPDX-FileCopyrightText: 2023 Noah Davis <noahadvs@gmail.com> 0002 * SPDX-License-Identifier: LGPL-2.0-or-later 0003 */ 0004 0005 #include "Geometry.h" 0006 0007 #include <KWindowSystem> 0008 0009 #include <QDebug> 0010 #include <QGuiApplication> 0011 #include <QScreen> 0012 #include <QWindow> 0013 0014 #include <cmath> 0015 0016 class GeometrySingleton 0017 { 0018 public: 0019 Geometry self; 0020 }; 0021 0022 Q_GLOBAL_STATIC(GeometrySingleton, privateGeometrySelf) 0023 0024 Geometry::Geometry(QObject *parent) 0025 : QObject(parent) 0026 {} 0027 0028 Geometry *Geometry::instance() 0029 { 0030 return &privateGeometrySelf->self; 0031 } 0032 0033 qreal Geometry::dpx(qreal dpr) 0034 { 0035 return 1 / dpr; 0036 } 0037 0038 qreal Geometry::dprRound(qreal value, qreal dpr) 0039 { 0040 return std::round(value * dpr) / dpr; 0041 } 0042 0043 qreal Geometry::mapFromPlatformValue(qreal value, qreal dpr) 0044 { 0045 if (KWindowSystem::isPlatformX11()) { 0046 return value / dpr; 0047 } 0048 return value; 0049 } 0050 0051 QPointF Geometry::mapFromPlatformPoint(const QPointF &point, qreal dpr) 0052 { 0053 if (KWindowSystem::isPlatformX11()) { 0054 return point / dpr; 0055 } 0056 return point; 0057 } 0058 0059 QRectF Geometry::mapFromPlatformRect(const QRectF &rect, qreal dpr) 0060 { 0061 if (rect.isEmpty() || KWindowSystem::isPlatformWayland()) { 0062 return rect; 0063 } 0064 0065 // Make the position scaled like Wayland. 0066 return {rect.topLeft() / dpr, rect.size()}; 0067 } 0068 0069 QRectF Geometry::logicalScreensRect() 0070 { 0071 QRectF rect; 0072 const auto &screens = qGuiApp->screens(); 0073 for (int i = 0; i < screens.size(); ++i) { 0074 rect |= Geometry::mapFromPlatformRect(screens[i]->geometry(), qGuiApp->devicePixelRatio()); 0075 } 0076 return rect; 0077 } 0078 0079 qreal Geometry::mapToPlatformValue(qreal value, qreal dpr) 0080 { 0081 if (KWindowSystem::isPlatformX11()) { 0082 return value * dpr; 0083 } 0084 return value; 0085 } 0086 0087 QPointF Geometry::mapToPlatformPoint(const QPointF &point, qreal dpr) 0088 { 0089 if (KWindowSystem::isPlatformX11()) { 0090 return point * dpr; 0091 } 0092 return point; 0093 } 0094 0095 QRectF Geometry::mapToPlatformRect(const QRectF &rect, qreal dpr) 0096 { 0097 if (rect.isEmpty() || KWindowSystem::isPlatformWayland()) { 0098 return rect; 0099 } 0100 return {rect.topLeft() * dpr, rect.size()}; 0101 } 0102 0103 QRectF Geometry::platformUnifiedRect() 0104 { 0105 QRectF rect; 0106 const auto &screens = qGuiApp->screens(); 0107 for (int i = 0; i < screens.size(); ++i) { 0108 rect |= screens[i]->geometry(); 0109 } 0110 return rect; 0111 } 0112 0113 QSize Geometry::rawSize(const QSizeF &size, qreal dpr) 0114 { 0115 return (size * dpr).toSize(); 0116 } 0117 0118 QRectF Geometry::rectAdjusted(const QRectF &rect, qreal xp1, qreal yp1, qreal xp2, qreal yp2) 0119 { 0120 return rect.adjusted(xp1, yp1, xp2, yp2); 0121 } 0122 0123 QRectF Geometry::rectAdjustedVisually(const QRectF &rect, qreal xp1, qreal yp1, qreal xp2, qreal yp2) 0124 { 0125 if (rect.width() < 0) { 0126 std::swap(xp1, xp2); 0127 } 0128 if (rect.height() < 0) { 0129 std::swap(yp1, yp2); 0130 } 0131 return rect.adjusted(xp1, yp1, xp2, yp2); 0132 } 0133 0134 QRectF Geometry::rectScaled(const QRectF &rect, qreal scale) 0135 { 0136 if (scale == 1) { 0137 return rect; 0138 } 0139 return {rect.topLeft() * scale, rect.size() * scale}; 0140 } 0141 0142 QRectF Geometry::rectIntersected(const QRectF &rect1, const QRectF &rect2) 0143 { 0144 return rect1.intersected(rect2); 0145 } 0146 0147 QRectF Geometry::rectBounded(const QRectF &rect, const QRectF &boundsRect, 0148 Qt::Orientations orientations) 0149 { 0150 if (rect == boundsRect) { 0151 return rect; 0152 } 0153 auto newRect = rect; 0154 const auto &nBoundsRect = boundsRect.normalized(); // normalize to make math easier 0155 if (orientations & Qt::Horizontal) { 0156 if (rect.width() >= 0) { 0157 newRect.moveLeft(std::clamp(rect.x(), nBoundsRect.x(), nBoundsRect.right() - rect.width())); 0158 } else { 0159 newRect.moveRight(std::clamp(rect.right(), nBoundsRect.x(), nBoundsRect.right() - std::abs(rect.width()))); 0160 } 0161 } 0162 if (orientations & Qt::Vertical) { 0163 if (rect.height() >= 0) { 0164 newRect.moveTop(std::clamp(rect.y(), nBoundsRect.y(), nBoundsRect.bottom() - std::abs(rect.height()))); 0165 } else { 0166 newRect.moveBottom(std::clamp(rect.bottom(), nBoundsRect.y(), nBoundsRect.bottom() - std::abs(rect.height()))); 0167 } 0168 } 0169 return newRect; 0170 } 0171 0172 QRectF Geometry::rectBounded(qreal x, qreal y, qreal width, qreal height, const QRectF &boundsRect, 0173 Qt::Orientations orientations) 0174 { 0175 return rectBounded({x, y, width, height}, boundsRect, orientations); 0176 } 0177 0178 QRectF Geometry::rectClipped(const QRectF &rect, const QRectF &clipRect, 0179 Qt::Orientations orientations) 0180 { 0181 if (rect == clipRect) { 0182 return rect; 0183 } 0184 auto newRect = rect; 0185 const auto &nClipRect = clipRect.normalized(); // normalize to make math easier 0186 if (orientations & Qt::Horizontal) { 0187 if (rect.width() >= 0) { 0188 newRect.setLeft(std::max(rect.x(), nClipRect.x())); 0189 newRect.setRight(std::min(rect.right(), nClipRect.right())); 0190 } else { 0191 newRect.setLeft(std::min(rect.x(), nClipRect.right())); 0192 newRect.setRight(std::max(rect.right(), nClipRect.x())); 0193 } 0194 } 0195 if (orientations & Qt::Vertical) { 0196 if (rect.height() >= 0) { 0197 newRect.setTop(std::max(rect.y(), nClipRect.y())); 0198 newRect.setBottom(std::min(rect.bottom(), nClipRect.bottom())); 0199 } else { 0200 newRect.setTop(std::min(rect.y(), nClipRect.bottom())); 0201 newRect.setBottom(std::max(rect.bottom(), nClipRect.y())); 0202 } 0203 } 0204 return newRect; 0205 } 0206 0207 bool Geometry::rectContains(const QRectF &rect, qreal v, Qt::Orientations orientations) 0208 { 0209 Q_ASSERT(orientations != Qt::Orientations{}); 0210 bool contains = true; 0211 if (orientations.testFlag(Qt::Horizontal)) { 0212 contains &= std::min(rect.left(), rect.right()) <= v && v <= std::max(rect.left(), rect.right()); 0213 } 0214 if (contains && orientations.testFlag(Qt::Vertical)) { 0215 contains &= std::min(rect.top(), rect.bottom()) <= v && v <= std::max(rect.top(), rect.bottom()); 0216 } 0217 return contains; 0218 } 0219 0220 bool Geometry::rectContains(const QRectF &rect, qreal x, qreal y) 0221 { 0222 return rect.contains(x, y); 0223 } 0224 0225 bool Geometry::rectContains(const QRectF &rect, const QPointF &point) 0226 { 0227 return rect.contains(point); 0228 } 0229 0230 bool Geometry::rectContains(const QRectF &rect1, const QRectF& rect2) 0231 { 0232 return rect1.contains(rect2); 0233 } 0234 0235 bool Geometry::rectContains(const QRectF &rect, qreal x, qreal y, qreal w, qreal h) 0236 { 0237 return rect.contains({x, y, w, h}); 0238 } 0239 0240 bool Geometry::ellipseContains(qreal ellipseX, qreal ellipseY, 0241 qreal ellipseWidth, qreal ellipseHeight, 0242 qreal x, qreal y) 0243 { 0244 if (isEmpty(ellipseWidth, ellipseHeight)) { 0245 return false; 0246 } 0247 auto xRadius = ellipseWidth / 2; 0248 auto yRadius = ellipseHeight / 2; 0249 auto centerX = ellipseX + xRadius; 0250 auto centerY = ellipseY + yRadius; 0251 // is inside or on edge 0252 return std::pow(x - centerX, 2) / std::pow(xRadius, 2) 0253 + std::pow(y - centerY, 2) / std::pow(yRadius, 2) <= 1; 0254 } 0255 0256 bool Geometry::ellipseContains(const QRectF &rect, qreal x, qreal y) 0257 { 0258 return ellipseContains(rect.x(), rect.y(), rect.width(), rect.height(), x, y); 0259 } 0260 0261 bool Geometry::ellipseContains(const QRectF &rect, const QPointF &point) 0262 { 0263 return ellipseContains(rect, point.x(), point.y()); 0264 } 0265 0266 bool Geometry::rectIntersects(const QRectF &rect1, const QRectF& rect2) 0267 { 0268 return rect1.intersects(rect2); 0269 } 0270 0271 bool Geometry::rectIntersects(const QRectF &rect, qreal x, qreal y, qreal w, qreal h) 0272 { 0273 return rect.intersects({x, y, w, h}); 0274 } 0275 0276 bool Geometry::isEmpty(qreal w, qreal h) 0277 { 0278 return w <= 0 || h <= 0; 0279 } 0280 0281 bool Geometry::isEmpty(const QSizeF &size) 0282 { 0283 return size.isEmpty(); 0284 } 0285 0286 bool Geometry::isEmpty(const QRectF &rect) 0287 { 0288 return rect.isEmpty(); 0289 } 0290 0291 bool Geometry::isNull(qreal w, qreal h) 0292 { 0293 return qIsNull(w) && qIsNull(h); 0294 } 0295 0296 bool Geometry::isNull(const QSizeF &size) 0297 { 0298 return size.isNull(); 0299 } 0300 0301 bool Geometry::isNull(const QRectF &rect) 0302 { 0303 return rect.isNull(); 0304 } 0305 0306 #include <moc_Geometry.cpp>