File indexing completed on 2024-05-05 04:34:53

0001 /* SPDX-FileCopyrightText: 2023 Noah Davis <noahadvs@gmail.com>
0002  * SPDX-License-Identifier: LGPL-2.0-or-later
0003  */
0004 
0005 #pragma once
0006 
0007 #include <QObject>
0008 
0009 #include <memory>
0010 
0011 class QPointF;
0012 class QRectF;
0013 class QSizeF;
0014 
0015 /**
0016  * This is a class for processing geometry.
0017  *
0018  * This class has functionality for converting between logical screen coordinates
0019  * and platform native screen coordinates. For Wayland, there is no difference.
0020  *
0021  * On X11, QScreen::geometry has the raw size (in hardware pixels) scaled down by
0022  * the device pixel ratio (DPR), but the position is still the screen's raw position.
0023  *
0024  * On Wayland, QScreen::geometry has the raw size scaled down by the DPR and
0025  * the position is also adjusted so that each QScreen::geometry matches the
0026  * layout used by the Wayland compositor (almost, see the next paragraph).
0027  * This kind of geometry is sometimes called "logical" geometry.
0028  *
0029  * QScreen::geometry is a QRect, which means some precision is lost when the
0030  * raw size is scale down. If QScreen::geometry is scaled back up, it may not
0031  * match the raw size, especially when fractional scale factors are used or the
0032  * resolution has an odd width/height.
0033  */
0034 class Geometry : public QObject
0035 {
0036     Q_OBJECT
0037 public:
0038     static Geometry *instance();
0039 
0040     /**
0041      * This returns the logical size of a raw device pixel based on the given device pixel ratio.
0042      * This has a short name so that it doesn't make lines of code really long.
0043      */
0044     [[nodiscard]] Q_INVOKABLE static qreal dpx(qreal dpr);
0045 
0046     /**
0047      * This rounds a logical axis value to a value that should be aligned to hardware pixels
0048      * if the given device pixel ratio is correct.
0049      */
0050     [[nodiscard]] Q_INVOKABLE static qreal dprRound(qreal value, qreal dpr);
0051 
0052     /**
0053      * This converts an X11 screen axis value to a logical screen axis value.
0054      * Otherwise, it returns the value as it already was.
0055      */
0056     [[nodiscard]] Q_INVOKABLE static qreal mapFromPlatformValue(qreal value, qreal dpr);
0057 
0058     /**
0059      * This converts an X11 screen point to a logical screen point.
0060      * Otherwise, it returns the point as it already was.
0061      */
0062     [[nodiscard]] Q_INVOKABLE static QPointF mapFromPlatformPoint(const QPointF &point, qreal dpr);
0063 
0064     /**
0065      * This converts an X11 screen rect to a logical screen rect.
0066      * Otherwise, it returns the rect as it already was.
0067      */
0068     [[nodiscard]] Q_INVOKABLE static QRectF mapFromPlatformRect(const QRectF &rect, qreal dpr);
0069 
0070     /**
0071      * This returns the union of all logical screen rects.
0072      *
0073      * NOTE: Not perfectly accurate with some device pixel ratios
0074      * and resolutions due to QScreen::geometry being a QRect.
0075      */
0076     [[nodiscard]] Q_INVOKABLE static QRectF logicalScreensRect();
0077 
0078     /**
0079      * This converts a logical screen axis value to a platform screen axis value.
0080      * On Wayland, it returns the value as it already was.
0081      */
0082     [[nodiscard]] Q_INVOKABLE static qreal mapToPlatformValue(qreal value, qreal dpr);
0083 
0084     /**
0085      * This converts a logical screen point to a platform screen point.
0086      * On Wayland, it returns the point as it already was.
0087      */
0088     [[nodiscard]] Q_INVOKABLE static QPointF mapToPlatformPoint(const QPointF &point, qreal dpr);
0089 
0090     /**
0091      * This converts a logical screen rect to a platform screen rect.
0092      * On Wayland, it returns the rect as it already was.
0093      */
0094     [[nodiscard]] Q_INVOKABLE static QRectF mapToPlatformRect(const QRectF &rect, qreal dpr);
0095 
0096     /**
0097      * This returns the union of all platform screen rects.
0098      *
0099      * NOTE: Not perfectly accurate with some device pixel ratios
0100      * and resolutions due to QScreen::geometry being a QRect.
0101      */
0102     [[nodiscard]] Q_INVOKABLE static QRectF platformUnifiedRect();
0103 
0104     /**
0105      * This returns a raw size for a logical size by multiplying by the DPR.
0106      * NOTE: The value will only be correct if no precision was lost prior to running the function.
0107      */
0108     [[nodiscard]] Q_INVOKABLE static QSize rawSize(const QSizeF &size, qreal dpr);
0109 
0110     /// Get the rectangle with adjusted left, top, right and bottom sides.
0111     [[nodiscard]] Q_INVOKABLE static QRectF rectAdjusted(const QRectF &rect, qreal xp1, qreal yp1, qreal xp2, qreal yp2);
0112 
0113     /**
0114      * Get the rectangle with adjusted left, top, right and bottom sides.
0115      * This affects the sides of invalid rectangles as if they were normalized.
0116      * If the rect is invalid, that will be preserved.
0117      */
0118     [[nodiscard]] Q_INVOKABLE static QRectF rectAdjustedVisually(const QRectF &rect, qreal xp1, qreal yp1, qreal xp2, qreal yp2);
0119 
0120     /// Get a rect with a size and position multiplied by the scale.
0121     [[nodiscard]] Q_INVOKABLE static QRectF rectScaled(const QRectF &rect, qreal scale);
0122 
0123     /// Get the intersection of two rectangles.
0124     [[nodiscard]] Q_INVOKABLE static QRectF rectIntersected(const QRectF &rect1, const QRectF &rect2);
0125 
0126     /**
0127      * Try to make the rect positioned fully inside boundsRect without clipping on the given axes.
0128      * The rect may still be out of bounds if the size is too large.
0129      * If the rect is invalid, that will be preserved.
0130      */
0131     [[nodiscard]] Q_INVOKABLE static QRectF rectBounded(const QRectF &rect, const QRectF &boundsRect, //
0132                                                         Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical);
0133     [[nodiscard]] Q_INVOKABLE static QRectF rectBounded(qreal x, qreal y, qreal width, qreal height, const QRectF &boundsRect, //
0134                                                         Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical);
0135 
0136     /**
0137      * Clip the rect to the clipRect on the given axes.
0138      * If the rect does not intersect with the clipRect,
0139      * it will become an empty rect on the edge of clipRect nearest to the original position.
0140      * If the rect is invalid, that will be preserved.
0141      */
0142     [[nodiscard]] Q_INVOKABLE static QRectF rectClipped(const QRectF &rect, const QRectF &clipRect, //
0143                                                         Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical);
0144 
0145     /// Check if the rectangle contains the value on the given axes.
0146     [[nodiscard]] Q_INVOKABLE static bool rectContains(const QRectF &rect, qreal v, //
0147                                                        Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical);
0148     /// Check if the rectangle contains the point.
0149     [[nodiscard]] Q_INVOKABLE static bool rectContains(const QRectF &rect, qreal x, qreal y);
0150     [[nodiscard]] Q_INVOKABLE static bool rectContains(const QRectF &rect, const QPointF& point);
0151     /// Check if the first rectangle contains the second rectangle.
0152     [[nodiscard]] Q_INVOKABLE static bool rectContains(const QRectF &rect1, const QRectF& rect2);
0153     [[nodiscard]] Q_INVOKABLE static bool rectContains(const QRectF &rect, qreal x, qreal y, qreal w, qreal h);
0154 
0155     /// Check if the ellipse contains the point.
0156     [[nodiscard]] Q_INVOKABLE static bool ellipseContains(qreal ellipseX, qreal ellipseY,
0157                                                           qreal ellipseWidth, qreal ellipseHeight,
0158                                                           qreal x, qreal y);
0159     /// Assuming the rectangle represents an ellipse's bounding box, check if it contains the point.
0160     [[nodiscard]] Q_INVOKABLE static bool ellipseContains(const QRectF &rect, qreal x, qreal y);
0161     [[nodiscard]] Q_INVOKABLE static bool ellipseContains(const QRectF &rect, const QPointF &point);
0162 
0163     /// Check if two rectangles intersect.
0164     [[nodiscard]] Q_INVOKABLE static bool rectIntersects(const QRectF &rect1, const QRectF& rect2);
0165     [[nodiscard]] Q_INVOKABLE static bool rectIntersects(const QRectF &rect, qreal x, qreal y, qreal w, qreal h);
0166 
0167     /// Is width or height less than or equal to 0
0168     [[nodiscard]] Q_INVOKABLE static bool isEmpty(qreal w, qreal h);
0169     [[nodiscard]] Q_INVOKABLE static bool isEmpty(const QSizeF &size);
0170     [[nodiscard]] Q_INVOKABLE static bool isEmpty(const QRectF &rect);
0171 
0172     /// Are width and height equal to 0
0173     [[nodiscard]] Q_INVOKABLE static bool isNull(qreal w, qreal h);
0174     [[nodiscard]] Q_INVOKABLE static bool isNull(const QSizeF &size);
0175     [[nodiscard]] Q_INVOKABLE static bool isNull(const QRectF &rect);
0176 
0177 private:
0178     explicit Geometry(QObject *parent = nullptr);
0179     Geometry(const Geometry &) = delete;
0180     Geometry(Geometry &&) = delete;
0181     Geometry &operator=(const Geometry &) = delete;
0182     Geometry &operator=(Geometry &&) = delete;
0183     friend class GeometrySingleton;
0184 };