File indexing completed on 2024-05-12 17:08:55

0001 /*
0002     SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QPointer>
0010 #include <QQmlParserStatus>
0011 #include <QQuickItem>
0012 
0013 #include "appletslayout.h"
0014 
0015 class QTimer;
0016 
0017 class ConfigOverlay;
0018 
0019 class ItemContainer : public QQuickItem
0020 {
0021     Q_OBJECT
0022     Q_INTERFACES(QQmlParserStatus)
0023 
0024     Q_PROPERTY(AppletsLayout *layout READ layout NOTIFY layoutChanged)
0025     // TODO: make it unchangeable? probably not
0026     Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged)
0027     Q_PROPERTY(ItemContainer::EditModeCondition editModeCondition READ editModeCondition WRITE setEditModeCondition NOTIFY editModeConditionChanged)
0028     Q_PROPERTY(bool editMode READ editMode WRITE setEditMode NOTIFY editModeChanged)
0029     Q_PROPERTY(bool dragActive READ dragActive NOTIFY dragActiveChanged)
0030     Q_PROPERTY(AppletsLayout::PreferredLayoutDirection preferredLayoutDirection READ preferredLayoutDirection WRITE setPreferredLayoutDirection NOTIFY
0031                    preferredLayoutDirectionChanged)
0032 
0033     Q_PROPERTY(QQmlComponent *configOverlayComponent READ configOverlayComponent WRITE setConfigOverlayComponent NOTIFY configOverlayComponentChanged)
0034     Q_PROPERTY(bool configOverlayVisible READ configOverlayVisible WRITE setConfigOverlayVisible NOTIFY configOverlayVisibleChanged)
0035     Q_PROPERTY(QQuickItem *configOverlayItem READ configOverlayItem NOTIFY configOverlayItemChanged)
0036 
0037     /**
0038      * Initial size this container asks to have upon creation. only positive values are considered
0039      */
0040     Q_PROPERTY(QSizeF initialSize READ initialSize WRITE setInitialSize NOTIFY initialSizeChanged)
0041     // From there mostly a clone of QQC2 Control
0042     Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged)
0043     Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged)
0044 
0045     /**
0046      * Padding adds a space between each edge of the content item and the background item, effectively controlling the size of the content item.
0047      */
0048     Q_PROPERTY(int leftPadding READ leftPadding WRITE setLeftPadding NOTIFY leftPaddingChanged)
0049     Q_PROPERTY(int rightPadding READ rightPadding WRITE setRightPadding NOTIFY rightPaddingChanged)
0050     Q_PROPERTY(int topPadding READ topPadding WRITE setTopPadding NOTIFY topPaddingChanged)
0051     Q_PROPERTY(int bottomPadding READ bottomPadding WRITE setBottomPadding NOTIFY bottomPaddingChanged)
0052 
0053     /**
0054      * The size of the contents: the size of this item minus the padding
0055      */
0056     Q_PROPERTY(int contentWidth READ contentWidth NOTIFY contentWidthChanged)
0057     Q_PROPERTY(int contentHeight READ contentHeight NOTIFY contentHeightChanged)
0058 
0059     Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL)
0060     // Q_CLASSINFO("DeferredPropertyNames", "background,contentItem")
0061     Q_CLASSINFO("DefaultProperty", "contentData")
0062 
0063 public:
0064     enum EditModeCondition {
0065         Locked = AppletsLayout::EditModeCondition::Locked,
0066         Manual = AppletsLayout::EditModeCondition::Manual,
0067         AfterPressAndHold = AppletsLayout::EditModeCondition::AfterPressAndHold,
0068         AfterPress,
0069         AfterMouseOver,
0070     };
0071     Q_ENUMS(EditModeCondition)
0072 
0073     ItemContainer(QQuickItem *parent = nullptr);
0074     ~ItemContainer();
0075 
0076     QQmlListProperty<QObject> contentData();
0077 
0078     QString key() const;
0079     void setKey(const QString &id);
0080 
0081     bool editMode() const;
0082     void setEditMode(bool edit);
0083 
0084     bool dragActive() const;
0085 
0086     Q_INVOKABLE void cancelEdit();
0087 
0088     EditModeCondition editModeCondition() const;
0089     void setEditModeCondition(EditModeCondition condition);
0090 
0091     AppletsLayout::PreferredLayoutDirection preferredLayoutDirection() const;
0092     void setPreferredLayoutDirection(AppletsLayout::PreferredLayoutDirection direction);
0093 
0094     QQmlComponent *configOverlayComponent() const;
0095     void setConfigOverlayComponent(QQmlComponent *component);
0096 
0097     bool configOverlayVisible() const;
0098     void setConfigOverlayVisible(bool visible);
0099 
0100     // TODO: keep this accessible?
0101     ConfigOverlay *configOverlayItem() const;
0102 
0103     QSizeF initialSize() const;
0104     void setInitialSize(const QSizeF &size);
0105 
0106     // Control-like api
0107     QQuickItem *contentItem() const;
0108     void setContentItem(QQuickItem *item);
0109 
0110     QQuickItem *background() const;
0111     void setBackground(QQuickItem *item);
0112 
0113     // Setters and getters for the padding
0114     int leftPadding() const;
0115     void setLeftPadding(int padding);
0116 
0117     int topPadding() const;
0118     void setTopPadding(int padding);
0119 
0120     int rightPadding() const;
0121     void setRightPadding(int padding);
0122 
0123     int bottomPadding() const;
0124     void setBottomPadding(int padding);
0125 
0126     int contentWidth() const;
0127     int contentHeight() const;
0128 
0129     AppletsLayout *layout() const;
0130 
0131     // Not for QML
0132     void setLayout(AppletsLayout *layout);
0133 
0134     QObject *layoutAttached() const
0135     {
0136         return m_layoutAttached;
0137     }
0138 
0139 protected:
0140 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0141     void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
0142 #else
0143     void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
0144 #endif
0145 
0146     // void classBegin() override;
0147     void componentComplete() override;
0148     bool childMouseEventFilter(QQuickItem *item, QEvent *event) override;
0149     void mousePressEvent(QMouseEvent *event) override;
0150     void mouseReleaseEvent(QMouseEvent *event) override;
0151     void mouseMoveEvent(QMouseEvent *event) override;
0152     void mouseUngrabEvent() override;
0153     void hoverEnterEvent(QHoverEvent *event) override;
0154     void hoverLeaveEvent(QHoverEvent *event) override;
0155 
0156 Q_SIGNALS:
0157 
0158     /**
0159      * The user manually dragged the ItemContainer around
0160      * @param newPosition new position of the ItemContainer in parent coordinates
0161      * @param dragCenter position in ItemContainer coordinates of the drag hotspot, i.e. where the user pressed the mouse or the
0162      * finger over the ItemContainer
0163      */
0164     void userDrag(const QPointF &newPosition, const QPointF &dragCenter);
0165 
0166     void dragActiveChanged();
0167 
0168     /**
0169      * The attached layout object changed some of its size hints
0170      */
0171     void sizeHintsChanged();
0172 
0173     // QML property notifiers
0174     void layoutChanged();
0175     void keyChanged();
0176     void editModeConditionChanged();
0177     void editModeChanged(bool editMode);
0178     void preferredLayoutDirectionChanged();
0179     void configOverlayComponentChanged();
0180     void configOverlayItemChanged();
0181     void initialSizeChanged();
0182     void configOverlayVisibleChanged(bool configOverlayVisile);
0183 
0184     void backgroundChanged();
0185     void contentItemChanged();
0186     void leftPaddingChanged();
0187     void rightPaddingChanged();
0188     void topPaddingChanged();
0189     void bottomPaddingChanged();
0190     void contentWidthChanged();
0191     void contentHeightChanged();
0192 
0193 private:
0194     void syncChildItemsGeometry(const QSizeF &size);
0195     void sendUngrabRecursive(QQuickItem *item);
0196 
0197     // internal accessorts for the contentData QProperty
0198     static void contentData_append(QQmlListProperty<QObject> *prop, QObject *object);
0199 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0200     static int contentData_count(QQmlListProperty<QObject> *prop);
0201     static QObject *contentData_at(QQmlListProperty<QObject> *prop, int index);
0202 #else
0203     static qsizetype contentData_count(QQmlListProperty<QObject> *prop);
0204     static QObject *contentData_at(QQmlListProperty<QObject> *prop, qsizetype index);
0205 #endif
0206     static void contentData_clear(QQmlListProperty<QObject> *prop);
0207 
0208     QPointer<QQuickItem> m_contentItem;
0209     QPointer<QQuickItem> m_backgroundItem;
0210 
0211     // Internal implementation detail: this is used to reparent all items to contentItem
0212     QList<QObject *> m_contentData;
0213 
0214     /**
0215      * Padding adds a space between each edge of the content item and the background item, effectively controlling the size of the content item.
0216      */
0217     int m_leftPadding = 0;
0218     int m_rightPadding = 0;
0219     int m_topPadding = 0;
0220     int m_bottomPadding = 0;
0221 
0222     QString m_key;
0223 
0224     QPointer<AppletsLayout> m_layout;
0225     QTimer *m_editModeTimer = nullptr;
0226     QTimer *m_closeEditModeTimer = nullptr;
0227     QTimer *m_sizeHintAdjustTimer = nullptr;
0228     QObject *m_layoutAttached = nullptr;
0229     EditModeCondition m_editModeCondition = Manual;
0230     QSizeF m_initialSize;
0231 
0232     QPointer<QQmlComponent> m_configOverlayComponent;
0233     ConfigOverlay *m_configOverlay = nullptr;
0234 
0235     QPointF m_lastMousePosition = QPoint(-1, -1);
0236     QPointF m_mouseDownPosition = QPoint(-1, -1);
0237     AppletsLayout::PreferredLayoutDirection m_preferredLayoutDirection = AppletsLayout::Closest;
0238     bool m_editMode = false;
0239     bool m_mouseDown = false;
0240     bool m_mouseSynthetizedFromTouch = false;
0241     bool m_dragActive = false;
0242 };