#include "timelineview.h" #include #include struct TimelineViewItem { TimelineViewItem(qint64 day, qint64 len, QQuickItem &qitem, TimelineView &view) : item(&qitem) , day(day) { item->setParentItem(&view); item->setPosition(QPointF(day * view.dayWidth(), 0.0)); item->setSize(QSizeF(len * view.dayWidth(), view.height())); item->setVisible(true); } ~TimelineViewItem() { item->setVisible(false); item->setParent(nullptr); } qreal left() const { return item->x(); } qreal right() const { return item->x() + item->width(); } QQuickItem *item; const qint64 day; }; TimelineView::TimelineView(QQuickItem *parent) : QQuickItem(parent) , m_dayWidth(24.0) , m_delegate(nullptr) , m_fromDate() , m_items() , m_reusableItems() , m_toDate() , m_viewportX(0) , m_prevViewportX(0) , m_viewportWidth(0) , m_prevViewportWidth(0) {} TimelineView::~TimelineView() { clear(); drainItems(); } qreal TimelineView::dayWidth() const { return m_dayWidth; } void TimelineView::setDayWidth(qreal width) { if (width == m_dayWidth) { return; } m_dayWidth = width; emit dayWidthChanged(m_dayWidth); updateImplicitWidth(); clear(); populate(); } QQmlComponent *TimelineView::delegate() const { return m_delegate; } void TimelineView::setDelegate(QQmlComponent *delegate) { if (delegate == m_delegate) { return; } m_delegate = delegate; emit delegateChanged(m_delegate); clear(); drainItems(); populate(); update(); } QDate TimelineView::fromDate() const { return m_fromDate; } void TimelineView::setFromDate(QDate date) { if (date == m_fromDate) { return; } m_fromDate = date; emit fromDateChanged(m_fromDate); updateImplicitWidth(); populate(); } QDate TimelineView::toDate() const { return m_toDate; } void TimelineView::setToDate(QDate date) { if (date == m_toDate) { return; } m_toDate = date; emit toDateChanged(m_toDate); updateImplicitWidth(); populate(); } qreal TimelineView::viewportX() const { return m_viewportX; } void TimelineView::setViewportX(qreal x) { if (x == m_viewportX) { return; } m_viewportX = x; emit viewportXChanged(m_viewportX); populate(); } qreal TimelineView::viewportWidth() const { return m_viewportWidth; } void TimelineView::setViewportWidth(qreal width) { if (width == m_viewportWidth) { return; } m_viewportWidth = width; emit viewportWidthChanged(m_viewportWidth); populate(); } void TimelineView::componentComplete() { QQuickItem::componentComplete(); populate(); } TimelineViewItem *TimelineView::createItem(qint64 day, qint64 len) { QQuickItem *item = m_reusableItems.isEmpty() ? qobject_cast(m_delegate->create( m_delegate->creationContext())) : m_reusableItems.takeLast(); if (!item) { qmlWarning(m_delegate) << TimelineView::tr("Delegate must be of Item type"); return nullptr; } auto *viewItem = new TimelineViewItem(day, len, *item, *this); return viewItem; } void TimelineView::releaseItem(TimelineViewItem *item) { if (!item) { return; } qsizetype index = m_items.indexOf(item); if (index < 0) { return; } QQuickItem *qitem = item->item; if (!m_reusableItems.contains(qitem)) { m_reusableItems.append(qitem); } m_items.removeAt(index); delete item; } void TimelineView::drainItems() { qDeleteAll(m_reusableItems); m_reusableItems.clear(); } void TimelineView::clear() { while (!m_items.isEmpty()) { releaseItem(m_items.last()); } } void TimelineView::populate() { if (!isComponentComplete() || !m_delegate) { return; } if (m_viewportX > m_prevViewportX) { // Delete from the left while (!m_items.isEmpty()) { TimelineViewItem *item = m_items.first(); if (item->right() >= m_viewportX) { break; } releaseItem(item); } } else if (m_viewportX < m_prevViewportX) { // Insert from the left for (qint64 day = m_items.isEmpty() ? qCeil(m_prevViewportX / m_dayWidth) - 1 : m_items.first()->day - 1, len = 1, lastDay = qMax(-1, qFloor((m_viewportX - (len * m_dayWidth)) / m_dayWidth)); day > lastDay; day -= len) { TimelineViewItem *viewItem = createItem(day, len); if (!viewItem) { break; } m_items.prepend(viewItem); } } int currentRight = m_viewportX + m_viewportWidth; int prevRight = m_prevViewportX + m_prevViewportWidth; if (currentRight < prevRight) { // Delete from the right while (!m_items.isEmpty()) { TimelineViewItem *item = m_items.last(); if (item->left() < currentRight) { break; } releaseItem(item); } } else if (currentRight > prevRight) { // Insert from the right for (qint64 day = m_items.isEmpty() ? qCeil(prevRight / m_dayWidth) - 1 : m_items.last()->day + 1, len = 1, lastDay = qFloor((currentRight + (len * m_dayWidth)) / m_dayWidth); day < lastDay; day += len) { TimelineViewItem *viewItem = createItem(day, len); if (!viewItem) { break; } m_items.append(viewItem); } } m_prevViewportX = m_viewportX; m_prevViewportWidth = m_viewportWidth; } void TimelineView::updateImplicitWidth() { setImplicitWidth(m_dayWidth * m_fromDate.daysTo(m_toDate)); } #include "moc_timelineview.cpp"