diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index 0059bd00..a3c489b4 100644 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt @@ -1,83 +1,85 @@ find_package(Qt5 COMPONENTS REQUIRED Core Concurrent Quick Qml WebEngineWidgets Test WebEngine Gui) find_package(KF5Mime 4.87.0 CONFIG REQUIRED) +find_package(KF5CalendarCore CONFIG REQUIRED) find_package(Sink 0.6.0 CONFIG REQUIRED) find_package(KAsync CONFIG REQUIRED) find_package(QGpgme CONFIG REQUIRED) find_package(KF5Codecs CONFIG REQUIRED) find_package(KF5Contacts CONFIG REQUIRED) add_definitions("-Wall -std=c++14 -g") set(CMAKE_CXX_VISIBILITY_PRESET default) include_directories(. domain/mime/mimetreeparser domain/ domain/mime) add_library(kubeframework SHARED settings/settings.cpp domain/maillistmodel.cpp domain/folderlistmodel.cpp + domain/perioddayeventmodel.cpp domain/composercontroller.cpp domain/modeltest.cpp domain/retriever.cpp domain/outboxmodel.cpp domain/identitiesmodel.cpp domain/recepientautocompletionmodel.cpp domain/settings/accountsettings.cpp domain/selector.cpp domain/completer.cpp domain/mouseproxy.cpp domain/contactcontroller.cpp domain/controller.cpp domain/peoplemodel.cpp domain/textdocumenthandler.cpp domain/mime/htmlutils.cpp domain/mime/messageparser.cpp domain/mime/attachmentmodel.cpp domain/mime/partmodel.cpp domain/mime/mailtemplates.cpp domain/mime/mailcrypto.cpp accounts/accountfactory.cpp accounts/accountsmodel.cpp fabric.cpp sinkfabric.cpp kubeimage.cpp clipboardproxy.cpp krecursivefilterproxymodel.cpp webengineprofile.cpp startupcheck.cpp keyring.cpp domainobjectcontroller.cpp extensionmodel.cpp viewhighlighter.cpp ) target_link_libraries(kubeframework sink kube_otp Qt5::Core Qt5::Quick Qt5::Qml Qt5::WebEngineWidgets Qt5::Test Qt5::WebEngine Qt5::Gui KF5::Codecs KF5::Contacts KAsync QGpgme ) install(TARGETS kubeframework DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) add_library(frameworkplugin SHARED frameworkplugin.cpp) target_link_libraries(frameworkplugin kubeframework ) install(TARGETS frameworkplugin DESTINATION ${FRAMEWORK_INSTALL_DIR}) set(BUILD_TESTING ON) add_subdirectory(tests) add_subdirectory(domain/mime/tests) add_subdirectory(domain/mime/mimetreeparser) add_subdirectory(domain/settings/tests) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/framework/src/domain/perioddayeventmodel.cpp b/framework/src/domain/perioddayeventmodel.cpp new file mode 100644 index 00000000..637e5584 --- /dev/null +++ b/framework/src/domain/perioddayeventmodel.cpp @@ -0,0 +1,256 @@ +/* + Copyright (c) 2018 Michael Bohlender + Copyright (c) 2018 Christian Mollekopf + Copyright (c) 2018 Rémi Nicole + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "perioddayeventmodel.h" + +#include +#include +#include + +#include +#include +#include + +PeriodDayEventModel::PeriodDayEventModel(QObject *parent) + : QAbstractItemModel(parent), partitionedEvents(7) +{ + Sink::Query query; + query.setFlags(Sink::Query::LiveQuery); + query.request(); + query.request(); + query.request(); + query.request(); + + eventModel = Sink::Store::loadModel(query); + + QObject::connect(eventModel.data(), &QAbstractItemModel::dataChanged, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::layoutChanged, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::modelReset, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::rowsInserted, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::rowsMoved, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::rowsRemoved, this, &PeriodDayEventModel::partitionData); + + partitionData(); +} + +void PeriodDayEventModel::partitionData() +{ + SinkLog() << "Partitioning event data"; + + beginResetModel(); + + partitionedEvents = QVector>>(mPeriodLength); + + for (int i = 0; i < eventModel->rowCount(); ++i) { + auto event = eventModel->index(i, 0).data(Sink::Store::DomainObjectRole).value(); + QDate eventDate = event->getStartTime().date(); + + if (!eventDate.isValid()) { + SinkWarning() << "Invalid date in the eventModel, ignoring..."; + continue; + } + + int bucket = bucketOf(eventDate); + + if (bucket >= 0) { + SinkTrace() << "Adding event:" << event->getSummary() << "in bucket #" << bucket; + partitionedEvents[bucket].append(event); + } + } + + endResetModel(); +} + +int PeriodDayEventModel::bucketOf(const QDate &candidate) const +{ + int bucket = mPeriodStart.daysTo(candidate); + if (bucket >= mPeriodLength || bucket < 0) { + return -1; + } + + return bucket; +} + +QModelIndex PeriodDayEventModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) { + return {}; + } + + if (!parent.isValid()) { + // Asking for a day + + if (!(0 <= row && row < mPeriodLength)) { + return {}; + } + + return createIndex(row, column, DAY_ID); + } + + // Asking for an Event + auto day = static_cast(parent.row()); + + Q_ASSERT(0 <= day && day <= mPeriodLength); + if (row >= partitionedEvents[day].size()) { + return {}; + } + + return createIndex(row, column, day); +} + +QModelIndex PeriodDayEventModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) { + return {}; + } + + if (index.internalId() == DAY_ID) { + return {}; + } + + auto day = index.internalId(); + + return this->index(day, 0); +} + +int PeriodDayEventModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return mPeriodLength; + } + + auto day = parent.row(); + + return partitionedEvents[day].size(); +} + +int PeriodDayEventModel::columnCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return 1; + } + + return eventModel->columnCount(); +} + +QVariant PeriodDayEventModel::data(const QModelIndex &id, int role) const +{ + if (id.internalId() == DAY_ID) { + auto day = id.row(); + + SinkTrace() << "Fetching data for day" << day << "with role" + << QMetaEnum::fromType().valueToKey(role); + + switch (role) { + case Qt::DisplayRole: + return mPeriodStart.addDays(day).toString(); + case Events: { + auto result = QVariantList{}; + + for (int i = 0; i < partitionedEvents[day].size(); ++i) { + auto eventId = index(i, 0, id); + SinkTrace() << "Appending event:" << data(eventId, Summary); + + auto startTime = data(eventId, StartTime).toDateTime().time(); + + result.append(QVariantMap{ + {"text", data(eventId, Summary)}, + {"description", data(eventId, Description)}, + {"starts", startTime.hour() + startTime.minute() / 60.}, + {"duration", data(eventId, Duration)}, + {"color", "#134bab"}, + {"indention", 0}, + }); + } + + return result; + } + default: + SinkWarning() << "Unknown role for day:" << QMetaEnum::fromType().valueToKey(role); + return {}; + } + } else { + auto day = id.internalId(); + SinkTrace() << "Fetching data for event on day" << day << "with role" + << QMetaEnum::fromType().valueToKey(role); + auto event = partitionedEvents[day].at(id.row()); + + switch (role) { + case Summary: + return event->getSummary(); + case Description: + return event->getDescription(); + case StartTime: + return event->getStartTime(); + case Duration: { + auto start = event->getStartTime(); + auto end = event->getEndTime(); + return start.secsTo(end) / 3600; + } + default: + SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType().valueToKey(role); + return {}; + } + } +} + +QHash PeriodDayEventModel::roleNames() const +{ + return { + {Events, "events"}, + {Summary, "summary"}, + {Description, "description"}, + {StartTime, "starts"}, + {Duration, "duration"}, + }; +} + +QDate PeriodDayEventModel::periodStart() const +{ + return mPeriodStart; +} + +void PeriodDayEventModel::setPeriodStart(const QDate &start) +{ + if (!start.isValid()) { + SinkWarning() << "Passed an invalid starting date in setPeriodStart, ignoring..."; + return; + } + + mPeriodStart = start; + partitionData(); +} + +void PeriodDayEventModel::setPeriodStart(const QVariant &start) +{ + setPeriodStart(start.toDate()); +} + +int PeriodDayEventModel::periodLength() const +{ + return mPeriodLength; +} + +void PeriodDayEventModel::setPeriodLength(int length) +{ + mPeriodLength = length; + partitionData(); +} diff --git a/framework/src/domain/perioddayeventmodel.h b/framework/src/domain/perioddayeventmodel.h new file mode 100644 index 00000000..e116d9a2 --- /dev/null +++ b/framework/src/domain/perioddayeventmodel.h @@ -0,0 +1,129 @@ +/* + Copyright (c) 2018 Michael Bohlender + Copyright (c) 2018 Christian Mollekopf + Copyright (c) 2018 Rémi Nicole + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +// Facility used to get a restricted period into a Sink model comprised of +// events, partitioned according to the day the events take place. +// +// Model Format +// ============ +// +// Day 0 +// |--- Event 0 starting at `periodStart + 0d` +// |--- Event 1 starting at `periodStart + 0d` +// '--- Event 2 starting at `periodStart + 0d` +// Day 1 +// '--- Event 0 starting at `periodStart + 1d` +// Day 2 +// Day 3 +// |--- Event 0 starting at `periodStart + 3d` +// '--- Event 1 starting at `periodStart + 3d` +// Day 4 +// ⋮ +// +// Implementation notes +// ==================== +// +// On the model side +// ----------------- +// +// Columns are never used. +// +// Top-level items just contains the ".events" attribute, and their rows +// correspond to their offset compared to the start of the period (in number of +// days). In that case the internalId contains DAY_ID. +// +// Direct children are events, and their rows corresponds to their index in +// their partition. In that case no internalId / internalPointer is used. +// +// Internally: +// ----------- +// +// On construction and on dataChanged, all events are processed and partitioned +// in partitionedEvents: +// +// QVector< QList > +// ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// | | +// | '--- List of event pointers for that day +// '--- Partition / day +// +class PeriodDayEventModel : public QAbstractItemModel +{ + Q_OBJECT + + Q_PROPERTY(QVariant start READ periodStart WRITE setPeriodStart) + Q_PROPERTY(int length READ periodLength WRITE setPeriodLength) + +public: + using Event = Sink::ApplicationDomain::Event; + + enum Roles + { + Events = Qt::UserRole + 1, + Summary, + Description, + StartTime, + Duration, + }; + Q_ENUM(Roles); + PeriodDayEventModel(QObject *parent = nullptr); + ~PeriodDayEventModel() = default; + + QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; + QModelIndex parent(const QModelIndex &index) const override; + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + + QVariant data(const QModelIndex &index, int role) const override; + + QHash roleNames() const override; + + QDate periodStart() const; + void setPeriodStart(const QDate &); + void setPeriodStart(const QVariant &); + int periodLength() const; + void setPeriodLength(int); + +private: + void partitionData(); + + int bucketOf(const QDate &candidate) const; + + QDate mPeriodStart; + int mPeriodLength = 7; + + QSharedPointer eventModel; + QVector>> partitionedEvents; + + static const constexpr quintptr DAY_ID = std::numeric_limits::max(); +}; diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp index d512ce10..2eb53237 100644 --- a/framework/src/frameworkplugin.cpp +++ b/framework/src/frameworkplugin.cpp @@ -1,149 +1,151 @@ /* Copyright (c) 2016 Michael Bohlender Copyright (c) 2016 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "frameworkplugin.h" #include "domain/maillistmodel.h" #include "domain/folderlistmodel.h" +#include "domain/perioddayeventmodel.h" #include "domain/composercontroller.h" #include "domain/mime/messageparser.h" #include "domain/retriever.h" #include "domain/outboxmodel.h" #include "domain/mouseproxy.h" #include "domain/contactcontroller.h" #include "domain/peoplemodel.h" #include "domain/textdocumenthandler.h" #include "accounts/accountsmodel.h" #include "accounts/accountfactory.h" #include "settings/settings.h" #include "fabric.h" #include "kubeimage.h" #include "clipboardproxy.h" #include "webengineprofile.h" #include "startupcheck.h" #include "keyring.h" #include "controller.h" #include "domainobjectcontroller.h" #include "extensionmodel.h" #include "viewhighlighter.h" #include #include #include class KubeImageProvider : public QQuickImageProvider { public: KubeImageProvider() : QQuickImageProvider(QQuickImageProvider::Pixmap) { } QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) Q_DECL_OVERRIDE { //The platform theme plugin can overwrite our setting again once it gets loaded, //so we check on every icon load request... if (QIcon::themeName() != "kube") { QIcon::setThemeName("kube"); } const auto icon = QIcon::fromTheme(id); auto expectedSize = requestedSize; //Get the largest size that is still smaller or equal than requested //Except if we only have larger sizes, then just pick the closest one bool first = true; for (const auto s : icon.availableSizes()) { if (first && s.width() > requestedSize.width()) { expectedSize = s; break; } first = false; if (s.width() <= requestedSize.width()) { expectedSize = s; } } const auto pixmap = icon.pixmap(expectedSize); if (size) { *size = pixmap.size(); } return pixmap; } }; static QObject *fabric_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new Kube::Fabric::Fabric; } static QObject *webengineprofile_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) return new WebEngineProfile; } static QObject *keyring_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) auto instance = Kube::Keyring::instance(); QQmlEngine::setObjectOwnership(instance, QQmlEngine::CppOwnership); return instance; } void FrameworkPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(uri); engine->addImageProvider(QLatin1String("kube"), new KubeImageProvider); } void FrameworkPlugin::registerTypes (const char *uri) { qmlRegisterType(uri, 1, 0, "FolderListModel"); qmlRegisterType(uri, 1, 0, "MailListModel"); + qmlRegisterType(uri, 1, 0, "PeriodDayEventModel"); qmlRegisterType(uri, 1, 0, "ComposerController"); qmlRegisterType(uri, 1, 0, "ControllerAction"); qmlRegisterType(uri, 1, 0, "MessageParser"); qmlRegisterType(uri, 1, 0, "Retriever"); qmlRegisterType(uri, 1, 0, "OutboxModel"); qmlRegisterType(uri, 1, 0, "MouseProxy"); qmlRegisterType(uri, 1, 0,"ContactController"); qmlRegisterType(uri, 1, 0,"PeopleModel"); qmlRegisterType(uri, 1, 0, "TextDocumentHandler"); qmlRegisterType(uri, 1, 0, "AccountFactory"); qmlRegisterType(uri, 1, 0, "AccountsModel"); qmlRegisterType(uri, 1, 0, "ExtensionModel"); qmlRegisterType(uri, 1, 0, "Settings"); qmlRegisterType(uri, 1, 0, "Listener"); qmlRegisterType(uri, 1, 0, "DomainObjectController"); qmlRegisterSingletonType(uri, 1, 0, "Fabric", fabric_singletontype_provider); qmlRegisterType(uri, 1, 0, "KubeImage"); qmlRegisterType(uri, 1, 0, "Clipboard"); qmlRegisterType(uri, 1, 0, "StartupCheck"); qmlRegisterType(uri, 1, 0, "ViewHighlighter"); qmlRegisterSingletonType(uri, 1, 0, "WebEngineProfile", webengineprofile_singletontype_provider); qmlRegisterSingletonType(uri, 1, 0, "Keyring", keyring_singletontype_provider); } diff --git a/views/calendar/qml/WeekEvents.qml b/views/calendar/qml/WeekEvents.qml index 005fb19e..774f254e 100644 --- a/views/calendar/qml/WeekEvents.qml +++ b/views/calendar/qml/WeekEvents.qml @@ -1,93 +1,8 @@ import QtQuick 2.7 -ListModel { - ListElement { - events: [ - ListElement { - color: "#af1a6a" - starts: 1 - duration: 4 - text: "Meeting" - indention: 0 - }, - ListElement { - color: "#134bab" - starts: 9 - duration: 5 - text: "Sport" - indention: 0 - } - ] - } - ListElement { - events: [ - ListElement { - color: "#134bab" - starts: 9 - duration: 5 - text: "Sport" - indention: 0 - } - ] - } - ListElement { - events: [] - } - ListElement { - events: [ - ListElement { - color: "#af1a6a" - starts: 1 - duration: 4 - indention: 0 - text: "Meeting" - } - ] - } - ListElement { - events: [ - ListElement { - color: "#134bab" - starts: 3 - duration: 5 - indention: 0 - text: "Meeting" - }, - ListElement { - color: "#af1a6a" - starts: 4 - duration: 7 - indention: 1 - text: "Meeting2" - } - ] - } - ListElement { - events: [ - ListElement { - color: "#134bab" - starts: 8 - duration: 5 - indention: 0 - text: "Meeting" - }, - ListElement { - color: "#af1a6a" - starts: 8 - duration: 4 - indention: 1 - text: "Meeting2" - }, - ListElement { - color: "#af1a6a" - starts: 9 - duration: 7 - indention: 2 - text: "Meeting2" - } - ] - } - ListElement { - events: [] - } +import org.kube.framework 1.0 as Kube + +Kube.PeriodDayEventModel { + start: "2018-04-09" + length: 7 } diff --git a/views/calendar/qml/WeekView.qml b/views/calendar/qml/WeekView.qml index 8eef3a92..877500c5 100644 --- a/views/calendar/qml/WeekView.qml +++ b/views/calendar/qml/WeekView.qml @@ -1,273 +1,273 @@ /* * Copyright (C) 2018 Michael Bohlender, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.3 import Qt.labs.calendar 1.0 import org.kube.framework 1.0 as Kube FocusScope { id: root Item { anchors { top: parent.top topMargin: Kube.Units.largeSpacing horizontalCenter: parent.horizontalCenter } width: Kube.Units.gridUnit * 7 * 7 + Kube.Units.gridUnit * 2 height: Kube.Units.gridUnit * 27 //BEGIN day labels DayOfWeekRow { id: dayLabels anchors.right: parent.right spacing: 0 locale: Qt.locale("en_GB") delegate: Rectangle { width: Kube.Units.gridUnit * 7 height: Kube.Units.gridUnit + Kube.Units.smallSpacing * 3 border.width: 1 border.color: "lightgrey" color: Kube.Colors.viewBackgroundColor Kube.Label { anchors { top: parent.top left: parent.left margins: Kube.Units.smallSpacing } text: model.shortName } } } //END day labels //BEGIN daylong events Rectangle { id: daylong anchors { top: dayLabels.bottom right: parent.right } height: Kube.Units.gridUnit * 3 width: Kube.Units.gridUnit * 7 * 7 color: Kube.Colors.viewBackgroundColor border.width: 1 border.color: Kube.Colors.buttonColor ListView { anchors { fill: parent margins: 1 } model: DaylongEvents {} delegate: Item { height: Kube.Units.gridUnit + 2 // +2 to make good for the white border width: daylong.width Rectangle { width: Kube.Units.gridUnit * 7 * model.duration height: parent.height x: Kube.Units.gridUnit * 7 * model.starts color: model.color border.width: 1 border.color: Kube.Colors.viewBackgroundColor Kube.Label { anchors { left: parent.left leftMargin: Kube.Units.smallSpacing } color: Kube.Colors.highlightedTextColor text: model.text } } } } } //END daylong events Flickable { id: mainWeekViewer anchors { top: daylong.bottom } height: Kube.Units.gridUnit * 24 width: Kube.Units.gridUnit * 7 * 7 + Kube.Units.gridUnit * 2 contentHeight: Kube.Units.gridUnit * 24 * 2 contentWidth: width clip: true boundsBehavior: Flickable.StopAtBounds ScrollBar.vertical: Kube.ScrollBar {} Row { height: Kube.Units.gridUnit * 24 * 2 width: Kube.Units.gridUnit * 7 * 7 + Kube.Units.gridUnit * 2 spacing: 0 //BEGIN time labels Column { anchors.bottom: parent.bottom Repeater { model: ["1:00","2:00","3:00","4:00","5:00","6:00","7:00","8:00","9:00","10:00","11:00","12:00","13:00","14:00","15:00","16:00","17:00","18:00","19:00","20:00","21:00","22:00","23:00","0:00"] delegate: Item { height: Kube.Units.gridUnit * 2 width: Kube.Units.gridUnit * 2 Kube.Label { anchors { right: parent.right rightMargin: Kube.Units.smallSpacing bottom: parent.bottom } text: model.modelData } } } } //END time labels Repeater { model: WeekEvents{} delegate: Rectangle { id: day property var events: model.events width: Kube.Units.gridUnit * 7 height: Kube.Units.gridUnit * 24 * 2 border.width: 1 border.color: "lightgrey" color: Kube.Colors.viewBackgroundColor Column { anchors.fill: parent Repeater { model: 24 delegate: Rectangle { height: Kube.Units.gridUnit * 4 width: parent.width color: "transparent" border.width:1 border.color: "lightgrey" } } } Repeater { model: parent.events delegate: Rectangle { id: eventDelegate states: [ State { name: "dnd" when: mouseArea.drag.active PropertyChanges {target: mouseArea; cursorShape: Qt.ClosedHandCursor} PropertyChanges {target: eventDelegate; x: x; y: y} PropertyChanges {target: eventDelegate; parent: root} PropertyChanges {target: eventDelegate; opacity: 0.7} PropertyChanges {target: eventDelegate; anchors.right: ""} PropertyChanges {target: eventDelegate; width: Kube.Units.gridUnit * 7 - Kube.Units.smallSpacing * 2} } ] anchors { right: parent.right rightMargin: Kube.Units.smallSpacing } - width: Kube.Units.gridUnit * 7 - Kube.Units.smallSpacing * 2 - Kube.Units.gridUnit * model.indention - height: Kube.Units.gridUnit * model.duration - y: Kube.Units.gridUnit * model.starts - x: Kube.Units.gridUnit * model.indention + width: Kube.Units.gridUnit * 7 - Kube.Units.smallSpacing * 2 - Kube.Units.gridUnit * model.modelData.indention + height: Kube.Units.gridUnit * model.modelData.duration + y: Kube.Units.gridUnit * model.modelData.starts + x: Kube.Units.gridUnit * model.modelData.indention - color: model.color + color: model.modelData.color border.width: 1 border.color: Kube.Colors.viewBackgroundColor Kube.Label { anchors { left: parent.left leftMargin: Kube.Units.smallSpacing } - text: model.text + text: model.modelData.text color: Kube.Colors.highlightedTextColor } Drag.active: mouseArea.drag.active Drag.hotSpot.x: mouseArea.mouseX Drag.hotSpot.y: mouseArea.mouseY Drag.source: eventDelegate MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true drag.target: parent onEntered: { eventDelegate.z = eventDelegate.z + 100 } onExited: { eventDelegate.z = eventDelegate.z - 100 } onReleased: eventDelegate.Drag.drop() } } } DropArea { anchors.fill: parent onDropped: { console.log("DROP") drop.accept(Qt.MoveAction) drop.source.visible = false console.log((drop.source.y - mainWeekViewer.y + mainWeekViewer.contentY) / Kube.Units.gridUnit) } } } } } } } }