File indexing completed on 2024-04-28 04:41:52

0001 /***************************************************************************
0002  *   Copyright (C) 2017 by Emmanuel Lepage Vallee                          *
0003  *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@kde.org>             *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 3 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
0017  **************************************************************************/
0018 #include "treeview.h"
0019 
0020 // KQuickItemViews
0021 #include "adapters/abstractitemadapter.h"
0022 #include "extensions/contextextension.h"
0023 #include "adapters/modeladapter.h"
0024 #include "contextadapterfactory.h"
0025 #include "adapters/contextadapter.h"
0026 
0027 // Qt
0028 #include <QQmlContext>
0029 
0030 /**
0031  * Polymorphic tree item for the SingleModelViewBase.
0032  *
0033  * Classes implementing SingleModelViewBase need to provide an implementation of the pure
0034  * virtual functions. It is useful, for example, to manage both a raster and
0035  * QQuickItem based version of a view.
0036  *
0037  * The state is managed by the SingleModelViewBase and it's own protected virtual methods.
0038  */
0039 class TreeViewItem final : public AbstractItemAdapter
0040 {
0041 public:
0042     explicit TreeViewItem(Viewport* r);
0043     virtual ~TreeViewItem();
0044 
0045     // Actions
0046     virtual bool move   () override;
0047     virtual bool remove () override;
0048 
0049 private:
0050     bool m_IsHead { false };
0051 };
0052 
0053 /// Add the same property as the QtQuick.ListView
0054 class TreeContextProperties final : public ContextExtension
0055 {
0056 public:
0057     virtual ~TreeContextProperties() {}
0058     virtual QVector<QByteArray>& propertyNames() const override;
0059     virtual QVariant getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const override;
0060     virtual bool setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const override;
0061 };
0062 
0063 class TreeViewPrivate
0064 {
0065 public:
0066 };
0067 
0068 TreeView::TreeView(QQuickItem* parent) : SingleModelViewBase(new ItemFactory<TreeViewItem>(), parent),
0069     d_ptr(new TreeViewPrivate)
0070 {
0071     modelAdapters().first()->contextAdapterFactory()->addContextExtension(
0072         new TreeContextProperties()
0073     );
0074 }
0075 
0076 TreeView::~TreeView()
0077 {
0078     delete d_ptr;
0079 }
0080 
0081 TreeViewItem::TreeViewItem(Viewport* r) : AbstractItemAdapter(r)
0082 {
0083 }
0084 
0085 TreeViewItem::~TreeViewItem()
0086 {
0087     delete container();
0088 }
0089 
0090 bool TreeViewItem::move()
0091 {
0092     // Will happen when trying to move a FAILED, but buffered item
0093     if (!container()) {
0094         qDebug() << "NO ITEM" << index().data();
0095         return false;
0096     }
0097 
0098     container()->setWidth(view()->contentItem()->width());
0099 
0100     auto nextElem = static_cast<TreeViewItem*>(next(Qt::BottomEdge));
0101     auto prevElem = static_cast<TreeViewItem*>(next(Qt::TopEdge));
0102 
0103     // The root has been moved in the middle of the tree, find the new root
0104     //TODO maybe add a deterministic API instead of O(N) lookup
0105     if (prevElem && m_IsHead) {
0106         m_IsHead = false;
0107 
0108         auto root = prevElem;
0109         while (auto prev = root->next(Qt::TopEdge))
0110             root = static_cast<TreeViewItem*>(prev);
0111 
0112         root->move();
0113         Q_ASSERT(root->m_IsHead);
0114     }
0115 
0116     // So other items can be GCed without always resetting to 0x0, note that it
0117     // might be a good idea to extend Flickable to support a virtual
0118     // origin point.
0119     if ((!prevElem) || (nextElem && nextElem->m_IsHead)) {
0120         auto anchors = qvariant_cast<QObject*>(container()->property("anchors"));
0121         anchors->setProperty("top", {});
0122         container()->setY(0);
0123         m_IsHead = true;
0124     }
0125     else if (prevElem) {
0126         Q_ASSERT(!m_IsHead);
0127         container()->setProperty("y", {});
0128         auto anchors = qvariant_cast<QObject*>(container()->property("anchors"));
0129         anchors->setProperty("top", prevElem->container()->property("bottom"));
0130     }
0131 
0132     // Now, update the next anchors
0133     if (nextElem) {
0134         nextElem->m_IsHead = false;
0135         nextElem->container()->setProperty("y", {});
0136 
0137         auto anchors = qvariant_cast<QObject*>(nextElem->container()->property("anchors"));
0138         anchors->setProperty("top", container()->property("bottom"));
0139     }
0140 
0141     updateGeometry();
0142 
0143     return true;
0144 }
0145 
0146 bool TreeViewItem::remove()
0147 {
0148     if (container()) {
0149         container()->setParent(nullptr);
0150         container()->setParentItem(nullptr);
0151         container()->setVisible(false);
0152     }
0153 
0154     auto nextElem = static_cast<TreeViewItem*>(next(Qt::BottomEdge));
0155     auto prevElem = static_cast<TreeViewItem*>(next(Qt::TopEdge));
0156 
0157     if (nextElem) {
0158         if (m_IsHead) {
0159             auto anchors = qvariant_cast<QObject*>(nextElem->container()->property("anchors"));
0160             anchors->setProperty("top", {});
0161             container()->setY(0);
0162             nextElem->m_IsHead = true;
0163         }
0164         else { //TODO maybe eventually use a state machine for this
0165             auto anchors = qvariant_cast<QObject*>(nextElem->container()->property("anchors"));
0166             anchors->setProperty("top", prevElem->container()->property("bottom"));
0167         }
0168     }
0169 
0170     return true;
0171 }
0172 
0173 QVector<QByteArray>& TreeContextProperties::propertyNames() const
0174 {
0175     static QVector<QByteArray> ret { "expanded" };
0176     return ret;
0177 }
0178 
0179 QVariant TreeContextProperties::getProperty(AbstractItemAdapter* item, uint id, const QModelIndex& index) const
0180 {
0181     Q_UNUSED(index);
0182     Q_ASSERT(id == 0 && item);
0183     return !item->isCollapsed();
0184 }
0185 
0186 bool TreeContextProperties::setProperty(AbstractItemAdapter* item, uint id, const QVariant& value, const QModelIndex& index) const
0187 {
0188     Q_ASSERT(id == 0 && item && value.canConvert<bool>());
0189     item->setCollapsed(!value.toBool());
0190     return true;
0191 }