├── doc
├── .gitignore
├── screen-gantt.png
└── implement-qabstractrangemodel.md
├── lib
├── .gitignore
├── qmlgantt.pro
├── qganttglobal.h
├── qganttmodelitem.cpp
├── qganttmodelitem.h
├── qabstractrangemodel.cpp
├── qganttmodel.h
├── qrangeview.h
├── qabstractrangemodel.h
├── qganttmodel.cpp
└── qrangeview.cpp
├── sample
├── .gitignore
├── src
│ ├── .gitignore
│ ├── src.pri
│ ├── qganttdata.cpp
│ ├── qganttdata.h
│ ├── qganttmodellist.h
│ ├── main.cpp
│ └── qganttmodellist.cpp
├── resource
│ ├── .gitignore
│ ├── img
│ │ ├── help-add-item.png
│ │ ├── help-edit-item.png
│ │ ├── help-edit-row.png
│ │ ├── help-move-item.png
│ │ └── help-stretch-item.png
│ ├── qml.qrc
│ ├── GanttEditWindow.qml
│ ├── GanttHelpWindow.qml
│ ├── main.qml
│ └── GanttLine.qml
├── deployment.pri
└── sample.pro
├── tests
├── .gitignore
├── tests.pro
├── main.cpp
├── qganttmodeltest.h
├── qviewportbenchmarktest.h
├── qtestrunner.h
├── qtestrunner.cpp
├── qrangeviewtest.h
├── qganttmodeltest.cpp
├── qviewportbenchmarktest.cpp
└── qrangeviewtest.cpp
├── .gitignore
├── LICENSE.LGPLv3
├── qmlgantt.pro
├── appveyor.yml
├── .travis.yml
└── README.md
/doc/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample/src/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | qmlgantt.pro.user
--------------------------------------------------------------------------------
/sample/resource/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE.LGPLv3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinusv/qml-gantt/HEAD/LICENSE.LGPLv3
--------------------------------------------------------------------------------
/doc/screen-gantt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinusv/qml-gantt/HEAD/doc/screen-gantt.png
--------------------------------------------------------------------------------
/sample/resource/img/help-add-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinusv/qml-gantt/HEAD/sample/resource/img/help-add-item.png
--------------------------------------------------------------------------------
/sample/resource/img/help-edit-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinusv/qml-gantt/HEAD/sample/resource/img/help-edit-item.png
--------------------------------------------------------------------------------
/sample/resource/img/help-edit-row.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinusv/qml-gantt/HEAD/sample/resource/img/help-edit-row.png
--------------------------------------------------------------------------------
/sample/resource/img/help-move-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinusv/qml-gantt/HEAD/sample/resource/img/help-move-item.png
--------------------------------------------------------------------------------
/sample/resource/img/help-stretch-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dinusv/qml-gantt/HEAD/sample/resource/img/help-stretch-item.png
--------------------------------------------------------------------------------
/qmlgantt.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 | CONFIG += ordered
3 |
4 | SUBDIRS += $$PWD/lib/qmlgantt.pro
5 | SUBDIRS += $$PWD/sample/sample.pro
6 | SUBDIRS += $$PWD/tests/tests.pro
7 |
--------------------------------------------------------------------------------
/sample/src/src.pri:
--------------------------------------------------------------------------------
1 | INCLUDEPATH += $$PWD
2 |
3 | SOURCES += \
4 | $$PWD/main.cpp \
5 | $$PWD/qganttdata.cpp \
6 | $$PWD/qganttmodellist.cpp
7 |
8 | HEADERS += \
9 | $$PWD/qganttdata.h \
10 | $$PWD/qganttmodellist.h
11 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | os: unstable
2 | image: Visual Studio 2017
3 |
4 | platform:
5 | - x64
6 |
7 | install:
8 | - SET QTDIR=C:\Qt\5.13.2\mingw73_32
9 | - set PATH=%PATH%;%QTDIR%\bin;C:\Qt\Tools\mingw730_32
10 | build_script:
11 | - qmake -r qmlgantt.pro
12 | - mingw32-make
13 |
--------------------------------------------------------------------------------
/sample/resource/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 | GanttEditWindow.qml
5 | GanttLine.qml
6 | GanttHelpWindow.qml
7 | img/help-add-item.png
8 | img/help-edit-item.png
9 | img/help-edit-row.png
10 | img/help-move-item.png
11 | img/help-stretch-item.png
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: cpp
2 |
3 | os:
4 | - linux
5 |
6 | sudo: required
7 | dist: bionic
8 |
9 | addons:
10 | apt:
11 | sources:
12 | - ubuntu-toolchain-r-test
13 | packages:
14 | - libdc1394-22-dev
15 | - libgl1-mesa-dev
16 |
17 | before_install:
18 | - sudo add-apt-repository ppa:beineri/opt-qt-5.14.0-bionic -y
19 | - sudo apt-get update -qq
20 | install:
21 | - sudo apt-get install -qq qt514base qt514declarative qt514quickcontrols
22 | script:
23 | - source /opt/qt514/bin/qt514-env.sh
24 | - qmake -r
25 | - make
26 | - export LD_LIBRARY_PATH=$(pwd)/build
27 | - make check
28 |
--------------------------------------------------------------------------------
/sample/deployment.pri:
--------------------------------------------------------------------------------
1 | android-no-sdk {
2 | target.path = /data/user/qt
3 | export(target.path)
4 | INSTALLS += target
5 | } else:android {
6 | x86 {
7 | target.path = /libs/x86
8 | } else: armeabi-v7a {
9 | target.path = /libs/armeabi-v7a
10 | } else {
11 | target.path = /libs/armeabi
12 | }
13 | export(target.path)
14 | INSTALLS += target
15 | } else:unix {
16 | isEmpty(target.path) {
17 | qnx {
18 | target.path = /tmp/$${TARGET}/bin
19 | } else {
20 | target.path = /opt/$${TARGET}/bin
21 | }
22 | export(target.path)
23 | }
24 | INSTALLS += target
25 | }
26 |
27 | export(INSTALLS)
28 |
--------------------------------------------------------------------------------
/sample/sample.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 | TARGET = sample
3 |
4 | QT += qml quick widgets
5 |
6 | RESOURCES += $$PWD/resource/qml.qrc
7 |
8 | # Additional import path used to resolve QML modules in Qt Creator's code model
9 | QML_IMPORT_PATH =
10 |
11 |
12 | # Configure Dependencies
13 |
14 | INCLUDEPATH += $$PWD/../lib
15 | DEPENDPATH += $$PWD/../lib
16 |
17 | win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../lib/release/ -lqmlgantt
18 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../lib/debug/ -lqmlgantt
19 | else:unix: LIBS += -L$$OUT_PWD/../build/ -lqmlgantt
20 |
21 | include($$PWD/src/src.pri)
22 |
23 | # Default rules for deployment.
24 | include($$PWD/deployment.pri)
25 |
--------------------------------------------------------------------------------
/tests/tests.pro:
--------------------------------------------------------------------------------
1 |
2 | TARGET = test_qmlgantt
3 | TEMPLATE = app
4 | QT += qml quick testlib
5 | CONFIG += console testcase
6 |
7 | # Configure Dependencies
8 |
9 | INCLUDEPATH += $$PWD/../lib
10 | DEPENDPATH += $$PWD/../lib
11 |
12 | win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../lib/release/ -lqmlgantt
13 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../lib/debug/ -lqmlgantt
14 | else:unix: LIBS += -L$$OUT_PWD/../build/ -lqmlgantt
15 |
16 | HEADERS += \
17 | qtestrunner.h \
18 | qganttmodeltest.h \
19 | qrangeviewtest.h \
20 | qviewportbenchmarktest.h
21 |
22 | SOURCES += \
23 | main.cpp \
24 | qtestrunner.cpp \
25 | qganttmodeltest.cpp \
26 | qrangeviewtest.cpp \
27 | qviewportbenchmarktest.cpp
28 |
29 |
--------------------------------------------------------------------------------
/lib/qmlgantt.pro:
--------------------------------------------------------------------------------
1 | # Config
2 |
3 | TEMPLATE = lib
4 | TARGET = qmlgantt
5 | QT += qml quick
6 | CONFIG += qt
7 |
8 | DEFINES += Q_GANTT
9 | DEFINES += Q_GANTT_LIB
10 |
11 | # Destination
12 |
13 | win32:CONFIG(debug, debug|release): DLLDESTDIR = $$quote($$OUT_PWD/../build/debug)
14 | else:win32:CONFIG(release, debug|release): DLLDESTDIR = $$quote($$OUT_PWD/../build/release)
15 | else:unix: TARGET = $$quote($$OUT_PWD/../build/qmlgantt)
16 |
17 | # Files
18 |
19 | INCLUDEPATH += $$PWD
20 |
21 | HEADERS += \
22 | $$PWD/qganttglobal.h \
23 | $$PWD/qrangeview.h \
24 | $$PWD/qabstractrangemodel.h \
25 | $$PWD/qganttmodel.h \
26 | $$PWD/qganttmodelitem.h
27 |
28 | SOURCES += \
29 | $$PWD/qrangeview.cpp \
30 | $$PWD/qabstractrangemodel.cpp \
31 | $$PWD/qganttmodel.cpp \
32 | $$PWD/qganttmodelitem.cpp
33 |
--------------------------------------------------------------------------------
/sample/src/qganttdata.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qganttdata.h"
18 |
19 | const QColor initColors[4] = {QColor("#bb4e4c"), QColor("#218c8d"), QColor("#4f83bd"), QColor("#e39541")};
20 |
21 | QGanttData::QGanttData(QObject *parent)
22 | : QObject(parent)
23 | , m_color(initColors[rand() % 4]){
24 | }
25 |
26 | QGanttData::~QGanttData(){
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/tests/main.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 |
18 | #include
19 | #include
20 | #include
21 |
22 | #include "qtestrunner.h"
23 | #include "qganttmodeltest.h"
24 | #include "qrangeviewtest.h"
25 | #include "qviewportbenchmarktest.h"
26 |
27 | int main(int argc, char *argv[]){
28 |
29 | QCoreApplication app(argc, argv);
30 | app.setAttribute(Qt::AA_Use96Dpi, true);
31 |
32 | return QTestRunner::runTests(argc, argv);
33 | }
34 |
--------------------------------------------------------------------------------
/lib/qganttglobal.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QGANTTGLOBAL_H
18 | #define QGANTTGLOBAL_H
19 |
20 | #include
21 |
22 | #define QML_GANTT_VERSION_MAJOR 0
23 | #define QML_GANTT_VERSION_MINOR 1
24 | #define QML_GANTT_VERSION_PATCH 0
25 |
26 | #ifndef Q_GANTT_STATIC
27 | # ifdef Q_GANTT_LIB
28 | # define Q_GANTT_EXPORT Q_DECL_EXPORT
29 | # else
30 | # define Q_GANTT_EXPORT Q_DECL_IMPORT
31 | # endif
32 | #else
33 | # define Q_GANTT_EXPORT
34 | #endif
35 |
36 | #endif // QGANTTGLOBAL_H
37 |
--------------------------------------------------------------------------------
/lib/qganttmodelitem.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qganttmodelitem.h"
18 | #include
19 |
20 | QGanttModelItem::QGanttModelItem(QObject *parent)
21 | : QObject(parent)
22 | , m_position(0)
23 | , m_length(0)
24 | {
25 | }
26 |
27 | QGanttModelItem::QGanttModelItem(qint64 position, qint64 length, QObject* parent)
28 | : QObject(parent)
29 | , m_position(position)
30 | , m_length(length)
31 | {
32 | }
33 |
34 | QGanttModelItem::~QGanttModelItem(){
35 | if ( !m_data.isNull() && m_data.canConvert())
36 | delete m_data.value();
37 | }
38 |
--------------------------------------------------------------------------------
/tests/qganttmodeltest.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QGANTTMODELTEST_HPP
18 | #define QGANTTMODELTEST_HPP
19 |
20 | #include
21 | #include "qtestrunner.h"
22 |
23 | class QGanttModelTest : public QObject{
24 |
25 | Q_OBJECT
26 | Q_TEST_RUNNER_SUITE
27 |
28 | public:
29 | explicit QGanttModelTest(QObject *parent = 0);
30 | ~QGanttModelTest();
31 |
32 | private slots:
33 | void initTestCase();
34 | void descendingTest();
35 | void alternateTest();
36 | void sameLengthTest();
37 | void sameLengthTest2();
38 |
39 | };
40 |
41 | #endif // QGANTTMODELTEST_HPP
42 |
--------------------------------------------------------------------------------
/tests/qviewportbenchmarktest.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QVIEWPORTBENCHMARKTEST_HPP
18 | #define QVIEWPORTBENCHMARKTEST_HPP
19 |
20 | #include
21 | #include "qtestrunner.h"
22 |
23 | class QViewportBenchmarkTest : public QObject{
24 |
25 | Q_OBJECT
26 | Q_TEST_RUNNER_SUITE
27 |
28 | public:
29 | static const int CONTENT_WIDTH = 20000;
30 | static const int VIEWPORT_WIDTH = 2000;
31 | static const int ITEM_WIDTH = 1;
32 | static const int SCROLL_ITERATIONS = 6000;
33 |
34 | public:
35 | QViewportBenchmarkTest(QObject* parent = 0);
36 | ~QViewportBenchmarkTest();
37 |
38 | private slots:
39 | void initTestCase();
40 | void minimalDelegateInsertionTest();
41 | void minimalDelegateViewTest();
42 | void complexDelegateInsertionTest();
43 | void complexDelegateViewTest();
44 |
45 | };
46 |
47 | #endif // QVIEWPORTBENCHMARKTEST_HPP
48 |
--------------------------------------------------------------------------------
/tests/qtestrunner.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QTESTRUNNER_HPP
18 | #define QTESTRUNNER_HPP
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | class QTestRunner{
25 |
26 | public:
27 | static int registerTest(QObject* test);
28 | static int runTests(int argc, char *argv[]);
29 | static int runTest(int index, int argc, char* argv[]);
30 | static int totalRegisteredTests();
31 | static void cleanupTests();
32 |
33 | private:
34 | static QList >* m_tests;
35 | };
36 |
37 | inline int QTestRunner::totalRegisteredTests(){
38 | return m_tests->size();
39 | }
40 |
41 | #define Q_TEST_RUNNER_SUITE \
42 | public:\
43 | static const int testIndex;
44 |
45 | #define Q_TEST_RUNNER_REGISTER(className) \
46 | const int className::testIndex = QTestRunner::registerTest(new className)
47 |
48 |
49 | #endif // QTESTRUNNER_HPP
50 |
--------------------------------------------------------------------------------
/tests/qtestrunner.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qtestrunner.h"
18 | #include
19 |
20 | QList >* QTestRunner::m_tests = 0;
21 |
22 | int QTestRunner::registerTest(QObject* test){
23 | if ( !m_tests ){
24 | m_tests = new QList >;
25 | atexit(&cleanupTests);
26 | }
27 | m_tests->append(QSharedPointer(test));
28 | return 0;
29 | }
30 |
31 | int QTestRunner::runTests(int argc, char *argv[]){
32 | int code = 0;
33 | for ( QList >::iterator it = m_tests->begin(); it != m_tests->end(); ++it ){
34 | code += QTest::qExec(it->data(), argc, argv);
35 | }
36 | return code;
37 | }
38 |
39 | int QTestRunner::runTest(int index, int argc, char* argv[]){
40 | if ( index > m_tests->size() )
41 | return -1;
42 | return QTest::qExec((*m_tests)[index].data(), argc, argv);
43 | }
44 |
45 | void QTestRunner::cleanupTests(){
46 | delete m_tests;
47 | }
48 |
--------------------------------------------------------------------------------
/tests/qrangeviewtest.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QRANGEVIEWTEST_H
18 | #define QRANGEVIEWTEST_H
19 |
20 | #include
21 | #include "qtestrunner.h"
22 |
23 | class QQmlEngine;
24 | class QQmlContext;
25 | class QQmlComponent;
26 | class QQuickItem;
27 | class QRangeView;
28 | class QRangeViewTest : public QObject{
29 |
30 | Q_OBJECT
31 | Q_TEST_RUNNER_SUITE
32 |
33 | public:
34 | explicit QRangeViewTest(QObject *parent = 0);
35 | ~QRangeViewTest();
36 |
37 | private slots:
38 | void initTestCase();
39 | void cleanupTestCase();
40 | void lookupTest();
41 | void edgeLookupTest();
42 | void modelItemChangeTest();
43 | void modelItemPositionChangeTest();
44 | void modelItemLengthChangeTest();
45 | void modelItemDataChangeTest();
46 |
47 | private:
48 | QQmlEngine* m_engine;
49 | QQmlContext* m_context;
50 | QQmlComponent* m_viewDelegate;
51 | QQuickItem* m_container;
52 |
53 | private:
54 | QRangeView* createView();
55 | QVariant getModelData(QRangeView* view, qint64 startPos, qint64 endPos, int role);
56 | };
57 |
58 | #endif // QRANGEVIEWTEST_H
59 |
--------------------------------------------------------------------------------
/sample/src/qganttdata.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QGANTTDATA_H
18 | #define QGANTTDATA_H
19 |
20 | #include
21 | #include
22 |
23 | class QGanttData : public QObject{
24 |
25 | Q_OBJECT
26 | Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
27 | Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
28 |
29 | public:
30 | explicit QGanttData(QObject *parent = 0);
31 | ~QGanttData();
32 |
33 | QString label() const;
34 | QColor color() const;
35 |
36 | signals:
37 | void labelChanged(QString arg);
38 | void colorChanged(QColor arg);
39 |
40 | public slots:
41 | void setLabel(QString arg);
42 | void setColor(QColor arg);
43 |
44 | private:
45 | QString m_label;
46 | QColor m_color;
47 | };
48 |
49 | inline QString QGanttData::label() const{
50 | return m_label;
51 | }
52 |
53 | inline QColor QGanttData::color() const{
54 | return m_color;
55 | }
56 |
57 | inline void QGanttData::setLabel(QString arg){
58 | if (m_label == arg)
59 | return;
60 |
61 | m_label = arg;
62 | emit labelChanged(arg);
63 | }
64 |
65 | inline void QGanttData::setColor(QColor arg){
66 | if (m_color == arg)
67 | return;
68 |
69 | m_color = arg;
70 | emit colorChanged(arg);
71 | }
72 |
73 | #endif // QGANTTDATA_H
74 |
--------------------------------------------------------------------------------
/sample/src/qganttmodellist.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QGANTTMODELLIST_H
18 | #define QGANTTMODELLIST_H
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | class QGanttModel;
25 | class QGanttModelContainer;
26 | class QGanttModelList : public QAbstractListModel{
27 |
28 | Q_OBJECT
29 | Q_PROPERTY(qint64 contentWidth READ contentWidth NOTIFY contentWidthChanged)
30 |
31 | public:
32 | enum Roles{
33 | Name = Qt::UserRole + 1,
34 | GanttModel
35 | };
36 |
37 | public:
38 | QGanttModelList(QObject *parent = 0);
39 | QGanttModelList(qint64 contentWidth, QObject* parent = 0);
40 | ~QGanttModelList();
41 |
42 | QVariant data(const QModelIndex &index, int role) const;
43 | bool setData(const QModelIndex &index, const QVariant &value, int role);
44 | int rowCount(const QModelIndex &parent) const;
45 | virtual QHash roleNames() const;
46 |
47 | void clearAll();
48 |
49 | void appendModel(QGanttModel* model);
50 |
51 | bool insertRows(int row, int count, const QModelIndex &parent);
52 | bool removeRows(int row, int count, const QModelIndex &parent);
53 |
54 | qint64 contentWidth() const;
55 | void setContentWidth(qint64 arg);
56 |
57 | signals:
58 | void contentWidthChanged(qint64 arg);
59 |
60 | private:
61 | QList m_items;
62 | QHash m_roles;
63 |
64 | qint64 m_contentWidth;
65 | };
66 |
67 | inline int QGanttModelList::rowCount(const QModelIndex &) const{
68 | return m_items.size();
69 | }
70 |
71 | inline qint64 QGanttModelList::contentWidth() const{
72 | return m_contentWidth;
73 | }
74 |
75 | inline void QGanttModelList::setContentWidth(qint64 arg){
76 | if (m_contentWidth == arg)
77 | return;
78 | m_contentWidth = arg;
79 | emit contentWidthChanged(arg);
80 | }
81 |
82 | #endif // QGANTTMODELLIST_H
83 |
--------------------------------------------------------------------------------
/lib/qganttmodelitem.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QGANTTMODELITEM_H
18 | #define QGANTTMODELITEM_H
19 |
20 | #include "qganttglobal.h"
21 | #include
22 | #include
23 |
24 | class Q_GANTT_EXPORT QGanttModelItem : public QObject{
25 |
26 | Q_OBJECT
27 | Q_PROPERTY(qint64 position READ position NOTIFY positionChanged)
28 | Q_PROPERTY(qint64 length READ length NOTIFY lengthChanged)
29 | Q_PROPERTY(QVariant data READ data NOTIFY dataChanged)
30 |
31 | public:
32 | explicit QGanttModelItem(QObject *parent = 0);
33 | QGanttModelItem(qint64 position, qint64 length, QObject* parent = 0);
34 | virtual ~QGanttModelItem();
35 |
36 | qint64 position() const;
37 | qint64 length() const;
38 | const QVariant& data() const;
39 |
40 | void setPosition(qint64 arg);
41 | void setLength(qint64 arg);
42 | void setData(const QVariant& data);
43 |
44 | signals:
45 | void positionChanged();
46 | void lengthChanged();
47 | void dataChanged();
48 |
49 | private:
50 | qint64 m_position;
51 | qint64 m_length;
52 | QVariant m_data;
53 | };
54 |
55 | inline qint64 QGanttModelItem::position() const{
56 | return m_position;
57 | }
58 |
59 | inline qint64 QGanttModelItem::length() const{
60 | return m_length;
61 | }
62 |
63 | inline void QGanttModelItem::setPosition(qint64 arg){
64 | if (m_position == arg)
65 | return;
66 |
67 | m_position = arg;
68 | emit positionChanged();
69 | }
70 |
71 | inline void QGanttModelItem::setLength(qint64 arg){
72 | if (m_length == arg)
73 | return;
74 |
75 | m_length = arg;
76 | emit lengthChanged();
77 | }
78 |
79 | inline const QVariant& QGanttModelItem::data() const{
80 | return m_data;
81 | }
82 |
83 | inline void QGanttModelItem::setData(const QVariant &data){
84 | if ( data != m_data ){
85 | m_data = data;
86 | emit dataChanged();
87 | }
88 | }
89 |
90 | #endif // QGANTTMODELITEM_H
91 |
--------------------------------------------------------------------------------
/sample/src/main.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | #include "qrangeview.h"
22 | #include "qganttmodel.h"
23 | #include "qganttmodelitem.h"
24 | #include "qganttmodellist.h"
25 | #include "qganttdata.h"
26 |
27 | static int CONFIGURATION_MODEL_SIZE = 20000;
28 |
29 | int randBetween(int min, int max){
30 | return rand() % (max - min + 1) + min;
31 | }
32 |
33 | QVariant createModelData(){
34 | return QVariant::fromValue(new QGanttData);
35 | }
36 |
37 |
38 | int main(int argc, char *argv[])
39 | {
40 | QApplication app(argc, argv);
41 |
42 | qmlRegisterUncreatableType(
43 | "Gantt", 1, 0, "AbstractRangeModel", "AbstractRangeModel is of abstract type.");
44 | qmlRegisterType( "Gantt", 1, 0, "RangeView");
45 | qmlRegisterType( "Gantt", 1, 0, "GanttModel");
46 | qmlRegisterType("Gantt", 1, 0, "GanttModelItem");
47 | qmlRegisterType( "Gantt", 1, 0, "GanttData");
48 | qmlRegisterType("Gantt", 1, 0, "GanttModelList");
49 |
50 | QGanttModelList modelList(CONFIGURATION_MODEL_SIZE);
51 |
52 | for( int i = 0; i < 100; ++i ){
53 | QGanttModel* m = new QGanttModel;
54 | m->setItemDataFactoryFunction(&createModelData);
55 | m->setContentWidth(CONFIGURATION_MODEL_SIZE);
56 |
57 | int pos = 0, length = 0;
58 | while ( pos < CONFIGURATION_MODEL_SIZE - 320 ){
59 | pos += randBetween(40, 300);
60 | length = randBetween(40, 120);
61 | m->insertItem(pos, length);
62 | pos += length;
63 | }
64 |
65 | modelList.appendModel(m);
66 | }
67 |
68 | QQmlApplicationEngine engine;
69 | engine.rootContext()->setContextProperty("ganttModelList", &modelList);
70 | engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
71 |
72 | return app.exec();
73 | }
74 |
--------------------------------------------------------------------------------
/lib/qabstractrangemodel.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qabstractrangemodel.h"
18 |
19 | // QAbstractRangeModelPrivate
20 | // ----------------------------------------------------------------------------
21 |
22 | class QAbstractRangeModelPrivate{
23 | public:
24 | class QRangeModelChange{
25 | public:
26 | qint64 startPosition;
27 | qint64 endPosition;
28 | };
29 |
30 | public:
31 | QAbstractRangeModelPrivate() : lastChange(0){}
32 |
33 | QRangeModelChange* change(qint64 startPosition, qint64 endPosition);
34 | QRangeModelChange* lastChange;
35 | };
36 |
37 |
38 | QAbstractRangeModelPrivate::QRangeModelChange* QAbstractRangeModelPrivate::change(
39 | qint64 startPosition,
40 | qint64 endPosition)
41 | {
42 | QRangeModelChange* change = new QRangeModelChange;
43 | change->startPosition = startPosition;
44 | change->endPosition = endPosition;
45 | return change;
46 | }
47 |
48 |
49 | // QAbstractRangeModel
50 | // ----------------------------------------------------------------------------
51 |
52 | QAbstractRangeModel::QAbstractRangeModel(QObject* parent)
53 | : QObject(parent)
54 | , d_ptr(new QAbstractRangeModelPrivate)
55 | {
56 | }
57 |
58 | QAbstractRangeModel::~QAbstractRangeModel(){
59 | delete d_ptr;
60 | }
61 |
62 | void QAbstractRangeModel::beginDataChange(qint64 startPosition, qint64 endPosition){
63 | Q_D(QAbstractRangeModel);
64 | if ( d->lastChange != 0 )
65 | delete d->lastChange;
66 | d->lastChange = d->change(startPosition, endPosition);
67 | emit itemsAboutToBeChanged(startPosition, endPosition);
68 | }
69 |
70 | void QAbstractRangeModel::endDataChange(){
71 | Q_D(QAbstractRangeModel);
72 | if ( d->lastChange != 0 ){
73 | emit itemsChanged(d->lastChange->startPosition, d->lastChange->endPosition);
74 | delete d->lastChange;
75 | d->lastChange = 0;
76 | }
77 | }
78 |
79 | void QAbstractRangeModel::insertItem(qint64, qint64){}
80 |
81 | void QAbstractRangeModel::removeItem(qint64, qint64, qint64){}
82 |
--------------------------------------------------------------------------------
/lib/qganttmodel.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QGANTTMODEL_H
18 | #define QGANTTMODEL_H
19 |
20 | #include "qganttglobal.h"
21 | #include "qabstractrangemodel.h"
22 |
23 | // QGanttModelIterator
24 | // -------------------
25 |
26 | class QGanttModelItem;
27 | class Q_GANTT_EXPORT QGanttModelIterator : public QAbstractRangeModelIterator{
28 |
29 | public:
30 | QGanttModelIterator(
31 | QList::iterator it,
32 | QList::iterator endIt,
33 | qint64 endPosition)
34 | : m_endIt(endIt)
35 | , m_currentIt(it)
36 | , m_endPosition(endPosition)
37 | {}
38 |
39 | bool isEnd() const;
40 | void nextItem();
41 |
42 | QVariant data(int role = 0);
43 |
44 | private:
45 | QList::iterator m_endIt;
46 | QList::iterator m_currentIt;
47 | qint64 m_endPosition;
48 |
49 | };
50 |
51 | // QGanttModel
52 | // -----------
53 |
54 | class QGanttModelPrivate;
55 | class Q_GANTT_EXPORT QGanttModel : public QAbstractRangeModel{
56 |
57 | Q_OBJECT
58 |
59 | public:
60 | typedef QVariant (*ItemDataFactoryFunction)();
61 |
62 | enum Role{
63 | modelData = QAbstractRangeModel::LengthRole + 1
64 | };
65 |
66 | public:
67 | explicit QGanttModel(QObject *parent = 0);
68 | ~QGanttModel();
69 |
70 | QAbstractRangeModelIterator* dataBetween(qint64 startPosition, qint64 endPosition);
71 |
72 | void setItemPosition(qint64 itemPosition, qint64 itemLength, int positionIndex, qint64 itemNewPosition);
73 | void setItemLength(qint64 itemPosition, qint64 itemLength, int positionIndex, qint64 newLength);
74 | void setItemData(qint64 itemPosition, qint64 itemLength, int positionIndex, int role, const QVariant& value);
75 |
76 | QHash roleNames() const;
77 |
78 | int insertItem(QGanttModelItem* item);
79 |
80 | void setItemDataFactoryFunction(ItemDataFactoryFunction fp);
81 |
82 | public slots:
83 | void insertItem(qint64 position, qint64 length);
84 | void removeItem(qint64 position, qint64 length, qint64 relativeIndex);
85 |
86 | private:
87 | QGanttModel(const QGanttModel&);
88 | QGanttModel& operator = (const QGanttModel&);
89 |
90 | QGanttModelPrivate* const d_ptr;
91 | Q_DECLARE_PRIVATE(QGanttModel)
92 |
93 | };
94 |
95 | #endif // QGANTTMODEL_H
96 |
--------------------------------------------------------------------------------
/sample/src/qganttmodellist.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qganttmodellist.h"
18 | #include "qganttmodel.h"
19 |
20 | #include
21 | class QGanttModelContainer{
22 | public:
23 | ~QGanttModelContainer(){ delete model; }
24 |
25 | QString name;
26 | QGanttModel* model;
27 | };
28 |
29 |
30 | QGanttModelList::QGanttModelList(QObject* parent)
31 | : QAbstractListModel(parent)
32 | {
33 | }
34 |
35 | QGanttModelList::QGanttModelList(qint64 size, QObject *parent)
36 | : QAbstractListModel(parent)
37 | , m_contentWidth(size)
38 | {
39 | }
40 |
41 | QGanttModelList::~QGanttModelList(){
42 | clearAll();
43 | }
44 |
45 | QVariant QGanttModelList::data(const QModelIndex &index, int role) const{
46 | if ( index.row() >= m_items.size() )
47 | return QVariant();
48 | if ( role == QGanttModelList::Name )
49 | return m_items[index.row()]->name;
50 | else if ( role == QGanttModelList::GanttModel )
51 | return QVariant::fromValue(m_items[index.row()]->model);
52 | else
53 | return QVariant();
54 | }
55 |
56 | bool QGanttModelList::setData(const QModelIndex &index, const QVariant &value, int role){
57 | if ( index.row() >= m_items.size() )
58 | return false;
59 | if ( role == QGanttModelList::Name ){
60 | m_items[index.row()]->name = value.toString();
61 | emit dataChanged(index, index, QVector() << role);
62 | return true;
63 | } else
64 | return false;
65 | }
66 |
67 | QHash QGanttModelList::roleNames() const{
68 | QHash rolenames;
69 | rolenames[Name] = "name";
70 | rolenames[GanttModel] = "ganttModel";
71 | return rolenames;
72 | }
73 |
74 | void QGanttModelList::clearAll(){
75 | for ( QList::Iterator it = m_items.begin(); it != m_items.end(); ++it )
76 | delete *it;
77 | m_items.clear();
78 | }
79 |
80 | void QGanttModelList::appendModel(QGanttModel *model){
81 | QGanttModelContainer* item = new QGanttModelContainer;
82 | item->model = model;
83 | item->name = "Gantt Row #" + QString::number(m_items.size());
84 | m_items.append(item);
85 | }
86 |
87 | bool QGanttModelList::insertRows(int row, int count, const QModelIndex &parent){
88 | beginInsertRows(parent, row, row + count - 1);
89 | for ( int i = row; i < row + count - 1; ++i ){
90 | QGanttModelContainer* item = new QGanttModelContainer;
91 | item->model = new QGanttModel;
92 | item->model->setContentWidth(m_contentWidth);
93 | item->name = "Gantt Row";
94 | m_items.insert(i, item);
95 | }
96 | endInsertRows();
97 | return true;
98 | }
99 |
100 | bool QGanttModelList::removeRows(int row, int count, const QModelIndex &parent){
101 | beginRemoveRows(parent, row, row + count - 1);
102 | for ( int i = row + count - 1; i <= row; --i )
103 | m_items.removeAt(i);
104 | endRemoveRows();
105 | return true;
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/lib/qrangeview.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QRANGEVIEW_H
18 | #define QRANGEVIEW_H
19 |
20 | #include "qganttglobal.h"
21 | #include "qabstractrangemodel.h"
22 |
23 | #include
24 | #include
25 |
26 | class QRangeViewPrivate;
27 | class Q_GANTT_EXPORT QRangeView : public QQuickItem{
28 |
29 | Q_OBJECT
30 | Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
31 | Q_PROPERTY(QAbstractRangeModel* model READ model WRITE setModel NOTIFY modelChanged)
32 | Q_PROPERTY(qint64 viewportX READ viewportX WRITE setViewportX NOTIFY viewportXChanged)
33 | Q_PROPERTY(qint64 viewportWidth READ viewportWidth WRITE setViewportWidth NOTIFY viewportWidthChanged)
34 |
35 | public:
36 | explicit QRangeView(QQuickItem *parent = 0);
37 | ~QRangeView();
38 |
39 | QQmlComponent* delegate() const;
40 | void setDelegate(QQmlComponent* delegate);
41 |
42 | qint64 viewportWidth() const;
43 | qint64 viewportX() const;
44 |
45 | QAbstractRangeModel* model() const;
46 |
47 | signals:
48 | void delegateChanged();
49 | void viewportWidthChanged(qint64 arg);
50 | void viewportXChanged(qint64 arg);
51 | void modelChanged(QAbstractRangeModel* arg);
52 |
53 | public slots:
54 | void setViewportWidth(qint64 arg);
55 | void setViewportX(qint64 arg);
56 | void setModel(QAbstractRangeModel* arg);
57 |
58 | void modelWidthChanged();
59 | void modelitemCoordinatesChanged(
60 | qint64 oldPosition, qint64 oldLength, int relativeIndex,
61 | QAbstractRangeModelIterator* modelIt, int newRelativeIndex);
62 | void modelItemDataChanged(QAbstractRangeModelIterator* modelIt, int relativeIndex, QList roles);
63 | void modelItemsAboutToBeChanged(qint64 beginPosition, qint64 endPosition);
64 | void modelItemsChanged(qint64 beginPosition, qint64 endPosition);
65 |
66 | void setPositionViaDelegate(QQuickItem* delegate, qint64 position);
67 | void setLengthViaDelegate(QQuickItem* delegate, qint64 length);
68 | void setDataViaDelegate(QQuickItem* delegate, const QString& role, const QVariant& value);
69 | void removeItemViaDelegate(QQuickItem* delegate);
70 |
71 | protected:
72 | void componentComplete();
73 |
74 | private:
75 | QRangeView(const QRangeView&);
76 | QRangeView& operator = (const QRangeView&);
77 |
78 | QRangeViewPrivate* const d_ptr;
79 | Q_DECLARE_PRIVATE(QRangeView)
80 |
81 | void regenerateNewContent();
82 |
83 | int m_viewportWidth;
84 | int m_viewportX;
85 | };
86 |
87 | inline qint64 QRangeView::viewportWidth() const{
88 | return m_viewportWidth;
89 | }
90 |
91 | inline qint64 QRangeView::viewportX() const{
92 | return m_viewportX;
93 | }
94 |
95 | inline void QRangeView::setViewportWidth(qint64 arg){
96 | if (m_viewportWidth == arg)
97 | return;
98 |
99 | m_viewportWidth = arg;
100 | regenerateNewContent();
101 | emit viewportWidthChanged(arg);
102 | }
103 |
104 | inline void QRangeView::setViewportX(qint64 arg){
105 | if (m_viewportX == arg)
106 | return;
107 |
108 | m_viewportX = arg;
109 | regenerateNewContent();
110 | emit viewportXChanged(arg);
111 | }
112 |
113 |
114 | #endif // QRANGEVIEW_H
115 |
--------------------------------------------------------------------------------
/lib/qabstractrangemodel.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #ifndef QABSTRACTRANGEMODEL_H
18 | #define QABSTRACTRANGEMODEL_H
19 |
20 | #include "qganttglobal.h"
21 | #include
22 |
23 | // QAbstractRangeModelIterator
24 | // --------------------------------
25 |
26 | class Q_GANTT_EXPORT QAbstractRangeModelIterator{
27 |
28 | public:
29 | virtual ~QAbstractRangeModelIterator(){}
30 |
31 | virtual bool isEnd() const = 0;
32 | virtual void nextItem() = 0;
33 | virtual QVariant data(int role = 0) = 0;
34 |
35 | };
36 |
37 |
38 | // QAbstractRangeModel
39 | // ------------------------
40 |
41 | class QAbstractRangeModelPrivate;
42 | class Q_GANTT_EXPORT QAbstractRangeModel : public QObject{
43 |
44 | Q_OBJECT
45 | Q_PROPERTY(qint64 contentWidth READ contentWidth NOTIFY contentWidthChanged)
46 |
47 | public:
48 | enum Role{
49 | PositionRole = 0,
50 | LengthRole
51 | };
52 |
53 | public:
54 | explicit QAbstractRangeModel(QObject* parent = 0);
55 | virtual ~QAbstractRangeModel();
56 |
57 | qint64 contentWidth() const;
58 | void setContentWidth(qint64 contentWidth);
59 |
60 | virtual QAbstractRangeModelIterator* dataBetween(qint64 startPosition, qint64 endPosition) = 0;
61 |
62 | virtual void setItemPosition(
63 | qint64 itemPosition,
64 | qint64 itemLength,
65 | int relativeIndex,
66 | qint64 itemNewPosition
67 | ) = 0;
68 | virtual void setItemLength(
69 | qint64 itemPosition,
70 | qint64 itemLength,
71 | int relativeIndex,
72 | qint64 newLength
73 | ) = 0;
74 | virtual void setItemData(
75 | qint64 itemPosition,
76 | qint64 itemLength,
77 | int relativeIndex,
78 | int role,
79 | const QVariant& value
80 | ) = 0;
81 | virtual QHash roleNames() const = 0;
82 |
83 | protected:
84 | void beginDataChange(qint64 startPosition, qint64 endPosition);
85 | void endDataChange();
86 |
87 | signals:
88 | void contentWidthChanged();
89 | void itemsAboutToBeChanged(qint64 startPosition, qint64 endPosition);
90 | void itemsChanged(qint64 startPosition, qint64 endPosition);
91 | void itemCoordinatesChanged(
92 | qint64 oldPosition, qint64 oldLength, int relativeIndex,
93 | QAbstractRangeModelIterator* modelIt, int newRelativeIndex);
94 | void itemsDataChanged(QAbstractRangeModelIterator*, int relativeIndex, QList roles = QList() );
95 |
96 | public slots:
97 | virtual void insertItem(qint64 position, qint64 length);
98 | virtual void removeItem(qint64 position, qint64 length, qint64 relativeIndex);
99 |
100 | private:
101 | QAbstractRangeModel(const QAbstractRangeModel&);
102 | QAbstractRangeModel& operator = (const QAbstractRangeModel&);
103 |
104 | QAbstractRangeModelPrivate* const d_ptr;
105 | Q_DECLARE_PRIVATE(QAbstractRangeModel)
106 |
107 | qint64 m_contentWidth;
108 | };
109 |
110 |
111 | inline qint64 QAbstractRangeModel::contentWidth() const{
112 | return m_contentWidth;
113 | }
114 |
115 | inline void QAbstractRangeModel::setContentWidth(qint64 contentWidth){
116 | if ( m_contentWidth != contentWidth ){
117 | m_contentWidth = contentWidth;
118 | emit contentWidthChanged();
119 | }
120 | }
121 |
122 | #endif // QABSTRACTRANGEMODEL_H
123 |
--------------------------------------------------------------------------------
/tests/qganttmodeltest.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qganttmodeltest.h"
18 | #include "qganttmodel.h"
19 |
20 | #include
21 | #include
22 | #include
23 |
24 |
25 | Q_TEST_RUNNER_REGISTER(QGanttModelTest);
26 |
27 | class QGanttModelTestHelper{
28 |
29 | public:
30 | typedef QPair Range;
31 | typedef QList RangeList;
32 |
33 | public:
34 | static bool compare(QGanttModel& model, const RangeList& ranges);
35 |
36 | };
37 |
38 | QGanttModelTest::QGanttModelTest(QObject *parent) : QObject(parent){
39 |
40 | }
41 |
42 | QGanttModelTest::~QGanttModelTest(){
43 |
44 | }
45 |
46 | void QGanttModelTest::initTestCase(){
47 | }
48 |
49 | void QGanttModelTest::descendingTest(){
50 | QGanttModel model;
51 | model.setContentWidth(100);
52 | model.insertItem(60, 10);
53 | model.insertItem(50, 10);
54 | model.insertItem(40, 10);
55 | model.insertItem(30, 10);
56 | QCOMPARE(QGanttModelTestHelper::compare(
57 | model,
58 | QGanttModelTestHelper::RangeList() <<
59 | QGanttModelTestHelper::Range(30, 10) <<
60 | QGanttModelTestHelper::Range(40, 10) <<
61 | QGanttModelTestHelper::Range(50, 10) <<
62 | QGanttModelTestHelper::Range(60, 10)
63 | ), true);
64 | }
65 |
66 | void QGanttModelTest::alternateTest(){
67 | QGanttModel model;
68 | model.setContentWidth(100);
69 | model.insertItem(10, 10);
70 | model.insertItem(5, 10);
71 | model.insertItem(20, 10);
72 | model.insertItem(4, 10);
73 | QCOMPARE(QGanttModelTestHelper::compare(
74 | model,
75 | QGanttModelTestHelper::RangeList() <<
76 | QGanttModelTestHelper::Range(4, 10) <<
77 | QGanttModelTestHelper::Range(5, 10) <<
78 | QGanttModelTestHelper::Range(10, 10) <<
79 | QGanttModelTestHelper::Range(20, 10)
80 | ), true);
81 | }
82 |
83 | void QGanttModelTest::sameLengthTest(){
84 | QGanttModel model;
85 | model.setContentWidth(100);
86 | model.insertItem(10, 10);
87 | model.insertItem(10, 10);
88 | model.insertItem(10, 12);
89 | model.insertItem(10, 11);
90 | QCOMPARE(QGanttModelTestHelper::compare(
91 | model,
92 | QGanttModelTestHelper::RangeList() <<
93 | QGanttModelTestHelper::Range(10, 10) <<
94 | QGanttModelTestHelper::Range(10, 10) <<
95 | QGanttModelTestHelper::Range(10, 11) <<
96 | QGanttModelTestHelper::Range(10, 12)
97 | ), true);
98 | }
99 |
100 | void QGanttModelTest::sameLengthTest2(){
101 | QGanttModel model;
102 | model.setContentWidth(100);
103 | model.insertItem(10, 10);
104 | model.insertItem(10, 10);
105 | model.insertItem(9, 11);
106 | model.insertItem(9, 11);
107 | model.insertItem(9, 10);
108 | model.insertItem(10, 12);
109 | model.insertItem(10, 10);
110 | model.insertItem(10, 11);
111 | model.insertItem(14, 2);
112 | model.insertItem(14, 1);
113 | QCOMPARE(QGanttModelTestHelper::compare(
114 | model,
115 | QGanttModelTestHelper::RangeList() <<
116 | QGanttModelTestHelper::Range(9, 10) <<
117 | QGanttModelTestHelper::Range(9, 11) <<
118 | QGanttModelTestHelper::Range(9, 11) <<
119 | QGanttModelTestHelper::Range(10, 10) <<
120 | QGanttModelTestHelper::Range(10, 10) <<
121 | QGanttModelTestHelper::Range(10, 10) <<
122 | QGanttModelTestHelper::Range(10, 11) <<
123 | QGanttModelTestHelper::Range(10, 12) <<
124 | QGanttModelTestHelper::Range(14, 1) <<
125 | QGanttModelTestHelper::Range(14, 2)
126 | ), true);
127 | }
128 |
129 | bool QGanttModelTestHelper::compare(QGanttModel& model, const QGanttModelTestHelper::RangeList& ranges){
130 | QScopedPointer modelIt(model.dataBetween(0, model.contentWidth()));
131 | QGanttModelTestHelper::RangeList::const_iterator it = ranges.begin();
132 | while ( !modelIt->isEnd() ){
133 | if ( it == ranges.end() )
134 | return false;
135 | qint64 position = modelIt->data(QAbstractRangeModel::PositionRole).toLongLong();
136 | qint64 length = modelIt->data(QAbstractRangeModel::LengthRole).toLongLong();
137 | if ( position != it->first || length != it->second )
138 | return false;
139 | ++it;
140 | modelIt->nextItem();
141 | }
142 | return true;
143 | }
144 |
--------------------------------------------------------------------------------
/sample/resource/GanttEditWindow.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | import QtQuick 2.3
18 | import QtQuick.Window 2.1
19 | import QtQuick.Controls 1.2
20 | import QtQuick.Controls.Styles 1.2
21 | import QtQuick.Dialogs 1.2
22 | import Gantt 1.0
23 |
24 | Window{
25 | id: root
26 | title: "Edit Gantt Item"
27 | modality: Qt.ApplicationModal
28 |
29 | visible: false
30 | minimumWidth: 300
31 | minimumHeight: 200
32 | width: 400
33 | height: 200
34 |
35 | property variant ganttItem : null
36 |
37 | signal update(string color, string data)
38 | signal canceled()
39 |
40 | Rectangle{
41 | color: "#ddd"
42 | anchors.fill: parent
43 | focus: true
44 |
45 | Keys.onEscapePressed: {
46 | labelInput.text = ""
47 | root.close()
48 | root.canceled()
49 | }
50 |
51 | TextArea{
52 | id: colorInput
53 | anchors.top: parent.top
54 | anchors.topMargin: 12
55 | anchors.left: parent.left
56 | anchors.right: parent.right
57 | anchors.leftMargin: 12
58 | anchors.rightMargin: 12
59 | height: 25
60 | clip: true
61 | text: root.ganttItem ? root.ganttItem.color : ""
62 | selectByMouse: true
63 | font.pixelSize: 12
64 | font.family: "Courier New"
65 | style: TextAreaStyle {
66 | textColor: "#333"
67 | selectionColor: "#eee"
68 | selectedTextColor: "#333"
69 | backgroundColor: "#bbb"
70 | }
71 | }
72 |
73 | TextArea{
74 | id: labelInput
75 | anchors.top: parent.top
76 | anchors.topMargin: 40
77 | anchors.left: parent.left
78 | anchors.right: parent.right
79 | anchors.leftMargin: 12
80 | anchors.rightMargin: 12
81 | height: parent.height - 100
82 | clip: true
83 | text: root.ganttItem ? root.ganttItem.label : ""
84 | selectByMouse: true
85 | font.pixelSize: 12
86 | font.family: "Courier New"
87 | style: TextAreaStyle {
88 | textColor: "#333"
89 | selectionColor: "#eee"
90 | selectedTextColor: "#333"
91 | backgroundColor: "#bbb"
92 | }
93 | }
94 |
95 | Rectangle{
96 | id: updateButton
97 | anchors.left: parent.left
98 | anchors.leftMargin: 10
99 | anchors.bottom: parent.bottom
100 | anchors.bottomMargin: 16
101 | color: createButtonArea.containsMouse ? "#334499" : "#334466"
102 | width: 100
103 | height: 30
104 | Text{
105 | anchors.centerIn: parent
106 | text: "Update"
107 | color: "#fff"
108 | font.pixelSize: 12
109 | font.family: "Arial, Helvetica, sans-serif"
110 | }
111 | MouseArea{
112 | id: createButtonArea
113 | anchors.fill: parent
114 | hoverEnabled: true
115 | onClicked: {
116 | colorInput.select(0, 0)
117 | labelInput.select(0, 0)
118 | if ( ganttItem ){
119 | ganttItem.label = labelInput.text
120 | ganttItem.color = colorInput.text
121 | }
122 | root.close()
123 | root.update(colorInput.text, labelInput.text)
124 | root.ganttItem = null
125 | }
126 | }
127 | }
128 |
129 | Rectangle{
130 | id: cancelButton
131 | anchors.right: parent.right
132 | anchors.rightMargin: 10
133 | anchors.bottom: parent.bottom
134 | anchors.bottomMargin: 16
135 | color: cancelButtonArea.containsMouse ? "#b14433" : "#884433"
136 | width: 100
137 | height: 30
138 | Text{
139 | anchors.centerIn: parent
140 | text: "Cancel"
141 | color: "#fff"
142 | font.pixelSize: 12
143 | font.family: "Arial, Helvetica, sans-serif"
144 | }
145 | MouseArea{
146 | id: cancelButtonArea
147 | anchors.fill: parent
148 | hoverEnabled: true
149 | onClicked: {
150 | ganttItem = null
151 | root.close()
152 | root.canceled()
153 | }
154 | }
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QML Gantt library
2 |
3 | | Linux | Windows |
4 | |:-------------:|:----------:|
5 | | [](https://travis-ci.org/dinusv/qml-gantt) | [](https://ci.appveyor.com/project/dinusv/qml-gantt/branch/master) |
6 |
7 |
8 |
9 | An efficient implementation of a gantt chart library for qml. The library provides a custom View element
10 | which handles lazy loading of items according to its viewport. Items are loaded from a model which can
11 | easily be implemented according to custom specifications.
12 |
13 | * **Version**: 0.1.0
14 | * **License**: LPGL
15 | * **Library requirements**: Qt5 or higher
16 | * **Sample project requirements**: Qt5.3 or higher
17 |
18 | A fully editable gantt chart element is provided in the associated sample project:
19 |
20 | 
21 |
22 | ## Usage
23 |
24 | The project can be used as an externally linked library or by including the source files directly. (The first option would be the preferred one).
25 |
26 | ### Compiling the library
27 |
28 | * Download the source code
29 | * Run the following command within the project directory to generate the library:
30 |
31 | ```
32 | cd build
33 | qmake -r ..
34 | make (or nmake)
35 | ```
36 |
37 | * The library will be available within the build directory. You can run the associated gantt example project from the sample directory:
38 |
39 | ```
40 | sample/./sample
41 | ```
42 |
43 | * Or you can link to the library from your own project and implement the model data:
44 |
45 | ```
46 | INCLUDEPATH += $$GANTTLIBPATH/include
47 |
48 | LIBS += -L$$GANTTLIBPATH/lib -lqmlgantt
49 | ```
50 |
51 | ### Manually including the source files
52 |
53 | * Download the source code
54 | * Include the following files into your project:
55 | * qganttglobal
56 | * qabstractrangemodel.h
57 | * qabstractrangemodel.cpp
58 | * qrangeview.h
59 | * qrangeview.cpp
60 | * qganttmodel.h
61 | * qganttmodel.cpp
62 | * qganttmodelitem.h
63 | * qganttmodelitem.cpp
64 | * Implement the model data and you're ready to go
65 |
66 |
67 | ## Implementing the model data
68 |
69 | There are two ways to add custom data through the model:
70 |
71 | * Adding a ```QGanntModel``` item (the easy way)
72 | * Implementing your own model by extending the ```QAbstractRangeModel``` (the custom way and also the bit more difficult one)
73 |
74 | ### Adding a ```QGanntModel``` data item
75 |
76 | The `QGanttModel` class is an already implemented version of the ```QAbstractRangeModel``` class, therefore provides all the methods
77 | required to communicate with the ```RangeView``` element. It's child items contain 3 properites: the **position** and **length**,
78 | which are used by the model to keep items sorted for easy access, and the **data** property, which is a variant type customizable by
79 | the user. It can contain any qml value type, or a reference to a QObject subclass, which can further extend the number of properties
80 | available.
81 |
82 | In the associated sample subproject, I've included the ```QGanttData``` class, which adds a color and label property to each gantt item.
83 | The ```QGanttModel``` class is assigned a ```QGanttData``` object whenever a new ```QGanttModelItem``` is created and inserted in the model. This
84 | is done internally by the model by calling a *configured* data factory function whenever an insertion is triggered. The function is configured
85 | by using the ```QGanttModelItem::setItemDataFactoryFunction``` setter. The factory function is defined in the *sample/src/main.cpp* file.
86 |
87 | To assign a different data item:
88 |
89 | ```cpp
90 | class CustomData : public QObject{
91 |
92 | Q_OBJECT
93 | Q_PROPERTY(QString property1...)
94 | ...
95 |
96 | }
97 | ```
98 |
99 | we register the type to qml:
100 |
101 | ```cpp
102 | qmlRegisterType("Gantt", 1, 0, "CustomData");
103 | ```
104 |
105 |
106 | then define the item factory function:
107 |
108 | ```cpp
109 | QVariant createModelData(){
110 | return QVariant::fromValue(new CustomData);
111 | }
112 | ```
113 |
114 | and register the function to our model:
115 |
116 | ```cpp
117 | QGanttModel* model = new QanttModel;
118 | model->setItemDataFactoryFunction(&createModelData);
119 | ```
120 |
121 | then, whenever we invoke a new insertion, a ```CustomData``` type object will be set in the data field:
122 |
123 | ```cpp
124 | model->insertItem(10, 10);
125 | ```
126 |
127 | Which, in our delegate, can be accessed through the ```modelData``` property:
128 |
129 | ```qml
130 | RangeView{
131 | ...
132 | delegate: Component{
133 | Text{ text: modelData.property1 }
134 | }
135 | }
136 | ```
137 |
138 | The properties are set through simple assignments:
139 |
140 | ```qml
141 | RangeView{
142 | ...
143 | delegate: Component{
144 | Rectangle{
145 | width: 200; height: 40;
146 | Text{ text: modelData.property1 }
147 | MouseArea{ anchors.fill: parent; onClicked: modelData.property1 = "new value"; }
148 | }
149 | }
150 | }
151 | ```
152 |
153 |
154 | The *sample/resource/GanttLine.qml* file provides a full example on assignments and other item handling.
155 |
156 | ### Implementing the ```QAbstractRangeModel```
157 |
158 | The QAbstractRangeModel and QAbstractRangeModelIterator provide the implementation
159 | requirements used by the QRangeView to work with the model data. The section is treated separately
160 | [here](doc/implement-qabstractrangemodel.md).
161 |
--------------------------------------------------------------------------------
/sample/resource/GanttHelpWindow.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | import QtQuick 2.3
18 | import QtQuick.Window 2.1
19 | import QtQuick.Controls 1.2
20 | import QtQuick.Controls.Styles 1.2
21 | import QtQuick.Dialogs 1.2
22 | import Gantt 1.0
23 |
24 | Window {
25 | id: root
26 | title: "Gantt Interactions"
27 | modality: Qt.ApplicationModal
28 |
29 | visible: false
30 | minimumWidth: 300
31 | minimumHeight: 200
32 | width: 400
33 | height: 200
34 |
35 | ScrollView {
36 | anchors.fill: parent
37 |
38 | Rectangle {
39 | anchors.top: parent.top
40 | anchors.topMargin: 10
41 | anchors.left: parent.left
42 | anchors.leftMargin: 10
43 | width: root.width - 30
44 | color: 'transparent'
45 | height: helpGrid.height + 20
46 |
47 | Grid {
48 | id: helpGrid
49 | columns: 2
50 | spacing: 10
51 | width: root.width - 30
52 |
53 | Image {
54 | width: 100
55 | height: 80
56 | source: "/img/help-add-item.png"
57 | }
58 |
59 | Rectangle {
60 | width: root.width - 140
61 | height: 80
62 | Text {
63 | anchors.verticalCenter: parent.verticalCenter
64 | width: root.width - 140
65 | text: "To add a new item to a row, double click in the empty area inside the row."
66 | color: "#333"
67 | wrapMode: Text.WordWrap
68 | font.family: "sans-serif"
69 | font.pixelSize: 12
70 | }
71 | }
72 |
73 | Image {
74 | width: 100
75 | height: 80
76 | source: "/img/help-move-item.png"
77 | }
78 |
79 | Rectangle {
80 | width: root.width - 140
81 | height: 80
82 | Text {
83 | anchors.verticalCenter: parent.verticalCenter
84 | width: root.width - 140
85 | text: "To move an item, click inside the item and drag while holding the mouse button pressed."
86 | color: "#333"
87 | wrapMode: Text.WordWrap
88 | font.family: "sans-serif"
89 | font.pixelSize: 12
90 | }
91 | }
92 |
93 | Image {
94 | width: 100
95 | height: 80
96 | source: "/img/help-stretch-item.png"
97 | }
98 |
99 | Rectangle {
100 | width: root.width - 140
101 | height: 80
102 | Text {
103 | anchors.verticalCenter: parent.verticalCenter
104 | width: parent.width
105 | text: "Stretch the item by dragging its edges."
106 | color: "#333"
107 | wrapMode: Text.WordWrap
108 | font.family: "sans-serif"
109 | font.pixelSize: 12
110 | }
111 | }
112 |
113 | Image {
114 | width: 100
115 | height: 80
116 | source: "/img/help-edit-item.png"
117 | }
118 |
119 | Rectangle {
120 | width: root.width - 140
121 | height: 80
122 | Text {
123 | anchors.verticalCenter: parent.verticalCenter
124 | width: root.width - 140
125 | text: "Double click an item to edit its color and label."
126 | color: "#333"
127 | wrapMode: Text.WordWrap
128 | font.family: "sans-serif"
129 | font.pixelSize: 12
130 | }
131 | }
132 |
133 | Image {
134 | width: 100
135 | height: 80
136 | source: "/img/help-edit-row.png"
137 | }
138 |
139 | Rectangle {
140 | width: root.width - 140
141 | height: 80
142 | Text {
143 | anchors.verticalCenter: parent.verticalCenter
144 | width: root.width - 140
145 | text: "Double click a row label to edit its name, press return once you're done."
146 | color: "#333"
147 | wrapMode: Text.WordWrap
148 | font.family: "sans-serif"
149 | font.pixelSize: 12
150 | }
151 | }
152 | }
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/sample/resource/main.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | import QtQuick 2.2
18 | import QtQuick.Controls 1.1
19 | import Gantt 1.0
20 |
21 | ApplicationWindow {
22 | visible: true
23 | width: 640
24 | height: 480
25 | title: qsTr("QML Gantt Sample")
26 |
27 | menuBar: MenuBar {
28 | Menu {
29 | title: qsTr("File")
30 | MenuItem {
31 | text: qsTr("Exit")
32 | onTriggered: Qt.quit();
33 | }
34 | }
35 | Menu {
36 | title: qsTr("Help")
37 | MenuItem {
38 | text: qsTr("Usage...")
39 | onTriggered: ganttHelpWindow.show()
40 | }
41 | }
42 | }
43 |
44 | Rectangle{
45 | height: parent.height - 14
46 | clip: true
47 | width: 150
48 |
49 | ListView{
50 | id: ganttNameView
51 |
52 | anchors.fill: parent
53 |
54 | interactive: false
55 | contentY: ganttView.contentY
56 |
57 | model: ganttModelList
58 | delegate: Rectangle{
59 | width: ganttNameView.width
60 | height: 40
61 | Rectangle{
62 | color: "#ccc"
63 | width: parent.width
64 | height: 38
65 | Text{
66 | anchors.verticalCenter: parent.verticalCenter
67 | anchors.left: parent.left
68 | anchors.leftMargin: 10
69 | text: name
70 | font.family: "Arial, Helvetica, open-sans"
71 | font.pixelSize: 13
72 | color: "#000"
73 | }
74 | MouseArea{
75 | anchors.fill: parent
76 | onDoubleClicked: {
77 | trackEditBox.visible = true
78 | trackEdit.selectAll()
79 | trackEdit.forceActiveFocus()
80 | }
81 | }
82 | Rectangle{
83 | id: trackEditBox
84 | anchors.verticalCenter: parent.verticalCenter
85 | anchors.fill: parent
86 | anchors.margins: 5
87 | visible: false
88 | color: "#aaa"
89 | border.color: "#6666cc"
90 | border.width: 1
91 | TextInput{
92 | id: trackEdit
93 | anchors.verticalCenter: parent.verticalCenter
94 | anchors.left: parent.left
95 | anchors.leftMargin: 5
96 | width: parent.width - 10
97 | text: name
98 | color: "#000"
99 | font.pixelSize: 13
100 | font.bold: false
101 | font.family: "Arial, Helvetica, sans-serif"
102 | selectByMouse: true
103 | selectionColor: "#444499"
104 | onActiveFocusChanged: {
105 | if ( !activeFocus ){
106 | name = text
107 | trackEditBox.visible = false
108 | }
109 | }
110 | Keys.onReturnPressed: {
111 | name = text
112 | event.accepted = true
113 | trackEditBox.visible = false
114 | }
115 | Keys.onEnterPressed: {
116 | name = text
117 | event.accepted = true
118 | trackEditBox.visible = false
119 | }
120 |
121 | Keys.onEscapePressed: {
122 | text = name
123 | event.accepted = true
124 | trackEditBox.visible = false
125 | }
126 | MouseArea{
127 | anchors.fill: parent
128 | acceptedButtons: Qt.NoButton
129 | cursorShape: Qt.IBeamCursor
130 | }
131 | }
132 | }
133 | }
134 | }
135 |
136 | MouseArea{
137 | anchors.fill: parent
138 | onWheel: {
139 | wheel.accepted = true
140 | var newContentY = ganttView.contentY -= wheel.angleDelta.y / 6
141 | if ( newContentY > ganttView.contentHeight - ganttView.height )
142 | ganttView.contentY = ganttView.contentHeight - ganttView.height
143 | else if ( newContentY < 0 )
144 | ganttView.contentY = 0
145 | else
146 | ganttView.contentY = newContentY
147 | }
148 | onClicked: mouse.accepted = false;
149 | onPressed: mouse.accepted = false;
150 | onReleased: mouse.accepted = false
151 | onDoubleClicked: mouse.accepted = false;
152 | onPositionChanged: mouse.accepted = false;
153 | onPressAndHold: mouse.accepted = false;
154 | }
155 | }
156 | }
157 |
158 | Rectangle{
159 | height: parent.height
160 | width: parent.width - 152
161 | x: 152
162 |
163 | ScrollView{
164 | id: mainScroll
165 | anchors.fill: parent
166 |
167 | ListView{
168 | id: ganttView
169 |
170 | height: parent.height
171 | contentWidth: ganttModelList.contentWidth
172 | model: ganttModelList
173 | delegate: Rectangle{
174 | height: 40
175 | width: ganttLine.width
176 | color: "#fff"
177 |
178 | Rectangle{
179 | height: 38
180 | width: ganttLine.width
181 | color: "#ccc"
182 | GanttLine{
183 | id: ganttLine
184 | color: "#ccc"
185 | height: 24
186 | anchors.centerIn: parent
187 | viewportX: ganttView.contentX / ganttLine.zoomScale
188 | viewportWidth: ganttView.width / ganttLine.zoomScale
189 | model: ganttModel
190 | onEditItem: {
191 | ganttEditWindow.ganttItem = item
192 | ganttEditWindow.visible = true
193 | }
194 | }
195 | }
196 | }
197 | }
198 | }
199 | }
200 |
201 |
202 | GanttEditWindow{
203 | id: ganttEditWindow
204 | }
205 | GanttHelpWindow{
206 | id: ganttHelpWindow
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/doc/implement-qabstractrangemodel.md:
--------------------------------------------------------------------------------
1 | # Implementing the QAbstractRangeModel
2 |
3 | To implement the QAbstractRangeModel, we must first understand how the QRangeView and QAbstractRangeModel
4 | communicate with each other. Since items are loaded according to their position, the QRangeView doesn't
5 | need to know the number of items within the model, nor their indexes. This means that the model can store
6 | items in linked-lists, trees or other container types as long as it can manage to find items between specific
7 | ranges. The boundaries of the model are configured by the ```contentLength``` property of the model.
8 |
9 | Since item indexes are not used, identification of items between the model and the view is done
10 | through an items position, length and relative index. The relative index is used in case 2 items with the
11 | same position and length are present, and refers to the order in which the item was received from the model.
12 | The following table provides an example:
13 |
14 | Position | Length | Relative Index
15 | -------- | ------ | --------------
16 | 10 | 5 | 0
17 | 15 | 5 | 0
18 | 15 | 5 | 1
19 | 20 | 5 | 0
20 |
21 | The second and third items are differentiated by their relative index, which helps the view identify a specific
22 | item for the model.
23 |
24 |
25 | ## Read-only model
26 |
27 | For a **read-only model** the following methods are required:
28 |
29 | * the ```contentWidth``` **property** needs to be set so the view knows the width of the model:
30 |
31 | ```cpp
32 | CustomModel::CustomModel(QObject* parent = 0)
33 | : QAbstractRangeModel(parent)
34 | {
35 | setContentWidth(1000);
36 | }
37 | ```
38 |
39 | * the ```roleNames()``` function is implemented to map item data. It doesn't include the item position and length
40 | properties, which are already defined in the QAbstractRangeModel. This functionality provides the same means of
41 | accessing data as the QAbstractItemModel:
42 |
43 | ```cpp
44 | // in our header file, we define the Role enum
45 |
46 | class CustomModel : public QAbstractRangeModel{
47 | // ...
48 | public:
49 | // custom roles are next in line after the position and length role
50 |
51 | enum Role{
52 | modelData = QAbstractRangeModel::LengthRole + 1
53 | };
54 | // ...
55 | QHash roleNames() const;
56 | // ...
57 | }
58 | ```
59 |
60 | ```cpp
61 | // Then we map our enum to property names
62 |
63 | QHash CustomModel::roleNames() const{
64 | QHash roles;
65 | roles[QGanttModel::ModelData] = "modelData";
66 | return roles;
67 | }
68 | ```
69 |
70 | * ```dataBetween(startPosition, endPosition)``` - returns an iterable set of items to display between startPosition and endPosition
71 | Items need to be ordered in ascending order according to their position and length. The items are returned in the form
72 | of a ```QAbstractRangeModelIterator```. The iterator is called by the view and iterates through the given items, and provides the
73 | data for the view from the set ```roleNames()```. The iterator will be deleted by the view once it reaches the end of the model.
74 |
75 | ```cpp
76 | QAbstractRangeModelIterator* CustomModel::dataBetween(qint64 startPosition, qint64 endPosition){
77 | // ItemIterator is used as an example, and will be used only internally by the CustomModelIterator to iterate items. This can be
78 | // an iterator for any container type defined by the user.
79 | ItemIterator start = itemIteratorFromPosition(startPosition);
80 | ItemIterator end = itemIteratorFromPosition(endPosition);
81 | return new CustomModelIterator(start, end);
82 | }
83 | ```
84 |
85 | Where a ```CustomModelIterator``` implements ```isEnd()```, ```nextItem()``` and ```data(int role)``` from
86 | ```QAbstractRangeModelIterator``` :
87 |
88 | ```cpp
89 | class CustomModelIterator : public QAbstractRangeModelIterator{
90 | CustomModelIterator(ItemIterator current, ItemIterator end) : m_current(current), m_end(end){}
91 |
92 | bool isEnd() const{ return m_start == m_end; }
93 | void nextItem(){ ++m_current; }
94 |
95 | QVariant data(int role = 0){
96 | if ( role == CustomModel::PositionRole ) return m_current->position;
97 | else if ( role == CustomModel::LengthRole) return m_current->length;
98 | else if ( role == CustomModel::ModelData) return m_current->data;
99 | else return QVariant();
100 | }
101 |
102 | private:
103 | ItemIterator m_start;
104 | ItemIterator m_end;
105 | }
106 | ```
107 |
108 |
109 | ## Writable model
110 |
111 | To make the model **writable**, an extra set of methods requires implementation:
112 |
113 | * the ```setItemPosition()``` function is called when the view requests an item to change its position. The item is
114 | searched for within the model, assigned a new position, then the view is notified through the
115 | ```itemCoordinatesChanged()``` signal:
116 |
117 | ```cpp
118 | void CustomModel::setItemPosition(qint64 itemPosition, qint64 itemLength, int indexOffset, qint64 itemNewPosition){
119 | ItemIterator itemIt = findItem(itemPosition, itemLength, indexOffset);
120 | itemIt->position = itemNewPosition;
121 |
122 | // update our container to have it sorted, here we simply place the item to its new index
123 | itemIt = sortItem(itemIt);
124 |
125 | CustomModelIterator* modelIt = new CustomModelIterator(itemIt, itemIt + 1);
126 |
127 | // I have considered sortItem to always position the item in front of items with the same position and length,
128 | // making its relativeIndex = 0
129 | int newRelativeIndex = 0;
130 | emit ItemCoordinatesChanged(itemPosition, itemLength, indexOffset, modelIt, newRelativeIndex);
131 | }
132 | ```
133 |
134 | * ```setItemLength()``` behaves the same as ```setItemPosition()```
135 |
136 | ```cpp
137 | void CustomModel::setItemLength(qint64 itemPosition, qint64 itemLength, int indexOffset, qint64 itemNewLength){
138 | ItemIterator itemIt = findItem(itemPosition, itemLength, indexOffset);
139 | itemIt->length = itemNewLength;
140 | itemIt = sortItem(itemIt);
141 | CustomModelIterator* modelIt = new CustomModelIterator(itemIt, itemIt + 1);
142 |
143 | int newRelativeIndex = 0;
144 | emit ItemCoordinatesChanged(itemPosition, itemLength, indexOffset, modelIt, newRelativeIndex);
145 | }
146 | ```
147 |
148 | * ```setItemData()``` sets custom data to the model from the given role names
149 |
150 | ```cpp
151 | void CustomModel::setItemData(
152 | qint64 itemPosition,
153 | qint64 itemLength,
154 | int indexOffset,
155 | int role,
156 | const QVariant& value)
157 | {
158 | ItemIterator itemIt = findItem(itemPosition, itemLength, indexOffset);
159 | if ( role == CustomModel::ModelData ){
160 | CustomModelIterator* modelIt = new CustomModelIterator(itemIt, itemIt + 1);
161 | itemIt->setData(value.toString());
162 | emit itemsDataChanged(modelIt, indexOffset, QList() << CustomModel::ModelData)
163 | }
164 | }
165 | ```
166 |
167 | * ```insertItem(position, length)``` - inserts an item in the model at the specified position and length.
168 | The view is notified by calling ```beginDataChange(startPosition, endPosition)``` and
169 | ```endDataChange()``` methods:
170 |
171 | ```cpp
172 | void CustomModel::insertItem(qint64 position, qint64 length){
173 | beginDataChange(position, position + length);
174 | insertItem(new CustomModelItem(position, length));
175 | endDataChange();
176 | }
177 | ```
178 |
179 | * removeItem(position, length, relativeIndex) - remove the item at the specified coordinates. The view is notified
180 | through the same 2 methods as the insertion:
181 |
182 | ```cpp
183 | void CustomModel::removeItem(qint64 position, qint64 length, qint64 relativeIndex){
184 | ItemIterator itemIt = findItem(position, length, relativeIndex);
185 | if (itemIt != NULL){
186 | beginDataChange(position, position + length);
187 | removeItem(itemIt);
188 | endDataChange();
189 | }
190 | }
191 | ```
192 |
193 | * The ```beginDataChange(startPosition, endPosition)``` and ```endDataChange()``` are used whenever the
194 | model wants to notify the view over a set of items that have changed between those positions. Changes
195 | to the whole model can be notified by signalling a change between 0 and the ```contentWidth``` of the model:
196 |
197 | ```cpp
198 | void CustomModel::resetModel(){
199 | beginDataChange(0, contentWidth());
200 | clearData();
201 | endDataChange();
202 | }
203 | ```
204 |
205 | ## Example
206 |
207 | The default ```QGanttModel``` provided in the library is an implementation of a ```QAbstractRangeModel```.
208 |
--------------------------------------------------------------------------------
/tests/qviewportbenchmarktest.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qviewportbenchmarktest.h"
18 | #include "qrangeview.h"
19 | #include "qganttmodel.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 | #include
30 |
31 | Q_TEST_RUNNER_REGISTER(QViewportBenchmarkTest);
32 |
33 | // QViewportBenchmarkTestData
34 | // --------------------------
35 |
36 | class QViewportBenchmarkTestData{
37 | public:
38 | QViewportBenchmarkTestData(const char* delegateData);
39 | ~QViewportBenchmarkTestData();
40 |
41 | QQmlEngine* engine;
42 | QQmlContext* context;
43 | QQmlComponent* viewDelegate;
44 | QQuickItem* container;
45 | QGanttModel* model;
46 | QRangeView* view;
47 |
48 | static const char* MINIMAL_DELEGATE;
49 | static const char* COMPLEX_DELEGATE;
50 |
51 | };
52 |
53 | const char* QViewportBenchmarkTestData::MINIMAL_DELEGATE =
54 | "import QtQuick 2.1\n"
55 | "Rectangle{\n"
56 | " x:position\n "
57 | " width:length\n"
58 | "}\n";
59 |
60 | const char* QViewportBenchmarkTestData::COMPLEX_DELEGATE =
61 | "import QtQuick 2.2\n"
62 | "Rectangle{\n"
63 | " id: ganttDelegate\n"
64 | " width: length \n"
65 | " height: 20\n"
66 | " clip: true\n"
67 | " x: position * 1\n"
68 | " Rectangle{\n"
69 | " id: stretchLeft\n"
70 | " anchors.left: parent.left\n"
71 | " anchors.top: parent.top\n"
72 | " width : 2\n"
73 | " height : parent.height\n"
74 | " color: \"#fff\"\n"
75 | " MouseArea{\n"
76 | " anchors.fill: parent\n"
77 | " cursorShape: Qt.SizeHorCursor\n"
78 | " property int oldMouseX\n"
79 | " onPressed: { oldMouseX = mouseX }\n"
80 | " onPositionChanged: {\n"
81 | " if ( pressed ){\n"
82 | " var newWidth = ganttDelegate.width + oldMouseX - mouseX\n"
83 | " var newX = ganttDelegate.x + mouseX - oldMouseX\n"
84 | " var newLength = Math.round(newWidth / 1)\n"
85 | " if ( newLength > 0 && newX + newWidth < rangeView.width ){\n"
86 | " if ( newX > 0 ){\n"
87 | " ganttDelegate.width = newWidth\n"
88 | " ganttDelegate.x = newX\n"
89 | " } else {\n"
90 | " ganttDelegate.width = ganttDelegate.x + ganttDelegate.width\n"
91 | " ganttDelegate.x = 0\n"
92 | " }\n"
93 | " } else {\n"
94 | " ganttDelegate.x =\n"
95 | " ganttDelegate.x + ganttDelegate.width - (1 * 1)\n"
96 | " ganttDelegate.width = 1 * 1\n"
97 | " }\n"
98 | " }\n"
99 | " }\n"
100 | " }\n"
101 | " }\n"
102 | " Rectangle{\n"
103 | " id: centerMove\n"
104 | " anchors.left: parent.left\n"
105 | " anchors.leftMargin: 2\n"
106 | " anchors.right: parent.right\n"
107 | " anchors.rightMargin: 2\n"
108 | " width : parent.width\n"
109 | " height : parent.height\n"
110 | " color : parent.color\n"
111 | " MouseArea{\n"
112 | " anchors.fill: parent\n"
113 | " cursorShape: Qt.SizeAllCursor\n"
114 | " onPressed: { ganttDelegate.forceActiveFocus() }\n"
115 | " }\n"
116 | " }\n"
117 | " Rectangle{\n"
118 | " id: strechRight\n"
119 | " anchors.right: parent.right\n"
120 | " anchors.top: parent.top\n"
121 | " width : 2\n"
122 | " height : parent.height\n"
123 | " color: \"#fff\"\n"
124 | " MouseArea{\n"
125 | " anchors.fill: parent\n"
126 | " cursorShape: Qt.SizeHorCursor\n"
127 | " property int oldMouseX\n"
128 | " onPressed: oldMouseX = mouseX\n"
129 | " onPositionChanged: {\n"
130 | " if ( pressed ){\n"
131 | " var newWidth = ganttDelegate.width + (mouseX - oldMouseX)\n"
132 | " var newLength = Math.round(newWidth / 1)\n"
133 | " if ( newLength > 0 ){\n"
134 | " if ( position + newLength < root.ganttLength)\n"
135 | " ganttDelegate.width = newWidth\n"
136 | " else\n"
137 | " ganttDelegate.width = (root.ganttLength - position) * 1\n"
138 | " } else\n"
139 | " ganttDelegate.width = 1 * 1\n"
140 | " }\n"
141 | " }\n"
142 | " }\n"
143 | " }\n"
144 | "}\n";
145 |
146 | QViewportBenchmarkTestData::QViewportBenchmarkTestData(const char* delegateData)
147 | : engine(new QQmlEngine)
148 | , context(new QQmlContext(engine))
149 | , viewDelegate(new QQmlComponent(engine))
150 | , container(new QQuickItem)
151 | , model(new QGanttModel)
152 | , view(new QRangeView(container))
153 | {
154 | engine->setContextForObject(viewDelegate, context);
155 | viewDelegate->setData(delegateData, QUrl());
156 |
157 | view->setDelegate(viewDelegate);
158 | view->setModel(model);
159 | }
160 |
161 | QViewportBenchmarkTestData::~QViewportBenchmarkTestData(){
162 | delete container;
163 | delete viewDelegate;
164 | delete engine;
165 | delete context;
166 | delete model;
167 | }
168 |
169 |
170 | // QViewportBenchmarkTest
171 | // ----------------------
172 |
173 | QViewportBenchmarkTest::QViewportBenchmarkTest(QObject* parent) : QObject(parent){
174 |
175 | }
176 |
177 | QViewportBenchmarkTest::~QViewportBenchmarkTest(){
178 |
179 | }
180 |
181 | void QViewportBenchmarkTest::initTestCase(){
182 | }
183 |
184 | void QViewportBenchmarkTest::minimalDelegateInsertionTest(){
185 | QViewportBenchmarkTestData data(QViewportBenchmarkTestData::MINIMAL_DELEGATE);
186 | data.model->setContentWidth(CONTENT_WIDTH);
187 | data.view->setViewportWidth(VIEWPORT_WIDTH);
188 | data.view->setViewportX(0);
189 |
190 | QBENCHMARK_ONCE{
191 | for ( int i = 0; i < CONTENT_WIDTH - ITEM_WIDTH; i += ITEM_WIDTH + 1 )
192 | data.model->insertItem(i, 1);
193 | }
194 | }
195 |
196 | void QViewportBenchmarkTest::minimalDelegateViewTest(){
197 | QViewportBenchmarkTestData data(QViewportBenchmarkTestData::MINIMAL_DELEGATE);
198 | data.model->setContentWidth(CONTENT_WIDTH);
199 | data.view->setViewportWidth(VIEWPORT_WIDTH);
200 | data.view->setViewportX(0);
201 | for ( int i = 0; i < CONTENT_WIDTH - ITEM_WIDTH; i += ITEM_WIDTH + 1 )
202 | data.model->insertItem(i, 1);
203 |
204 | QBENCHMARK_ONCE{
205 | for ( int i = 0; i < SCROLL_ITERATIONS; ++i )
206 | data.view->setViewportX(i);
207 | }
208 | }
209 |
210 | void QViewportBenchmarkTest::complexDelegateInsertionTest(){
211 | QViewportBenchmarkTestData data(QViewportBenchmarkTestData::COMPLEX_DELEGATE);
212 |
213 | data.model->setContentWidth(CONTENT_WIDTH);
214 | data.view->setViewportWidth(VIEWPORT_WIDTH);
215 | data.view->setViewportX(0);
216 |
217 | QBENCHMARK_ONCE{
218 | for ( int i = 0; i < CONTENT_WIDTH - ITEM_WIDTH; i += ITEM_WIDTH + 1 )
219 | data.model->insertItem(i, 1);
220 | }
221 | }
222 |
223 | void QViewportBenchmarkTest::complexDelegateViewTest(){
224 | QViewportBenchmarkTestData data(QViewportBenchmarkTestData::COMPLEX_DELEGATE);
225 | data.model->setContentWidth(CONTENT_WIDTH);
226 | data.view->setViewportWidth(VIEWPORT_WIDTH);
227 | data.view->setViewportX(0);
228 | for ( int i = 0; i < CONTENT_WIDTH - ITEM_WIDTH; i += ITEM_WIDTH + 1 )
229 | data.model->insertItem(i, 1);
230 |
231 | QBENCHMARK_ONCE{
232 | for ( int i = 0; i < SCROLL_ITERATIONS; ++i )
233 | data.view->setViewportX(i);
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/tests/qrangeviewtest.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qrangeviewtest.h"
18 | #include "qganttmodel.h"
19 | #include "qrangeview.h"
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | Q_TEST_RUNNER_REGISTER(QRangeViewTest);
29 |
30 | QRangeViewTest::QRangeViewTest(QObject *parent)
31 | : QObject(parent)
32 | , m_engine(0)
33 | , m_context(0)
34 | , m_viewDelegate(0)
35 | {
36 | }
37 |
38 | QRangeViewTest::~QRangeViewTest(){
39 | }
40 |
41 | void QRangeViewTest::initTestCase(){
42 | m_engine = new QQmlEngine;
43 | m_context = new QQmlContext(m_engine);
44 | m_viewDelegate = new QQmlComponent(m_engine);
45 | m_container = new QQuickItem;
46 |
47 | m_engine->setContextForObject(m_viewDelegate, m_context);
48 | m_viewDelegate->setData("import QtQuick 2.1\nRectangle{x:position; width:length}", QUrl());
49 | }
50 |
51 | void QRangeViewTest::cleanupTestCase(){
52 | delete m_container;
53 | delete m_viewDelegate;
54 | delete m_engine;
55 | delete m_context;
56 | }
57 |
58 | void QRangeViewTest::lookupTest(){
59 | QRangeView* view = createView();
60 |
61 | view->model()->setContentWidth(200);
62 | for ( int i = 10; i < 200; i += 10 )
63 | view->model()->insertItem(i, 10);
64 | view->setViewportWidth(100);
65 |
66 | view->setViewportX(0);
67 | QCOMPARE(m_container->childItems().size(), 10);
68 |
69 | view->setViewportX(100);
70 | QCOMPARE(m_container->childItems().size(), 11);
71 |
72 | view->setViewportX(150);
73 | QCOMPARE(m_container->childItems().size(), 6);
74 |
75 | view->setViewportWidth(15);
76 | QCOMPARE(m_container->childItems().size(), 3);
77 |
78 | view->setViewportWidth(10);
79 | QCOMPARE(m_container->childItems().size(), 2);
80 |
81 | view->setViewportWidth(11);
82 | QCOMPARE(m_container->childItems().size(), 3);
83 |
84 | view->setViewportX(159);
85 | QCOMPARE(m_container->childItems().size(), 3);
86 |
87 | delete view->model();
88 | delete view;
89 | }
90 |
91 | void QRangeViewTest::edgeLookupTest(){
92 | QRangeView* view = createView();
93 |
94 | view->model()->setContentWidth(200);
95 | view->setViewportWidth(100);
96 |
97 | view->model()->insertItem(10, 30);
98 | view->model()->insertItem(5, 40);
99 | view->model()->insertItem(10, 40);
100 |
101 | view->setViewportX(60);
102 | QCOMPARE(m_container->childItems().size(), 1);
103 | view->setViewportX(45);
104 | QCOMPARE(m_container->childItems().size(), 2);
105 | view->setViewportX(0);
106 | QCOMPARE(m_container->childItems().size(), 4);
107 |
108 | view->model()->insertItem(20, 10);
109 | QCOMPARE(m_container->childItems().size(), 5);
110 |
111 | // keep all items, due to the first segment (5, 40) that covers the rest
112 | view->setViewportX(40);
113 | QCOMPARE(m_container->childItems().size(), 5);
114 |
115 | delete view->model();
116 | delete view;
117 | }
118 |
119 | void QRangeViewTest::modelItemChangeTest(){
120 | QRangeView* view = createView();
121 |
122 | view->model()->setContentWidth(200);
123 | for ( int i = 10; i < 200; i += 10 )
124 | view->model()->insertItem(i, 10);
125 |
126 | view->setViewportWidth(100);
127 | view->setViewportX(0);
128 | QCOMPARE(m_container->childItems().size(), 10);
129 |
130 | view->model()->removeItem(10, 10, 0);
131 | QCOMPARE(m_container->childItems().size(), 9);
132 |
133 | view->model()->removeItem(20, 10, 0);
134 | view->model()->removeItem(30, 10, 0);
135 | view->model()->removeItem(40, 10, 0);
136 | view->model()->removeItem(50, 10, 0);
137 | QCOMPARE(m_container->childItems().size(), 5);
138 |
139 | view->model()->insertItem(33, 5);
140 | view->model()->insertItem(99, 10);
141 | view->model()->insertItem(99, 10);
142 | view->model()->insertItem(99, 10);
143 | view->model()->insertItem(98, 10);
144 | QCOMPARE(m_container->childItems().size(), 10);
145 |
146 | view->model()->removeItem(99, 10, 1);
147 | QCOMPARE(m_container->childItems().size(), 9);
148 |
149 | view->model()->removeItem(98, 10, 0);
150 | QCOMPARE(m_container->childItems().size(), 8);
151 |
152 | view->setViewportX(100);
153 | QCOMPARE(m_container->childItems().size(), 13);
154 |
155 | view->model()->insertItem(99, 1);
156 | QCOMPARE(m_container->childItems().size(), 13);
157 |
158 | delete view->model();
159 | delete view;
160 | }
161 |
162 | void QRangeViewTest::modelItemPositionChangeTest(){
163 | QRangeView* view = createView();
164 |
165 | view->model()->setContentWidth(200);
166 | for ( int i = 10; i < 200; i += 10 )
167 | view->model()->insertItem(i, 10);
168 |
169 | view->setViewportWidth(100);
170 | view->setViewportX(0);
171 | QCOMPARE(m_container->childItems().size(), 10);
172 |
173 | view->model()->setItemPosition(10, 10, 0, 100);
174 | QCOMPARE(m_container->childItems().size(), 9);
175 |
176 | view->model()->setItemPosition(150, 10, 0, 99);
177 | QCOMPARE(m_container->childItems().size(), 10);
178 |
179 | view->model()->insertItem(99, 10);
180 | view->model()->insertItem(99, 10);
181 | QCOMPARE(m_container->childItems().size(), 12);
182 |
183 | view->model()->setItemPosition(99, 10, 2, 101);
184 | QCOMPARE(m_container->childItems().size(), 11);
185 |
186 | view->model()->setItemPosition(101, 10, 0, 99);
187 | QCOMPARE(m_container->childItems().size(), 12);
188 |
189 | delete view->model();
190 | delete view;
191 | }
192 |
193 | void QRangeViewTest::modelItemLengthChangeTest(){
194 | QRangeView* view = createView();
195 |
196 | view->model()->setContentWidth(200);
197 | for ( int i = 10; i < 200; i += 10 )
198 | view->model()->insertItem(i, 10);
199 |
200 | view->setViewportWidth(100);
201 | view->setViewportX(15);
202 | QCOMPARE(m_container->childItems().size(), 12);
203 |
204 | view->model()->setItemLength(10, 10, 0, 5);
205 | QCOMPARE(m_container->childItems().size(), 11);
206 |
207 | view->model()->setItemLength(20, 10, 0, 5);
208 | QCOMPARE(m_container->childItems().size(), 11);
209 |
210 | view->model()->insertItem(5, 10);
211 | QCOMPARE(m_container->childItems().size(), 11);
212 |
213 | view->model()->setItemLength(5, 10, 0, 15);
214 | QCOMPARE(m_container->childItems().size(), 12);
215 |
216 | delete view->model();
217 | delete view;
218 | }
219 |
220 | void QRangeViewTest::modelItemDataChangeTest(){
221 | QRangeView* view = createView();
222 |
223 | view->model()->setContentWidth(200);
224 | for ( int i = 10; i < 200; i += 10 )
225 | view->model()->insertItem(i, 10);
226 |
227 | view->setViewportWidth(100);
228 | view->setViewportX(0);
229 |
230 | QCOMPARE(m_container->childItems().size(), 10);
231 |
232 | view->setDataViaDelegate(m_container->childItems()[2], "modelData", "200");
233 | QCOMPARE(getModelData(view, 30, 31, QGanttModel::modelData), QVariant("200"));
234 | QCOMPARE(qmlContext(m_container->childItems()[2])->contextProperty("modelData"), QVariant("200"));
235 |
236 | view->setDataViaDelegate(m_container->childItems()[1], "modelData", "3300");
237 | QCOMPARE(getModelData(view, 20, 21, QGanttModel::modelData), QVariant("3300"));
238 | QCOMPARE(qmlContext(m_container->childItems()[1])->contextProperty("modelData"), QVariant("3300"));
239 |
240 | view->setDataViaDelegate(m_container->childItems()[9], "modelData", "100");
241 | QCOMPARE(getModelData(view, 100, 101, QGanttModel::modelData), QVariant("100"));
242 | QCOMPARE(qmlContext(m_container->childItems()[9])->contextProperty("modelData"), QVariant("100"));
243 |
244 | delete view->model();
245 | delete view;
246 | }
247 |
248 | QRangeView *QRangeViewTest::createView(){
249 | QRangeView* view = new QRangeView(m_container);
250 | view->setDelegate(m_viewDelegate);
251 | view->setModel(new QGanttModel);
252 | return view;
253 | }
254 |
255 | QVariant QRangeViewTest::getModelData(QRangeView* view, qint64 startPos, qint64 endPos, int role){
256 | QAbstractRangeModelIterator* modelIt = view->model()->dataBetween(startPos, endPos);
257 | if ( modelIt->isEnd() )
258 | return QVariant();
259 | QVariant value = modelIt->data(role);
260 | delete modelIt;
261 | return value;
262 | }
263 |
--------------------------------------------------------------------------------
/lib/qganttmodel.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qganttmodel.h"
18 | #include "qganttmodelitem.h"
19 | #include
20 |
21 | // QGanttModelPrivate
22 | // ----------------------------------------------------------------------------
23 |
24 | class QGanttModelPrivate{
25 | public:
26 | QGanttModelPrivate() : lastChange(0), itemDataFactory(0){}
27 | ~QGanttModelPrivate();
28 |
29 | class QGanttModelChange{
30 | public:
31 | qint64 startPosition;
32 | qint64 endPosition;
33 | };
34 |
35 | QList items;
36 | QGanttModelChange* lastChange;
37 | QGanttModelChange* change(qint64 startPosition, qint64 endPosition);
38 |
39 | QGanttModel::ItemDataFactoryFunction itemDataFactory;
40 |
41 | int searchFirstIndex(qint64 position);
42 | int searchFirstIndex(qint64 position, qint64 length);
43 | };
44 |
45 | QGanttModelPrivate::~QGanttModelPrivate(){
46 | for (QList::iterator it = items.begin(); it != items.end(); ++it ){
47 | delete *it;
48 | }
49 | items.clear();
50 | }
51 |
52 | QGanttModelPrivate::QGanttModelChange* QGanttModelPrivate::change(qint64 startPosition, qint64 endPosition){
53 | QGanttModelChange* change = new QGanttModelChange;
54 | change->startPosition = startPosition;
55 | change->endPosition = endPosition;
56 | return change;
57 | }
58 |
59 | int QGanttModelPrivate::searchFirstIndex(qint64 position){
60 | if ( items.size() == 0 )
61 | return 0;
62 | if ( items.last()->position() < position )
63 | items.size();
64 |
65 | int first = 0, last = items.size() - 1, middle = (first + last) / 2;
66 |
67 | while( first <= last ){
68 | if ( items[middle]->position() < position ){
69 | first = middle + 1;
70 | } else {
71 | last = middle - 1;
72 | }
73 |
74 | middle = (first + last) / 2;
75 | }
76 |
77 | if ( items[middle]->position() < position )
78 | return middle + 1;
79 | else
80 | return middle;
81 | }
82 |
83 | int QGanttModelPrivate::searchFirstIndex(qint64 position, qint64 length){
84 | int index = searchFirstIndex(position);
85 | while ( index < items.size() ){
86 | if ( items[index]->position() != position )
87 | return items.size();
88 | if ( items[index]->length() == length )
89 | return index;
90 | ++index;
91 | }
92 | return items.size();
93 | }
94 |
95 | // QGanttModel
96 | // ----------------------------------------------------------------------------
97 |
98 | QGanttModel::QGanttModel(QObject *parent)
99 | : QAbstractRangeModel(parent)
100 | , d_ptr(new QGanttModelPrivate)
101 | {
102 | }
103 |
104 | QGanttModel::~QGanttModel(){
105 | delete d_ptr;
106 | }
107 |
108 | QAbstractRangeModelIterator* QGanttModel::dataBetween(qint64 startPosition, qint64 endPosition){
109 | Q_D(QGanttModel);
110 |
111 | int startIndex = d->searchFirstIndex(startPosition);
112 | while ( startIndex > 0 ){
113 | --startIndex;
114 | if ( d->items[startIndex]->position() + d->items[startIndex]->length() < startPosition ){
115 | ++startIndex;
116 | break;
117 | }
118 | }
119 | if ( startIndex < d->items.size() ){
120 | return new QGanttModelIterator(d->items.begin() + startIndex, d->items.end(), endPosition);
121 | }
122 | return new QGanttModelIterator(d->items.end(), d->items.end(), endPosition);
123 | }
124 |
125 | void QGanttModel::setItemPosition(qint64 itemPosition, qint64 itemLength, int indexOffset, qint64 itemNewPosition){
126 | Q_D(QGanttModel);
127 |
128 | int index = d->searchFirstIndex(itemPosition, itemLength) + indexOffset;
129 | if ( index >= d->items.size() )
130 | return;
131 |
132 | QGanttModelItem* item = d->items[index];
133 | if ( item->position() != itemPosition )
134 | return;
135 |
136 | d->items.takeAt(index);
137 | item->setPosition(itemNewPosition);
138 | int newIndex = insertItem(item);
139 |
140 | QList::iterator itemIt = d->items.begin() + newIndex;
141 | QGanttModelIterator* modelIt = new QGanttModelIterator(itemIt, itemIt + 1, itemNewPosition + 1);
142 | emit itemCoordinatesChanged(itemPosition, itemLength, indexOffset, modelIt, 0);
143 | }
144 |
145 | void QGanttModel::setItemLength(qint64 itemPosition, qint64 itemLength, int indexOffset, qint64 newLength){
146 | Q_D(QGanttModel);
147 | if ( itemLength == newLength )
148 | return;
149 |
150 | int positionIndex = d->searchFirstIndex(itemPosition);
151 | int index = positionIndex;
152 | while ( index < d->items.size() ){
153 | if ( d->items[index]->length() == itemLength )
154 | break;
155 | ++index;
156 | }
157 |
158 | index += indexOffset;
159 | if ( index >= d->items.size() )
160 | return;
161 |
162 | QGanttModelItem* item = d->items[index];
163 | if ( item->position() != itemPosition )
164 | return;
165 |
166 | while ( positionIndex < d->items.size() ){
167 | if ( d->items[positionIndex]->position() == item->position() && d->items[positionIndex]->length() < newLength )
168 | ++positionIndex;
169 | else
170 | break;
171 | }
172 |
173 | if ( index == positionIndex || index == positionIndex + 1 ){
174 | item->setLength(newLength);
175 |
176 | QList::iterator itemIt = d->items.begin() + index;
177 | QGanttModelIterator* modelIt = new QGanttModelIterator(itemIt, itemIt + 1, itemPosition + 1);
178 | emit itemCoordinatesChanged(itemPosition, itemLength, indexOffset, modelIt, indexOffset);
179 | } else {
180 | d->items.takeAt(index);
181 | item->setLength(newLength);
182 | int newIndex = insertItem(item);
183 |
184 | QList::iterator itemIt = d->items.begin() + newIndex;
185 | QGanttModelIterator* modelIt = new QGanttModelIterator(itemIt, itemIt + 1, itemPosition + 1);
186 | emit itemCoordinatesChanged(itemPosition, itemLength, indexOffset, modelIt, 0);
187 | }
188 |
189 | }
190 |
191 | void QGanttModel::setItemData(
192 | qint64 itemPosition,
193 | qint64 itemLength,
194 | int positionIndex,
195 | int role,
196 | const QVariant& value)
197 | {
198 | Q_D(QGanttModel);
199 | int index = d->searchFirstIndex(itemPosition, itemLength) + positionIndex;
200 | if ( index >= d->items.size() )
201 | return;
202 |
203 | QList::iterator itemIt = d->items.begin() + index;
204 | QGanttModelItem* item = *itemIt;
205 | QGanttModelIterator* modelIt = new QGanttModelIterator(itemIt, itemIt + 1, itemPosition + 1);
206 |
207 | if ( role == QGanttModel::modelData ){
208 | item->setData(value.toString());
209 | emit itemsDataChanged(modelIt, positionIndex, QList() << QGanttModel::modelData);
210 | }
211 | }
212 |
213 | QHash QGanttModel::roleNames() const{
214 | QHash roles;
215 | roles[QGanttModel::modelData] = "modelData";
216 | return roles;
217 | }
218 |
219 | int QGanttModel::insertItem(QGanttModelItem* item){
220 | Q_D(QGanttModel);
221 | int index = d->searchFirstIndex(item->position());
222 | while ( index < d->items.size() ){
223 | if ( d->items[index]->position() == item->position() && d->items[index]->length() < item->length() )
224 | ++index;
225 | else {
226 | break;
227 | }
228 | }
229 | d->items.insert(index, item);
230 | return index;
231 | }
232 |
233 | void QGanttModel::setItemDataFactoryFunction(QGanttModel::ItemDataFactoryFunction fp){
234 | Q_D(QGanttModel);
235 | d->itemDataFactory = fp;
236 | }
237 |
238 | void QGanttModel::insertItem(qint64 position, qint64 length){
239 | Q_D(QGanttModel);
240 | QGanttModelItem* item = new QGanttModelItem(position, length);
241 | if ( d->itemDataFactory )
242 | item->setData(d->itemDataFactory());
243 |
244 | beginDataChange(position, position + 1);
245 | insertItem(item);
246 | endDataChange();
247 | }
248 |
249 | void QGanttModel::removeItem(qint64 position, qint64 length, qint64 relativeIndex){
250 | Q_D(QGanttModel);
251 | int index = d->searchFirstIndex(position);
252 | while ( index < d->items.size() ){
253 | if ( d->items[index]->position() != position )
254 | return;
255 |
256 | if ( d->items[index]->length() == length && index + relativeIndex < d->items.size() ){
257 | index += relativeIndex;
258 | if ( d->items[index]->position() == position && d->items[index]->length() == length ){
259 | beginDataChange(position, position + 1);
260 | delete d->items[index];
261 | d->items.removeAt(index);
262 | endDataChange();
263 | return;
264 | }
265 | }
266 | ++index;
267 | }
268 | }
269 |
270 | bool QGanttModelIterator::isEnd() const{
271 | if ( m_currentIt == m_endIt )
272 | return true;
273 | if ( (*m_currentIt)->position() >= m_endPosition )
274 | return true;
275 | return false;
276 | }
277 |
278 | void QGanttModelIterator::nextItem(){
279 | if ( isEnd() )
280 | return;
281 | ++m_currentIt;
282 | }
283 |
284 | QVariant QGanttModelIterator::data(int role){
285 | if ( role == QGanttModel::PositionRole )
286 | return (*m_currentIt)->position();
287 | else if ( role == QGanttModel::LengthRole )
288 | return (*m_currentIt)->length();
289 | else if ( role == QGanttModel::modelData )
290 | return (*m_currentIt)->data();
291 | return QVariant();
292 | }
293 |
--------------------------------------------------------------------------------
/sample/resource/GanttLine.qml:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | import QtQuick 2.2
18 | import QtQuick.Controls 1.1
19 | import Gantt 1.0
20 |
21 | Rectangle{
22 | id: root
23 |
24 | signal editItem(variant item)
25 |
26 | property variant model: GanttModel{}
27 |
28 | property int zoomScale: 1
29 | property int ganttLength: ganttModel.contentWidth
30 |
31 | property alias viewportX: rangeView.viewportX
32 | property alias viewportWidth: rangeView.viewportWidth
33 |
34 | property color elementBorderColor: "#ccc"
35 | property color elementBorderFocusColor: "#fff"
36 |
37 | color: "#ccc"
38 | width: rangeView.width * zoomScale
39 |
40 | RangeView {
41 | id: rangeView
42 |
43 | model: ganttModel
44 | height: root.height
45 |
46 | MouseArea {
47 | anchors.fill: parent
48 | onDoubleClicked: {
49 | var itemPosition = mouse.x - 10 >= 0 ? mouse.x - 10 : 0
50 | if (itemPosition + 20 >= ganttModel.contentWidth)
51 | itemPosition = ganttModel.contentWidth - 20
52 | ganttModel.insertItem(itemPosition, 20)
53 | }
54 | }
55 |
56 | delegate: Component {
57 |
58 | Rectangle {
59 | id: ganttDelegate
60 |
61 | width: length * root.zoomScale
62 | height: rangeView.height
63 |
64 | clip: true
65 | color: modelData.color
66 |
67 | x: position * root.zoomScale
68 |
69 | Keys.onPressed: {
70 | if (event.key === Qt.Key_Delete) {
71 | event.accepted = true
72 | rangeView.removeItemViaDelegate(ganttDelegate)
73 | } else if (event.key === Qt.Key_Left) {
74 |
75 | if (event.modifiers & Qt.ShiftModifier) {
76 | var newLength = length - 1
77 | if (newLength > 0) {
78 | rangeView.setLengthViaDelegate(ganttDelegate,
79 | newLength)
80 | ganttDelegate.width = newLength * root.zoomScale
81 | }
82 | } else {
83 | var newPosition = position - 1
84 | if (newPosition >= 0) {
85 | rangeView.setPositionViaDelegate(ganttDelegate,
86 | newPosition)
87 | ganttDelegate.x = newPosition * root.zoomScale
88 | }
89 | }
90 | event.accepted = true
91 | } else if (event.key === Qt.Key_Right) {
92 |
93 | if (event.modifiers & Qt.ShiftModifier) {
94 | var newLength = length + 1
95 | if (position + length < ganttModel.contentWidth) {
96 | rangeView.setLengthViaDelegate(ganttDelegate,
97 | newLength)
98 | ganttDelegate.width = newLength * root.zoomScale
99 | }
100 | } else {
101 | var newPosition = position + 1
102 | if (position + length < ganttModel.contentWidth) {
103 | rangeView.setPositionViaDelegate(ganttDelegate,
104 | newPosition)
105 | ganttDelegate.x = newPosition * root.zoomScale
106 | }
107 | }
108 |
109 | event.accepted = true
110 | }
111 | }
112 |
113 | Rectangle {
114 | id: stretchLeft
115 | anchors.left: parent.left
116 | anchors.top: parent.top
117 | width: 2
118 | height: parent.height
119 | color: ganttDelegate.activeFocus ? root.elementBorderFocusColor : root.elementBorderColor
120 |
121 | MouseArea {
122 | anchors.fill: parent
123 | cursorShape: Qt.SizeHorCursor
124 |
125 | property int oldMouseX
126 |
127 | onPressed: {
128 | oldMouseX = mouseX
129 | }
130 | onPositionChanged: {
131 | if (pressed) {
132 | var newWidth = ganttDelegate.width + oldMouseX - mouseX
133 | var newX = ganttDelegate.x + mouseX - oldMouseX
134 | var newLength = Math.round(
135 | newWidth / root.zoomScale)
136 | if (newLength > 0
137 | && newX + newWidth < rangeView.width) {
138 | if (newX > 0) {
139 | ganttDelegate.width = newWidth
140 | ganttDelegate.x = newX
141 | } else {
142 | ganttDelegate.width = ganttDelegate.x + ganttDelegate.width
143 | ganttDelegate.x = 0
144 | }
145 | } else {
146 | ganttDelegate.x = ganttDelegate.x
147 | + ganttDelegate.width - (1 * root.zoomScale)
148 | ganttDelegate.width = 1 * root.zoomScale
149 | }
150 | }
151 | }
152 |
153 | onReleased: {
154 | rangeView.setLengthViaDelegate(
155 | ganttDelegate, Math.round(
156 | ganttDelegate.width / root.zoomScale))
157 | rangeView.setPositionViaDelegate(
158 | ganttDelegate, Math.round(
159 | ganttDelegate.x / root.zoomScale))
160 | ganttDelegate.width = length * root.zoomScale
161 | ganttDelegate.x = position * root.zoomScale
162 | }
163 | }
164 | }
165 | Rectangle {
166 | id: centerMove
167 | anchors.left: parent.left
168 | anchors.leftMargin: 2
169 | anchors.right: parent.right
170 | anchors.rightMargin: 2
171 | width: parent.width
172 | height: parent.height
173 | color: parent.color
174 | MouseArea {
175 | anchors.fill: parent
176 | cursorShape: Qt.SizeAllCursor
177 | drag.target: ganttDelegate
178 | drag.axis: Drag.XAxis
179 | drag.minimumX: 0
180 | drag.maximumX: rangeView.width - ganttDelegate.width
181 | onPressed: {
182 | ganttDelegate.forceActiveFocus()
183 | }
184 | onDoubleClicked: {
185 | if (mouse.modifiers & Qt.ControlModifier) {
186 | var leftDistance = root.currentFrame - position
187 | var rightDistance = root.currentFrame - (position + length)
188 | if (Math.abs(rightDistance) > Math.abs(
189 | leftDistance)) {
190 | length -= leftDistance
191 | position += leftDistance
192 | ganttDelegate.width = length * root.zoomScale
193 | ganttDelegate.x = position * root.zoomScale
194 | } else {
195 | length += rightDistance
196 | if (length < 1)
197 | length = 1
198 | ganttDelegate.width = length * root.zoomScale
199 | }
200 | } else {
201 | root.editItem(modelData)
202 | }
203 | }
204 | onReleased: {
205 | rangeView.setPositionViaDelegate(
206 | ganttDelegate, Math.round(
207 | ganttDelegate.x / root.zoomScale))
208 | ganttDelegate.x = position * root.zoomScale
209 | }
210 | }
211 | }
212 | Rectangle {
213 | id: strechRight
214 | anchors.right: parent.right
215 | anchors.top: parent.top
216 | width: 2
217 | height: parent.height
218 | color: ganttDelegate.activeFocus ? root.elementBorderFocusColor : root.elementBorderColor
219 |
220 | MouseArea {
221 | anchors.fill: parent
222 | cursorShape: Qt.SizeHorCursor
223 |
224 | property int oldMouseX
225 |
226 | onPressed: oldMouseX = mouseX
227 | onPositionChanged: {
228 | if (pressed) {
229 | var newWidth = ganttDelegate.width + (mouseX - oldMouseX)
230 | var newLength = Math.round(
231 | newWidth / root.zoomScale)
232 | if (newLength > 0) {
233 | if (position + newLength < root.ganttLength)
234 | ganttDelegate.width = newWidth
235 | else
236 | ganttDelegate.width = (root.ganttLength - position)
237 | * root.zoomScale
238 | } else
239 | ganttDelegate.width = 1 * root.zoomScale
240 | }
241 | }
242 | onReleased: {
243 | rangeView.setLengthViaDelegate(
244 | ganttDelegate, Math.round(
245 | ganttDelegate.width / root.zoomScale))
246 | ganttDelegate.width = length * root.zoomScale
247 | }
248 | }
249 | }
250 |
251 | Text {
252 | anchors.left: parent.left
253 | anchors.leftMargin: 5
254 | anchors.verticalCenter: parent.verticalCenter
255 | color: "#fff"
256 | text: modelData.label
257 | font.family: "sans-serif"
258 | font.pixelSize: 10
259 | }
260 | }
261 | }
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/lib/qrangeview.cpp:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2015-2016 Dinu SV.
4 | ** (contact: mail@dinusv.com)
5 | ** This file is part of QML Gantt library.
6 | **
7 | ** GNU Lesser General Public License Usage
8 | ** This file may be used under the terms of the GNU Lesser
9 | ** General Public License version 3 as published by the Free Software
10 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
11 | ** packaging of this file. Please review the following information to
12 | ** ensure the GNU Lesser General Public License version 3 requirements
13 | ** will be met: https://www.gnu.org/licenses/lgpl.html.
14 | **
15 | ****************************************************************************/
16 |
17 | #include "qrangeview.h"
18 | #include "qabstractrangemodel.h"
19 | #include
20 |
21 | #include
22 |
23 | // QRangeViewItem
24 | // ----------------------------------------------------------------------------
25 |
26 | class QRangeViewItem{
27 | public:
28 | QQmlContext* context;
29 | QQuickItem* delegate;
30 | qint64 position;
31 | qint64 length;
32 |
33 | ~QRangeViewItem();
34 | };
35 |
36 | QRangeViewItem::~QRangeViewItem(){
37 | delegate->setParentItem(0);
38 | delegate->deleteLater();
39 | context->deleteLater();
40 | }
41 |
42 | // QRangeViewPrivate
43 | // ----------------------------------------------------------------------------
44 |
45 | class QRangeViewPrivate{
46 |
47 | public:
48 | QRangeViewPrivate();
49 | ~QRangeViewPrivate();
50 |
51 | // Fields
52 | // ------
53 |
54 | QList items;
55 |
56 | int lastViewportWidth;
57 | int lastViewportX;
58 |
59 | QAbstractRangeModel* model;
60 | QQmlComponent* delegate;
61 | QHash modelRoles;
62 |
63 | // Methods
64 | // -------
65 |
66 | int findItemFrom(qint64 position);
67 | int findItem(qint64 position, qint64 length);
68 | int findItemByDelegate(QQuickItem* delegate);
69 |
70 | int findRelativeIndex(int index);
71 |
72 | void clearItemsInInterval(qint64 positionBegin, qint64 positionEnd);
73 | void clearItems();
74 |
75 | QString listItems();
76 | };
77 |
78 | QRangeViewPrivate::QRangeViewPrivate()
79 | : lastViewportWidth(0)
80 | , lastViewportX(0)
81 | , model(0)
82 | , delegate(0)
83 | {
84 | }
85 |
86 | QRangeViewPrivate::~QRangeViewPrivate(){
87 | clearItems();
88 | }
89 |
90 | int QRangeViewPrivate::findItemFrom(qint64 position){
91 | if ( items.size() == 0 )
92 | return 0;
93 | if ( items.last()->position < position )
94 | return items.size();
95 |
96 | int first = 0, last = items.size() - 1, middle = (first + last) / 2;
97 |
98 | while( first <= last ){
99 | if ( items[middle]->position < position ){
100 | first = middle + 1;
101 | } else {
102 | last = middle - 1;
103 | }
104 |
105 | middle = (first + last) / 2;
106 | }
107 |
108 | if ( items[middle]->position < position )
109 | return middle + 1;
110 | else
111 | return middle;
112 | }
113 |
114 | int QRangeViewPrivate::findItem(qint64 position, qint64 length){
115 | int index = findItemFrom(position);
116 | while ( index < items.size() ){
117 | if ( items[index]->position != position )
118 | return items.size();
119 | if ( items[index]->length == length )
120 | return index;
121 | ++index;
122 | }
123 | return items.size();
124 | }
125 |
126 | int QRangeViewPrivate::findItemByDelegate(QQuickItem* delegate){
127 | int index = findItemFrom(qmlContext(delegate)->contextProperty("position").toLongLong());
128 | while( index != items.size() ){
129 | if ( items[index]->delegate == delegate )
130 | return index;
131 | ++index;
132 | }
133 | return index;
134 | }
135 |
136 | int QRangeViewPrivate::findRelativeIndex(int index){
137 | if ( index >= items.size() )
138 | return 0;
139 |
140 | QRangeViewItem* rangeItem = items[index];
141 | int itemIndex = 0;
142 | while ( index > 0 ){
143 | --index;
144 | if ( items[index]->position == rangeItem->position && items[index]->length == rangeItem->length)
145 | ++itemIndex;
146 | else
147 | break;
148 | }
149 | return itemIndex;
150 | }
151 |
152 | void QRangeViewPrivate::clearItemsInInterval(qint64 positionBegin, qint64 positionEnd){
153 | int endIndex = findItemFrom(positionEnd);
154 | int index = endIndex;
155 | while ( index > 0 ){
156 | --index;
157 | if ( items[index]->position + items[index]->length < positionBegin ){
158 | ++index;
159 | break;
160 | }
161 | delete items[index];
162 | }
163 |
164 | if (index != endIndex)
165 | items.erase(items.begin() + index, items.begin() + endIndex);
166 | }
167 |
168 | void QRangeViewPrivate::clearItems(){
169 | for ( QList::iterator it = items.begin(); it != items.end(); ++it )
170 | delete *it;
171 | items.clear();
172 | }
173 |
174 | QString QRangeViewPrivate::listItems(){
175 | QString base = "";
176 | for ( int i = 0; i < items.size(); ++i ){
177 | if ( i != 0 )
178 | base += " ";
179 | base += "[" + QString::number(items[i]->position) + "," +
180 | QString::number(items[i]->position + items[i]->length) + "]";
181 | }
182 | return base;
183 | }
184 |
185 | // QRangeView
186 | // ----------------------------------------------------------------------------
187 |
188 | QRangeView::QRangeView(QQuickItem *parent)
189 | : QQuickItem(parent)
190 | , d_ptr(new QRangeViewPrivate)
191 | , m_viewportWidth(0)
192 | , m_viewportX(0)
193 | {
194 | setFlag(QQuickItem::ItemHasContents, true);
195 | }
196 |
197 | QRangeView::~QRangeView(){
198 | delete d_ptr;
199 | }
200 |
201 | void QRangeView::modelWidthChanged(){
202 | Q_D(QRangeView);
203 | setImplicitWidth(d->model->contentWidth());
204 | }
205 |
206 | void QRangeView::modelitemCoordinatesChanged(qint64 oldPosition, qint64 oldLength, int relativeIndex,
207 | QAbstractRangeModelIterator *modelIt, int newRelativeIndex)
208 | {
209 | Q_D(QRangeView);
210 |
211 | QRangeViewItem* item = 0;
212 |
213 | if ( modelIt->isEnd() ){
214 | qWarning("Empty model iterator sent by model.");
215 | return;
216 | }
217 |
218 | qint64 newPosition = modelIt->data(QAbstractRangeModel::PositionRole).toLongLong();
219 | qint64 newLength = modelIt->data(QAbstractRangeModel::LengthRole).toLongLong();
220 |
221 | if ( oldPosition < m_viewportX + m_viewportWidth && oldPosition + oldLength > m_viewportX ){
222 | int index = d->findItem(oldPosition, oldLength) + relativeIndex;
223 | if ( index >= d->items.size() ){
224 | qWarning("Failed to find item from given position: %lld, %lld", oldPosition, oldLength);
225 | return;
226 | }
227 | item = d->items.takeAt(index);
228 | }
229 |
230 | if ( newPosition >= m_viewportX + m_viewportWidth || newPosition + newLength <= m_viewportX ){
231 | if (item)
232 | delete item;
233 | } else {
234 | if (!item){
235 | item = new QRangeViewItem;
236 | item->context = new QQmlContext(d->delegate->creationContext(), this);
237 | item->context->setContextProperty("position", newPosition);
238 | item->context->setContextProperty("length", newLength);
239 | for(QHash::iterator rolesIt = d->modelRoles.begin(); rolesIt != d->modelRoles.end(); ++rolesIt){
240 | item->context->setContextProperty(rolesIt.value(), modelIt->data(rolesIt.key()));
241 | }
242 |
243 | item->delegate = qobject_cast(d->delegate->create(item->context));
244 | item->delegate->setParentItem(parentItem());
245 | }
246 |
247 | item->position = newPosition;
248 | item->length = newLength;
249 | item->context->setContextProperty("position", newPosition);
250 | item->context->setContextProperty("length", newLength);
251 |
252 | int newIndex = d->findItemFrom(newPosition);
253 | while ( newIndex < d->items.size() ){
254 | if ( d->items[newIndex]->position == newPosition && d->items[newIndex]->length < newLength )
255 | ++newIndex;
256 | else {
257 | d->items.insert(newIndex + newRelativeIndex, item);
258 | return;
259 | }
260 | }
261 | d->items.insert(newIndex, item);
262 | }
263 | }
264 |
265 | void QRangeView::modelItemDataChanged(QAbstractRangeModelIterator* modelIt, int relativeIndex, QList roles){
266 | Q_D(QRangeView);
267 |
268 | int itemAbsoluteIndex = d->items.size();
269 | if ( !modelIt->isEnd() ){
270 | qint64 position = modelIt->data(QAbstractRangeModel::PositionRole).toLongLong();
271 | qint64 length = modelIt->data(QAbstractRangeModel::LengthRole).toLongLong();
272 | if ( position >= m_viewportX + m_viewportWidth || position + length <= m_viewportX )
273 | return;
274 |
275 | itemAbsoluteIndex = d->findItem(position, length) + relativeIndex;
276 | if ( itemAbsoluteIndex >= d->items.size() ){
277 | qWarning("Failed to find item from given model.");
278 | return;
279 | }
280 |
281 | for ( int i = 0; i < roles.size(); ++i ){
282 | d->items[itemAbsoluteIndex]->context->setContextProperty(
283 | d->modelRoles[roles[i]], modelIt->data(roles[i])
284 | );
285 | }
286 | }
287 | }
288 |
289 | void QRangeView::modelItemsAboutToBeChanged(qint64, qint64){}
290 |
291 | void QRangeView::modelItemsChanged(qint64 beginPosition, qint64 endPosition){
292 | Q_D(QRangeView);
293 |
294 | if ( beginPosition >= m_viewportX + m_viewportWidth || endPosition <= m_viewportX )
295 | return;
296 |
297 | d->clearItemsInInterval(beginPosition, endPosition);
298 |
299 | QAbstractRangeModelIterator* modelIt = d->model->dataBetween(beginPosition, endPosition);
300 |
301 | QList::iterator itemsIt = d->items.end();
302 | if ( !modelIt->isEnd() ){
303 | itemsIt = d->items.begin() + d->findItemFrom(modelIt->data(QAbstractRangeModel::PositionRole).toLongLong());
304 | }
305 |
306 | while ( !modelIt->isEnd() ){
307 | QRangeViewItem* viewItem = new QRangeViewItem;
308 | viewItem->position = modelIt->data(QAbstractRangeModel::PositionRole).toLongLong();
309 | viewItem->length = modelIt->data(QAbstractRangeModel::LengthRole).toLongLong();
310 |
311 | viewItem->context = new QQmlContext(d->delegate->creationContext(), this);
312 | viewItem->context->setContextProperty("position", viewItem->position);
313 | viewItem->context->setContextProperty("length", viewItem->length);
314 |
315 | for(QHash::iterator rolesIt = d->modelRoles.begin(); rolesIt != d->modelRoles.end(); ++rolesIt){
316 | viewItem->context->setContextProperty(rolesIt.value(), modelIt->data(rolesIt.key()));
317 | }
318 |
319 | viewItem->delegate = qobject_cast(d->delegate->create(viewItem->context));
320 | viewItem->delegate->setParentItem(parentItem());
321 |
322 | itemsIt = d->items.insert(itemsIt, viewItem);
323 | ++itemsIt;
324 |
325 | modelIt->nextItem();
326 | }
327 |
328 | delete modelIt;
329 | }
330 |
331 | void QRangeView::setPositionViaDelegate(QQuickItem* item, qint64 position){
332 | Q_D(QRangeView);
333 | int index = d->findItemByDelegate(item);
334 | if ( index == d->items.size() ){
335 | qWarning("Cannot find item by its position: %lld", static_cast(item->x()));
336 | return;
337 | }
338 |
339 | QRangeViewItem* rangeItem = d->items[index];
340 | if ( rangeItem->position == position )
341 | return;
342 |
343 | int relativeIndex = d->findRelativeIndex(index);
344 |
345 | d->model->setItemPosition(rangeItem->position, rangeItem->length, relativeIndex, position);
346 | }
347 |
348 | void QRangeView::setLengthViaDelegate(QQuickItem* item, qint64 newLength){
349 | Q_D(QRangeView);
350 | int index = d->findItemByDelegate(item);
351 | if ( index == d->items.size() ){
352 | qWarning("Cannot find item by its position: %lld", static_cast(item->x()));
353 | return;
354 | }
355 |
356 | QRangeViewItem* rangeItem = d->items[index];
357 | if ( rangeItem->length == newLength )
358 | return;
359 |
360 | int relativeIndex = d->findRelativeIndex(index);
361 |
362 | d->model->setItemLength(rangeItem->position, rangeItem->length, relativeIndex, newLength);
363 |
364 | }
365 |
366 | void QRangeView::setDataViaDelegate(QQuickItem* item, const QString& role, const QVariant& value){
367 | Q_D(QRangeView);
368 | int index = d->findItemByDelegate(item);
369 | if ( index == d->items.size() ){
370 | qWarning("Cannot find item by its position: %lld.", static_cast(item->x()));
371 | return;
372 | }
373 |
374 | QRangeViewItem* rangeItem = d->items[index];
375 | int relativeIndex = d->findRelativeIndex(index);
376 |
377 | d->model->setItemData(
378 | rangeItem->position,
379 | rangeItem->length,
380 | relativeIndex,
381 | d->modelRoles.key(role.toLatin1()),
382 | value
383 | );
384 | }
385 |
386 | void QRangeView::removeItemViaDelegate(QQuickItem *item){
387 | Q_D(QRangeView);
388 | int index = d->findItemByDelegate(item);
389 | if ( index == d->items.size() ){
390 | qWarning("Cannot find item by its position: %lld.", static_cast(item->x()));
391 | return;
392 | }
393 |
394 | QRangeViewItem* rangeItem = d->items[index];
395 | int relativeIndex = d->findRelativeIndex(index);
396 |
397 | d->model->removeItem(rangeItem->position, rangeItem->length, relativeIndex);
398 | }
399 |
400 | void QRangeView::componentComplete(){
401 | regenerateNewContent();
402 | }
403 |
404 | void QRangeView::regenerateNewContent(){
405 | Q_D(QRangeView);
406 | if ( !d->delegate || !d->model )
407 | return;
408 |
409 | if ( m_viewportX > d->lastViewportX ){ // delete left
410 | QList::iterator it = d->items.begin();
411 | while ( it != d->items.end() ){
412 | if ( (*it)->position + (*it)->length > m_viewportX ){
413 | if ( it != d->items.begin() )
414 | d->items.erase(d->items.begin(), it);
415 | break;
416 | }
417 | delete *it;
418 | ++it;
419 | }
420 | if ( it == d->items.end() )
421 | d->items.clear();
422 | } else if ( m_viewportX < d->lastViewportX ){ // append left
423 | QAbstractRangeModelIterator *modelIt = d->model->dataBetween(
424 | m_viewportX,
425 | d->lastViewportX < m_viewportX + m_viewportWidth ? d->lastViewportX : m_viewportX + m_viewportWidth
426 | );
427 |
428 | QList::iterator itemsIt = d->items.end();
429 | if ( !modelIt->isEnd() ){
430 | itemsIt = d->items.begin() + d->findItemFrom(modelIt->data(QAbstractRangeModel::PositionRole).toLongLong());
431 | if ( itemsIt != d->items.begin() && itemsIt != d->items.end() )
432 | --itemsIt;
433 | }
434 |
435 | while ( !modelIt->isEnd() ){
436 |
437 | qint64 position = modelIt->data(QAbstractRangeModel::PositionRole).toLongLong();
438 | qint64 length = modelIt->data(QAbstractRangeModel::LengthRole).toLongLong();
439 | if ( position + length > d->lastViewportX && d->items.size() > 0 ){
440 | if ( d->items.first()->position < position ||
441 | ( d->items.first()->position == position && d->items.first()->length < length ) )
442 | break;
443 | }
444 |
445 | QRangeViewItem* viewItem = new QRangeViewItem;
446 | viewItem->position = position;
447 | viewItem->length = length;
448 |
449 | viewItem->context = new QQmlContext(d->delegate->creationContext(), this);
450 | viewItem->context->setContextProperty("position", position);
451 | viewItem->context->setContextProperty("length", length);
452 |
453 | for(QHash::iterator rolesIt = d->modelRoles.begin(); rolesIt != d->modelRoles.end(); ++rolesIt){
454 | viewItem->context->setContextProperty(rolesIt.value(), modelIt->data(rolesIt.key()));
455 | }
456 |
457 | viewItem->delegate = qobject_cast(d->delegate->create(viewItem->context));
458 | viewItem->delegate->setParentItem(parentItem());
459 |
460 | modelIt->nextItem();
461 |
462 | itemsIt = d->items.insert(itemsIt, viewItem);
463 | ++itemsIt;
464 | }
465 |
466 | delete modelIt;
467 | }
468 |
469 | if ( m_viewportX + m_viewportWidth < d->lastViewportX + d->lastViewportWidth ){ // delete right
470 | QList::iterator it = d->items.end();
471 | while ( it != d->items.begin() ){
472 | --it;
473 | if ( (*it)->position < m_viewportX + m_viewportWidth ){
474 | ++it;
475 | if ( it != d->items.end() )
476 | d->items.erase(it, d->items.end());
477 | break;
478 | }
479 | delete *it;
480 | }
481 | if ( it == d->items.begin() )
482 | d->items.clear();
483 |
484 | } else if ( m_viewportX + m_viewportWidth > d->lastViewportX + d->lastViewportWidth ){ // append right
485 | QAbstractRangeModelIterator* modelIt = d->model->dataBetween(
486 | m_viewportX > d->lastViewportX + d->lastViewportWidth ? m_viewportX : d->lastViewportX + d->lastViewportWidth,
487 | m_viewportX + m_viewportWidth
488 | );
489 |
490 | while ( !modelIt->isEnd() ){
491 | if ( modelIt->data(QAbstractRangeModel::PositionRole).toLongLong() >= d->lastViewportX + d->lastViewportWidth )
492 | break;
493 | modelIt->nextItem();
494 | }
495 |
496 | QList::iterator itemsIt = d->items.end();
497 | if ( !modelIt->isEnd() ){
498 | itemsIt = d->items.begin() + d->findItemFrom(modelIt->data(QAbstractRangeModel::PositionRole).toLongLong());
499 | if ( itemsIt != d->items.begin() && itemsIt != d->items.end() )
500 | --itemsIt;
501 | }
502 |
503 | while ( !modelIt->isEnd() ){
504 |
505 | QRangeViewItem* viewItem = new QRangeViewItem;
506 | viewItem->position = modelIt->data(QAbstractRangeModel::PositionRole).toLongLong();
507 | viewItem->length = modelIt->data(QAbstractRangeModel::LengthRole).toLongLong();
508 |
509 | viewItem->context = new QQmlContext(d->delegate->creationContext(), this);
510 | viewItem->context->setContextProperty("position", viewItem->position);
511 | viewItem->context->setContextProperty("length", viewItem->length);
512 |
513 | for(QHash::iterator rolesIt = d->modelRoles.begin(); rolesIt != d->modelRoles.end(); ++rolesIt){
514 | viewItem->context->setContextProperty(rolesIt.value(), modelIt->data(rolesIt.key()));
515 | }
516 |
517 | viewItem->delegate = qobject_cast(d->delegate->create(viewItem->context));
518 | viewItem->delegate->setParentItem(parentItem());
519 |
520 | modelIt->nextItem();
521 |
522 | itemsIt = d->items.insert(itemsIt, viewItem);
523 | ++itemsIt;
524 | }
525 |
526 | delete modelIt;
527 | }
528 |
529 | d->lastViewportX = m_viewportX;
530 | d->lastViewportWidth = m_viewportWidth;
531 | }
532 |
533 | QQmlComponent*QRangeView::delegate() const{
534 | const Q_D(QRangeView);
535 | return d->delegate;
536 | }
537 |
538 | void QRangeView::setDelegate(QQmlComponent* delegate){
539 | Q_D(QRangeView);
540 | if ( d->delegate != delegate ){
541 | d->delegate = delegate;
542 | emit delegateChanged();
543 | regenerateNewContent();
544 | update();
545 | }
546 | }
547 |
548 | QAbstractRangeModel*QRangeView::model() const{
549 | const Q_D(QRangeView);
550 | return d->model;
551 | }
552 |
553 | void QRangeView::setModel(QAbstractRangeModel* arg){
554 | Q_D(QRangeView);
555 | if (d->model == arg)
556 | return;
557 |
558 | if (d->model)
559 | disconnect(d->model, 0, this, 0);
560 |
561 | d->model = arg;
562 | if ( d->model == 0 )
563 | return;
564 |
565 | d->modelRoles = d->model->roleNames();
566 | setImplicitWidth(d->model->contentWidth());
567 |
568 | connect(d->model, SIGNAL(contentWidthChanged()), this, SLOT(modelWidthChanged()));
569 | connect(d->model, SIGNAL(itemCoordinatesChanged(qint64,qint64,int,QAbstractRangeModelIterator*,int)),
570 | this, SLOT(modelitemCoordinatesChanged(qint64,qint64,int,QAbstractRangeModelIterator*,int)));
571 | connect(d->model, SIGNAL(itemsDataChanged(QAbstractRangeModelIterator*,int, QList)),
572 | this, SLOT(modelItemDataChanged(QAbstractRangeModelIterator*,int, QList)));
573 | connect(d->model, SIGNAL(itemsAboutToBeChanged(qint64,qint64)),
574 | this, SLOT(modelItemsAboutToBeChanged(qint64,qint64)));
575 | connect(d->model, SIGNAL(itemsChanged(qint64,qint64)),
576 | this, SLOT(modelItemsChanged(qint64,qint64)));
577 |
578 | d->lastViewportX = 0;
579 | d->lastViewportWidth = 0;
580 | regenerateNewContent();
581 |
582 | emit modelChanged(arg);
583 | }
584 |
585 |
--------------------------------------------------------------------------------