#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ZShell::components { class CarouselView : public QQuickItem { Q_OBJECT QML_ELEMENT Q_PROPERTY( QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) Q_PROPERTY(QVariantList model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY( qreal focalWidth READ focalWidth WRITE setFocalWidth NOTIFY focalWidthChanged) Q_PROPERTY( qreal minEdgeWidth READ minEdgeWidth WRITE setMinEdgeWidth NOTIFY minEdgeWidthChanged) Q_PROPERTY( qreal itemSpacing READ itemSpacing WRITE setItemSpacing NOTIFY itemSpacingChanged) Q_PROPERTY( qreal tileHeight READ tileHeight WRITE setTileHeight NOTIFY tileHeightChanged) Q_PROPERTY( qreal maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged) Q_PROPERTY( int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY( QQuickItem* currentItem READ currentItem NOTIFY currentItemChanged) Q_PROPERTY( int glideDuration READ glideDuration WRITE setGlideDuration NOTIFY glideDurationChanged) Q_PROPERTY( QEasingCurve::Type glideEasingType READ glideEasingType WRITE setGlideEasingType NOTIFY glideEasingTypeChanged) Q_PROPERTY( QVariantMap delegateProperties READ delegateProperties WRITE setDelegateProperties NOTIFY delegatePropertiesChanged) public: explicit CarouselView(QQuickItem* parent = nullptr); ~CarouselView() override; QQmlComponent* delegate() const { return m_delegate; } void setDelegate(QQmlComponent* c); QVariantList model() const { return m_model; } void setModel(const QVariantList& m); qreal focalWidth() const { return m_focalWidth; } void setFocalWidth(qreal v); qreal minEdgeWidth() const { return m_minEdgeWidth; } void setMinEdgeWidth(qreal v); qreal itemSpacing() const { return m_itemSpacing; } void setItemSpacing(qreal v); qreal tileHeight() const { return m_tileHeight; } void setTileHeight(qreal v); qreal maxWidth() const { return m_maxWidth; } void setMaxWidth(qreal v); int currentIndex() const { return m_currentRealIndex; } void setCurrentIndex(int idx); QQuickItem* currentItem() const { return m_currentItem; } int glideDuration() const { return m_glideDuration; } void setGlideDuration(int v); QEasingCurve::Type glideEasingType() const { return m_glideEasingType; } void setGlideEasingType(QEasingCurve::Type t); QVariantMap delegateProperties() const { return m_delegateProperties; } void setDelegateProperties(const QVariantMap& v); Q_INVOKABLE void goToIndex(int realIndex); Q_INVOKABLE void incrementCurrentIndex(); Q_INVOKABLE void decrementCurrentIndex(); signals: void delegateChanged(); void modelChanged(); void focalWidthChanged(); void minEdgeWidthChanged(); void itemSpacingChanged(); void tileHeightChanged(); void maxWidthChanged(); void currentIndexChanged(); void currentItemChanged(); void glideDurationChanged(); void glideEasingTypeChanged(); void delegatePropertiesChanged(); void activated(int realIndex, QVariant modelData); void previewIndexChanged(int realIndex, QVariant modelData); protected: void classBegin() override; void componentComplete() override; bool childMouseEventFilter(QQuickItem* item, QEvent* event) override; void geometryChange(const QRectF& newGeo, const QRectF& oldGeo) override; void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseUngrabEvent() override; void wheelEvent(QWheelEvent* event) override; private slots: void handleDelegateActivate(); private: struct Arrangement { int n = 0; qreal width = 0; qreal outerSpacing = 0; QVector sizes; QVector centers; qreal effectiveExtraWidth = 0; }; struct Slot { QQuickItem* item = nullptr; int virtualIndex = 0; int realIndex = -1; bool active = false; }; struct VelocitySample { qint64 t; qreal x; }; void rebuildArrangement(); void relayout(); Arrangement arrangementForCount(int n) const; int layoutCapacityForWidth(qreal width) const; qreal targetWidthForMaxWidth(qreal maxWidth) const; void updateSlotContent(Slot& slot, int virtualIndex); void positionSlot(const Slot& slot) const; void applyPropertiesToItem(QQuickItem* item, int realIndex) const; qreal pitch() const { return m_focalWidth + m_itemSpacing; } qreal contentXForIndex(qreal index) const; int indexForContentX(qreal x) const; qreal snapTargetX(qreal x) const; void snapToNearest(); void glideTo(qreal dest); void cancelAnimations(); qreal keylineSize(int io) const; qreal sampleSize(qreal childLoc) const; qreal continuousIndex() const; int realIndexOf(int virtualIndex) const; void updateCurrentIndexFromContentX(bool emitPreview); void updateCurrentItem(); void wrapContentIfNeeded(); void centerInstantlyOnReal(int realIndex); QQuickItem* acquireItem(); void releaseAllItems(); void addVelocitySample(qint64 t, qreal x); qreal sampledVelocity() const; void notifyCurrentIndexChanged(); void notifyCurrentItemChanged(); void notifyPreviewIndexChanged(int realIndex); QQmlComponent* m_delegate = nullptr; QVariantList m_model; QVariantMap m_delegateProperties; qreal m_focalWidth = 0; qreal m_minEdgeWidth = 56; qreal m_itemSpacing = 8; qreal m_tileHeight = 0; qreal m_maxWidth = 0; Arrangement m_arrangement; int m_layoutCapacity = 0; qreal m_contentX = 0; int m_currentVirtualIndex = 0; int m_currentRealIndex = -1; int m_previewRealIndex = -1; QQuickItem* m_currentItem = nullptr; int m_centerBlockStart = 0; QList m_slots; int m_glideDuration = 250; QEasingCurve::Type m_glideEasingType = QEasingCurve::OutCubic; QVariantAnimation* m_glideAnim = nullptr; bool m_dragging = false; bool m_dragActive = false; QPointF m_pressPos; qreal m_pressContentX = 0; QElapsedTimer m_dragTimer; QVector m_velocitySamples; static constexpr qint64 kVelocitySampleWindowMs = 80; QVariantAnimation* m_flickAnim = nullptr; bool m_completed = false; int m_pendingCurrentIndex = -1; bool m_initializedLayout = false; bool m_relayoutQueued = false; }; } // namespace ZShell::components