From 9e8926ae86d17c4c25ffa4ddf2c6b97839ae2718 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Mon, 2 Aug 2021 16:42:16 +0200 Subject: [PATCH 01/13] Fix Windows support --- src/libskribisto-data/src/skrpluginloader.h | 2 +- src/translations/CMakeLists.txt | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libskribisto-data/src/skrpluginloader.h b/src/libskribisto-data/src/skrpluginloader.h index 169a3af32..5588b6f32 100755 --- a/src/libskribisto-data/src/skrpluginloader.h +++ b/src/libskribisto-data/src/skrpluginloader.h @@ -99,7 +99,7 @@ class EXPORT SKRPluginLoader : public QObject { "fileName").toString(), obj); if (!m_pluginsListHash.contains(plugin.name)) { - qDebug() << "plugin found : " << plugin.name; + qDebug() << "plugin found : " << plugin.name << "at" << path; m_pluginsListHash.insert(plugin.name, plugin); } } diff --git a/src/translations/CMakeLists.txt b/src/translations/CMakeLists.txt index 50b9e8fcc..384c40df6 100644 --- a/src/translations/CMakeLists.txt +++ b/src/translations/CMakeLists.txt @@ -19,7 +19,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lupdate") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/../../bin/lupdate.exe") + if(EXISTS "${CMAKE_PREFIX_PATH}/../../bin/lupdate.exe") + set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/../../bin/lupdate.exe") + else() + set(LUPDATE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lupdate.exe") + endif() endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") message(${LUPDATE_EXECUTABLE}) @@ -37,7 +41,11 @@ add_custom_target("update_ts_en_US" ALL set(LRELEASE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lrelease") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(LRELEASE_EXECUTABLE "${CMAKE_PREFIX_PATH}/../../bin/lrelease.exe") + if(EXISTS "${CMAKE_PREFIX_PATH}/../../bin/lrelease.exe") + set(LRELEASE_EXECUTABLE "${CMAKE_PREFIX_PATH}/../../bin/lrelease.exe") + else() + set(LRELEASE_EXECUTABLE "${CMAKE_PREFIX_PATH}/bin/lrelease.exe") + endif() endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") message(${LRELEASE_EXECUTABLE}) From 56c8bf0c5132692c6f7189ccf5b53a7c33f7b920 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Mon, 2 Aug 2021 18:34:19 +0200 Subject: [PATCH 02/13] Navigation, Save, Shortcuts Navigation: disable animation for side lists Save: add a save button at the top of the window Shortcuts: display shortcuts in tips --- src/app/src/qml/Commons/CheckableTree.qml | 2 + src/app/src/qml/Items/SkrToolButton.qml | 8 +- src/app/src/qml/RootPageForm.ui.qml | 9 ++ src/app/src/qml/main.qml | 121 ++++++++++++------ src/app/src/skrshortcutmanager.cpp | 38 ++++++ src/app/src/skrshortcutmanager.h | 2 + .../NavigationList.qml | 1 + .../themePage/ThemePage/ThemePageForm.ui.qml | 6 +- src/translations/skribisto_en_US.ts | 32 ++--- 9 files changed, 159 insertions(+), 60 deletions(-) diff --git a/src/app/src/qml/Commons/CheckableTree.qml b/src/app/src/qml/Commons/CheckableTree.qml index 8e488ad4b..0d2ce8488 100755 --- a/src/app/src/qml/Commons/CheckableTree.qml +++ b/src/app/src/qml/Commons/CheckableTree.qml @@ -444,6 +444,7 @@ ListView { } text: "Open document" + property string shortcutText: "" onTriggered: { //console.log("model.openedProjectId", openedProjectId) //console.log("model.projectId", model.projectId) @@ -455,6 +456,7 @@ ListView { Action { id: openDocumentInAnotherViewAction //shortcut: "Alt+Return" + property string shortcutText: "" enabled: { if (root.focus === true && titleTextField.visible === false diff --git a/src/app/src/qml/Items/SkrToolButton.qml b/src/app/src/qml/Items/SkrToolButton.qml index 1385a8adb..2610b6d6a 100755 --- a/src/app/src/qml/Items/SkrToolButton.qml +++ b/src/app/src/qml/Items/SkrToolButton.qml @@ -37,7 +37,7 @@ ToolButton { display: AbstractButton.IconOnly SkrToolTip { - text: control.tip ? control.tip : control.text + text: control.tip ? control.tip + priv.finalShortcutText : control.text + priv.finalShortcutText visible: control.hovered && text.length !== 0 } @@ -53,5 +53,11 @@ ToolButton { visible: control.checked } + QtObject{ + id: priv + property string finalShortcutText: shortcutText ? " (" + shortcutText +")" : "" + } + + property string shortcutText: action === null ? "" : action.shortcutText } diff --git a/src/app/src/qml/RootPageForm.ui.qml b/src/app/src/qml/RootPageForm.ui.qml index 1978d63cc..febe5e480 100755 --- a/src/app/src/qml/RootPageForm.ui.qml +++ b/src/app/src/qml/RootPageForm.ui.qml @@ -83,6 +83,15 @@ Item { } + SkrToolButton { + id: saveButton + action: saveAction + + Layout.preferredHeight: 30 + Layout.preferredWidth: 30 + + } + Breadcrumb { id: breadcrumb Layout.preferredHeight: 30 diff --git a/src/app/src/qml/main.qml b/src/app/src/qml/main.qml index eb3b02105..f7950663a 100755 --- a/src/app/src/qml/main.qml +++ b/src/app/src/qml/main.qml @@ -267,6 +267,7 @@ ApplicationWindow { Action { id: fullscreenAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("fullscreen") text: skrShortcutManager.description("fullscreen") icon { source: "qrc:///icons/backup/view-fullscreen.svg" @@ -398,6 +399,7 @@ ApplicationWindow { Action { id: minimapAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("show-minimap-scrollbar") text: skrShortcutManager.description("show-minimap-scrollbar") icon { source: "qrc:///icons/backup/view-preview.svg" @@ -413,7 +415,7 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("show-minimap-scrollbar") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: minimapAction.trigger() } @@ -425,6 +427,7 @@ ApplicationWindow { Action { id: centerTextCursorAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("center-vert-text-cursor") text: skrShortcutManager.description("center-vert-text-cursor") icon { source: "qrc:///icons/backup/format-align-vertical-center.svg" @@ -440,7 +443,7 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("center-vert-text-cursor") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: centerTextCursorAction.trigger() } @@ -472,6 +475,7 @@ ApplicationWindow { Action { id: showUserManualAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("user-manual") text: skrShortcutManager.description("user-manual") icon { source: "qrc:///icons/backup/system-help.svg" @@ -487,7 +491,7 @@ ApplicationWindow { } Shortcut { sequences: skrShortcutManager.shortcuts("user-manual") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: showUserManualAction.trigger() } @@ -556,6 +560,7 @@ ApplicationWindow { Action { id: newProjectAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("new-project") text: skrShortcutManager.description("new-project") icon { source: "qrc:///icons/backup/document-new.svg" @@ -580,6 +585,7 @@ ApplicationWindow { Action { id: checkSpellingAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("check-spelling") text: skrShortcutManager.description("check-spelling") icon { source: "qrc:///icons/backup/tools-check-spelling.svg" @@ -598,7 +604,7 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("check-spelling") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: checkSpellingAction.trigger() } @@ -608,6 +614,7 @@ ApplicationWindow { Action { id: openProjectAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("open-project") text: skrShortcutManager.description("open-project") icon { source: "qrc:///icons/backup/document-open.svg" @@ -621,7 +628,7 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("open-project") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: openProjectAction.trigger() } @@ -670,6 +677,7 @@ ApplicationWindow { Action { id: saveAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("save-project") text: skrShortcutManager.description("save-project") icon { source: "qrc:///icons/backup/document-save.svg" @@ -688,7 +696,7 @@ ApplicationWindow { } Shortcut { sequences: skrShortcutManager.shortcuts("save-project") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: saveAction.trigger() } @@ -729,6 +737,7 @@ ApplicationWindow { Action { id: saveAllAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("save-all-project") text: skrShortcutManager.description("save-all-project") icon { source: "qrc:///icons/backup/document-save-all.svg" @@ -773,6 +782,7 @@ ApplicationWindow { Action { id: saveAsAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("save-as-project") text: skrShortcutManager.description("save-as-project") icon { source: "qrc:///icons/backup/document-save-as.svg" @@ -801,7 +811,7 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("save-as-project") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: saveAsAction.trigger() } @@ -988,7 +998,7 @@ ApplicationWindow { SkrPopup { id: loadingPopup parent: Overlay.overlay - property int timeoutInterval: 5000 + property int timeoutInterval: 3000 signal timeout anchors.centerIn: Overlay.overlay height: 200 @@ -1156,6 +1166,7 @@ ApplicationWindow { //------------------------------------------------------------ Action { id: showSettingsAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("settings") text: skrShortcutManager.description("settings") icon { source: "qrc:///icons/backup/configure.svg" @@ -1178,6 +1189,7 @@ ApplicationWindow { //------------------------------------------------------------ Action { id: printAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("print") text: skrShortcutManager.description("print") icon { source: "qrc:///icons/backup/document-print.svg" @@ -1390,6 +1402,7 @@ ApplicationWindow { Action { id: quitAction + property string shortcutText: skrShortcutManager.nativeShortcutsToString("quit") text: skrShortcutManager.description("quit") icon { source: "qrc:///icons/backup/window-close.svg" @@ -1573,10 +1586,13 @@ ApplicationWindow { lastFocusedItem = item } + + //------------------------------------------------------------------- + Action { id: cutTextAction text: skrShortcutManager.description("cut") - shortcut: StandardKey.Cut + property string shortcutText: skrShortcutManager.nativeShortcutsToString("cut") icon { source: "qrc:///icons/backup/edit-cut.svg" } @@ -1588,14 +1604,17 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("cut") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: cutTextAction.trigger() } + + //------------------------------------------------------------------- + Action { id: copyTextAction text: skrShortcutManager.description("copy") - shortcut: StandardKey.Copy + property string shortcutText: skrShortcutManager.nativeShortcutsToString("copy") icon { source: "qrc:///icons/backup/edit-copy.svg" } @@ -1607,13 +1626,17 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("copy") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: copyTextAction.trigger() } + + //------------------------------------------------------------------- + Action { id: pasteTextAction text: skrShortcutManager.description("paste") + property string shortcutText: skrShortcutManager.nativeShortcutsToString("paste") icon { source: "qrc:///icons/backup/edit-paste.svg" } @@ -1625,21 +1648,23 @@ ApplicationWindow { Shortcut { sequences: skrShortcutManager.shortcuts("paste") - context: Qt.ApplicationShortcut + context: Qt.WindowShortcut onActivated: pastetextAction.trigger() } + //------------------------------------------------------------------- + Action { property bool preventTrigger: false property bool actionTriggeredVolontarily: false id: italicAction - text: qsTr("Italic") + text: skrShortcutManager.description("italic") + property string shortcutText: skrShortcutManager.nativeShortcutsToString("italic") icon { source: "qrc:///icons/backup/format-text-italic.svg" } - shortcut: StandardKey.Italic checkable: true onCheckedChanged: { @@ -1649,22 +1674,25 @@ ApplicationWindow { skrEditMenuSignalHub.italicActionTriggered(italicAction.checked) } } + Shortcut{ + sequence: skrShortcutManager.shortcuts("italic") + context: Qt.WindowShortcut + onActivated: italicAction.trigger() + } + + + //------------------------------------------------------------------- - // Shortcut{ - // sequence: StandardKey.Italic - // context: Qt.ApplicationShortcut - // onActivated: italicAction.trigger() - // } Action { property bool preventTrigger: false id: boldAction - text: qsTr("Bold") + text: skrShortcutManager.description("bold") + property string shortcutText: skrShortcutManager.nativeShortcutsToString("bold") icon { source: "qrc:///icons/backup/format-text-bold.svg" } - shortcut: StandardKey.Bold checkable: true onCheckedChanged: { @@ -1676,21 +1704,25 @@ ApplicationWindow { } } - // Shortcut{ - // sequence: StandardKey.Bold - // context: Qt.ApplicationShortcut - // onActivated: boldAction.trigger() - // } + Shortcut{ + sequence: skrShortcutManager.shortcuts("bold") + context: Qt.WindowShortcut + onActivated: boldAction.trigger() + } + + + //------------------------------------------------------------------- + Action { property bool preventTrigger: false id: strikeAction - text: qsTr("Strike") + text: skrShortcutManager.description("strike") + property string shortcutText: skrShortcutManager.nativeShortcutsToString("strike") icon { source: "qrc:///icons/backup/format-text-strikethrough.svg" } - //shortcut: StandardKey checkable: true onCheckedChanged: { @@ -1701,21 +1733,26 @@ ApplicationWindow { } } - // Shortcut{ - // sequence: - // context: Qt.ApplicationShortcut - // onActivated: strikeAction.trigger() - // } + Shortcut{ + sequence: skrShortcutManager.shortcuts("strike") + context: Qt.WindowShortcut + onActivated: strikeAction.trigger() + } + + + + //------------------------------------------------------------------- + Action { property bool preventTrigger: false id: underlineAction - text: qsTr("Underline") + text: skrShortcutManager.description("underline") + property string shortcutText: skrShortcutManager.nativeShortcutsToString("underline") icon { source: "qrc:///icons/backup/format-text-underline.svg" } - shortcut: StandardKey.Underline checkable: true onCheckedChanged: { @@ -1726,11 +1763,13 @@ ApplicationWindow { underlineAction.checked) } } - // Shortcut{ - // sequence: StandardKey.Underline - // context: Qt.ApplicationShortcut - // onActivated: underlineAction.trigger() - // } + Shortcut{ + sequence: skrShortcutManager.shortcuts("underline") + context: Qt.WindowShortcut + onActivated: underlineAction.trigger() + } + + // Keys.onReleased: { // if(event.key === Qt.Key_Alt){ // console.log("alt") diff --git a/src/app/src/skrshortcutmanager.cpp b/src/app/src/skrshortcutmanager.cpp index 3be1820d0..a2e73d006 100644 --- a/src/app/src/skrshortcutmanager.cpp +++ b/src/app/src/skrshortcutmanager.cpp @@ -134,6 +134,18 @@ void SKRShortcutManager::populateShortcutList() m_shortcutList << createShortcut("paste", tr("Paste"), (QStringList() << "text" << "navigation"), (QStringList() << convertStandardKeyToList(QKeySequence::Paste))); + m_shortcutList << + createShortcut("italic", tr("Italic"), (QStringList() << "text"), + (QStringList() << convertStandardKeyToList(QKeySequence::Italic))); + m_shortcutList << + createShortcut("bold", tr("Bold"), (QStringList() << "text"), + (QStringList() << convertStandardKeyToList(QKeySequence::Bold))); + m_shortcutList << + createShortcut("strike", tr("Strike"), (QStringList() << "text"), + (QStringList())); + m_shortcutList << + createShortcut("underline", tr("Underline"), (QStringList() << "text"), + (QStringList() << convertStandardKeyToList(QKeySequence::Underline))); m_shortcutList << createShortcut("settings", tr("&Settings"), (QStringList() << "global"), @@ -277,12 +289,38 @@ QStringList SKRShortcutManager::shortcuts(const QString& name) const break; } } + shortcuts.removeDuplicates(); return shortcuts; } // ----------------------------------------------------------------- +QStringList SKRShortcutManager::nativeShortcuts(const QString& name) const +{ + QStringList shortcuts = this->shortcuts(name); + + QStringList nativeShortcuts; + + for (const QString& shortcut : qAsConst(shortcuts)) { + nativeShortcuts << QKeySequence(shortcut).toString(QKeySequence::NativeText); + } + + + return nativeShortcuts; +} +// ----------------------------------------------------------------- + + +QString SKRShortcutManager::nativeShortcutsToString(const QString &name) const +{ + QStringList nativeShortcuts = this->nativeShortcuts(name); + + return nativeShortcuts.join(","); +} + +// ----------------------------------------------------------------- + void SKRShortcutManager::setUserShortcuts(const QString& name, const QStringList& userShortcuts) { QSettings settings; diff --git a/src/app/src/skrshortcutmanager.h b/src/app/src/skrshortcutmanager.h index 7528970c9..a1a919fec 100644 --- a/src/app/src/skrshortcutmanager.h +++ b/src/app/src/skrshortcutmanager.h @@ -65,6 +65,8 @@ class SKRShortcutManager : public QObject { const QStringList& userShortcuts); Q_INVOKABLE QStringList getNativeFormatOfDefaultShortcuts(const QString& name) const; + Q_INVOKABLE QStringList nativeShortcuts(const QString &name) const; + Q_INVOKABLE QString nativeShortcutsToString(const QString &name) const; public slots: diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml index 363d37ac8..78e1f13f3 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml @@ -2881,6 +2881,7 @@ NavigationListForm { property int popupId: sidePopupListModel.count - 1 property int headerHeight: 0 enabled: false + enableBehavior: false width: 200 z: 100 - popupId diff --git a/src/plugins/themePage/ThemePage/ThemePageForm.ui.qml b/src/plugins/themePage/ThemePage/ThemePageForm.ui.qml index 17db8ad13..a25a49bd7 100755 --- a/src/plugins/themePage/ThemePage/ThemePageForm.ui.qml +++ b/src/plugins/themePage/ThemePage/ThemePageForm.ui.qml @@ -251,11 +251,13 @@ SkrBasePage { } SkrPane { - Layout.fillWidth: true - Layout.preferredHeight: 50 + //Layout.fillWidth: true + Layout.preferredHeight: 100 + Layout.preferredWidth: 500 GridLayout { anchors.fill: parent + columns: 4 SkrButton { text: qsTr("Button") diff --git a/src/translations/skribisto_en_US.ts b/src/translations/skribisto_en_US.ts index 9f40ba2f9..5620d3394 100644 --- a/src/translations/skribisto_en_US.ts +++ b/src/translations/skribisto_en_US.ts @@ -2075,6 +2075,22 @@ center-vert-text-cursor + + Italic + + + + Bold + + + + Strike + + + + Underline + + SKRTreeManager @@ -2967,22 +2983,6 @@ &Close "%1" project - - Italic - - - - Bold - - - - Strike - - - - Underline - - First steps From 4380a2506f99ffb1d7285411a5b774831ba1bfeb Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Mon, 2 Aug 2021 18:47:11 +0200 Subject: [PATCH 03/13] Minimap: Minimap: fix wobbling of minimap width and text area width --- src/app/src/qml/main.qml | 2 +- src/plugins/textPage/TextPage/TextPage.qml | 1 - src/translations/skribisto_en_US.ts | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/src/qml/main.qml b/src/app/src/qml/main.qml index f7950663a..ce42af134 100755 --- a/src/app/src/qml/main.qml +++ b/src/app/src/qml/main.qml @@ -1021,7 +1021,7 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter - text: "

" + qsTr("Loading a project") + "

" + text: "

" + qsTr("Loading a projects") + "

" focus: true } diff --git a/src/plugins/textPage/TextPage/TextPage.qml b/src/plugins/textPage/TextPage/TextPage.qml index b0a4b9139..bb60557d5 100755 --- a/src/plugins/textPage/TextPage/TextPage.qml +++ b/src/plugins/textPage/TextPage/TextPage.qml @@ -606,7 +606,6 @@ TextPageForm { Binding on sourceViewWidth { value: writingZone.textArea.width restoreMode: Binding.RestoreBindingOrValue - delayed: true } diff --git a/src/translations/skribisto_en_US.ts b/src/translations/skribisto_en_US.ts index 5620d3394..66f225a9e 100644 --- a/src/translations/skribisto_en_US.ts +++ b/src/translations/skribisto_en_US.ts @@ -2939,10 +2939,6 @@ Save a copy of the "%1" project as …
- - Loading a project - - Back up @@ -2999,5 +2995,9 @@ Warning + + Loading a projects + +
From a7783ab915fd8aebfe73e870b1edfaac923cacda Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Tue, 3 Aug 2021 22:52:40 +0200 Subject: [PATCH 04/13] Navigation, Notes Navigation: better navigation with cleaner code and more usable Navigation: fix internal drag & drop Navigation: fix keyboard navigation Notes: fix setting a note folder in Project page Section : fix section icons not displaying correctly --- src/app/src/qml/Commons/NewItemPopup.qml | 3 +- src/app/src/qml/Commons/RelationshipPanel.qml | 3 +- src/app/src/qml/Items/SkrBasePage.qml | 3 +- src/app/src/qml/RightDock.qml | 1 + .../src/qml/WelcomePage/NewProjectPage.qml | 2 +- src/app/src/qml/main.qml | 1 + .../models/skrsearchtreelistproxymodel.cpp | 15 +- .../src/models/skrtreelistmodel.cpp | 322 ++- src/libskribisto-data/src/skrtreehub.h | 4 +- .../NavigationProjectToolbox/Navigation.qml | 14 +- .../NavigationList.qml | 2573 +---------------- .../NavigationListForm.ui.qml | 1 - .../NavigationListView.qml | 2457 ++++++++++++++++ .../NavigationProjectToolbox.qml | 33 +- .../RestoreListView.qml | 32 +- .../TrashedListView.qml | 29 +- .../navigationProjectToolbox/plugin_qml.qrc | 1 + .../OverviewPage/OverviewPage.qml | 2 +- .../projectPage/ProjectPage/ProjectPage.qml | 13 +- .../SectionPage/SectionCreationParameters.qml | 2 +- src/plugins/sectionPage/sectionpage.cpp | 5 + src/translations/skribisto_en_US.ts | 61 +- 22 files changed, 2816 insertions(+), 2761 deletions(-) create mode 100644 src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml diff --git a/src/app/src/qml/Commons/NewItemPopup.qml b/src/app/src/qml/Commons/NewItemPopup.qml index 494f53007..4e9b535a9 100755 --- a/src/app/src/qml/Commons/NewItemPopup.qml +++ b/src/app/src/qml/Commons/NewItemPopup.qml @@ -57,7 +57,8 @@ NewItemPopupForm { //--------------------------------------------------------- - listView.onCurrentIndexChanged: { + listView.onCurrentItemChanged: { + chosenPageType = listView.currentItem.type detailsTextArea.text = skrTreeManager.getPageDetailText(listView.currentItem.type) parametersLoader.source = skrTreeManager.getCreationParametersQmlUrlFromPageType(chosenPageType) diff --git a/src/app/src/qml/Commons/RelationshipPanel.qml b/src/app/src/qml/Commons/RelationshipPanel.qml index ea737c5df..755819e32 100644 --- a/src/app/src/qml/Commons/RelationshipPanel.qml +++ b/src/app/src/qml/Commons/RelationshipPanel.qml @@ -794,7 +794,6 @@ RelationshipPanelForm { action: Action { id: renameAction text: qsTr("Rename") - shortcut: "F2" icon { source: "qrc:///icons/backup/edit-rename.svg" } @@ -850,7 +849,7 @@ RelationshipPanelForm { property int projectId: model.projectId property int treeItemId: model.treeItemId property string treeItemTitle: model.title - title: qsTr("Rename an item") + title: qsTr("Rename a linked item") contentItem: SkrTextField { id: inner_renameTextField text: renameDialog.treeItemTitle diff --git a/src/app/src/qml/Items/SkrBasePage.qml b/src/app/src/qml/Items/SkrBasePage.qml index 014c0fef6..4d6355d66 100755 --- a/src/app/src/qml/Items/SkrBasePage.qml +++ b/src/app/src/qml/Items/SkrBasePage.qml @@ -102,6 +102,7 @@ FocusScope { Keys.onPressed: function(event) { if (event.key === Qt.Key_F2 && treeItemId != -1) { if (control.projectId !== -1) { + console.log("control.projectId", control.projectId, control.treeItemId) renameDialog.projectId = control.projectId renameDialog.treeItemId = control.treeItemId renameDialog.treeItemTitle = skrData.treeHub().getTitle( @@ -122,7 +123,7 @@ FocusScope { control.forceActiveFocus() } - + grabPermissions: PointerHandler.TakeOverForbidden } SimpleDialog { diff --git a/src/app/src/qml/RightDock.qml b/src/app/src/qml/RightDock.qml index b84eefa5f..0073490fd 100755 --- a/src/app/src/qml/RightDock.qml +++ b/src/app/src/qml/RightDock.qml @@ -20,6 +20,7 @@ RightDockForm { } } + // property int treeItemId : -1 // property int paperId : -1 diff --git a/src/app/src/qml/WelcomePage/NewProjectPage.qml b/src/app/src/qml/WelcomePage/NewProjectPage.qml index 793402e56..ccf32847f 100755 --- a/src/app/src/qml/WelcomePage/NewProjectPage.qml +++ b/src/app/src/qml/WelcomePage/NewProjectPage.qml @@ -117,7 +117,7 @@ NewProjectPageForm { var result = skrData.treeHub().addChildTreeItem(projectId, 0, "TEXT") //console.log("new project : add sheet : ", result.isSuccess()) var treeItemId = skrData.treeHub().getLastAddedId() - skrData.treeHub().setTitle(projectId, treeItemId, qsTr("Part ") + i) + skrData.treeHub().setTitle(projectId, treeItemId, qsTr("Part %1").arg(i)) // if(sheetId === 1){ // firstSheetId = sheetId diff --git a/src/app/src/qml/main.qml b/src/app/src/qml/main.qml index ce42af134..9c75a6488 100755 --- a/src/app/src/qml/main.qml +++ b/src/app/src/qml/main.qml @@ -1428,6 +1428,7 @@ ApplicationWindow { saveOrNotBeforeQuittingDialog.projectName = skrData.projectHub( ).getProjectName(projectId) saveOrNotBeforeQuittingDialog.open() + saveOrNotBeforeQuittingDialog.forceActiveFocus() //saveAsBeforeQuittingFileDialog.currentFile = LabPlatform.StandardPaths.writableLocation(LabPlatform.StandardPaths.DocumentsLocation) } } diff --git a/src/libskribisto-data/src/models/skrsearchtreelistproxymodel.cpp b/src/libskribisto-data/src/models/skrsearchtreelistproxymodel.cpp index 3dfecb51d..3d75c0825 100755 --- a/src/libskribisto-data/src/models/skrsearchtreelistproxymodel.cpp +++ b/src/libskribisto-data/src/models/skrsearchtreelistproxymodel.cpp @@ -31,16 +31,15 @@ SKRSearchTreeListProxyModel::SKRSearchTreeListProxyModel() connect(skrdata->projectHub(), &PLMProjectHub::projectClosed, this, [this](int projectId) { - this->clearHistory(projectId); }); connect(skrdata->projectHub(), &PLMProjectHub::projectClosed, this, [this]() { this->invalidateFilter(); }); - connect(skrdata->treeHub(), &SKRTreeHub::sortOrderChanged, this, [this]() { - sort(0); - emit sortOtherProxyModelsCalled(); - this->invalidateFilter(); - }); +// connect(skrdata->treeHub(), &SKRTreeHub::sortOrderChanged, this, [this]() { +// sort(0); +// emit sortOtherProxyModelsCalled(); +// this->invalidateFilter(); +// }); connect(this->sourceModel(), &SKRTreeListModel::dataChanged, this, [this](const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) { @@ -1418,8 +1417,8 @@ void SKRSearchTreeListProxyModel::moveItem(int from, int to) { sort(0); emit sortOtherProxyModelsCalled(); - - this->invalidate(); + this->invalidateFilter(); + //this->invalidate(); } // -------------------------------------------------------------- diff --git a/src/libskribisto-data/src/models/skrtreelistmodel.cpp b/src/libskribisto-data/src/models/skrtreelistmodel.cpp index 026d1c658..00a39a2b5 100755 --- a/src/libskribisto-data/src/models/skrtreelistmodel.cpp +++ b/src/libskribisto-data/src/models/skrtreelistmodel.cpp @@ -64,7 +64,7 @@ SKRTreeListModel::SKRTreeListModel(QObject *parent) connect(m_treeHub, &SKRTreeHub::trashedChanged, // careful, treeItem is trashed = true, - // not a true removal + // not a true removal this, &SKRTreeListModel::refreshAfterTrashedStateChanged); @@ -472,112 +472,114 @@ void SKRTreeListModel::clear() void SKRTreeListModel::refreshAfterDataAddition(int projectId, int treeItemId) { + this->populate(); + // find parentIndex and row // QModelIndex parentIndex; - int row = 0; +// int row = 0; - auto idList = m_treeHub->getAllIds(projectId); - auto sortOrdersHash = m_treeHub->getAllSortOrders(projectId); - auto indentsHash = m_treeHub->getAllIndents(projectId); +// auto idList = m_treeHub->getAllIds(projectId); +// auto sortOrdersHash = m_treeHub->getAllSortOrders(projectId); +// auto indentsHash = m_treeHub->getAllIndents(projectId); - int treeItemIndex = idList.indexOf(treeItemId); +// int treeItemIndex = idList.indexOf(treeItemId); - if (treeItemIndex == 0) { // meaning the parent have to be a project item - for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { - item->invalidateData(SKRTreeItem::Roles::SortOrderRole); - item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); - } +// if (treeItemIndex == 0) { // meaning the parent have to be a project item +// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { +// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); +// item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); +// } - // find project PLMTreeItemItem: - SKRTreeItem *itemBefore = this->getItem(projectId, -1); +// // find project PLMTreeItemItem: +// SKRTreeItem *itemBefore = this->getItem(projectId, -1); - int indexBefore = m_allTreeItems.indexOf(itemBefore); +// int indexBefore = m_allTreeItems.indexOf(itemBefore); - int itemIndex = indexBefore + 1; +// int itemIndex = indexBefore + 1; - beginInsertRows(QModelIndex(), itemIndex, itemIndex); +// beginInsertRows(QModelIndex(), itemIndex, itemIndex); - m_allTreeItems.insert(itemIndex, - new SKRTreeItem(projectId, treeItemId, - indentsHash.value(treeItemId), - sortOrdersHash.value(treeItemId))); - this->index(itemIndex, 0, QModelIndex()); - endInsertRows(); +// m_allTreeItems.insert(itemIndex, +// new SKRTreeItem(projectId, treeItemId, +// indentsHash.value(treeItemId), +// sortOrdersHash.value(treeItemId))); +// this->index(itemIndex, 0, QModelIndex()); +// endInsertRows(); - return; - } +// return; +// } - // if (!parentFound) { - // for (int i = treeItemIndex - 1; i >= 0; --i) { - // int possibleParentId = idList.at(i); - // int possibleParentIndent = - // indentsHash.value(possibleParentId); - - // if (treeItemIndent == possibleParentIndent + 1) { - // // auto modelIndexList = - // // this->getModelIndex(projectId, possibleParentId); - // // if(modelIndexList.isEmpty()){ - // // qWarning() << Q_FUNC_INFO << "if - // // treeItemIndent == possibleParentIndent => - // // modelIndexList.isEmpty()"; - // // return; - // // } - // // parentIndex = modelIndexList.first(); - // // int parentTreeItemId = - // // - // parentIndex.data(SKRTreeItem::Roles::TreeItemIdRole).toInt(); - // row = treeItemIndex - i; - // parentFound = true; - // break; - // } - // } - // } +// // if (!parentFound) { +// // for (int i = treeItemIndex - 1; i >= 0; --i) { +// // int possibleParentId = idList.at(i); +// // int possibleParentIndent = +// // indentsHash.value(possibleParentId); - // if (!parentFound) { - // qWarning() << Q_FUNC_INFO << "parent not found, failsafe used"; - // this->populate(); - // return; - // } +// // if (treeItemIndent == possibleParentIndent + 1) { +// // // auto modelIndexList = +// // // this->getModelIndex(projectId, possibleParentId); +// // // if(modelIndexList.isEmpty()){ +// // // qWarning() << Q_FUNC_INFO << "if +// // // treeItemIndent == possibleParentIndent => +// // // modelIndexList.isEmpty()"; +// // // return; +// // // } +// // // parentIndex = modelIndexList.first(); +// // // int parentTreeItemId = +// // // +// // parentIndex.data(SKRTreeItem::Roles::TreeItemIdRole).toInt(); +// // row = treeItemIndex - i; +// // parentFound = true; +// // break; +// // } +// // } +// // } +// // if (!parentFound) { +// // qWarning() << Q_FUNC_INFO << "parent not found, failsafe used"; +// // this->populate(); +// // return; +// // } - // find item just before in m_allNoteItems to determine item index to insert - // in: +// // find item just before in m_allNoteItems to determine item index to insert +// // in: - int idBefore = idList.at(treeItemIndex - 1); - SKRTreeItem *itemBefore = this->getItem(projectId, idBefore); - // needed because m_allTreeItems can have multiple projects : - int indexBefore = m_allTreeItems.indexOf(itemBefore); +// int idBefore = idList.at(treeItemIndex - 1); +// SKRTreeItem *itemBefore = this->getItem(projectId, idBefore); - int itemIndex = indexBefore + 1; +// // needed because m_allTreeItems can have multiple projects : +// int indexBefore = m_allTreeItems.indexOf(itemBefore); - // if(itemIndex >= m_allNoteItems.count() && treeItemIndent == - // itemBefore->indent() + 1){ - // qWarning() << Q_FUNC_INFO << "last in the m_allNoteItems list - // and child of previous item, so failsafe used"; - // this->populate(); - // return; - // } +// int itemIndex = indexBefore + 1; +// // if(itemIndex >= m_allNoteItems.count() && treeItemIndent == +// // itemBefore->indent() + 1){ +// // qWarning() << Q_FUNC_INFO << "last in the m_allNoteItems list +// // and child of previous item, so failsafe used"; +// // this->populate(); +// // return; +// // } - for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { - item->invalidateData(SKRTreeItem::Roles::SortOrderRole); - item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); - } + +// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { +// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); +// item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); +// } - beginInsertRows(QModelIndex(), itemIndex, itemIndex); +// beginInsertRows(QModelIndex(), itemIndex, itemIndex); - m_allTreeItems.insert(itemIndex, new SKRTreeItem(projectId, treeItemId, - indentsHash.value(treeItemId), - sortOrdersHash.value(treeItemId))); - this->index(itemIndex, 0, QModelIndex()); - this->sortAllTreeItemItems(); - endInsertRows(); +// m_allTreeItems.insert(itemIndex, new SKRTreeItem(projectId, treeItemId, +// indentsHash.value(treeItemId), +// sortOrdersHash.value(treeItemId))); +// this->index(itemIndex, 0, QModelIndex()); +// this->sortAllTreeItemItems(); +// endInsertRows(); } // -------------------------------------------------------------------- @@ -587,24 +589,26 @@ void SKRTreeListModel::refreshAfterDataRemove(int projectId, int treeItemId) Q_UNUSED(projectId) Q_UNUSED(treeItemId) - QModelIndex modelIndex = this->getModelIndex(projectId, treeItemId).first(); + this->populate(); - beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row()); +// QModelIndex modelIndex = this->getModelIndex(projectId, treeItemId).first(); - SKRTreeItem *item = this->getItem(projectId, treeItemId); +// beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row()); - m_allTreeItems.removeAll(item); - this->sortAllTreeItemItems(); +// SKRTreeItem *item = this->getItem(projectId, treeItemId); - endRemoveRows(); +// m_allTreeItems.removeAll(item); +// this->sortAllTreeItemItems(); +// endRemoveRows(); - for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { - item->invalidateData(SKRTreeItem::Roles::SortOrderRole); - item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); - this->exploitSignalFromSKRData(projectId, item->treeItemId(), SKRTreeItem::Roles::SortOrderRole); - this->exploitSignalFromSKRData(projectId, item->treeItemId(), SKRTreeItem::Roles::HasChildrenRole); - } + +// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { +// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); +// item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); +// this->exploitSignalFromSKRData(projectId, item->treeItemId(), SKRTreeItem::Roles::SortOrderRole); +// this->exploitSignalFromSKRData(projectId, item->treeItemId(), SKRTreeItem::Roles::HasChildrenRole); +// } } // -------------------------------------------------------------------- @@ -614,83 +618,87 @@ void SKRTreeListModel::refreshAfterDataMove(int sourceProjectId, int targetProjectId, int targetTreeItemId) { - // this->resetAllTreeItemsList(); + this->populate(); - for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { - item->invalidateData(SKRTreeItem::Roles::SortOrderRole); - } + // this->resetAllTreeItemsList(); - this->sortAllTreeItemItems(); + // beginMoveRows(QModelIndex(), 0, m_allTreeItems.count(), QModelIndex(), 0); // for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { - // qDebug() << "sortOrder" << item->sortOrder(); - - // this->exploitSignalFromSKRData(targetProjectId, item->treeItemId(), - // SKRTreeItem::Roles::SortOrderRole); + // item->invalidateData(SKRTreeItem::Roles::SortOrderRole); // } + // endMoveRows(); + // this->sortAllTreeItemItems(); - // this->exploitSignalFromSKRData(targetProjectId, targetTreeItemId, - // SKRTreeItem::Roles::SortOrderRole); - // for(int sourceTreeItemId : sourceTreeItemIds){ - // this->exploitSignalFromSKRData(sourceProjectId, sourceTreeItemId, - // SKRTreeItem::Roles::SortOrderRole); - // } - // QList sourceIndexList; - // int targetIndex = -2; - // int sourceRow = -2; - // int targetRow = -2; +// QList sourceIndexList; +// int targetIndex = -2; +// int sourceRow = -2; +// int targetRow = -2; - // SKRTreeItem *targetItem = this->getItem(targetProjectId, - // targetTreeItemId); +// SKRTreeItem *targetItem = this->getItem(targetProjectId, +// targetTreeItemId); - // if (!targetItem) { - // qWarning() << "refreshAfterDataMove no targetItem"; - // return; - // } +// if (!targetItem) { +// qWarning() << "refreshAfterDataMove no targetItem"; +// return; +// } - // SKRTreeItem *sourceItem = this->getItem(sourceProjectId, - // sourceTreeItemIds.first()); +// SKRTreeItem *sourceItem = this->getItem(sourceProjectId, +// sourceTreeItemIds.first()); - // if (!sourceItem) { - // qWarning() << "refreshAfterDataMove no sourceItem"; - // return; - // } +// if (!sourceItem) { +// qWarning() << "refreshAfterDataMove no sourceItem"; +// return; +// } - // int i = 0; +// int i = 0; - // for (SKRTreeItem *item : m_allTreeItems) { - // if (sourceTreeItemIds.contains(item->treeItemId())) { - // sourceIndexList.append(i); - // } +// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { +// if (sourceTreeItemIds.contains(item->treeItemId())) { +// sourceIndexList.append(i); +// } - // if ((item->treeItemId() == targetTreeItemId) && (targetIndex == - // -2)) { - // targetIndex = i; - // } - // i++; - // } - // int firstSourceIndex = sourceIndexList.first(); +// if ((item->treeItemId() == targetTreeItemId) && (targetIndex == +// -2)) { +// targetIndex = i; +// } +// i++; +// } +// int firstSourceIndex = sourceIndexList.first(); - // sourceRow = firstSourceIndex; - // targetRow = targetIndex; +// sourceRow = firstSourceIndex; +// targetRow = targetIndex; - // if (sourceRow < targetRow) { - // targetRow += 1; - // } +// if (sourceRow < targetRow) { +// targetRow += 1; +// } - // beginMoveRows(QModelIndex(), sourceRow, sourceRow + - // sourceIndexList.count() - 1, QModelIndex(), targetRow); +// beginMoveRows(QModelIndex(), sourceRow, sourceRow + +// sourceIndexList.count() - 1, QModelIndex(), targetRow); - // this->sortAllTreeItemItems(); - // for (SKRTreeItem *item : m_allTreeItems) { - // item->invalidateData(SKRTreeItem::Roles::SortOrderRole); - // } +// this->sortAllTreeItemItems(); +// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { +// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); +// } +// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { +// //qDebug() << "sortOrder" << item->sortOrder(); - // endMoveRows(); +// this->exploitSignalFromSKRData(item->projectId(), item->treeItemId(), +// SKRTreeItem::Roles::SortOrderRole); +// } + +// this->exploitSignalFromSKRData(targetProjectId, targetTreeItemId, +// SKRTreeItem::Roles::SortOrderRole); +// for(int sourceTreeItemId : sourceTreeItemIds){ +// this->exploitSignalFromSKRData(sourceProjectId, sourceTreeItemId, +// SKRTreeItem::Roles::SortOrderRole); +// } + +// endMoveRows(); } // -------------------------------------------------------------------- @@ -759,7 +767,7 @@ void SKRTreeListModel::sortAllTreeItemItems() { std::sort(m_allTreeItems.begin(), m_allTreeItems.end(), [](SKRTreeItem *item1, SKRTreeItem *item2) -> bool { return item1->sortOrder() < item2->sortOrder(); } - ); + ); } // -------------------------------------------------------------------- @@ -806,7 +814,7 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(m_treeHub, &SKRTreeHub::titleChanged, this, [this](int projectId, int treeItemId, - const QString& value) { + const QString& value) { Q_UNUSED(value) this->exploitSignalFromSKRData(projectId, treeItemId, SKRTreeItem::Roles::TitleRole); }); @@ -814,7 +822,7 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(skrdata->projectHub(), &PLMProjectHub::projectNameChanged, this, [this](int projectId, - const QString& value) { + const QString& value) { Q_UNUSED(value) this->exploitSignalFromSKRData(projectId, 0, SKRTreeItem::Roles::ProjectNameRole); @@ -824,7 +832,7 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(m_treeHub, &SKRTreeHub::treeItemIdChanged, this, [this](int projectId, int treeItemId, - int value) { + int value) { Q_UNUSED(value) this->exploitSignalFromSKRData(projectId, treeItemId, SKRTreeItem::Roles::TreeItemIdRole); @@ -833,7 +841,7 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(m_treeHub, &SKRTreeHub::indentChanged, this, [this](int projectId, int treeItemId, - int value) { + int value) { Q_UNUSED(value) this->exploitSignalFromSKRData(projectId, treeItemId, SKRTreeItem::Roles::IndentRole); @@ -842,7 +850,7 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(m_treeHub, &SKRTreeHub::sortOrderChanged, this, [this](int projectId, int treeItemId, - int value) { + int value) { Q_UNUSED(value) this->exploitSignalFromSKRData(projectId, treeItemId, SKRTreeItem::Roles::SortOrderRole); @@ -852,7 +860,7 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(m_treeHub, &SKRTreeHub::updateDateChanged, this, [this](int projectId, int treeItemId, - const QDateTime& value) { + const QDateTime& value) { Q_UNUSED(value) this->exploitSignalFromSKRData(projectId, treeItemId, SKRTreeItem::Roles::UpdateDateRole); @@ -861,7 +869,7 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(m_treeHub, &SKRTreeHub::trashedChanged, this, [this](int projectId, int treeItemId, - bool value) { + bool value) { Q_UNUSED(value) this->exploitSignalFromSKRData(projectId, treeItemId, SKRTreeItem::Roles::TrashedRole); @@ -882,9 +890,9 @@ void SKRTreeListModel::connectToSKRDataSignals() m_dataConnectionsList << this->connect(m_propertyHub, &SKRPropertyHub::propertyChanged, this, [this](int projectId, int propertyId, - int treeItemCode, - const QString& name, - const QString& value) { + int treeItemCode, + const QString& name, + const QString& value) { Q_UNUSED(value) Q_UNUSED(propertyId) diff --git a/src/libskribisto-data/src/skrtreehub.h b/src/libskribisto-data/src/skrtreehub.h index 58ba13ec9..298ce5a4f 100755 --- a/src/libskribisto-data/src/skrtreehub.h +++ b/src/libskribisto-data/src/skrtreehub.h @@ -93,7 +93,7 @@ class EXPORT SKRTreeHub : public QObject { SKRResult setIndent(int projectId, int treeItemId, int newIndent); - int getIndent(int projectId, + Q_INVOKABLE int getIndent(int projectId, int treeItemId) const; SKRResult setSortOrder(int projectId, int treeItemId, @@ -184,7 +184,7 @@ class EXPORT SKRTreeHub : public QObject { Q_INVOKABLE SKRResult addChildTreeItem(int projectId, int targetId, const QString& type); - SKRResult removeTreeItem(int projectId, + Q_INVOKABLE SKRResult removeTreeItem(int projectId, int targetId); diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml index c8aa1b07f..a2796e25a 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/Navigation.qml @@ -13,7 +13,7 @@ NavigationForm { property int openedProjectId property int openedTreeItemId - signal openDocument(int openedProjectId, int openedTreeItemId, int projectId, int treeItemId) + signal openDocument(int projectId, int treeItemId) signal openDocumentInAnotherView(int projectId, int treeItemId) signal openDocumentInNewWindow(int projectId, int treeItemId) @@ -30,9 +30,6 @@ NavigationForm { NavigationList { id: navigationList - proxyModel: root.navigationListProxyModel - openedProjectId: root.openedProjectId - openedTreeItemId: root.openedTreeItemId Component.onCompleted: { navigationList.openDocument.connect(root.openDocument) @@ -64,7 +61,6 @@ NavigationForm { TrashedListView { id: trashedListView - proxyModel: root.trashedListViewProxyModel Component.onCompleted: { trashedListView.openDocument.connect(root.openDocument) @@ -82,8 +78,8 @@ NavigationForm { // if get children : - var trashedChildrenList = trashedListViewProxyModel.getChildrenList(projectId, treeItemId, true, false) - var trashedAncestorsList = trashedListViewProxyModel.getAncestorsList(projectId, treeItemId, true, false) + var trashedChildrenList = skrData.treeHub().getAllChildren(projectId, treeItemId) + var trashedAncestorsList = skrData.treeHub().getAllAncestors(projectId, treeItemId) // if no children : if(trashedChildrenList.length === 0 && trashedAncestorsList === 0){ restoreDocumentList(projectId, [treeItemId]) @@ -108,7 +104,6 @@ NavigationForm { RestoreListView { id: restoreListView - proxyModel: root.restoreListViewProxyModel Component.onCompleted: { restoreListView.openDocument.connect(root.openDocument) @@ -124,7 +119,7 @@ NavigationForm { trashedChildrenList.push(parentTreeItemIdToBeRestored) - var treeIndentOffset = root.restoreListViewProxyModel.getItemIndent(projectId, parentTreeItemIdToBeRestored) + var treeIndentOffset = skrData.treeHub().getIndent(projectId, parentTreeItemIdToBeRestored) stackView.push(restoreListViewComponent, {currentProjectId: projectId, parentTreeItemIdToBeRestored: parentTreeItemIdToBeRestored, @@ -135,7 +130,6 @@ NavigationForm { function popRestoreListView() { stackView.pop() - root.restoreListViewProxyModel.clearCheckedList() } diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml index 78e1f13f3..5c2c0567c 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml @@ -12,9 +12,11 @@ import "../.." NavigationListForm { id: root - property var proxyModel - signal openDocument(int openedProjectId, int openedTreeItemId, int projectId, int treeItemId) + + + + signal openDocument(int projectId, int treeItemId) signal openDocumentInAnotherView(int projectId, int treeItemId) signal openDocumentInNewWindow(int projectId, int treeItemId) signal showTrashedList @@ -22,8 +24,6 @@ NavigationListForm { readonly property int currentParentId: priv.currentParentId readonly property int currentProjectId: priv.currentProjectId readonly property int currentTreeItemId: priv.currentTreeItemId - property int openedProjectId: -2 - property int openedTreeItemId: -2 readonly property alias selectedTreeItemsIds: priv.selectedTreeItemsIds readonly property alias selectedProjectId: priv.selectedProjectId @@ -41,7 +41,7 @@ NavigationListForm { onSelectingChanged: { if (!selecting) { - navigationListStackView.currentItem.proxyModel.checkNone() + navigationListStackView.currentItem.checkNone() } } property var selectedTreeItemsIds: [] @@ -56,37 +56,38 @@ NavigationListForm { onCurrentProjectIdChanged: { - // if (currentParent !== -2 & currentProject !== -2) { - // p_section.parentTitle = proxyModel.getItemName( - // currentProject, currentParent) - // listView.section.delegate = sectionHeading - // //console.log("onCurrentProjectChanged") - // } - // // clear : - // if (currentParent === -2 & currentProject === -2 ){ - // listView.section.delegate = null - // } } + Component.onCompleted: { + var newItem = navigationListStackView.push("NavigationListView.qml") + newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) + newItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) + newItem.openDocument.connect(openDocument) + newItem.openDocumentInAnotherView.connect(openDocumentInAnotherView) + newItem.openDocumentInNewWindow.connect(openDocumentInNewWindow) } + + + function setCurrentTreeItemParentId(projectId, treeItemParentId) { + sidePopupListModel.clear() + + //find parent id if (projectId > -1 & treeItemParentId > -1) { - var ancestorsList = proxyModel.getAncestorsList( - projectId, treeItemParentId, - proxyModel.showTrashedFilter, - proxyModel.showNotTrashedFilter) + var ancestorsList = skrData.treeHub().getAllAncestors( + projectId, treeItemParentId) } //compare with current parent id if (projectId === root.currentProjectId & treeItemParentId === root.currentParentId) { + console.log("nothing to do") + } + else if (projectId < 0 & treeItemParentId < 0) { - // navigationListStackView.currentItem.proxyModel.setCurrentTreeItemId( - // projectId, -1) - } else if (projectId === -1 & treeItemParentId === -1) { navigationListStackView.pop(null, priv.transitionOperation) //project item navigationListStackView.get(0).projectId = -2 @@ -94,68 +95,75 @@ NavigationListForm { navigationListStackView.get(0).treeItemId = -2 navigationListStackView.get(0).init() navigationListStackView.get(0).setCurrent() - } else { + } + else { navigationListStackView.pop(null, priv.transitionOperation) ancestorsList.reverse() - ancestorsList.push(treeItemParentId, priv.transitionOperation) + ancestorsList.push(treeItemParentId) - if (ancestorsList[ancestorsList.length - 1] === -1) { - ancestorsList.pop() - } //project item navigationListStackView.get(0).projectId = projectId + navigationListStackView.get(0).parentId = -2 navigationListStackView.get(0).treeItemId = 0 navigationListStackView.get(0).init() navigationListStackView.get(0).setCurrent() for (var i = 1; i < ancestorsList.length; i++) { - var newItem = navigationListStackView.push(stackViewComponent, { + var newItem = navigationListStackView.push("NavigationListView.qml", { "projectId": projectId, "treeItemId": ancestorsList[i] }, - priv.transitionOperation) + StackView.Immediate) + + + newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) + newItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) + newItem.openDocument.connect(openDocument) + newItem.openDocumentInAnotherView.connect(openDocumentInAnotherView) + newItem.openDocumentInNewWindow.connect(openDocumentInNewWindow) + newItem.init() newItem.setCurrent() } - var lastNewItem = navigationListStackView.push(stackViewComponent, { + var lastNewItem = navigationListStackView.push("NavigationListView.qml", { "projectId": projectId, "parentId": treeItemParentId }, priv.transitionOperation) + lastNewItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) + lastNewItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) + lastNewItem.openDocument.connect(openDocument) + lastNewItem.openDocumentInAnotherView.connect(openDocumentInAnotherView) + lastNewItem.openDocumentInNewWindow.connect(openDocumentInNewWindow) + lastNewItem.init() lastNewItem.setCurrent() - rootWindow.protectedSignals.setBreadcrumbCurrentTreeItemCalled( - priv.currentProjectId, priv.currentParentId) - } - sidePopupListModel.clear() + } + rootWindow.protectedSignals.setBreadcrumbCurrentTreeItemCalled( + priv.currentProjectId, priv.currentParentId) determineIfGoUpButtonEnabled() priv.selecting = false } function setCurrentTreeItemId(projectId, treeItemId) { + sidePopupListModel.clear() //find parent id - var ancestorsList = proxyModel.getAncestorsList( - projectId, treeItemId, proxyModel.showTrashedFilter, - proxyModel.showNotTrashedFilter) + var ancestorsList = skrData.treeHub().getAllAncestors( + projectId, treeItemId) var newParentId = ancestorsList[0] //compare with current parent id if (projectId === root.currentProjectId & newParentId === root.currentParentId) { - navigationListStackView.currentItem.proxyModel.setCurrentTreeItemId( - projectId, treeItemId) + navigationListStackView.currentItem.setCurrentTreeItemId(projectId, treeItemId) } // else if(projectId === root.currentProjectId){ // } else { navigationListStackView.pop(null, priv.transitionOperation) ancestorsList.reverse() - ancestorsList.push(treeItemId, priv.transitionOperation) - - if (ancestorsList[ancestorsList.length - 1] === -1) { - ancestorsList.pop() - } + ancestorsList.push(treeItemId) //project item navigationListStackView.get(0).projectId = projectId @@ -164,16 +172,24 @@ NavigationListForm { navigationListStackView.get(0).setCurrent() for (var i = 1; i < ancestorsList.length; i++) { - var newItem = navigationListStackView.push(stackViewComponent, { + var newItem = navigationListStackView.push("NavigationListView.qml", { "projectId": projectId, "treeItemId": ancestorsList[i] }, - priv.transitionOperation) + StackView.Immediate) + newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) + newItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) + newItem.openDocument.connect(openDocument) + newItem.openDocumentInAnotherView.connect(openDocumentInAnotherView) + newItem.openDocumentInNewWindow.connect(openDocumentInNewWindow) + newItem.init() newItem.setCurrent() } } - sidePopupListModel.clear() + rootWindow.protectedSignals.setBreadcrumbCurrentTreeItemCalled( + priv.currentProjectId, priv.currentParentId) + determineIfGoUpButtonEnabled() priv.selecting = false } @@ -194,26 +210,25 @@ NavigationListForm { } //enabled: onTriggered: { + console.log("depth", navigationListStackView.depth) - //var parentTreeItemId = proxyModel.getAncestorsList(root.currentProjectId, root.currentTreeItemId, proxyModel.showTrashedFilter, proxyModel.showNotTrashedFilter)[0] navigationListStackView.pop(priv.transitionOperation) navigationListStackView.currentItem.setCurrent() - //console.log("index", navigationListStackView.currentItem.currentIndex) - navigationListStackView.currentItem.listView.currentItem.forceActiveFocus() - - var index = navigationListStackView.currentItem.listView.currentIndex - var item = navigationListStackView.currentItem.listView.itemAtIndex( - index) - if (item) { - item.forceActiveFocus() - } else { - navigationListStackView.currentItem.listView.forceActiveFocus() - } + +// if(navigationListStackView.currentItem.listView.currentItem){ +// navigationListStackView.currentItem.listView.currentItem.forceActiveFocus() +// } + +// var index = navigationListStackView.currentItem.listView.currentIndex +// var item = navigationListStackView.currentItem.listView.itemAtIndex(index) + +// if (item) { +// item.forceActiveFocus() +// } else { +// navigationListStackView.currentItem.listView.forceActiveFocus() +// } priv.currentProjectId = navigationListStackView.currentItem.projectId priv.currentParentId = navigationListStackView.currentItem.parentId -// console.log("priv.currentProjectId", priv.currentProjectId) -// console.log("priv.currentParentId", priv.currentParentId) -// console.log("priv.currentTreeItemId", priv.currentTreeItemId) rootWindow.protectedSignals.setBreadcrumbCurrentTreeItemCalled( priv.currentProjectId, priv.currentParentId) @@ -239,21 +254,6 @@ NavigationListForm { determineIfGoUpButtonEnabled() } - //----------------------------------------------------------------------------- - // current parent button : - // Binding { - // target: root - // property: "currentProjectId" - // value: proxyModel.projectIdFilter - // } - // Binding { - // target: root - // property: "currentParentId" - // value: proxyModel.parentIdFilter - // } - //currentParent: proxyModel.parentIdFilter - //currentProject: proxyModel.projectIdFilter - //---------------------------------------------------------------------------- treeMenuToolButton.icon.source: "qrc:///icons/backup/overflow-menu.svg" treeMenuToolButton.onClicked: { @@ -268,15 +268,6 @@ NavigationListForm { id: navigationMenu y: treeMenuToolButton.height - // Action { - // text: qsTr("Rename") - // } - - // MenuSeparator {} - // Action { - // text: qsTr("Remove") - // } - // MenuSeparator {} Action { text: qsTr("Paste") enabled: root.enabled && currentParentId !== -2 @@ -358,8 +349,9 @@ NavigationListForm { function addItemAtCurrentParent(type) { //console.log(currentProjectId, currentParentId, navigationListStackView.currentItem.visualModel.items.count) - navigationListStackView.currentItem.proxyModel.addChildItem( - currentProjectId, currentParentId, type) + + skrData.treeHub().addChildTreeItem(currentProjectId, currentParentId, type) + navigationListStackView.currentItem.listView.currentIndex = navigationListStackView.currentItem.listView.count - 1 navigationListStackView.currentItem.listView.positionViewAtEnd() navigationListStackView.currentItem.listView.currentItem.editName() } @@ -472,2400 +464,7 @@ NavigationListForm { //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- - Component { - id: stackViewComponent - - Item { - id: stackViewBaseItem - property alias listView: listView - property alias visualModel: visualModel - property var proxyModel: root.proxyModel.clone() - property int currentIndex: listView.currentIndex - property int parentId: -2 - property int projectId: -2 - property int treeItemId: -2 - property int popupId: -1 - property bool hoverEnabled: true - - // used to remember the source when moving an item - property int moveSourceInt: -2 - property int moveSourceTreeItemId: -2 - property int moveSourceProjectId: -2 - - focus: false - - Component.onCompleted: { - init() - } - - function init() { - p_section.parentTitle = qsTr("Projects") - listView.section.delegate = sectionHeading - - if (treeItemId < 0 && parentId >= -1) { - stackViewBaseItem.proxyModel.setParentFilter(projectId, - parentId) - } else { - stackViewBaseItem.proxyModel.setCurrentTreeItemId( - projectId, treeItemId) - parentId = stackViewBaseItem.proxyModel.parentIdFilter - } - determineSectionTitle() - } - - Component.onDestruction: { - delete proxyModel - } - - function setCurrent() { - priv.currentProjectId = projectId - priv.currentParentId = parentId - priv.currentTreeItemId = treeItemId - - listView.positionViewAtIndex(currentIndex, ListView.Contain) - } - - onActiveFocusChanged: { - if (activeFocus) { - listView.forceActiveFocus() - } - } - - function determineSectionTitle() { - - var projectId = stackViewBaseItem.projectId - var parentId = stackViewBaseItem.parentId - if (parentId === 0 && projectId !== -2) { - var projectTitle = skrData.projectHub().getProjectName( - projectId) - - p_section.parentTitle = projectTitle - listView.section.delegate = sectionHeading - } else if (parentId !== -2 && projectId !== -2) { - var parentTitle = proxyModel.getItemName(projectId, - parentId) - - //console.log("onCurrentParentChanged") - p_section.parentTitle = parentTitle - listView.section.delegate = sectionHeading - } else if (parentId === -2) { - - // show "projects" section - p_section.parentTitle = qsTr("Projects") - listView.section.delegate = sectionHeading - } // clear : - else if (parentId === -2 && root.currentProjectId === -2) { - listView.section.delegate = null - } - } - - Item { - id: topDraggingMover - anchors.top: scrollView.top - anchors.right: scrollView.right - anchors.left: scrollView.left - height: 30 - z: 1 - - visible: priv.dragging - - HoverHandler { - onHoveredChanged: { - if (hovered) { - topDraggingMoverTimer.start() - } else { - topDraggingMoverTimer.stop() - } - } - } - - Timer { - id: topDraggingMoverTimer - repeat: true - interval: 10 - onTriggered: { - if (listView.atYBeginning) { - listView.contentY = 0 - } else { - listView.contentY = listView.contentY - 2 - } - } - } - } - Item { - id: bottomDraggingMover - anchors.bottom: scrollView.bottom - anchors.right: scrollView.right - anchors.left: scrollView.left - height: 30 - z: 1 - - visible: priv.dragging - - HoverHandler { - onHoveredChanged: { - if (hovered) { - bottomDraggingMoverTimer.start() - } else { - bottomDraggingMoverTimer.stop() - } - } - } - - Timer { - id: bottomDraggingMoverTimer - repeat: true - interval: 10 - onTriggered: { - if (listView.atYEnd) { - listView.positionViewAtEnd() - } else { - listView.contentY = listView.contentY + 2 - } - } - } - } - - Item { - id: focusZone - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - height: listView.height - listView.contentHeight - > 0 ? listView.height - listView.contentHeight : 0 - z: 1 - - TapHandler { - onSingleTapped: function(eventPoint) { - listView.forceActiveFocus() - console.log("focusZone", "forceActiveFocus") - - var index = listView.currentIndex - var item = listView.itemAtIndex(index) - if (item) { - item.forceActiveFocus() - } else { - listView.forceActiveFocus() - } - - eventPoint.accepted = false - } - grabPermissions: PointerHandler.ApprovesTakeOverByAnything - } - } - - ScrollView { - id: scrollView - clip: true - anchors.fill: parent - focusPolicy: Qt.StrongFocus - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded //scrollBarVerticalPolicy - - ListView { - id: listView - smooth: true - boundsBehavior: Flickable.StopAtBounds - spacing: 1 - - Accessible.name: qsTr("Navigation list") - Accessible.role: Accessible.List - - DelegateModel { - id: visualModel - delegate: listItemComponent - model: stackViewBaseItem.proxyModel - } - model: visualModel - - // scrollBar interactivity : - onContentHeightChanged: { - //fix scrollbar visible at start - if (scrollView.height === 0) { - scrollBarVerticalPolicy = ScrollBar.AlwaysOff - return - } - - if (listView.contentHeight > scrollView.height) { - scrollBarVerticalPolicy = ScrollBar.AlwaysOn - } else { - scrollBarVerticalPolicy = ScrollBar.AlwaysOff - } - } - - //----------------------------------------------------------------------------- - - - Binding { - target: listView - property: "currentIndex" - value: proxyModel.forcedCurrentIndex - } - - //---------------------------------------------------------------------------- - - // move : - addDisplaced: Transition { - enabled: SkrSettings.ePaperSettings.animationEnabled - NumberAnimation { - properties: "x,y" - duration: 250 - } - } - - removeDisplaced: Transition { - enabled: SkrSettings.ePaperSettings.animationEnabled - SequentialAnimation { - PauseAnimation { - duration: 250 - } - NumberAnimation { - properties: "x,y" - duration: 250 - } - } - } - displaced: Transition { - enabled: SkrSettings.ePaperSettings.animationEnabled - NumberAnimation { - properties: "x,y" - duration: 250 - } - } - - moveDisplaced: Transition { - enabled: SkrSettings.ePaperSettings.animationEnabled - NumberAnimation { - properties: "x,y" - duration: 100 - } - } - QtObject { - id: p_section - property string parentTitle: "" - } - - //---------------------------------------------------------------------------- - section.property: "indent" - section.criteria: ViewSection.FullString - section.labelPositioning: ViewSection.CurrentLabelAtStart - | ViewSection.InlineLabels - section.delegate: null - - // The delegate for each section header - Component { - id: sectionHeading - Rectangle { - width: listView.width - height: childrenRect.height - color: SkrTheme.buttonBackground - - required property string section - - SkrLabel { - anchors.left: parent.left - anchors.right: parent.right - activeFocusOnTab: false - text: p_section.parentTitle - font.bold: true - horizontalAlignment: Qt.AlignHCenter - color: SkrTheme.buttonForeground - } - } - } - - //---------------------------------------------------------------------- - //--- listview keys ------------------------------------------ - //---------------------------------------------------------------------- - Keys.onShortcutOverride: function(event) { - if ((event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_N) { - event.accepted = true - } - if ((event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_V) { - event.accepted = true - } - } - Keys.onPressed: function(event) { - // if (event.key === Qt.Key_Up) { - // listView.currentItem.forceActiveFocus() - // event.accepted = false - // } - if (event.key === Qt.Key_Backspace - || event.key === Qt.Key_Left) { - console.log("Backspace / Left key pressed") - goUpAction.trigger() - event.accepted = true - } - // paste - if ((event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_V) { - skrData.treeHub().paste(currentProjectId, - currentParentId) - event.accepted = true - } - - // add - if ((event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_N) { - - newItemPopup.projectId = currentProjectId - newItemPopup.treeItemId = currentParentId - newItemPopup.visualIndex = 0 - newItemPopup.createFunction = afterNewItemTypeIsChosen - newItemPopup.open() - - event.accepted = true - } - } - - function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { - - for(var i = 0; i < quantity ; i++){ - addItemAtCurrentParent(pageType) - } - } - - //---------------------------------------------------------------------- - //--- Start list item component ------------------------------------------ - //---------------------------------------------------------------------- - Component { - id: listItemComponent - - SwipeDelegate { - id: swipeDelegate - property int indent: model.indent - property alias dropArea: dropArea - property alias checkState: selectionCheckBox.checkState - focus: true - - Accessible.name: labelLabel.text.length - === 0 ? titleLabel.text + (model.hasChildren ? " " + qsTr("is a folder") : "") : titleLabel.text + " " + qsTr( - "label:") + " " + labelLabel.text - + (model.hasChildren ? " " + qsTr( - "has child") : "") - Accessible.role: Accessible.ListItem - Accessible.description: qsTr("navigation item") - - anchors { - left: Qt.isQtObject( - parent) ? parent.left : undefined - right: Qt.isQtObject( - parent) ? parent.right : undefined - rightMargin: 5 - } - - height: 40 - - padding: 0 - topPadding: 0 - bottomPadding: 0 - topInset: 0 - bottomInset: 0 - - property int visualIndex: { - return DelegateModel.itemsIndex - } - - Binding { - target: content - property: "visualIndex" - value: visualIndex - } - - onActiveFocusChanged: { - if (listView.currentIndex === model.index - && model.index !== -1 && activeFocus) { - stackViewBaseItem.treeItemId = model.treeItemId - stackViewBaseItem.setCurrent() - } - } - - function editName() { - priv.renaming = true - state = "edit_name" - titleTextFieldForceActiveFocusTimer.start() - titleTextField.selectAll() - } - - Timer { - id: titleTextFieldForceActiveFocusTimer - repeat: false - interval: 100 - onTriggered: { - titleTextField.forceActiveFocus() - } - } - - function editLabel() { - priv.renaming = true - state = "edit_label" - labelTextFieldForceActiveFocusTimer.start() - labelTextField.selectAll() - } - - Timer { - id: labelTextFieldForceActiveFocusTimer - repeat: false - interval: 100 - onTriggered: { - labelTextField.forceActiveFocus() - } - } - - Keys.priority: Keys.AfterItem - - Keys.onShortcutOverride: function(event) { - if ((event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_N) { - event.accepted = true - } - if ((event.modifiers & Qt.ControlModifier) - && (event.modifiers & Qt.ShiftModifier) - && event.key === Qt.Key_N) { - event.accepted = true - } - if ((event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_C) { - event.accepted = true - } - if (model.isMovable - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_X) { - event.accepted = true - } - if ((event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_V) { - event.accepted = true - } - if (event.key === Qt.Key_Escape - && (swipeDelegate.state == "edit_name" - || swipeDelegate.state == "edit_label")) { - event.accepted = true - } - } - - Keys.onPressed: function(event) { - if (event.key === Qt.Key_Right) { - console.log("Right key pressed") - goToChildAction.trigger() - event.accepted = true - } - if (event.key === Qt.Key_Backspace - || event.key === Qt.Key_Left) { - console.log("Backspace / Left key pressed") - goUpAction.trigger() - event.accepted = true - } - if (model.isOpenable - && (event.modifiers & Qt.AltModifier) - && event.key === Qt.Key_Return) { - console.log("Alt Return key pressed") - openDocumentInAnotherViewAction.trigger() - event.accepted = true - } - if (model.isOpenable - && event.key === Qt.Key_Return - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - console.log("Return key pressed") - openDocumentAction.trigger() - event.accepted = true - } - - // rename - if (model.isRenamable && event.key === Qt.Key_F2 - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - renameAction.trigger() - event.accepted = true - } - - // cut - if (model.isMovable - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_X - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - cutAction.trigger() - event.accepted = true - } - - // copy - if (model.isCopyable - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_C - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - copyAction.trigger() - event.accepted = true - } - - // paste - if (model.canAddChildTreeItem - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_V - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - pasteAction.trigger() - event.accepted = true - } - - // add before - if (model.canAddSiblingTreeItem - && (event.modifiers & Qt.ControlModifier) - && (event.modifiers & Qt.ShiftModifier) - && event.key === Qt.Key_N - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - addBeforeAction.trigger() - event.accepted = true - } - - // add after - if (model.canAddSiblingTreeItem - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_N - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - addAfterAction.trigger() - event.accepted = true - } - - // add child - if (model.canAddChildTreeItem - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_Space - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - addChildAction.trigger() - event.accepted = true - } - - // move up - if (model.isMovable - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_Up - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - moveUpAction.trigger() - event.accepted = true - } - - // move down - if (model.isMovable - && (event.modifiers & Qt.ControlModifier) - && event.key === Qt.Key_Down - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - moveDownAction.trigger() - event.accepted = true - } - - // send to trash - if (model.isTrashable - && event.key === Qt.Key_Delete - && swipeDelegate.state !== "edit_name" - && swipeDelegate.state !== "edit_label") { - sendToTrashAction.trigger() - event.accepted = true - } - } - - swipe.right: RowLayout { - - height: parent.height - anchors.right: parent.right - - SkrToolButton { - id: sendToTrashToolButton - - focusPolicy: Qt.NoFocus - display: AbstractButton.IconOnly - - Layout.fillHeight: true - Layout.fillWidth: true - - action: sendToTrashAction - } - - Item { - id: stretcher - Layout.fillHeight: true - Layout.fillWidth: true - Layout.minimumWidth: 50 - } - - SkrToolButton { - id: openDocumentToolButton - visible: model.hasChildren - focusPolicy: Qt.NoFocus - display: AbstractButton.IconOnly - - Layout.fillHeight: true - Layout.fillWidth: true - - action: openTreeItemAction - } - - SkrToolButton { - id: openMergedViewToolButton - visible: model.hasChildren - focusPolicy: Qt.NoFocus - display: AbstractButton.IconOnly - - Layout.fillHeight: true - Layout.fillWidth: true - - icon.source: "qrc:///icons/backup/view-list-text.svg" - } - } - - swipe.onOpened: closeSwipeTimer.start() - - // Item { - // anchors.fill: parent - // z:1 - Timer { - id: closeSwipeTimer - interval: 3000 - onTriggered: { - swipeDelegate.swipe.close() - } - } - - HoverHandler { - id: itemHoverHandler - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus - enabled: stackViewBaseItem.hoverEnabled - - //grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything - onHoveredChanged: { - //console.log("item hovered", itemHoverHandler.hovered) - if (priv.dragging || priv.selecting) { - hoveringTimer.stop() - closeSideNavigationListPopupTimer.popupId - = stackViewBaseItem.popupId + 1 - closeSideNavigationListPopupTimer.start( - ) - } else if (hovered && model.hasChildren) { - - if (!rootWindow.compactMode) { - - hoveringTimer.start() - } - } else if (hovered && !model.hasChildren) { - if (!addSideNavigationListPopupTimer.running) { - hoveringTimer.stop() - addSideNavigationListPopupTimer.stop() - closeSideNavigationListPopupTimer.popupId - = stackViewBaseItem.popupId + 1 - closeSideNavigationListPopupTimer.start() - } - } else if (!model.hasChildren) { - if (!addSideNavigationListPopupTimer.running) { - hoveringTimer.stop() - closeSideNavigationListPopupTimer.popupId - = stackViewBaseItem.popupId + 1 - closeSideNavigationListPopupTimer.start() - } - } else { - hoveringTimer.stop() - closeSideNavigationListPopupTimer.stop() - //addSideNavigationListPopupTimer.stop() - } - } - - // } - } - - Timer { - id: hoveringTimer - interval: 200 - onTriggered: { - - addSideNavigationListPopupTimer.projectId = model.projectId - addSideNavigationListPopupTimer.parentId = model.treeItemId - addSideNavigationListPopupTimer.popupId - = stackViewBaseItem.popupId - addSideNavigationListPopupTimer.parentItem = swipeDelegate - addSideNavigationListPopupTimer.listView = listView - - // console.log("popupId", stackViewBaseItem.popupId) - addSideNavigationListPopupTimer.start() - } - } - - contentItem: DropArea { - id: dropArea - - keys: ["application/skribisto-tree-item"] - onEntered: { - - //console.log("entered") - //content.sourceIndex = drag.source.visualIndex - visualModel.items.move( - drag.source.visualIndex, - content.visualIndex) - } - onExited: { - - } - onDropped: { - // console.log("dropped") - if (drop.proposedAction === Qt.MoveAction) { - - //console.log("dropped from :", moveSourceInt, "to :", content.visualIndex) - cancelDragTimer.stop() - listView.interactive = true - priv.dragging = false - proxyModel.moveItem(moveSourceInt, - content.visualIndex) - //proxyModel.moveItemById(moveSourceProjectId, moveSourceTreeItemId, content.treeItemId) - } - } - - SkrListItemPane { - id: content - property int visualIndex: model.index - property int sourceIndex: -2 - property int projectId: model.projectId - property int treeItemId: model.treeItemId - - property bool isCurrent: model.index === listView.currentIndex ? true : false - - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } - width: parent.width - height: swipeDelegate.height - - Drag.active: mouseDragHandler.active | touchDragHandler.active - Drag.source: content - Drag.hotSpot.x: width / 2 - Drag.hotSpot.y: height / 2 - Drag.keys: ["application/skribisto-tree-item"] - - Drag.supportedActions: Qt.MoveAction - - opacity: mouseDragHandler.active | touchDragHandler.active ? 0.2 : 1.0 - //sDrag.dragType: Drag.Internal - borderWidth: 2 - borderColor: mouseDragHandler.active | content.dragging ? SkrTheme.accent : "transparent" - Behavior on borderColor { - enabled: SkrSettings.ePaperSettings.animationEnabled - ColorAnimation { - duration: 200 - } - } - - property bool dragging: false - DragHandler { - id: mouseDragHandler - acceptedDevices: PointerDevice.Mouse - - //xAxis.enabled: false - //grabPermissions: PointerHandler.TakeOverForbidden - onActiveChanged: { - if (active) { - Globals.touchUsed = false - listView.interactive = false - moveSourceInt = content.visualIndex - moveSourceTreeItemId = content.treeItemId - moveSourceProjectId = content.projectId - priv.dragging = true - cancelDragTimer.stop() - } else { - cancelDragTimer.stop() - priv.dragging = false - content.dragging = false - - content.Drag.drop() - proxyModel.invalidate() - } - } - enabled: true - - onCanceled: { - cancelDragTimer.stop() - priv.dragging = false - content.dragging = false - } - - grabPermissions: PointerHandler.CanTakeOverFromItems - | PointerHandler.CanTakeOverFromAnything | PointerHandler.TakeOverForbidden - - - } - - DragHandler { - id: touchDragHandler - acceptedDevices: PointerDevice.TouchScreen - | PointerDevice.Stylus - - //xAxis.enabled: false - //grabPermissions: PointerHandler.TakeOverForbidden - onActiveChanged: { - if (active) { - Globals.touchUsed = true - listView.interactive = false - moveSourceInt = content.visualIndex - moveSourceTreeItemId = content.treeItemId - moveSourceProjectId = content.projectId - priv.dragging = true - cancelDragTimer.stop() - } else { - listView.interactive = true - cancelDragTimer.stop() - priv.dragging = false - content.dragging = false - content.Drag.drop() - proxyModel.invalidate() - } - } - enabled: content.dragging - - onCanceled: { - cancelDragTimer.stop() - priv.dragging = false - content.dragging = false - } - grabPermissions: PointerHandler.CanTakeOverFromItems - | PointerHandler.CanTakeOverFromAnything - } - - Timer { - id: cancelDragTimer - repeat: false - interval: 3000 - onTriggered: { - priv.dragging = false - content.dragging = false - } - } - - TapHandler { - id: tapHandler - - onSingleTapped: function(eventPoint) { - priv.selecting = false - //console.log("dragThreshold", mouseDragHandler.dragThreshold) - - if (content.dragging) { - eventPoint.accepted = false - return - } - if (eventPoint.event.device.type - === PointerDevice.Mouse) { - Globals.touchUsed = false - listView.interactive = false - } - - if (eventPoint.event.device.type - === PointerDevice.TouchScreen - | eventPoint.event.device.type - === PointerDevice.Stylus) { - Globals.touchUsed = true - listView.interactive = true - } - - listView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId - - //console.log("tapped") - if (skrData.treePropertyHub( - ).getProperty( - model.projectId, - model.treeItemId, - "can_add_child_paper", - "true") === "true") { - goToChildTimer.start() - } else { - openDocumentTimer.start() - } - - //delegateRoot.forceActiveFocus() - eventPoint.accepted = true - } - - onDoubleTapped: function(eventPoint) { - openDocumentTimer.stop() - goToChildTimer.stop() - if (content.dragging) { - eventPoint.accepted = false - return - } - if (eventPoint.event.device.type - === PointerDevice.Mouse) { - Globals.touchUsed = false - listView.interactive = false - } - - if (eventPoint.event.device.type - === PointerDevice.TouchScreen - | eventPoint.event.device.type - === PointerDevice.Stylus) { - Globals.touchUsed = true - listView.interactive = true - } - //console.log("double tapped") - listView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId - - openDocumentTimer.start() - - eventPoint.accepted = true - } - - onGrabChanged: function(transition, point) { - point.accepted = false - } - - grabPermissions: PointerHandler.TakeOverForbidden - } - Timer { - id: openDocumentTimer - interval: 150 - onTriggered: { - openDocumentAction.trigger() - } - } - - Timer { - id: goToChildTimer - interval: 150 - onTriggered: { - goToChildAction.trigger() - } - } - - TapHandler { - id: rightClickTapHandler - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus - acceptedButtons: Qt.RightButton - onSingleTapped: function(eventPoint) { - listView.interactive = eventPoint.event.device.type - === PointerDevice.Mouse - Globals.touchUsed = false - - if (menu.visible) { - menu.close() - return - } - - listView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId - - - menu.index = model.index - menu.treeItemId = model.treeItemId - menu.projectId = model.projectId - menu.isOpenable = model.isOpenable - menu.canAddChildTreeItem = model.canAddChildTreeItem - menu.canAddSiblingTreeItem = model.canAddSiblingTreeItem - menu.isCopyable = model.isCopyable - menu.isMovable = model.isMovable - menu.isRenamable = model.isRenamable - menu.isTrashable = model.isTrashable - - - - menu.open() - eventPoint.accepted = true - } - - grabPermissions: PointerHandler.TakeOverForbidden - } - - TapHandler { - id: middleClickTapHandler - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus - acceptedButtons: Qt.MiddleButton - onSingleTapped: function(eventPoint) { - listView.interactive = eventPoint.event.device.type - === PointerDevice.Mouse - Globals.touchUsed = true - listView.currentIndex = model.index - priv.currentTreeItemId = model.treeItemId - swipeDelegate.forceActiveFocus() - openDocumentInAnotherViewAction.trigger() - eventPoint.accepted = true - } - grabPermissions: PointerHandler.TakeOverForbidden - } - - /// without MouseArea, it breaks while dragging and scrolling: - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.NoButton - onWheel: function(wheel) { - listView.interactive = false - listView.flick( - 0, - wheel.angleDelta.y * 50) - wheel.accepted = true - } - - enabled: listView.interactive === false - } - - TapHandler { - acceptedDevices: PointerDevice.TouchScreen - | PointerDevice.Stylus - - onLongPressed: { - Globals.touchUsed = true - - // needed to activate the grab handler - - // if(content.dragging){ - // eventPoint.accepted = false - // return - // } - content.dragging = true - listView.interactive = false - cancelDragTimer.start() - priv.selecting = false - } - } - - Action { - id: goToChildAction - //shortcut: "Right" - enabled: listView.enabled - && listView.currentIndex === model.index - && model.canAddChildTreeItem - - //icon.source: model.hasChildren ? "qrc:///icons/backup/go-next.svg" : (model.canAddChildTreeItem ? "qrc:///icons/backup/list-add.svg" : "") - text: qsTr("See sub-items") - onTriggered: { - console.log("goToChildAction triggered") - - // goToChildActionToBeTriggered = true - // goToChildActionCurrentIndent = model.indent - - // var _proxyModel = proxyModel - - //save current ids: - listView.currentIndex = model.index - navigationListStackView.currentItem.treeItemId - = model.treeItemId - - sidePopupListModel.clear() - - // push new view - var newItem = navigationListStackView.push( - stackViewComponent, { - "projectId": model.projectId, - "parentId": model.treeItemId - }, - priv.transitionOperation) - newItem.setCurrent() - newItem.listView.currentIndex = 0 - newItem.forceActiveFocus() - - rootWindow.protectedSignals.setBreadcrumbCurrentTreeItemCalled( - priv.currentProjectId, - priv.currentParentId) - } - } - - Timer { - property int treeItemIdToEdit: -2 - onTreeItemIdToEditChanged: { - if (treeItemIdToEdit !== -2) { - editNameTimer.start() - } - } - - id: editNameTimer - repeat: false - interval: animationDuration - onTriggered: { - var index = proxyModel.findVisualIndex( - model.projectId, - treeItemIdToEdit) - if (index !== -2) { - listView.itemAtIndex( - index).editName() - } - treeItemIdToEdit = -2 - } - } - - Action { - id: openDocumentAction - //shortcut: "Return" - enabled: { - if (!model.isOpenable) { - return false - } - - if (listView.enabled - && titleTextField.visible === false - && listView.currentIndex === model.index) { - return true - } else - return false - } - - text: qsTr("Open document") - onTriggered: { - //console.log("model.openedProjectId", openedProjectId) - //console.log("model.projectId", model.projectId) - root.openDocument(openedProjectId, - openedTreeItemId, - model.projectId, - model.treeItemId) - } - } - - Action { - id: openDocumentInAnotherViewAction - //shortcut: "Alt+Return" - enabled: { - if (!model.isOpenable) { - return false - } - if (listView.enabled - && titleTextField.visible === false - && listView.currentIndex === model.index) { - return true - } else - return false - } - - text: qsTr("Open document in a new tab") - onTriggered: { - root.openDocumentInAnotherView( - model.projectId, - model.treeItemId) - } - } - - Action { - id: openDocumentInNewWindowAction - //shortcut: "Alt+Return" - enabled: { - if (!model.isOpenable) { - return false - } - if (listView.enabled - && titleTextField.visible === false - && listView.currentIndex === model.index) { - return true - } else - return false - } - - text: qsTr("Open document in a window") - onTriggered: { - root.openDocumentInNewWindow( - model.projectId, - model.treeItemId) - // solve bug where this window's tree disapears - setCurrentTreeItemIdTimer.projectId = model.projectId - setCurrentTreeItemIdTimer.treeItemId = model.treeItemId - - setCurrentTreeItemIdTimer.start() - } - } - - contentItem: ColumnLayout { - id: columnLayout3 - anchors.fill: parent - - RowLayout { - id: rowLayout - spacing: 2 - Layout.fillHeight: true - Layout.fillWidth: true - - Rectangle { - id: currentItemIndicator - color: "lightsteelblue" - Layout.fillHeight: true - Layout.preferredWidth: 5 - visible: listView.currentIndex === model.index - } - Rectangle { - id: openedItemIndicator - color: SkrTheme.accent - Layout.fillHeight: true - Layout.preferredWidth: 5 - visible: model.projectId === openedProjectId - && model.treeItemId === openedTreeItemId - } - - SkrToolButton { - id: projectIsBackupIndicator - visible: model.projectIsBackup - && model.type === 'PROJECT' - enabled: true - focusPolicy: Qt.NoFocus - implicitHeight: 32 - implicitWidth: 32 - Layout.maximumHeight: 30 - padding: 0 - rightPadding: 0 - bottomPadding: 0 - leftPadding: 2 - topPadding: 0 - flat: true - onDownChanged: down = false - - icon { - source: "qrc:///icons/backup/tools-media-optical-burn-image.svg" - height: 32 - width: 32 - } - - hoverEnabled: true - ToolTip.delay: 1000 - ToolTip.timeout: 5000 - ToolTip.visible: hovered - ToolTip.text: qsTr("This project is a backup") - } - - SkrCheckBox { - id: selectionCheckBox - visible: priv.selecting - implicitHeight: 32 - implicitWidth: 32 - Layout.maximumHeight: 30 - padding: 0 - rightPadding: 0 - bottomPadding: 0 - leftPadding: 0 - topPadding: 0 - focusPolicy: Qt.NoFocus - - onCheckStateChanged: { - model.checkState = selectionCheckBox.checkState - determineSelectedTreeItems() - } - - Binding on checkState { - value: model.checkState - delayed: true - restoreMode: Binding.RestoreBindingOrValue - } - - function determineSelectedTreeItems() { - priv.selectedTreeItemsIds = [] - priv.selectedTreeItemsIds - = proxyModel.getCheckedIdsList() - priv.selectedProjectId = currentProjectId - - console.log(selectedTreeItemsIds) - } - } - - Item { - Layout.maximumHeight: 30 - implicitHeight: treeItemIconIndicator.implicitHeight - implicitWidth: treeItemIconIndicator.implicitWidth - - SkrToolButton { - id: treeItemIconIndicator - //visible: model.projectIsBackup && model.treeItemId === -1 - enabled: true - focusPolicy: Qt.NoFocus - implicitHeight: 36 - implicitWidth: 36 - padding: 0 - rightPadding: 0 - bottomPadding: 0 - leftPadding: 2 - topPadding: 0 - flat: true - onDownChanged: down = false - - onClicked: { - - } - - icon { - source: getIconUrlFromPageType( - model.type, model.projectId, model.treeItemId) - - } - - hoverEnabled: true - - // ToolTip.delay: 1000 - // ToolTip.timeout: 5000 - // ToolTip.visible: hovered - // ToolTip.text: qsTr("") - Item { - id: treeItemIconIndicatorHat - anchors.fill: treeItemIconIndicator - z: 1 - - TapHandler { - - onSingleTapped: function(eventPoint) { - tapHandler.singleTapped( - eventPoint) - } - - onDoubleTapped: function(eventPoint) { - tapHandler.doubleTapped( - eventPoint) - } - - onGrabChanged: function(transition, point) { - tapHandler.grabChanged( - transition, - point) - } - } - - TapHandler { - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus - acceptedButtons: Qt.RightButton - - onSingleTapped: function(eventPoint) { - rightClickTapHandler.singleTapped( - eventPoint) - } - - onDoubleTapped: function(eventPoint) { - rightClickTapHandler.doubleTapped( - eventPoint) - } - - onGrabChanged: function(transition, point) { - rightClickTapHandler.grabChanged( - transition, - point) - } - } - - TapHandler { - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus - acceptedButtons: Qt.MiddleButton - - onSingleTapped: function(eventPoint) { - middleClickTapHandler.singleTapped( - eventPoint) - } - - onDoubleTapped: function(eventPoint) { - middleClickTapHandler.doubleTapped( - eventPoint) - } - - onGrabChanged: function(transition, point) { - middleClickTapHandler.grabChanged( - transition, - point) - } - } - } - } - } - - Rectangle { - color: "transparent" - //border.width: 1 - Layout.fillWidth: true - Layout.fillHeight: true - - ColumnLayout { - id: columnLayout2 - spacing: 1 - anchors.fill: parent - - SkrLabel { - id: titleLabel - activeFocusOnTab: false - - Layout.fillWidth: true - Layout.topMargin: 2 - Layout.leftMargin: 4 - Layout.alignment: Qt.AlignLeft - | Qt.AlignVCenter - font.bold: model.projectIsActive - && model.indent - === -1 ? true : false - text: model.type === 'PROJECT' ? model.projectName : model.title - elide: Text.ElideRight - } - - SkrTextField { - id: labelTextField - visible: false - - Layout.fillWidth: true - Layout.fillHeight: true - text: labelLabel.text - maximumLength: 50 - - placeholderText: qsTr("Enter label") - - onEditingFinished: { - - console.log("editing label finished") - model.label = text - swipeDelegate.state = "" - - //fix bug while new lone child - titleLabel.visible = true - labelLayout.visible = true - priv.renaming = false - } - - //Keys.priority: Keys.AfterItem - Keys.onShortcutOverride: function(event) { event.accepted = (event.key === Qt.Key_Escape)} - Keys.onPressed: function(event) { - if (event.key === Qt.Key_Return) { - console.log("Return key pressed title") - editingFinished( - ) - event.accepted = true - } - if ((event.modifiers & Qt.CtrlModifier) - && event.key - === Qt.Key_Return) { - console.log("Ctrl Return key pressed title") - editingFinished( - ) - event.accepted = true - } - if (event.key === Qt.Key_Escape) { - console.log("Escape key pressed title") - swipeDelegate.state = "" - event.accepted = true - } - } - } - - SkrTextField { - id: titleTextField - visible: false - - Layout.fillWidth: true - Layout.fillHeight: true - text: titleLabel.text - maximumLength: 50 - - placeholderText: qsTr("Enter title") - - onEditingFinished: { - - console.log("editing finished") - if (model.type === "PROJECT") { - //project item - model.projectName = text - } else { - model.title = text - } - - swipeDelegate.state = "" - - //fix bug while new lone child - titleLabel.visible = true - labelLayout.visible = true - priv.renaming = false - } - - //Keys.priority: Keys.AfterItem - Keys.onShortcutOverride: function(event) { event.accepted = (event.key === Qt.Key_Escape)} - Keys.onPressed: function(event) { - if (event.key === Qt.Key_Return) { - console.log("Return key pressed title") - editingFinished( - ) - event.accepted = true - } - if ((event.modifiers & Qt.CtrlModifier) - && event.key - === Qt.Key_Return) { - console.log("Ctrl Return key pressed title") - editingFinished( - ) - event.accepted = true - } - if (event.key === Qt.Key_Escape) { - console.log("Escape key pressed title") - swipeDelegate.state = "" - event.accepted = true - } - } - } - - RowLayout { - id: labelLayout - Layout.fillWidth: true - Layout.leftMargin: 5 - - ListItemAttributes { - id: attributes - treeItemId: model.treeItemId - projectId: model.projectId - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: 4 - Layout.bottomMargin: 2 - } - - SkrLabel { - id: labelLabel - activeFocusOnTab: false - text: model.label - === undefined ? "" : model.label - visible: text.length - === 0 ? false : true - Layout.bottomMargin: 2 - Layout.rightMargin: 4 - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - elide: Text.ElideRight - font.italic: true - horizontalAlignment: Qt.AlignRight - Layout.fillWidth: true - } - } - } - } - - SkrLabel { - id: devLabel - activeFocusOnTab: false - text: model.index + "-" + model.treeItemId - + "-" + model.sortOrder - visible: +priv.devModeEnabled - elide: Text.ElideNone - Layout.bottomMargin: 2 - Layout.rightMargin: 4 - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - horizontalAlignment: Qt.AlignRight - Layout.fillHeight: true - //Layout.preferredWidth: 30 - } - - SkrToolButton { - id: menuButton - Layout.fillHeight: true - Layout.preferredWidth: 30 - - text: qsTr("Item menu") - icon.source: "qrc:///icons/backup/overflow-menu.svg" - flat: true - focusPolicy: Qt.NoFocus - - onClicked: { - - if (menu.visible) { - menu.close() - return - } - - listView.currentIndex = model.index - swipeDelegate.forceActiveFocus() - - - - - menu.index = model.index - menu.treeItemId = model.treeItemId - menu.projectId = model.projectId - menu.isOpenable = model.isOpenable - menu.canAddChildTreeItem = model.canAddChildTreeItem - menu.canAddSiblingTreeItem = model.canAddSiblingTreeItem - menu.isCopyable = model.isCopyable - menu.isMovable = model.isMovable - menu.isRenamable = model.isRenamable - menu.isTrashable = model.isTrashable - - - - menu.open() - } - - visible: itemHoverHandler.hovered - || content.isCurrent - } - - Rectangle { - Layout.fillHeight: true - Layout.preferredWidth: 2 - - color: model.indent === 0 ? Material.color(Material.Indigo) : (model.indent === 1 ? Material.color(Material.LightBlue) : (model.indent === 2 ? Material.color(Material.LightGreen) : (model.indent === 3 ? Material.color(Material.Amber) : (model.indent === 4 ? Material.color(Material.DeepOrange) : Material.color(Material.Teal))))) - } - } - Rectangle { - id: separator - Layout.preferredHeight: 1 - Layout.preferredWidth: content.width / 2 - Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom - gradient: Gradient { - orientation: Qt.Horizontal - GradientStop { - position: 0.00 - color: "transparent" - } - GradientStop { - position: 0.30 - color: SkrTheme.divider - } - GradientStop { - position: 0.70 - color: SkrTheme.divider - } - GradientStop { - position: 1.00 - color: "transparent" - } - } - } - } - } - // DropArea { - // id: dropArea - // anchors { - // fill: parent - // margins: 10 - // } - // property int sourceIndex: -1 - // property int targetIndex: -1 - // onEntered: { - // sourceIndex = drag.source.DelegateModel.itemsIndex - // targetIndex = dragArea.DelegateModel.itemsIndex - // // var sourceIndex = drag.source.DelegateModel.itemsIndex - // // var targetIndex = dragArea.DelegateModel.itemsIndex - // visualModel.items.move(sourceIndex, targetIndex) - - // // var sourceModelIndex = drag.source.DelegateModel.modelIndex( - // // sourceIndex) - // // var targetModelIndex = dragArea.DelegateModel.modelIndex( - // // targetIndex) - - // // console.log("targetIndex : ", sourceModelIndex.name) - // } - - // onDropped: { - // console.log("onDropped") - // } - // } - - // Shortcut { - // sequences: ["Ctrl+Shift+N"] - // onActivated: addBeforeAction.trigger() - // enabled: root.visible - // } - SkrMenu { - id: menu - y: menuButton.height - width: 300 - z: 101 - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape - - property int index - property int treeItemId - property int projectId - property bool isOpenable - property bool canAddChildTreeItem - property bool canAddSiblingTreeItem - property bool isCopyable - property bool isMovable - property bool isRenamable - property bool isTrashable - - - onOpened: { - - } - - onClosed: { - - } - - SkrMenuItem { - height: !menu.isOpenable ? 0 : undefined - visible: menu.isOpenable - action: Action { - id: openTreeItemAction - text: qsTr("Open") - //shortcut: "Return" - icon { - source: "qrc:///icons/backup/document-edit.svg" - } - - enabled: titleTextField.visible === false - && listView.enabled - onTriggered: { - console.log("open treeItem action", - menu.projectId, - menu.treeItemId) - openDocumentAction.trigger() - } - } - } - SkrMenuItem { - height: ! menu.isOpenable - || menu.treeItemId === -1 ? 0 : undefined - visible: menu.isOpenable - - action: Action { - id: openTreeItemInAnotherViewAction - text: qsTr("Open in another view") - //shortcut: "Alt+Return" - icon { - source: "qrc:///icons/backup/tab-new.svg" - } - enabled: titleTextField.visible === false - && listView.enabled - onTriggered: { - console.log("open treeItem in another view action", - menu.projectId, - menu.treeItemId) - openDocumentInAnotherViewAction.trigger() - } - } - } - - SkrMenuItem { - height: ! menu.isOpenable ? 0 : undefined - visible: menu.isOpenable - - action: Action { - id: openTreeItemInNewWindowAction - text: qsTr("Open in new window") - //shortcut: "Alt+Return" - icon { - source: "qrc:///icons/backup/window-new.svg" - } - enabled: titleTextField.visible === false - && listView.enabled - onTriggered: { - console.log("open treeItem in new window action", - menu.projectId, - menu.treeItemId) - // solve bug where this window's tree disapears - setCurrentTreeItemIdTimer.projectId - = menu.projectId - setCurrentTreeItemIdTimer.treeItemId - = menu.treeItemId - setCurrentTreeItemIdTimer.start( - ) - root.openDocumentInNewWindow( - menu.projectId, - menu.treeItemId) - // setCurrentTreeItemId( menu.projectId, menu.treeItemId) - } - } - } - - SkrMenuItem { - height: menu.treeItemId === 0 ? undefined : 0 - visible: menu.treeItemId === 0 - enabled: menu.projectIsActive === false - && listView.enabled - && menu.treeItemId === 0 - text: qsTr("Set as active project") - icon { - source: "qrc:///icons/backup/tab-new.svg" - } - onTriggered: { - console.log("set active project", - menu.projectId) - skrData.projectHub( - ).setActiveProject( - menu.projectId) - } - } - - MenuSeparator { - height: menu.isRenamable ? undefined : 0 - visible: menu.isRenamable - } - - SkrMenuItem { - height: menu.isRenamable ? undefined : 0 - visible: menu.isRenamable - action: Action { - id: renameAction - text: qsTr("Rename") - //shortcut: "F2" - icon { - source: "qrc:///icons/backup/edit-rename.svg" - } - enabled: listView.enabled - - onTriggered: { - console.log("rename action", - menu.projectId, - menu.treeItemId) - swipeDelegate.editName() - } - } - } - - SkrMenuItem { - height: ! menu.isRenamable ? 0 : undefined - visible: menu.isRenamable - action: Action { - id: setLabelAction - text: qsTr("Set label") - //shortcut: "F2" - icon { - source: "qrc:///icons/backup/label.svg" - } - enabled: listView.enabled - onTriggered: { - console.log("sel label", - menu.projectId, - menu.treeItemId) - swipeDelegate.editLabel() - } - } - } - - MenuSeparator { - height: ! menu.isMovable - || ! menu.isCopyable - || ! menu.canAddChildTreeItem ? 0 : undefined - visible: menu.isMovable - || menu.isCopyable - || menu.canAddChildTreeItem - } - - SkrMenuItem { - height: ! menu.isMovable ? 0 : undefined - visible: menu.isMovable - action: Action { - id: cutAction - text: qsTr("Cut") - //shortcut: StandardKey.Cut - icon { - source: "qrc:///icons/backup/edit-cut.svg" - } - enabled: listView.enabled - - onTriggered: { - - if (selectedTreeItemsIds.length > 0) { - console.log("cut action", - menu.projectId, - selectedTreeItemsIds) - skrData.treeHub().cut( - menu.projectId, - selectedTreeItemsIds) - } else { - console.log("cut action", - menu.projectId, - menu.treeItemId) - skrData.treeHub().cut( - menu.projectId, - [ menu.treeItemId]) - } - } - } - } - - SkrMenuItem { - height: ! menu.isCopyable ? 0 : undefined - visible: menu.isCopyable - action: Action { - - id: copyAction - text: qsTr("Copy") - //shortcut: StandardKey.Copy - icon { - source: "qrc:///icons/backup/edit-copy.svg" - } - enabled: listView.enabled - - onTriggered: { - if (selectedTreeItemsIds.length > 0) { - console.log("copy action", - menu.projectId, - selectedTreeItemsIds) - skrData.treeHub().copy( - menu.projectId, - selectedTreeItemsIds) - } else { - console.log("copy action", - menu.projectId, - menu.treeItemId) - skrData.treeHub().copy( - menu.projectId, - [ menu.treeItemId]) - } - } - } - } - - SkrMenuItem { - height: ! menu.canAddChildTreeItem ? 0 : undefined - visible: menu.canAddChildTreeItem - action: Action { - - id: pasteAction - text: qsTr("Paste") - //shortcut: StandardKey.Paste - icon { - source: "qrc:///icons/backup/edit-paste.svg" - } - enabled: listView.enabled - - onTriggered: { - console.log("paste action", - menu.projectId, - menu.treeItemId) - var result = skrData.treeHub( - ).paste( - menu.projectId, - menu.treeItemId) - - if (!result.success) { - console.debug( - "paste action: error") - } - } - } - } - - MenuSeparator { - height: !( menu.canAddSiblingTreeItem - || menu.canAddChildTreeItem) ? 0 : undefined - visible: ( menu.canAddSiblingTreeItem - || menu.canAddChildTreeItem) - } - - SkrMenuItem { - height: ! menu.canAddSiblingTreeItem ? 0 : undefined - visible: menu.canAddSiblingTreeItem - action: Action { - id: addBeforeAction - text: qsTr("Add before") - icon { - source: "qrc:///icons/backup/document-new.svg" - } - enabled: listView.enabled - onTriggered: { - console.log("add before action", - menu.projectId, - menu.treeItemId) - - var visualIndex = listView.currentIndex - - newItemPopup.projectId = menu.projectId - newItemPopup.treeItemId = menu.treeItemId - newItemPopup.visualIndex = visualIndex - newItemPopup.createFunction - = afterNewItemTypeIsChosen - newItemPopup.open() - } - - function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { - - for(var i = 0; i < quantity ; i++){ - proxyModel.addItemAbove( - projectId, - treeItemId, - pageType) - - } - listView.itemAtIndex( - visualIndex).editName() - } - } - } - - SkrMenuItem { - height: ! menu.canAddSiblingTreeItem ? 0 : undefined - visible: menu.canAddSiblingTreeItem - action: Action { - id: addAfterAction - text: qsTr("Add after") - icon { - source: "qrc:///icons/backup/document-new.svg" - } - enabled: listView.enabled - onTriggered: { - console.log("add after action", - menu.projectId, - menu.treeItemId) - - var visualIndex = listView.currentIndex + 1 - - newItemPopup.projectId = menu.projectId - newItemPopup.treeItemId = menu.treeItemId - newItemPopup.visualIndex = visualIndex - newItemPopup.createFunction - = afterNewItemTypeIsChosen - newItemPopup.open() - } - - function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { - - for(var i = 0; i < quantity ; i++){ - proxyModel.addItemBelow( - projectId, - treeItemId, - pageType) - } - - listView.itemAtIndex( - visualIndex).editName() - } - } - } - - SkrMenuItem { - height: ! menu.canAddChildTreeItem ? 0 : undefined - visible: menu.canAddChildTreeItem - action: Action { - id: addChildAction - text: qsTr("Add a sub-item") - icon { - source: "qrc:///icons/backup/document-new.svg" - } - enabled: listView.enabled - onTriggered: { - console.log("add child action", - menu.projectId, - menu.treeItemId) - - //save current ids: - listView.currentIndex = menu.index - navigationListStackView.currentItem.treeItemId - = menu.treeItemId - - newItemPopup.projectId = menu.projectId - newItemPopup.treeItemId = menu.treeItemId - newItemPopup.visualIndex = 0 - newItemPopup.createFunction - = afterNewItemTypeIsChosen - newItemPopup.open() - } - - function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { - - // push new view - var newItem = navigationListStackView.push( - stackViewComponent, - { - "projectId": projectId, - "parentId": treeItemId - }, - priv.transitionOperation) - newItem.setCurrent() - - for(var i = 0; i < quantity ; i++){ - addItemAtCurrentParent(pageType) - } - } - } - } - MenuSeparator { - height: ! menu.isMovable ? 0 : undefined - visible: menu.isMovable - } - - SkrMenuItem { - height: ! menu.isMovable ? 0 : undefined - visible: menu.isMovable - action: Action { - id: moveUpAction - text: qsTr("Move up") - //shortcut: "Ctrl+Up" - icon { - source: "qrc:///icons/backup/object-order-raise.svg" - } - enabled: listView.enabled - && menu.index !== 0 - onTriggered: { - console.log("move up action", - menu.projectId, - menu.treeItemId) - - if (temporarilyDisableMove) { - return - } - temporarilyDisableMove = true - temporarilyDisableMoveTimer.start() - - proxyModel.moveUp( - menu.projectId, - menu.treeItemId, - menu.index) - } - } - } - - SkrMenuItem { - height: ! menu.isMovable ? 0 : undefined - visible: menu.isMovable - action: Action { - id: moveDownAction - text: qsTr("Move down") - //shortcut: "Ctrl+Down" - icon { - source: "qrc:///icons/backup/object-order-lower.svg" - } - enabled: menu.index !== visualModel.items.count - 1 - && listView.enabled - - onTriggered: { - console.log("move down action", - menu.projectId, - menu.treeItemId) - - if (temporarilyDisableMove) { - return - } - temporarilyDisableMove = true - temporarilyDisableMoveTimer.start() - - proxyModel.moveDown( - menu.projectId, - menu.treeItemId, - menu.index) - } - } - } - - MenuSeparator { - height: ! menu.isTrashable ? 0 : undefined - visible: menu.isTrashable - } - - SkrMenuItem { - height: ! menu.isTrashable ? 0 : undefined - visible: menu.isTrashable - action: Action { - id: sendToTrashAction - text: qsTr("Send to trash") - //shortcut: "Del" - icon { - source: "qrc:///icons/backup/edit-delete.svg" - color: "transparent" - } - enabled: listView.enabled - && menu.indent !== -1 - onTriggered: { - console.log("sent to trash action", - menu.projectId, - menu.treeItemId) - - swipeDelegate.swipe.close() - removeTreeItemAnimation.start() - proxyModel.trashItemWithChildren( - menu.projectId, - menu.treeItemId) - } - } - } - - MenuSeparator { - height: menu.treeItemId !== 0 ? 0 : undefined - visible: menu.treeItemId === 0 - } - - SkrMenuItem { - height: menu.treeItemId === 0 ? undefined : 0 - visible: menu.treeItemId === 0 - enabled: listView.enabled - && menu.treeItemId === 0 - text: qsTr("Close this project") - icon { - source: "qrc:///icons/backup/document-close.svg" - color: "transparent" - } - onTriggered: { - console.log("close this project", - menu.projectId) - - sidePopupListModel.clear() - Globals.closeProjectCalled( - menu.projectId) - } - } - } - - //---------------------------------------------------------- - - // ListView.onRemove: { - - // var goUpActionCurrentParentIndent = proxyModel.getItemIndent(currentProject, goUpActionCurrentParent) - - // if(goUpActionToBeTriggered && goUpActionCurrentParentIndent + 1 === delegateRoot.indent){ - // paperGoesRightAnimation.start() - // } - - // if(goToChildActionToBeTriggered && goToChildActionCurrentIndent === delegateRoot.indent){ - // paperGoesLeftAnimation.start() - - // } - // } - - //goUpActionToBeTriggered - // ListView.onAdd: { - - // var goUpActionCurrentParentIndent = proxyModel.getItemIndent(currentProject, goUpActionCurrentParent) - - // if(goUpActionToBeTriggered && goUpActionCurrentParentIndent === model.indent){ - // paperAppearsFromLeftAnimation.start() - // } - - // if(goToChildActionToBeTriggered && goToChildActionCurrentIndent + 1 === delegateRoot.indent){ - // paperAppearsFromRightAnimation.start() - // } - // } - } - property int animationDuration: 150 - - states: [ - State { - name: "drag_active" - when: content.Drag.active - - ParentChange { - target: content - parent: Overlay.overlay - } - AnchorChanges { - target: content - anchors { - horizontalCenter: undefined - verticalCenter: undefined - } - } - }, - State { - name: "edit_name" - PropertyChanges { - target: menuButton - visible: false - } - PropertyChanges { - target: titleLabel - visible: false - } - PropertyChanges { - target: swipeDelegate - height: 50 - } - PropertyChanges { - target: labelLayout - visible: false - } - PropertyChanges { - target: labelTextField - visible: false - } - PropertyChanges { - target: titleTextField - visible: true - } - }, - State { - name: "edit_label" - PropertyChanges { - target: menuButton - visible: false - } - PropertyChanges { - target: titleLabel - visible: false - } - PropertyChanges { - target: swipeDelegate - height: 50 - } - PropertyChanges { - target: labelLayout - visible: false - } - PropertyChanges { - target: titleTextField - visible: false - } - PropertyChanges { - target: labelTextField - visible: true - } - }, - - State { - name: "unset_anchors" - AnchorChanges { - target: swipeDelegate - anchors.left: undefined - anchors.right: undefined - } - } - ] - - SequentialAnimation { - id: removeTreeItemAnimation - PropertyAction { - property: "ListView.delayRemove" - value: true - } - - ScriptAction { - script: swipeDelegate.state = "unset_anchors" - } - - NumberAnimation { - target: swipeDelegate - property: "x" - to: listView.width - duration: 250 - easing.type: Easing.InBack - } - - PropertyAction { - property: "ListView.delayRemove" - value: false - } - ScriptAction { - script: proxyModel.invalidate() - } - } - SequentialAnimation { - id: addTreeItemAtEndAnimation - - PropertyAction { - target: swipeDelegate - property: "height" - value: 0 - } - NumberAnimation { - target: swipeDelegate - property: "height" - to: swipeDelegate.height - duration: 250 - easing.type: Easing.InOutQuad - } - } - } - } - - //---------------------------------------------------------------------- - //--- End list item component ------------------------------------------ - //---------------------------------------------------------------------- - } - } - } - } - - Timer { - id: setCurrentTreeItemIdTimer - property int projectId - property int treeItemId - interval: 30 - onTriggered: { - - setCurrentTreeItemId(projectId, treeItemId) - } - } //------------------------------------------------------------------------------------- //---------side Navigation Popup--------------------------------------------------------- @@ -2875,7 +474,6 @@ NavigationListForm { SkrPopup { id: sideNavigationPopup - //enableBehavior: false property int projectId: -2 property int parentId: -2 property int popupId: sidePopupListModel.count - 1 @@ -2913,12 +511,17 @@ NavigationListForm { id: sideNavigationLoader anchors.fill: parent asynchronous: true - sourceComponent: stackViewComponent + source: "NavigationListView.qml" visible: status === Loader.Ready onStatusChanged: { if (status === Loader.Ready) { //sideNavigationLoader.item.hoverEnabled = false + sideNavigationLoader.item.openDocument.connect(openDocument) + sideNavigationLoader.item.openDocumentInAnotherView.connect(openDocumentInAnotherView) + sideNavigationLoader.item.openDocumentInNewWindow.connect(openDocumentInNewWindow) + sideNavigationLoader.item.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) + sideNavigationLoader.item.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) sideNavigationLoader.item.projectId = sideNavigationPopup.projectId sideNavigationLoader.item.parentId = sideNavigationPopup.parentId sideNavigationLoader.item.popupId = sideNavigationPopup.popupId @@ -3070,9 +673,7 @@ NavigationListForm { //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- - NewItemPopup { - id: newItemPopup - } + //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml index b3f285e82..04e242965 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml @@ -75,7 +75,6 @@ FocusScope { Layout.fillWidth: true Layout.fillHeight: true - initialItem: stackViewComponent } } } diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml new file mode 100644 index 000000000..1b22b7491 --- /dev/null +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml @@ -0,0 +1,2457 @@ +import QtQuick 2.15 +import QtQml 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQml.Models 2.15 +import QtQuick.Controls.Material 2.15 +import eu.skribisto.searchtreelistproxymodel 1.0 +import "../../Commons" +import "../../Items" +import "../.." + + +Item { + id: root + property alias listView: listView + property alias visualModel: visualModel + + SKRSearchTreeListProxyModel { + id: navigationProxyModel + showTrashedFilter: false + showNotTrashedFilter: true + navigateByBranchesEnabled: true + } + + property int currentIndex: listView.currentIndex + property int parentId: -2 + property int projectId: -2 + property int treeItemId: -2 + property int popupId: -1 + property bool hoverEnabled: true + + // used to remember the source when moving an item + property int moveSourceInt: -2 + property int moveSourceTreeItemId: -2 + property int moveSourceProjectId: -2 + signal setCurrentTreeItemParentIdCalled(int currentProjectId, int currentParentId) + signal setCurrentTreeItemIdCalled(int currentProjectId, int currentTreeItemId) + signal openDocument(int projectId, int treeItemId) + signal openDocumentInAnotherView(int projectId, int treeItemId) + signal openDocumentInNewWindow(int projectId, int treeItemId) + + focus: false + + function init() { + p_section.parentTitle = qsTr("Projects") + listView.section.delegate = sectionHeading + + + if (projectId > 0 && treeItemId === 0 && parentId === -2) { + //list of project items + + navigationProxyModel.setCurrentTreeItemId(projectId, + treeItemId) + } + else if (treeItemId === -2 && parentId === -2) { + //list of project items + navigationProxyModel.setParentFilter(projectId, + parentId) + } + else if (projectId > 0 && parentId >= 0) { + navigationProxyModel.setParentFilter(projectId, + parentId) + listView.currentIndex = 0 + + } + else if (projectId > 0 && treeItemId >= 0){ + navigationProxyModel.setCurrentTreeItemId( + projectId, treeItemId) + parentId = navigationProxyModel.parentIdFilter + + } + determineSectionTitle() + } + + Component.onDestruction: { + delete navigationProxyModel + } + + function setCurrent() { + priv.currentProjectId = projectId + priv.currentParentId = parentId + + listView.positionViewAtIndex(currentIndex, ListView.Contain) + } + + function setCurrentTreeItemId(projectId, treeItemId) { + navigationProxyModel.setCurrentTreeItemId(projectId, treeItemId) + } + + + onActiveFocusChanged: { + if (activeFocus) { + listView.forceActiveFocus() + } + } + + NewItemPopup { + id: newItemPopup + } + + function determineSectionTitle() { + + var projectId = root.projectId + var parentId = root.parentId + if (parentId === 0 && projectId !== -2) { + var projectTitle = skrData.projectHub().getProjectName( + projectId) + + p_section.parentTitle = projectTitle + listView.section.delegate = sectionHeading + } else if (parentId !== -2 && projectId !== -2) { + var parentTitle = navigationProxyModel.getItemName(projectId, + parentId) + + //console.log("onCurrentParentChanged") + p_section.parentTitle = parentTitle + listView.section.delegate = sectionHeading + } else if (parentId === -2) { + + // show "projects" section + p_section.parentTitle = qsTr("Projects") + listView.section.delegate = sectionHeading + } // clear : + else if (parentId === -2 && root.currentProjectId === -2) { + listView.section.delegate = null + } + } + + + + function checkNone(){ + navigationProxyModel.checkNone() + } + + Item { + id: topDraggingMover + anchors.top: scrollView.top + anchors.right: scrollView.right + anchors.left: scrollView.left + height: 30 + z: 1 + + visible: priv.dragging + + HoverHandler { + onHoveredChanged: { + if (hovered) { + topDraggingMoverTimer.start() + } else { + topDraggingMoverTimer.stop() + } + } + } + + Timer { + id: topDraggingMoverTimer + repeat: true + interval: 10 + onTriggered: { + if (listView.atYBeginning) { + listView.contentY = 0 + } else { + listView.contentY = listView.contentY - 2 + } + } + } + } + + Item { + id: bottomDraggingMover + anchors.bottom: scrollView.bottom + anchors.right: scrollView.right + anchors.left: scrollView.left + height: 30 + z: 1 + + visible: priv.dragging + + HoverHandler { + onHoveredChanged: { + if (hovered) { + bottomDraggingMoverTimer.start() + } else { + bottomDraggingMoverTimer.stop() + } + } + } + + Timer { + id: bottomDraggingMoverTimer + repeat: true + interval: 10 + onTriggered: { + if (listView.atYEnd) { + listView.positionViewAtEnd() + } else { + listView.contentY = listView.contentY + 2 + } + } + } + } + + DropArea { + id: focusZone + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: listView.height - listView.contentHeight + > 0 ? listView.height - listView.contentHeight : 0 + z: 1 + + + keys: ["application/skribisto-tree-item"] + onEntered: { + + //console.log("entered") + //content.sourceIndex = drag.source.visualIndex + visualModel.items.move( + drag.source.visualIndex, + visualModel.items.count - 1) + } + onExited: { + + } + onDropped: { + // console.log("dropped") + if (drop.proposedAction === Qt.MoveAction) { + + //console.log("dropped from :", moveSourceInt, "to :", content.visualIndex) + listView.interactive = true + priv.dragging = false + dropTimer.start() + //proxyModel.moveItemById(moveSourceProjectId, moveSourceTreeItemId, content.treeItemId) + } + } + + + + Timer{ + id: dropTimer + interval: 20 + onTriggered: { + navigationProxyModel.moveItem(moveSourceInt, + visualModel.items.count - 1) + } + } + + + + TapHandler { + onSingleTapped: function(eventPoint) { + if(priv.renaming){ + return + } + + + listView.forceActiveFocus() + console.log("focusZone", "forceActiveFocus") + + var index = listView.currentIndex + var item = listView.itemAtIndex(index) + if (item) { + item.forceActiveFocus() + } else { + listView.forceActiveFocus() + } + + eventPoint.accepted = false + } + grabPermissions: PointerHandler.ApprovesTakeOverByAnything + } + + + + } + + ScrollView { + id: scrollView + clip: true + anchors.fill: parent + focusPolicy: Qt.StrongFocus + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded //scrollBarVerticalPolicy + + ListView { + id: listView + smooth: true + boundsBehavior: Flickable.StopAtBounds + spacing: 1 + + Accessible.name: qsTr("Navigation list") + Accessible.role: Accessible.List + + DelegateModel { + id: visualModel + delegate: listItemComponent + model: navigationProxyModel + } + model: visualModel + + // scrollBar interactivity : + onContentHeightChanged: { + //fix scrollbar visible at start + if (scrollView.height === 0) { + scrollBarVerticalPolicy = ScrollBar.AlwaysOff + return + } + + if (listView.contentHeight > scrollView.height) { + scrollBarVerticalPolicy = ScrollBar.AlwaysOn + } else { + scrollBarVerticalPolicy = ScrollBar.AlwaysOff + } + } + + //----------------------------------------------------------------------------- + property int popupId: root.popupId + property alias proxyModel: navigationProxyModel + + + + Binding { + target: listView + property: "currentIndex" + value: navigationProxyModel.forcedCurrentIndex + } + + //---------------------------------------------------------------------------- + + // move : + addDisplaced: Transition { + enabled: SkrSettings.ePaperSettings.animationEnabled + NumberAnimation { + properties: "x,y" + duration: 250 + } + } + + removeDisplaced: Transition { + enabled: SkrSettings.ePaperSettings.animationEnabled + SequentialAnimation { + PauseAnimation { + duration: 250 + } + NumberAnimation { + properties: "x,y" + duration: 250 + } + } + } + displaced: Transition { + enabled: SkrSettings.ePaperSettings.animationEnabled + NumberAnimation { + properties: "x,y" + duration: 250 + } + } + + moveDisplaced: Transition { + enabled: SkrSettings.ePaperSettings.animationEnabled + NumberAnimation { + properties: "x,y" + duration: 100 + } + } + QtObject { + id: p_section + property string parentTitle: "" + } + + //---------------------------------------------------------------------------- + + + function enableSection(value){ + if(value){ + determineSectionTitle() + } + else{ + listView.section.delegate = null + } + } + + section.property: "indent" + section.criteria: ViewSection.FullString + section.labelPositioning: ViewSection.CurrentLabelAtStart + | ViewSection.InlineLabels + section.delegate: null + + // The delegate for each section header + Component { + id: sectionHeading + Rectangle { + width: listView.width + height: childrenRect.height + color: SkrTheme.buttonBackground + + required property string section + + SkrLabel { + anchors.left: parent.left + anchors.right: parent.right + activeFocusOnTab: false + text: p_section.parentTitle + font.bold: true + horizontalAlignment: Qt.AlignHCenter + color: SkrTheme.buttonForeground + } + } + } + + //---------------------------------------------------------------------- + //--- listview keys ------------------------------------------ + //---------------------------------------------------------------------- + Keys.onShortcutOverride: function(event) { + if ((event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_N) { + event.accepted = true + } + if ((event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_V) { + event.accepted = true + } + } + Keys.onPressed: function(event) { + // if (event.key === Qt.Key_Up) { + // listView.currentItem.forceActiveFocus() + // event.accepted = false + // } + if (event.key === Qt.Key_Backspace + || event.key === Qt.Key_Left) { + console.log("Backspace / Left key pressed") + goUpAction.trigger() + event.accepted = true + } + // paste + if ((event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_V) { + skrData.treeHub().paste(currentProjectId, + currentParentId) + event.accepted = true + } + + // add + if ((event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_N) { + + newItemPopup.projectId = currentProjectId + newItemPopup.treeItemId = currentParentId + newItemPopup.visualIndex = 0 + newItemPopup.createFunction = afterNewItemTypeIsChosen + newItemPopup.open() + + event.accepted = true + } + } + + function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { + + for(var i = 0; i < quantity ; i++){ + addItemAtCurrentParent(pageType) + } + } + + //---------------------------------------------------------------------- + //--- Start list item component ------------------------------------------ + //---------------------------------------------------------------------- + Component { + id: listItemComponent + + SwipeDelegate { + id: swipeDelegate + property int indent: model.indent + property alias content: content + property alias dropArea: dropArea + property alias checkState: selectionCheckBox.checkState + focus: true + + Accessible.name: labelLabel.text.length + === 0 ? titleLabel.text + (model.hasChildren ? " " + qsTr("is a folder") : "") : titleLabel.text + " " + qsTr( + "label:") + " " + labelLabel.text + + (model.hasChildren ? " " + qsTr( + "has child") : "") + Accessible.role: Accessible.ListItem + Accessible.description: qsTr("navigation item") + + anchors { + left: Qt.isQtObject( + parent) ? parent.left : undefined + right: Qt.isQtObject( + parent) ? parent.right : undefined + rightMargin: 5 + } + + height: 40 + + padding: 0 + topPadding: 0 + bottomPadding: 0 + topInset: 0 + bottomInset: 0 + + property int visualIndex: { + return DelegateModel.itemsIndex + } + + Binding { + target: content + property: "visualIndex" + value: visualIndex + } + + onActiveFocusChanged: { + if (listView.currentIndex === model.index + && model.index !== -1 && activeFocus) { + + // root.treeItemId = model.treeItemId + } + } + + function editName() { + priv.renaming = true + titleTextFieldForceActiveFocusTimer.start() + } + + Timer { + id: titleTextFieldForceActiveFocusTimer + repeat: false + interval: 100 + onTriggered: { + state = "edit_name" + titleTextField.forceActiveFocus() + titleTextField.selectAll() + } + } + + function editLabel() { + priv.renaming = true + labelTextFieldForceActiveFocusTimer.start() + } + + Timer { + id: labelTextFieldForceActiveFocusTimer + repeat: false + interval: 100 + onTriggered: { + state = "edit_label" + labelTextField.forceActiveFocus() + labelTextField.selectAll() + } + } + + function configureMenu(){ + menu.index = model.index + menu.treeItemId = model.treeItemId + menu.projectId = model.projectId + menu.type = model.type + menu.isOpenable = model.isOpenable + menu.projectIsActive = model.projectIsActive + menu.canAddChildTreeItem = model.canAddChildTreeItem + menu.canAddSiblingTreeItem = model.canAddSiblingTreeItem + menu.isCopyable = model.isCopyable + menu.isMovable = model.isMovable + menu.isRenamable = model.isRenamable + menu.isTrashable = model.isTrashable + } + + Keys.priority: Keys.AfterItem + + Keys.onShortcutOverride: function(event) { + if (event.key === Qt.Key_F2) { + event.accepted = true + } + if ((event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_N) { + event.accepted = true + } + if ((event.modifiers & Qt.ControlModifier) + && (event.modifiers & Qt.ShiftModifier) + && event.key === Qt.Key_N) { + event.accepted = true + } + if ((event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_C) { + event.accepted = true + } + if (model.isMovable + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_X) { + event.accepted = true + } + if ((event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_V) { + event.accepted = true + } + if (event.key === Qt.Key_Escape + && (swipeDelegate.state == "edit_name" + || swipeDelegate.state == "edit_label")) { + event.accepted = true + } + } + + Keys.onPressed: function(event) { + if (event.key === Qt.Key_Right) { + console.log("Right key pressed") + goToChildAction.trigger() + event.accepted = true + } + if (event.key === Qt.Key_Backspace + || event.key === Qt.Key_Left) { + console.log("Backspace / Left key pressed") + goUpAction.trigger() + event.accepted = true + } + if (model.isOpenable + && (event.modifiers & Qt.AltModifier) + && event.key === Qt.Key_Return) { + console.log("Alt Return key pressed") + configureMenu() + openTreeItemInAnotherViewAction.trigger() + event.accepted = true + } + if (model.isOpenable + && event.key === Qt.Key_Return + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + console.log("Return key pressed") + swipeDelegate.configureMenu() + openTreeItemAction.trigger() + event.accepted = true + } + + // rename + if (model.isRenamable && event.key === Qt.Key_F2 + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + swipeDelegate.configureMenu() + renameAction.trigger() + event.accepted = true + } + + // cut + if (model.isMovable + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_X + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + cutAction.trigger() + event.accepted = true + } + + // copy + if (model.isCopyable + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_C + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + copyAction.trigger() + event.accepted = true + } + + // paste + if (model.canAddChildTreeItem + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_V + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + pasteAction.trigger() + event.accepted = true + } + + // add before + if (model.canAddSiblingTreeItem + && (event.modifiers & Qt.ControlModifier) + && (event.modifiers & Qt.ShiftModifier) + && event.key === Qt.Key_N + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + addBeforeAction.trigger() + event.accepted = true + } + + // add after + if (model.canAddSiblingTreeItem + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_N + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + addAfterAction.trigger() + event.accepted = true + } + + // add child + if (model.canAddChildTreeItem + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_Space + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + addChildAction.trigger() + event.accepted = true + } + + // move up + if (model.isMovable + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_Up + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + configureMenu() + moveUpAction.trigger() + event.accepted = true + } + + // move down + if (model.isMovable + && (event.modifiers & Qt.ControlModifier) + && event.key === Qt.Key_Down + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + swipeDelegate.configureMenu() + moveDownAction.trigger() + event.accepted = true + } + // go up + if (event.key === Qt.Key_Up + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + swipeDelegate.ListView.view.decrementCurrentIndex() + event.accepted = true + } + + // go down + if (event.key === Qt.Key_Down + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + swipeDelegate.ListView.view.incrementCurrentIndex() + event.accepted = true + } + + // send to trash + if (model.isTrashable + && event.key === Qt.Key_Delete + && swipeDelegate.state !== "edit_name" + && swipeDelegate.state !== "edit_label") { + swipeDelegate.configureMenu() + sendToTrashAction.trigger() + event.accepted = true + } + } + + swipe.right: RowLayout { + + height: parent.height + anchors.right: parent.right + + SkrToolButton { + id: sendToTrashToolButton + + focusPolicy: Qt.NoFocus + display: AbstractButton.IconOnly + + Layout.fillHeight: true + Layout.fillWidth: true + + action: sendToTrashAction + } + + Item { + id: stretcher + Layout.fillHeight: true + Layout.fillWidth: true + Layout.minimumWidth: 50 + } + + SkrToolButton { + id: openDocumentToolButton + visible: model.hasChildren + focusPolicy: Qt.NoFocus + display: AbstractButton.IconOnly + + Layout.fillHeight: true + Layout.fillWidth: true + + action: openTreeItemAction + } + + SkrToolButton { + id: openMergedViewToolButton + visible: model.hasChildren + focusPolicy: Qt.NoFocus + display: AbstractButton.IconOnly + + Layout.fillHeight: true + Layout.fillWidth: true + + icon.source: "qrc:///icons/backup/view-list-text.svg" + } + } + + swipe.onOpened: closeSwipeTimer.start() + + // Item { + // anchors.fill: parent + // z:1 + Timer { + id: closeSwipeTimer + interval: 3000 + onTriggered: { + swipeDelegate.swipe.close() + } + } + + HoverHandler { + id: itemHoverHandler + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + enabled: root.hoverEnabled + + //grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything + onHoveredChanged: { + //console.log("item hovered", itemHoverHandler.hovered) + if (priv.dragging || priv.selecting) { + hoveringTimer.stop() + closeSideNavigationListPopupTimer.popupId + = root.popupId + 1 + closeSideNavigationListPopupTimer.start( + ) + } else if (hovered && model.hasChildren) { + + if (!rootWindow.compactMode) { + + hoveringTimer.start() + } + } else if (hovered && !model.hasChildren) { + if (!addSideNavigationListPopupTimer.running) { + hoveringTimer.stop() + addSideNavigationListPopupTimer.stop() + closeSideNavigationListPopupTimer.popupId + = root.popupId + 1 + closeSideNavigationListPopupTimer.start() + } + } else if (!model.hasChildren) { + if (!addSideNavigationListPopupTimer.running) { + hoveringTimer.stop() + closeSideNavigationListPopupTimer.popupId + = root.popupId + 1 + closeSideNavigationListPopupTimer.start() + } + } else { + hoveringTimer.stop() + closeSideNavigationListPopupTimer.stop() + //addSideNavigationListPopupTimer.stop() + } + } + + // } + } + + Timer { + id: hoveringTimer + interval: 50 + onTriggered: { + + addSideNavigationListPopupTimer.projectId = model.projectId + addSideNavigationListPopupTimer.parentId = model.treeItemId + addSideNavigationListPopupTimer.popupId + = root.popupId + addSideNavigationListPopupTimer.parentItem = swipeDelegate + addSideNavigationListPopupTimer.listView = listView + + // console.log("popupId", root.popupId) + addSideNavigationListPopupTimer.start() + } + } + + contentItem: DropArea { + id: dropArea + + keys: ["application/skribisto-tree-item"] + onEntered: { + + console.log("entered", content.visualIndex) + if(content.visualIndex === 0){ + + } + + //content.sourceIndex = drag.source.visualIndex + visualModel.items.move( + drag.source.visualIndex, + content.visualIndex) + } + onExited: { + + } + onDropped: { + // console.log("dropped") + if (drop.proposedAction === Qt.MoveAction) { + + //console.log("dropped from :", moveSourceInt, "to :", content.visualIndex) + cancelDragTimer.stop() + listView.interactive = true + priv.dragging = false + dropTimer.start() + //proxyModel.moveItemById(moveSourceProjectId, moveSourceTreeItemId, content.treeItemId) + } + } + + + + Timer{ + id: dropTimer + interval: 20 + onTriggered: { + swipeDelegate.ListView.view.proxyModel.moveItem(moveSourceInt, + content.visualIndex) + } + } + + + SkrListItemPane { + id: content + property int visualIndex: model.index + property int sourceIndex: -2 + property int projectId: model.projectId + property int treeItemId: model.treeItemId + + property bool isCurrent: model.index === listView.currentIndex ? true : false + + anchors { + horizontalCenter: priv.dragging ? undefined : parent.horizontalCenter + verticalCenter: priv.dragging ? undefined : parent.verticalCenter + } + width: parent.width + height: swipeDelegate.height + + Drag.active: mouseDragHandler.active | touchDragHandler.active + Drag.source: content + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + Drag.keys: ["application/skribisto-tree-item"] + + Drag.supportedActions: Qt.MoveAction + + opacity: mouseDragHandler.active | touchDragHandler.active ? 0.2 : 1.0 + //sDrag.dragType: Drag.Internal + borderWidth: 2 + borderColor: mouseDragHandler.active | content.dragging ? SkrTheme.accent : "transparent" + Behavior on borderColor { + enabled: SkrSettings.ePaperSettings.animationEnabled + ColorAnimation { + duration: 200 + } + } + + property bool dragging: false + DragHandler { + id: mouseDragHandler + acceptedDevices: PointerDevice.Mouse + cursorShape: Qt.ClosedHandCursor + + //xAxis.enabled: false + //grabPermissions: PointerHandler.TakeOverForbidden + onActiveChanged: { + if (active) { + swipeDelegate.ListView.view.enableSection(false) + Globals.touchUsed = false + listView.interactive = false + moveSourceInt = content.visualIndex + moveSourceTreeItemId = content.treeItemId + moveSourceProjectId = content.projectId + priv.dragging = true + cancelDragTimer.stop() + } else { + cancelDragTimer.stop() + priv.dragging = false + content.dragging = false + + content.Drag.drop() + swipeDelegate.ListView.view.enableSection(true) + } + } + enabled: swipeDelegate.ListView.view.popupId === -1 + + onCanceled: { + console.log("drag cancelled") + cancelDragTimer.stop() + priv.dragging = false + content.dragging = false + } + + grabPermissions: PointerHandler.CanTakeOverFromItems + | PointerHandler.CanTakeOverFromAnything | PointerHandler.TakeOverForbidden + + + } + + DragHandler { + id: touchDragHandler + acceptedDevices: PointerDevice.TouchScreen + | PointerDevice.Stylus + + //xAxis.enabled: false + //grabPermissions: PointerHandler.TakeOverForbidden + onActiveChanged: { + if (active) { + swipeDelegate.ListView.view.enableSection(false) + Globals.touchUsed = true + listView.interactive = false + moveSourceInt = content.visualIndex + moveSourceTreeItemId = content.treeItemId + moveSourceProjectId = content.projectId + priv.dragging = true + cancelDragTimer.stop() + } else { + listView.interactive = true + cancelDragTimer.stop() + priv.dragging = false + content.dragging = false + content.Drag.drop() + swipeDelegate.ListView.view.enableSection(true) + } + } + enabled: content.dragging && swipeDelegate.ListView.view.popupId === -1 + + onCanceled: { + cancelDragTimer.stop() + priv.dragging = false + content.dragging = false + } + grabPermissions: PointerHandler.CanTakeOverFromItems + | PointerHandler.CanTakeOverFromAnything + } + + Timer { + id: cancelDragTimer + repeat: false + interval: 3000 + onTriggered: { + priv.dragging = false + content.dragging = false + } + } + + TapHandler { + id: tapHandler + acceptedDevices: PointerDevice.Mouse + + onSingleTapped: function(eventPoint) { + priv.selecting = false + + if (content.dragging || priv.renaming) { + eventPoint.accepted = false + return + } + + Globals.touchUsed = false + swipeDelegate.ListView.view.interactive = false + + + if (skrData.treePropertyHub( + ).getProperty( + model.projectId, + model.treeItemId, + "can_add_child_paper", + "true") === "true") { + swipeDelegate.ListView.view.currentIndex = model.index + goToChildTimer.start() + } else { + swipeDelegate.ListView.view.currentIndex = model.index + } + + swipeDelegate.forceActiveFocus() + eventPoint.accepted = true + } + + onDoubleTapped: function(eventPoint) { + openDocumentTimer.stop() + goToChildTimer.stop() + if (content.dragging || priv.renaming) { + eventPoint.accepted = false + return + } + + Globals.touchUsed = false + swipeDelegate.ListView.view.interactive = false + + + //console.log("double tapped") + swipeDelegate.ListView.view.currentIndex = model.index + + openDocumentTimer.start() + + eventPoint.accepted = true + } + + onGrabChanged: function(transition, point) { + point.accepted = true + } + + grabPermissions: PointerHandler.TakeOverForbidden + } + + TapHandler { + id: touchTapHandler + acceptedDevices: PointerDevice.TouchScreen | PointerDevice.Stylus + + + onSingleTapped: function(eventPoint) { + priv.selecting = false + + if (content.dragging || priv.renaming) { + eventPoint.accepted = false + return + } + + Globals.touchUsed = true + swipeDelegate.ListView.view.interactive = true + + swipeDelegate.ListView.view.currentIndex = model.index + + if (skrData.treePropertyHub( + ).getProperty( + model.projectId, + model.treeItemId, + "can_add_child_paper", + "true") === "true") { + goToChildTimer.start() + } else { + openDocumentTimer.start() + } + + //delegateRoot.forceActiveFocus() + eventPoint.accepted = true + } + + onDoubleTapped: function(eventPoint) { + openDocumentTimer.stop() + goToChildTimer.stop() + if (content.dragging || priv.renaming) { + eventPoint.accepted = false + return + } + + Globals.touchUsed = true + swipeDelegate.ListView.view.interactive = true + + //console.log("double tapped") + swipeDelegate.ListView.view.currentIndex = model.index + + openDocumentTimer.start() + + eventPoint.accepted = true + } + + onLongPressed: { + Globals.touchUsed = true + + // needed to activate the grab handler + + content.dragging = true + listView.interactive = false + cancelDragTimer.start() + priv.selecting = false + } + + onGrabChanged: function(transition, point) { + point.accepted = true + } + + grabPermissions: PointerHandler.TakeOverForbidden + } + Timer { + id: openDocumentTimer + interval: 150 + onTriggered: { + swipeDelegate.configureMenu() + openTreeItemAction.trigger() + } + } + + Timer { + id: goToChildTimer + interval: 150 + onTriggered: { + goToChildAction.trigger() + } + } + + TapHandler { + id: rightClickTapHandler + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + acceptedButtons: Qt.RightButton + onSingleTapped: function(eventPoint) { + listView.interactive = eventPoint.event.device.type + === PointerDevice.Mouse + Globals.touchUsed = false + + if (menu.visible) { + menu.close() + return + } + + listView.currentIndex = model.index + + + + swipeDelegate.configureMenu() + + menu.popup(swipeDelegate, 0, swipeDelegate.height) + + eventPoint.accepted = true + } + + grabPermissions: PointerHandler.TakeOverForbidden + } + + TapHandler { + id: middleClickTapHandler + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + acceptedButtons: Qt.MiddleButton + onSingleTapped: function(eventPoint) { + listView.interactive = eventPoint.event.device.type + === PointerDevice.Mouse + Globals.touchUsed = false + listView.currentIndex = model.index + swipeDelegate.configureMenu() + openTreeItemInAnotherViewAction.trigger() + eventPoint.accepted = true + } + grabPermissions: PointerHandler.TakeOverForbidden + } + + /// without MouseArea, it breaks while dragging and scrolling: + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + onWheel: function(wheel) { + listView.interactive = false + listView.flick( + 0, + wheel.angleDelta.y * 50) + wheel.accepted = true + } + + enabled: listView.interactive === false + } + + + + + Action { + id: goToChildAction + //shortcut: "Right" + enabled: listView.enabled + && listView.currentIndex === model.index + && model.canAddChildTreeItem + + //icon.source: model.hasChildren ? "qrc:///icons/backup/go-next.svg" : (model.canAddChildTreeItem ? "qrc:///icons/backup/list-add.svg" : "") + text: qsTr("See sub-items") + onTriggered: { + console.log("goToChildAction triggered") + + + root.setCurrentTreeItemParentIdCalled(model.projectId, model.treeItemId) + + + + } + } + + Timer { + property int treeItemIdToEdit: -2 + onTreeItemIdToEditChanged: { + if (treeItemIdToEdit !== -2) { + editNameTimer.start() + } + } + + id: editNameTimer + repeat: false + interval: animationDuration + onTriggered: { + var index = swipeDelegate.ListView.view.proxyModel.findVisualIndex( + model.projectId, + treeItemIdToEdit) + if (index !== -2) { + listView.itemAtIndex( + index).editName() + } + treeItemIdToEdit = -2 + } + } + + + contentItem: ColumnLayout { + id: columnLayout3 + anchors.fill: parent + + RowLayout { + id: rowLayout + spacing: 2 + Layout.fillHeight: true + Layout.fillWidth: true + + Rectangle { + id: currentItemIndicator + color: listView.currentIndex === model.index ? "lightsteelblue" : "transparent" + Layout.fillHeight: true + Layout.preferredWidth: 3 + } + Rectangle { + id: openedItemIndicator + color: model.projectId === rootWindow.viewManager.focusedPage.projectId + && model.treeItemId === rootWindow.viewManager.focusedPage.treeItemId ? SkrTheme.accent : "transparent" + Layout.fillHeight: true + Layout.preferredWidth: 3 + } + + SkrToolButton { + id: projectIsBackupIndicator + visible: model.projectIsBackup + && model.type === 'PROJECT' + enabled: true + focusPolicy: Qt.NoFocus + implicitHeight: 32 + implicitWidth: 32 + Layout.maximumHeight: 30 + padding: 0 + rightPadding: 0 + bottomPadding: 0 + leftPadding: 2 + topPadding: 0 + flat: true + onDownChanged: down = false + + icon { + source: "qrc:///icons/backup/tools-media-optical-burn-image.svg" + height: 32 + width: 32 + } + + hoverEnabled: true + ToolTip.delay: 1000 + ToolTip.timeout: 5000 + ToolTip.visible: hovered + ToolTip.text: qsTr("This project is a backup") + } + + SkrCheckBox { + id: selectionCheckBox + visible: priv.selecting + implicitHeight: 32 + implicitWidth: 32 + Layout.maximumHeight: 30 + padding: 0 + rightPadding: 0 + bottomPadding: 0 + leftPadding: 0 + topPadding: 0 + focusPolicy: Qt.NoFocus + + onCheckStateChanged: { + model.checkState = selectionCheckBox.checkState + determineSelectedTreeItems() + } + + Binding on checkState { + value: model.checkState + delayed: true + restoreMode: Binding.RestoreBindingOrValue + } + + function determineSelectedTreeItems() { + priv.selectedTreeItemsIds = [] + priv.selectedTreeItemsIds + = navigationProxyModel.getCheckedIdsList() + priv.selectedProjectId = currentProjectId + + console.log(selectedTreeItemsIds) + } + } + + Item { + Layout.maximumHeight: 30 + implicitHeight: treeItemIconIndicator.implicitHeight + implicitWidth: treeItemIconIndicator.implicitWidth + + SkrToolButton { + id: treeItemIconIndicator + //visible: model.projectIsBackup && model.treeItemId === -1 + enabled: true + focusPolicy: Qt.NoFocus + implicitHeight: 36 + implicitWidth: 36 + padding: 0 + rightPadding: 0 + bottomPadding: 0 + leftPadding: 2 + topPadding: 0 + flat: true + onDownChanged: down = false + + onClicked: { + + } + + icon { + source: model.otherProperties ? getIconUrlFromPageType( + model.type, model.projectId, model.treeItemId) : getIconUrlFromPageType( + model.type, model.projectId, model.treeItemId) + + } + + hoverEnabled: true + + // ToolTip.delay: 1000 + // ToolTip.timeout: 5000 + // ToolTip.visible: hovered + // ToolTip.text: qsTr("") + Item { + id: treeItemIconIndicatorHat + anchors.fill: treeItemIconIndicator + z: 1 + + TapHandler { + + onSingleTapped: function(eventPoint) { + tapHandler.singleTapped( + eventPoint) + } + + onDoubleTapped: function(eventPoint) { + tapHandler.doubleTapped( + eventPoint) + } + + onGrabChanged: function(transition, point) { + tapHandler.grabChanged( + transition, + point) + } + } + + TapHandler { + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + acceptedButtons: Qt.RightButton + + onSingleTapped: function(eventPoint) { + rightClickTapHandler.singleTapped( + eventPoint) + } + + onDoubleTapped: function(eventPoint) { + rightClickTapHandler.doubleTapped( + eventPoint) + } + + onGrabChanged: function(transition, point) { + rightClickTapHandler.grabChanged( + transition, + point) + } + } + + TapHandler { + acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus + acceptedButtons: Qt.MiddleButton + + onSingleTapped: function(eventPoint) { + middleClickTapHandler.singleTapped( + eventPoint) + } + + onDoubleTapped: function(eventPoint) { + middleClickTapHandler.doubleTapped( + eventPoint) + } + + onGrabChanged: function(transition, point) { + middleClickTapHandler.grabChanged( + transition, + point) + } + } + } + } + } + + Rectangle { + color: "transparent" + //border.width: 1 + Layout.fillWidth: true + Layout.fillHeight: true + + ColumnLayout { + id: columnLayout2 + spacing: 1 + anchors.fill: parent + + SkrLabel { + id: titleLabel + activeFocusOnTab: false + + Layout.fillWidth: true + Layout.topMargin: 2 + Layout.leftMargin: 4 + Layout.alignment: Qt.AlignLeft + | Qt.AlignVCenter + font.bold: model.projectIsActive + && model.indent + === -1 ? true : false + text: model.type === 'PROJECT' ? model.projectName : model.title + elide: Text.ElideRight + } + + SkrTextField { + id: labelTextField + visible: false + + Layout.fillWidth: true + Layout.fillHeight: true + text: labelLabel.text + maximumLength: 50 + + placeholderText: qsTr("Enter label") + + onEditingFinished: { + + console.log("editing label finished") + model.label = text + swipeDelegate.state = "" + + //fix bug while new lone child + titleLabel.visible = true + labelLayout.visible = true + priv.renaming = false + } + + //Keys.priority: Keys.AfterItem + Keys.onShortcutOverride: function(event) { event.accepted = (event.key === Qt.Key_Escape)} + Keys.onPressed: function(event) { + if (event.key === Qt.Key_Return) { + console.log("Return key pressed title") + editingFinished( + ) + event.accepted = true + } + if ((event.modifiers & Qt.CtrlModifier) + && event.key + === Qt.Key_Return) { + console.log("Ctrl Return key pressed title") + editingFinished( + ) + event.accepted = true + } + if (event.key === Qt.Key_Escape) { + console.log("Escape key pressed title") + swipeDelegate.state = "" + event.accepted = true + } + } + } + + SkrTextField { + id: titleTextField + visible: false + + Layout.fillWidth: true + Layout.fillHeight: true + text: titleLabel.text + maximumLength: 50 + + placeholderText: qsTr("Enter title") + + onEditingFinished: { + + console.log("editing finished") + if (model.type === "PROJECT") { + //project item + model.projectName = text + } else { + model.title = text + } + + swipeDelegate.state = "" + + //fix bug while new lone child + titleLabel.visible = true + labelLayout.visible = true + priv.renaming = false + } + + //Keys.priority: Keys.AfterItem + Keys.onShortcutOverride: function(event) { event.accepted = (event.key === Qt.Key_Escape)} + Keys.onPressed: function(event) { + if (event.key === Qt.Key_Return) { + console.log("Return key pressed title") + editingFinished( + ) + event.accepted = true + } + if ((event.modifiers & Qt.CtrlModifier) + && event.key + === Qt.Key_Return) { + console.log("Ctrl Return key pressed title") + editingFinished( + ) + event.accepted = true + } + if (event.key === Qt.Key_Escape) { + console.log("Escape key pressed title") + swipeDelegate.state = "" + event.accepted = true + } + } + } + + RowLayout { + id: labelLayout + Layout.fillWidth: true + Layout.leftMargin: 5 + + ListItemAttributes { + id: attributes + treeItemId: model.treeItemId + projectId: model.projectId + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.leftMargin: 4 + Layout.bottomMargin: 2 + } + + SkrLabel { + id: labelLabel + activeFocusOnTab: false + text: model.label + === undefined ? "" : model.label + visible: text.length + === 0 ? false : true + Layout.bottomMargin: 2 + Layout.rightMargin: 4 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + elide: Text.ElideRight + font.italic: true + horizontalAlignment: Qt.AlignRight + Layout.fillWidth: true + } + } + } + } + + SkrLabel { + id: devLabel + activeFocusOnTab: false + text: model.index + "-" + model.treeItemId + + "-" + model.sortOrder + visible: +priv.devModeEnabled + elide: Text.ElideNone + Layout.bottomMargin: 2 + Layout.rightMargin: 4 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + horizontalAlignment: Qt.AlignRight + Layout.fillHeight: true + //Layout.preferredWidth: 30 + } + + SkrToolButton { + id: menuButton + Layout.fillHeight: true + Layout.preferredWidth: 30 + + text: qsTr("Item menu") + icon.source: "qrc:///icons/backup/overflow-menu.svg" + flat: true + focusPolicy: Qt.NoFocus + + onClicked: { + + if (menu.visible) { + menu.close() + return + } + + listView.currentIndex = model.index + swipeDelegate.forceActiveFocus() + + swipeDelegate.configureMenu() + + menu.popup(menuButton, 0, menuButton.height) + } + + visible: itemHoverHandler.hovered + || content.isCurrent + } + + Rectangle { + Layout.fillHeight: true + Layout.preferredWidth: 2 + + color: model.indent === 0 ? Material.color(Material.Indigo) : (model.indent === 1 ? Material.color(Material.LightBlue) : (model.indent === 2 ? Material.color(Material.LightGreen) : (model.indent === 3 ? Material.color(Material.Amber) : (model.indent === 4 ? Material.color(Material.DeepOrange) : Material.color(Material.Teal))))) + } + } + Rectangle { + id: separator + Layout.preferredHeight: 1 + Layout.preferredWidth: content.width / 2 + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + gradient: Gradient { + orientation: Qt.Horizontal + GradientStop { + position: 0.00 + color: "transparent" + } + GradientStop { + position: 0.30 + color: SkrTheme.divider + } + GradientStop { + position: 0.70 + color: SkrTheme.divider + } + GradientStop { + position: 1.00 + color: "transparent" + } + } + } + } + } + // DropArea { + // id: dropArea + // anchors { + // fill: parent + // margins: 10 + // } + // property int sourceIndex: -1 + // property int targetIndex: -1 + // onEntered: { + // sourceIndex = drag.source.DelegateModel.itemsIndex + // targetIndex = dragArea.DelegateModel.itemsIndex + // // var sourceIndex = drag.source.DelegateModel.itemsIndex + // // var targetIndex = dragArea.DelegateModel.itemsIndex + // visualModel.items.move(sourceIndex, targetIndex) + + // // var sourceModelIndex = drag.source.DelegateModel.modelIndex( + // // sourceIndex) + // // var targetModelIndex = dragArea.DelegateModel.modelIndex( + // // targetIndex) + + // // console.log("targetIndex : ", sourceModelIndex.name) + // } + + // onDropped: { + // console.log("onDropped") + // } + // } + + // Shortcut { + // sequences: ["Ctrl+Shift+N"] + // onActivated: addBeforeAction.trigger() + // enabled: root.visible + // } + + + //---------------------------------------------------------- + + // ListView.onRemove: { + + // var goUpActionCurrentParentIndent = proxyModel.getItemIndent(currentProject, goUpActionCurrentParent) + + // if(goUpActionToBeTriggered && goUpActionCurrentParentIndent + 1 === delegateRoot.indent){ + // paperGoesRightAnimation.start() + // } + + // if(goToChildActionToBeTriggered && goToChildActionCurrentIndent === delegateRoot.indent){ + // paperGoesLeftAnimation.start() + + // } + // } + + //goUpActionToBeTriggered + // ListView.onAdd: { + + // var goUpActionCurrentParentIndent = proxyModel.getItemIndent(currentProject, goUpActionCurrentParent) + + // if(goUpActionToBeTriggered && goUpActionCurrentParentIndent === model.indent){ + // paperAppearsFromLeftAnimation.start() + // } + + // if(goToChildActionToBeTriggered && goToChildActionCurrentIndent + 1 === delegateRoot.indent){ + // paperAppearsFromRightAnimation.start() + // } + // } + } + property int animationDuration: 150 + + states: [ + State { + name: "drag_active" + when: content.Drag.active + + // ParentChange { + // target: swipeDelegate + // parent: Overlay.overlay + // } + PropertyChanges { + target: swipeDelegate + z: 3 + } + AnchorChanges { + target: content + anchors { + horizontalCenter: undefined + verticalCenter: undefined + } + } + }, + State { + name: "edit_name" + PropertyChanges { + target: menuButton + visible: false + } + PropertyChanges { + target: titleLabel + visible: false + } + PropertyChanges { + target: swipeDelegate + height: 50 + } + PropertyChanges { + target: labelLayout + visible: false + } + PropertyChanges { + target: labelTextField + visible: false + } + PropertyChanges { + target: titleTextField + visible: true + } + }, + State { + name: "edit_label" + PropertyChanges { + target: menuButton + visible: false + } + PropertyChanges { + target: titleLabel + visible: false + } + PropertyChanges { + target: swipeDelegate + height: 50 + } + PropertyChanges { + target: labelLayout + visible: false + } + PropertyChanges { + target: titleTextField + visible: false + } + PropertyChanges { + target: labelTextField + visible: true + } + }, + + State { + name: "unset_anchors" + AnchorChanges { + target: swipeDelegate + anchors.left: undefined + anchors.right: undefined + } + } + ] + + property alias removeTreeItemAnimation: removeTreeItemAnimation + + SequentialAnimation { + id: removeTreeItemAnimation + PropertyAction { + property: "ListView.delayRemove" + value: true + } + + ScriptAction { + script: swipeDelegate.state = "unset_anchors" + } + + NumberAnimation { + target: swipeDelegate + property: "x" + to: listView.width + duration: 250 + easing.type: Easing.InBack + } + + PropertyAction { + property: "ListView.delayRemove" + value: false + } + ScriptAction { + script: swipeDelegate.ListView.view.proxyModel.invalidate() + } + } + property alias addTreeItemAtEndAnimation: addTreeItemAtEndAnimation + SequentialAnimation { + id: addTreeItemAtEndAnimation + + PropertyAction { + target: swipeDelegate + property: "height" + value: 0 + } + NumberAnimation { + target: swipeDelegate + property: "height" + to: swipeDelegate.height + duration: 250 + easing.type: Easing.InOutQuad + } + } + } + } + + //---------------------------------------------------------------------- + //--- End list item component ------------------------------------------ + //---------------------------------------------------------------------- + } + } + + SkrMenu { + id: menu + //y: menuButton.height + width: 300 + z: 101 + closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape + + property int index + property int treeItemId + property int projectId + property string type + property bool projectIsActive + property bool isOpenable + property bool canAddChildTreeItem + property bool canAddSiblingTreeItem + property bool isCopyable + property bool isMovable + property bool isRenamable + property bool isTrashable + + + onOpened: { + + } + + onClosed: { + + } + + SkrMenuItem { + height: !menu.isOpenable ? 0 : undefined + visible: menu.isOpenable + action: Action { + id: openTreeItemAction + text: qsTr("Open") + //shortcut: "Return" + icon { + source: "qrc:///icons/backup/document-edit.svg" + } + enabled: menu.isOpenable && listView.enabled + + onTriggered: { + console.log("open treeItem action", + menu.projectId, + menu.treeItemId) + root.openDocument(menu.projectId, + menu.treeItemId) + } + } + } + SkrMenuItem { + height: ! menu.isOpenable + || menu.treeItemId === -1 ? 0 : undefined + visible: menu.isOpenable + + action: Action { + id: openTreeItemInAnotherViewAction + text: qsTr("Open in another view") + //shortcut: "Alt+Return" + icon { + source: "qrc:///icons/backup/tab-new.svg" + } + enabled: menu.isOpenable && listView.enabled + onTriggered: { + console.log("open treeItem in another view action", + menu.projectId, + menu.treeItemId) + root.openDocumentInAnotherView( + menu.projectId, + menu.treeItemId) + } + } + } + + SkrMenuItem { + height: ! menu.isOpenable ? 0 : undefined + visible: menu.isOpenable + + action: Action { + id: openTreeItemInNewWindowAction + text: qsTr("Open in a new window") + //shortcut: "Alt+Return" + icon { + source: "qrc:///icons/backup/window-new.svg" + } + enabled: menu.isOpenable && listView.enabled + onTriggered: { + console.log("open treeItem in new window action", + menu.projectId, + menu.treeItemId) + + root.openDocumentInNewWindow( + menu.projectId, + menu.treeItemId) + // setCurrentTreeItemId( menu.projectId, menu.treeItemId) + } + + + + } + } + + SkrMenuItem { + height: menu.treeItemId === 0 ? undefined : 0 + visible: menu.treeItemId === 0 + enabled: menu.projectIsActive === false + && listView.enabled + && menu.treeItemId === 0 + text: qsTr("Set as active project") + icon { + source: "qrc:///icons/backup/tab-new.svg" + } + onTriggered: { + console.log("set active project", + menu.projectId) + skrData.projectHub( + ).setActiveProject( + menu.projectId) + } + } + + MenuSeparator { + height: menu.isRenamable ? undefined : 0 + visible: menu.isRenamable + } + + SkrMenuItem { + height: menu.isRenamable ? undefined : 0 + visible: menu.isRenamable + action: Action { + id: renameAction + text: qsTr("Rename") + icon { + source: "qrc:///icons/backup/edit-rename.svg" + } + enabled: listView.enabled + + onTriggered: { + console.log("rename action", + menu.projectId, + menu.treeItemId) + listView.currentItem.editName() + } + } + } + + SkrMenuItem { + height: ! menu.isRenamable ? 0 : undefined + visible: menu.isRenamable + action: Action { + id: setLabelAction + text: qsTr("Set label") + //shortcut: "F2" + icon { + source: "qrc:///icons/backup/label.svg" + } + enabled: listView.enabled + onTriggered: { + console.log("sel label", + menu.projectId, + menu.treeItemId) + listView.currentItem.editLabel() + } + } + } + + MenuSeparator { + height: ! menu.isMovable + || ! menu.isCopyable + || ! menu.canAddChildTreeItem ? 0 : undefined + visible: menu.isMovable + || menu.isCopyable + || menu.canAddChildTreeItem + } + + SkrMenuItem { + height: ! menu.isMovable ? 0 : undefined + visible: menu.isMovable + action: Action { + id: cutAction + text: qsTr("Cut") + //shortcut: StandardKey.Cut + icon { + source: "qrc:///icons/backup/edit-cut.svg" + } + enabled: listView.enabled + + onTriggered: { + + if (selectedTreeItemsIds.length > 0) { + console.log("cut action", + menu.projectId, + selectedTreeItemsIds) + skrData.treeHub().cut( + menu.projectId, + selectedTreeItemsIds) + } else { + console.log("cut action", + menu.projectId, + menu.treeItemId) + skrData.treeHub().cut( + menu.projectId, + [ menu.treeItemId]) + } + } + } + } + + SkrMenuItem { + height: ! menu.isCopyable ? 0 : undefined + visible: menu.isCopyable + action: Action { + + id: copyAction + text: qsTr("Copy") + //shortcut: StandardKey.Copy + icon { + source: "qrc:///icons/backup/edit-copy.svg" + } + enabled: listView.enabled + + onTriggered: { + if (selectedTreeItemsIds.length > 0) { + console.log("copy action", + menu.projectId, + selectedTreeItemsIds) + skrData.treeHub().copy( + menu.projectId, + selectedTreeItemsIds) + } else { + console.log("copy action", + menu.projectId, + menu.treeItemId) + skrData.treeHub().copy( + menu.projectId, + [ menu.treeItemId]) + } + } + } + } + + SkrMenuItem { + height: ! menu.canAddChildTreeItem ? 0 : undefined + visible: menu.canAddChildTreeItem + action: Action { + + id: pasteAction + text: qsTr("Paste") + //shortcut: StandardKey.Paste + icon { + source: "qrc:///icons/backup/edit-paste.svg" + } + enabled: listView.enabled + + onTriggered: { + console.log("paste action", + menu.projectId, + menu.treeItemId) + var result = skrData.treeHub( + ).paste( + menu.projectId, + menu.treeItemId) + + if (!result.success) { + console.debug( + "paste action: error") + } + } + } + } + + MenuSeparator { + height: !( menu.canAddSiblingTreeItem + || menu.canAddChildTreeItem) ? 0 : undefined + visible: ( menu.canAddSiblingTreeItem + || menu.canAddChildTreeItem) + } + + SkrMenuItem { + height: ! menu.canAddSiblingTreeItem ? 0 : undefined + visible: menu.canAddSiblingTreeItem + action: Action { + id: addBeforeAction + text: qsTr("Add before") + icon { + source: "qrc:///icons/backup/document-new.svg" + } + enabled: listView.enabled + onTriggered: { + console.log("add before action", + menu.projectId, + menu.treeItemId) + + var visualIndex = listView.currentIndex + + newItemPopup.projectId = menu.projectId + newItemPopup.treeItemId = menu.treeItemId + newItemPopup.visualIndex = visualIndex + newItemPopup.createFunction + = afterNewItemTypeIsChosen + newItemPopup.open() + } + + function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { + + for(var i = 0; i < quantity ; i++){ + skrData.treeHub().addTreeItemAbove( + projectId, + treeItemId, + pageType) + + } + listView.itemAtIndex( + visualIndex).editName() + } + } + } + + SkrMenuItem { + height: ! menu.canAddSiblingTreeItem ? 0 : undefined + visible: menu.canAddSiblingTreeItem + action: Action { + id: addAfterAction + text: qsTr("Add after") + icon { + source: "qrc:///icons/backup/document-new.svg" + } + enabled: listView.enabled + onTriggered: { + console.log("add after action", + menu.projectId, + menu.treeItemId) + + var visualIndex = listView.currentIndex + 1 + + newItemPopup.projectId = menu.projectId + newItemPopup.treeItemId = menu.treeItemId + newItemPopup.visualIndex = visualIndex + newItemPopup.createFunction + = afterNewItemTypeIsChosen + newItemPopup.open() + } + + function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { + + for(var i = 0; i < quantity ; i++){ + skrData.treeHub().addTreeItemBelow( + projectId, + treeItemId, + pageType) + + } + listView.itemAtIndex( + visualIndex).editName() + } + + } + } + + SkrMenuItem { + height: ! menu.canAddChildTreeItem ? 0 : undefined + visible: menu.canAddChildTreeItem + action: Action { + id: addChildAction + text: qsTr("Add a sub-item") + icon { + source: "qrc:///icons/backup/document-new.svg" + } + enabled: listView.enabled + onTriggered: { + console.log("add child action", + menu.projectId, + menu.treeItemId) + + //save current ids: + + newItemPopup.projectId = menu.projectId + newItemPopup.treeItemId = menu.treeItemId + newItemPopup.visualIndex = 0 + newItemPopup.createFunction + = afterNewItemTypeIsChosen + newItemPopup.open() + } + + function afterNewItemTypeIsChosen(projectId, treeItemId, visualIndex, pageType, quantity) { + + // push new view + setCurrentTreeItemParentIdCalled(projectId, treeItemId) + + for(var i = 0; i < quantity ; i++){ + skrData.treeHub().addChildTreeItem(projectId, treeItemId, pageType) + } + } + } + } + MenuSeparator { + height: ! menu.isMovable ? 0 : undefined + visible: menu.isMovable + } + + SkrMenuItem { + height: ! menu.isMovable ? 0 : undefined + visible: menu.isMovable + action: Action { + id: moveUpAction + text: qsTr("Move up") + //shortcut: "Ctrl+Up" + icon { + source: "qrc:///icons/backup/object-order-raise.svg" + } + enabled: listView.enabled + && menu.index !== 0 + onTriggered: { + console.log("move up action", + menu.projectId, + menu.treeItemId) + + if (temporarilyDisableMove) { + return + } + temporarilyDisableMove = true + temporarilyDisableMoveTimer.start() + + skrData.treeHub().moveTreeItemUp( + menu.projectId, + menu.treeItemId) + listView.decrementCurrentIndex() + + } + } + } + + SkrMenuItem { + height: ! menu.isMovable ? 0 : undefined + visible: menu.isMovable + action: Action { + id: moveDownAction + text: qsTr("Move down") + //shortcut: "Ctrl+Down" + icon { + source: "qrc:///icons/backup/object-order-lower.svg" + } + enabled: menu.index !== visualModel.items.count - 1 + && listView.enabled + + onTriggered: { + console.log("move down action", + menu.projectId, + menu.treeItemId) + + if (temporarilyDisableMove) { + return + } + temporarilyDisableMove = true + temporarilyDisableMoveTimer.start() + + skrData.treeHub().moveTreeItemDown( + menu.projectId, + menu.treeItemId) + listView.incrementCurrentIndex() + } + } + } + + MenuSeparator { + height: ! menu.isTrashable ? 0 : undefined + visible: menu.isTrashable + } + + SkrMenuItem { + height: ! menu.isTrashable ? 0 : undefined + visible: menu.isTrashable + action: Action { + id: sendToTrashAction + text: qsTr("Send to trash") + //shortcut: "Del" + icon { + source: "qrc:///icons/backup/edit-delete.svg" + color: "transparent" + } + enabled: listView.enabled + && menu.indent !== -1 + onTriggered: { + console.log("sent to trash action", + menu.projectId, + menu.treeItemId) + + listView.currentItem.swipe.close() + listView.currentItem.removeTreeItemAnimation.start() + skrData.treeHub().setTrashedWithChildren( + menu.projectId, + menu.treeItemId, true) + } + } + } + + MenuSeparator { + height: menu.treeItemId !== 0 ? 0 : undefined + visible: menu.treeItemId === 0 + } + + SkrMenuItem { + height: menu.treeItemId === 0 ? undefined : 0 + visible: menu.treeItemId === 0 + enabled: listView.enabled + && menu.treeItemId === 0 + text: qsTr("Close this project") + icon { + source: "qrc:///icons/backup/document-close.svg" + color: "transparent" + } + onTriggered: { + console.log("close this project", + menu.projectId) + + sidePopupListModel.clear() + Globals.closeProjectCalled( + menu.projectId) + } + } + } +} + diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml index 5b6695cb8..4fce4ffcb 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationProjectToolbox.qml @@ -3,7 +3,6 @@ import QtQml 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import eu.skribisto.projecthub 1.0 -import eu.skribisto.searchtreelistproxymodel 1.0 import "../../Items" import "../../Commons" @@ -21,10 +20,6 @@ SkrToolbox { width: scrollView.width height: navigationView.implicitHeight - - navigationListProxyModel: navigationProxyModel - trashedListViewProxyModel: trashedTreeProxyModel - restoreListViewProxyModel: restoreTreeProxyModel } // Connections { @@ -35,7 +30,7 @@ SkrToolbox { // } Connections { target: navigationView - function onOpenDocument(openedProjectId, openedPaperId, _projectId, _treeItemId) { + function onOpenDocument(_projectId, _treeItemId) { viewManager.loadTreeItem(_projectId, _treeItemId) } } @@ -58,32 +53,6 @@ SkrToolbox { navigationView.restoreDocumentList.connect(root.restoreTreeItemList) } - SKRSearchTreeListProxyModel { - id: navigationProxyModel - showTrashedFilter: false - showNotTrashedFilter: true - navigateByBranchesEnabled: true - } - - Connections { - target: skrData.projectHub() - function onActiveProjectChanged(projectId) { - navigationProxyModel.projectIdFilter = projectId - navigationProxyModel.parentIdFilter = -1 - } - } - - SKRSearchTreeListProxyModel { - id: trashedTreeProxyModel - showTrashedFilter: true - showNotTrashedFilter: false - } - - SKRSearchTreeListProxyModel { - id: restoreTreeProxyModel - showTrashedFilter: true - showNotTrashedFilter: false - } function restoreTreeItemList(projectId, treeItemIdList) { diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml index 1ace3042c..ca0f2c647 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/RestoreListView.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.15 import QtQml.Models 2.15 import QtQuick.Controls.Material 2.15 import eu.skribisto.projecthub 1.0 +import eu.skribisto.searchtreelistproxymodel 1.0 import "../../Commons" import "../../Items" @@ -12,11 +13,16 @@ RestoreListViewForm { id: root - property var proxyModel + SKRSearchTreeListProxyModel { + id: restoreTreeProxyModel + showTrashedFilter: true + showNotTrashedFilter: false + } + property var model onModelChanged: { listView.visualModel.model = model - proxyModel.projectIdFilter = currentProjectId + restoreTreeProxyModel.projectIdFilter = currentProjectId } signal openDocument(int openedProjectId, int openedTreeItemId, int projectId, int treeItemId) @@ -25,7 +31,7 @@ RestoreListViewForm { property var trashedChildrenList: [] onTrashedChildrenListChanged: { - proxyModel.treeItemIdListFilter = trashedChildrenList + restoreTreeProxyModel.treeItemIdListFilter = trashedChildrenList } property int parentTreeItemIdToBeRestored: -2 @@ -71,17 +77,17 @@ RestoreListViewForm { listView.escapeKeyPressed.connect( function() {goBackAction.trigger()}) listView.deleteDefinitivelyCalled.connect(root.prepareDeleteDefinitivelyDialog) - listView.proxyModel = proxyModel + listView.proxyModel = restoreTreeProxyModel listView.treeIndentOffset = treeIndentOffset listView.currentProjectId = currentProjectId - proxyModel.projectIdFilter = currentProjectId + restoreTreeProxyModel.projectIdFilter = currentProjectId listView.currentTreeItemId = parentTreeItemIdToBeRestored listView.currentIndex = currentIndex //pre-check same time trashed : - var idList = proxyModel.findIdsTrashedAtTheSameTimeThan(currentProjectId, parentTreeItemIdToBeRestored) - proxyModel.setCheckedIdsList(idList) + var idList = restoreTreeProxyModel.findIdsTrashedAtTheSameTimeThan(currentProjectId, parentTreeItemIdToBeRestored) + restoreTreeProxyModel.setCheckedIdsList(idList) } @@ -148,7 +154,7 @@ RestoreListViewForm { function prepareDeleteDefinitivelyDialog(projectId, treeItemId){ var idList = [treeItemId] - idList = idList.concat(proxyModel.getChildrenList(projectId, treeItemId, true, false)) + idList = idList.concat(skrData.treeHub().getAllChildren(projectId, treeItemId)) //get names var nameList = [] @@ -156,7 +162,7 @@ RestoreListViewForm { for(i = 0 ; i < idList.length ; i++){ var id = idList[i] - nameList.push(proxyModel.getItemName(projectId, id)) + nameList.push(skrData.treeHub().getTitle(projectId, id)) } @@ -190,7 +196,7 @@ RestoreListViewForm { for(i = 0 ; i < treeItemIdList.length ; i++){ var id = treeItemIdList[i] - proxyModel.deleteDefinitively(projectId, id) + skrData.treeHub().removeTreeItem(projectId, id) } } @@ -241,10 +247,10 @@ RestoreListViewForm { onTriggered: { if(selectAllAction.checked){ - proxyModel.checkAll() + restoreTreeProxyModel.checkAll() } else { - proxyModel.checkNone() + restoreTreeProxyModel.checkNone() } @@ -283,7 +289,7 @@ RestoreListViewForm { Binding { target: listView property: "currentIndex" - value: proxyModel.forcedCurrentIndex + value: restoreTreeProxyModel.forcedCurrentIndex } //---------------------------------------------------------------------------- diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/TrashedListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/TrashedListView.qml index 922d0bf71..4df30bd1f 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/TrashedListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/TrashedListView.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.15 import QtQml.Models 2.15 import eu.skribisto.projecthub 1.0 import QtQuick.Controls.Material 2.15 +import eu.skribisto.searchtreelistproxymodel 1.0 import "../../Commons" import "../../Items" import "../.." @@ -11,11 +12,17 @@ import "../.." TrashedListViewForm { id: root - property var proxyModel + SKRSearchTreeListProxyModel { + id: trashedTreeProxyModel + showTrashedFilter: true + showNotTrashedFilter: false + } + + property var model onModelChanged: { listView.visualModel.model = model - proxyModel.projectIdFilter = currentProjectId + trashedTreeProxyModel.projectIdFilter = currentProjectId } signal openDocument(int openedProjectId, int openedTreeItemId, int projectId, int treeItemId) signal openDocumentInAnotherView(int projectId, int treeItemId) @@ -64,9 +71,9 @@ TrashedListViewForm { listView.escapeKeyPressed.connect( function() {goBackAction.trigger()}) listView.deleteDefinitivelyCalled.connect(root.prepareDeleteDefinitivelyDialog) - listView.proxyModel = proxyModel + listView.proxyModel = trashedTreeProxyModel listView.currentProjectId = currentProjectId - proxyModel.projectIdFilter = currentProjectId + trashedTreeProxyModel.projectIdFilter = currentProjectId listView.currentIndex = currentIndex } @@ -122,7 +129,7 @@ TrashedListViewForm { if (projectList.length > 0){ trashProjectComboBox.currentIndex = projectList.length - 1; var _projectId = trashProjectComboBox.valueAt(projectList.length - 1) - proxyModel.projectIdFilter = _projectId + trashedTreeProxyModel.projectIdFilter = _projectId currentProjectId = _projectId } } @@ -134,11 +141,11 @@ TrashedListViewForm { var projectList = skrData.projectHub().getProjectIdList() trashProjectComboBox.currentIndex = projectList.length - 1; var _projectId = trashProjectComboBox.valueAt(projectList.length - 1) - proxyModel.projectIdFilter = _projectId + trashedTreeProxyModel.projectIdFilter = _projectId currentProjectId = _projectId } else { - proxyModel.projectIdFilter = trashProjectComboBox.currentValue + trashedTreeProxyModel.projectIdFilter = trashProjectComboBox.currentValue currentProjectId = trashProjectComboBox.currentValue } @@ -179,7 +186,7 @@ TrashedListViewForm { function prepareDeleteDefinitivelyDialog(projectId, treeItemId){ var idList = [treeItemId] - idList = idList.concat(proxyModel.getChildrenList(projectId, treeItemId, true, false)) + idList = idList.concat(skrData.treeHub().getAllChildren(projectId, treeItemId)) //get names var nameList = [] @@ -187,7 +194,7 @@ TrashedListViewForm { for(i = 0 ; i < idList.length ; i++){ var id = idList[i] - nameList.push(proxyModel.getItemName(projectId, id)) + nameList.push(skrData.treeHub().getTitle(projectId, id)) } @@ -221,7 +228,7 @@ TrashedListViewForm { for(i = 0 ; i < treeItemIdList.length ; i++){ var id = treeItemIdList[i] - proxyModel.deleteDefinitively(projectId, id) + skrData.treeHub().removeTreeItem(projectId, id) } } @@ -303,7 +310,7 @@ TrashedListViewForm { Binding { target: listView property: "currentIndex" - value: proxyModel.forcedCurrentIndex + value: trashedTreeProxyModel.forcedCurrentIndex } //---------------------------------------------------------------------------- diff --git a/src/plugins/navigationProjectToolbox/plugin_qml.qrc b/src/plugins/navigationProjectToolbox/plugin_qml.qrc index a64586de8..ec847edd0 100755 --- a/src/plugins/navigationProjectToolbox/plugin_qml.qrc +++ b/src/plugins/navigationProjectToolbox/plugin_qml.qrc @@ -9,5 +9,6 @@ NavigationProjectToolbox/RestoreListViewForm.ui.qml NavigationProjectToolbox/RestoreListView.qml NavigationProjectToolbox/Navigation.qml + NavigationProjectToolbox/NavigationListView.qml diff --git a/src/plugins/overviewPage/OverviewPage/OverviewPage.qml b/src/plugins/overviewPage/OverviewPage/OverviewPage.qml index 325acf026..a18aa7e48 100755 --- a/src/plugins/overviewPage/OverviewPage/OverviewPage.qml +++ b/src/plugins/overviewPage/OverviewPage/OverviewPage.qml @@ -72,7 +72,7 @@ OverviewPageForm { id: overviewProxyModel showTrashedFilter: false showNotTrashedFilter: true - projectIdFilter: root.projectId + projectIdFilter: skrData.projectHub().activeProjectId onParentIdFilterChanged: { if (overviewProxyModel.parentIdFilter === -2) { diff --git a/src/plugins/projectPage/ProjectPage/ProjectPage.qml b/src/plugins/projectPage/ProjectPage/ProjectPage.qml index d94860f07..2bec8e938 100755 --- a/src/plugins/projectPage/ProjectPage/ProjectPage.qml +++ b/src/plugins/projectPage/ProjectPage/ProjectPage.qml @@ -477,9 +477,17 @@ ProjectPageForm { } } + QtObject{ + id: priv + property bool internalTitleConnectionEnabled: true + } + noteFolderComboBox.onCurrentValueChanged: { if(built){ // clear other folders: + var foldersList = skrData.treeHub().getIdsWithInternalTitle(projectId, "note_folder") + + priv.internalTitleConnectionEnabled = false for(var j in foldersList){ if(skrData.treeHub().getInternalTitle(projectId, foldersList[j]) === "note_folder"){ skrData.treeHub().setInternalTitle(root.projectId, foldersList[j], "") @@ -488,6 +496,7 @@ ProjectPageForm { //set new folder skrData.treeHub().setInternalTitle(root.projectId, noteFolderComboBox.currentValue, "note_folder") + priv.internalTitleConnectionEnabled = true } } @@ -507,7 +516,9 @@ ProjectPageForm { Connections{ target: skrData.treeHub() function onInternalTitleChanged(projectId, treeItemId, newTitle) { - populateNoteFolderComboBox() + if(priv.internalTitleConnectionEnabled){ + populateNoteFolderComboBox() + } } } Connections{ diff --git a/src/plugins/sectionPage/SectionPage/SectionCreationParameters.qml b/src/plugins/sectionPage/SectionPage/SectionCreationParameters.qml index 9689cf010..e2eef04c6 100644 --- a/src/plugins/sectionPage/SectionPage/SectionCreationParameters.qml +++ b/src/plugins/sectionPage/SectionPage/SectionCreationParameters.qml @@ -23,7 +23,7 @@ Item { wheelEnabled: true //visible: SkrSettings.accessibilitySettings.accessibilityEnabled model: ListModel { - ListElement{ value: "book-beginning "; text: qsTr("Beginning of a book") } + ListElement{ value: "book-beginning"; text: qsTr("Beginning of a book") } ListElement{ value: "chapter"; text: qsTr("Chapter") } ListElement{ value: "separator"; text: qsTr("Separator") } ListElement{ value: "book-end"; text: qsTr("End of a book") } diff --git a/src/plugins/sectionPage/sectionpage.cpp b/src/plugins/sectionPage/sectionpage.cpp index 2604c9529..0f92bcfc9 100755 --- a/src/plugins/sectionPage/sectionpage.cpp +++ b/src/plugins/sectionPage/sectionpage.cpp @@ -32,6 +32,11 @@ SectionPage::~SectionPage() // --------------------------------------------------- QString SectionPage::pageTypeIconUrl(int projectId, int treeItemId) const { + + if(projectId == -1 && treeItemId == -1){ + return "qrc:///icons/backup/bookmark-new.svg"; + } + QString section_type = skrdata->treePropertyHub()->getProperty(projectId, treeItemId, "section_type", "separator"); diff --git a/src/translations/skribisto_en_US.ts b/src/translations/skribisto_en_US.ts index 66f225a9e..9c191dfd3 100644 --- a/src/translations/skribisto_en_US.ts +++ b/src/translations/skribisto_en_US.ts @@ -922,43 +922,53 @@ - Projects + New item's parameters + + + NavigationListForm.ui - Navigation list + Add a document - is a folder + Navigation menu - label: + Select + + + NavigationListView - has child + Projects - navigation item + Navigation list - See sub-items + is a folder - Open document + label: - Open document in a new tab + has child - Open document in a window + navigation item + + + + See sub-items @@ -986,17 +996,13 @@ - Open in new window + Open in a new window Set as active project - - Close this project - - Rename @@ -1013,6 +1019,10 @@ Copy + + Paste + + Add before @@ -1038,22 +1048,7 @@ - New item's parameters - - - - - NavigationListForm.ui - - Add a document - - - - Navigation menu - - - - Select + Close this project @@ -1125,7 +1120,7 @@ NewProjectPage - Part + Part %1 @@ -1769,7 +1764,7 @@ - Rename an item + Rename a linked item From 0226f24d7257d65a003094877637d1f5553c4715 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Wed, 4 Aug 2021 01:45:05 +0200 Subject: [PATCH 05/13] Navigation, Test project Navigation: fix cut copy paste Navigation: fix clicking in side popups doing nothing Test project: new test project --- resources/test/skribisto_test_project.skrib | Bin 253952 -> 126976 bytes .../src/models/skrtreelistmodel.cpp | 254 ++---------------- .../src/models/skrtreelistmodel.h | 8 - src/libskribisto-data/src/plmprojecthub.cpp | 22 +- src/libskribisto-data/src/skrtreehub.cpp | 30 ++- src/libskribisto-data/src/skrtreehub.h | 2 + .../tests/auto/writecase/tst_writecase.cpp | 199 ++++++++++++-- .../NavigationList.qml | 1 - .../NavigationListView.qml | 38 ++- 9 files changed, 266 insertions(+), 288 deletions(-) diff --git a/resources/test/skribisto_test_project.skrib b/resources/test/skribisto_test_project.skrib index 1602a0b7258f85ff7ed7dbbd1645c0331fe06b8a..c5fda83ef1608f50ab01f308ad14c2be323e0619 100755 GIT binary patch literal 126976 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 HJ52sRFl1fN literal 253952 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 e>q^Sq41`~zyKcJ)vOpopulate(); + }); + + connect(m_treeHub, + &SKRTreeHub::treeItemsAdded, + this, [this](){ + this->populate(); + }); connect(m_treeHub, &SKRTreeHub::treeItemMoved, - this, - &SKRTreeListModel::refreshAfterDataMove); + this, [this](){ + this->populate(); + }); connect(m_treeHub, &SKRTreeHub::treeItemRemoved, - this, - &SKRTreeListModel::refreshAfterDataRemove); + this, [this](){ + this->populate(); + }); connect(m_treeHub, &SKRTreeHub::indentChanged, @@ -468,239 +477,6 @@ void SKRTreeListModel::clear() this->endResetModel(); } -// -------------------------------------------------------------------- - -void SKRTreeListModel::refreshAfterDataAddition(int projectId, int treeItemId) -{ - this->populate(); - - // find parentIndex and row - // QModelIndex parentIndex; -// int row = 0; - -// auto idList = m_treeHub->getAllIds(projectId); -// auto sortOrdersHash = m_treeHub->getAllSortOrders(projectId); -// auto indentsHash = m_treeHub->getAllIndents(projectId); - - -// int treeItemIndex = idList.indexOf(treeItemId); - -// if (treeItemIndex == 0) { // meaning the parent have to be a project item -// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { -// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); -// item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); -// } - - -// // find project PLMTreeItemItem: -// SKRTreeItem *itemBefore = this->getItem(projectId, -1); - -// int indexBefore = m_allTreeItems.indexOf(itemBefore); - -// int itemIndex = indexBefore + 1; - - -// beginInsertRows(QModelIndex(), itemIndex, itemIndex); - -// m_allTreeItems.insert(itemIndex, -// new SKRTreeItem(projectId, treeItemId, -// indentsHash.value(treeItemId), -// sortOrdersHash.value(treeItemId))); -// this->index(itemIndex, 0, QModelIndex()); -// endInsertRows(); - -// return; -// } - -// // if (!parentFound) { -// // for (int i = treeItemIndex - 1; i >= 0; --i) { -// // int possibleParentId = idList.at(i); -// // int possibleParentIndent = -// // indentsHash.value(possibleParentId); - -// // if (treeItemIndent == possibleParentIndent + 1) { -// // // auto modelIndexList = -// // // this->getModelIndex(projectId, possibleParentId); -// // // if(modelIndexList.isEmpty()){ -// // // qWarning() << Q_FUNC_INFO << "if -// // // treeItemIndent == possibleParentIndent => -// // // modelIndexList.isEmpty()"; -// // // return; -// // // } -// // // parentIndex = modelIndexList.first(); -// // // int parentTreeItemId = -// // // -// // parentIndex.data(SKRTreeItem::Roles::TreeItemIdRole).toInt(); -// // row = treeItemIndex - i; -// // parentFound = true; -// // break; -// // } -// // } -// // } - -// // if (!parentFound) { -// // qWarning() << Q_FUNC_INFO << "parent not found, failsafe used"; -// // this->populate(); -// // return; -// // } - - -// // find item just before in m_allNoteItems to determine item index to insert -// // in: - - -// int idBefore = idList.at(treeItemIndex - 1); -// SKRTreeItem *itemBefore = this->getItem(projectId, idBefore); - -// // needed because m_allTreeItems can have multiple projects : -// int indexBefore = m_allTreeItems.indexOf(itemBefore); - -// int itemIndex = indexBefore + 1; - -// // if(itemIndex >= m_allNoteItems.count() && treeItemIndent == -// // itemBefore->indent() + 1){ -// // qWarning() << Q_FUNC_INFO << "last in the m_allNoteItems list -// // and child of previous item, so failsafe used"; -// // this->populate(); -// // return; -// // } - - -// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { -// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); -// item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); -// } - - -// beginInsertRows(QModelIndex(), itemIndex, itemIndex); - -// m_allTreeItems.insert(itemIndex, new SKRTreeItem(projectId, treeItemId, -// indentsHash.value(treeItemId), -// sortOrdersHash.value(treeItemId))); -// this->index(itemIndex, 0, QModelIndex()); -// this->sortAllTreeItemItems(); -// endInsertRows(); -} - -// -------------------------------------------------------------------- - -void SKRTreeListModel::refreshAfterDataRemove(int projectId, int treeItemId) -{ - Q_UNUSED(projectId) - Q_UNUSED(treeItemId) - - this->populate(); - -// QModelIndex modelIndex = this->getModelIndex(projectId, treeItemId).first(); - -// beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row()); - -// SKRTreeItem *item = this->getItem(projectId, treeItemId); - -// m_allTreeItems.removeAll(item); -// this->sortAllTreeItemItems(); - -// endRemoveRows(); - - -// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { -// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); -// item->invalidateData(SKRTreeItem::Roles::HasChildrenRole); -// this->exploitSignalFromSKRData(projectId, item->treeItemId(), SKRTreeItem::Roles::SortOrderRole); -// this->exploitSignalFromSKRData(projectId, item->treeItemId(), SKRTreeItem::Roles::HasChildrenRole); -// } -} - -// -------------------------------------------------------------------- - -void SKRTreeListModel::refreshAfterDataMove(int sourceProjectId, - QListsourceTreeItemIds, - int targetProjectId, - int targetTreeItemId) -{ - this->populate(); - - // this->resetAllTreeItemsList(); - - // beginMoveRows(QModelIndex(), 0, m_allTreeItems.count(), QModelIndex(), 0); - - // for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { - // item->invalidateData(SKRTreeItem::Roles::SortOrderRole); - // } - // endMoveRows(); - // this->sortAllTreeItemItems(); - - -// QList sourceIndexList; -// int targetIndex = -2; -// int sourceRow = -2; -// int targetRow = -2; - - -// SKRTreeItem *targetItem = this->getItem(targetProjectId, -// targetTreeItemId); - -// if (!targetItem) { -// qWarning() << "refreshAfterDataMove no targetItem"; -// return; -// } - -// SKRTreeItem *sourceItem = this->getItem(sourceProjectId, -// sourceTreeItemIds.first()); - -// if (!sourceItem) { -// qWarning() << "refreshAfterDataMove no sourceItem"; -// return; -// } - -// int i = 0; - -// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { -// if (sourceTreeItemIds.contains(item->treeItemId())) { -// sourceIndexList.append(i); -// } - -// if ((item->treeItemId() == targetTreeItemId) && (targetIndex == -// -2)) { -// targetIndex = i; -// } -// i++; -// } -// int firstSourceIndex = sourceIndexList.first(); - -// sourceRow = firstSourceIndex; -// targetRow = targetIndex; - -// if (sourceRow < targetRow) { -// targetRow += 1; -// } - - -// beginMoveRows(QModelIndex(), sourceRow, sourceRow + -// sourceIndexList.count() - 1, QModelIndex(), targetRow); - - -// this->sortAllTreeItemItems(); -// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { -// item->invalidateData(SKRTreeItem::Roles::SortOrderRole); -// } -// for (SKRTreeItem *item : qAsConst(m_allTreeItems)) { -// //qDebug() << "sortOrder" << item->sortOrder(); - -// this->exploitSignalFromSKRData(item->projectId(), item->treeItemId(), -// SKRTreeItem::Roles::SortOrderRole); -// } - -// this->exploitSignalFromSKRData(targetProjectId, targetTreeItemId, -// SKRTreeItem::Roles::SortOrderRole); -// for(int sourceTreeItemId : sourceTreeItemIds){ -// this->exploitSignalFromSKRData(sourceProjectId, sourceTreeItemId, -// SKRTreeItem::Roles::SortOrderRole); -// } - -// endMoveRows(); -} - // -------------------------------------------------------------------- /// /// \brief SKRTreeListModel::refreshAfterTrashedStateChanged diff --git a/src/libskribisto-data/src/models/skrtreelistmodel.h b/src/libskribisto-data/src/models/skrtreelistmodel.h index 5dc39c45a..7b69574df 100755 --- a/src/libskribisto-data/src/models/skrtreelistmodel.h +++ b/src/libskribisto-data/src/models/skrtreelistmodel.h @@ -81,14 +81,6 @@ private slots: int treeItemId, SKRTreeItem::Roles role); - void refreshAfterDataAddition(int projectId, - int treeItemId); - void refreshAfterDataRemove(int projectId, - int treeItemId); - void refreshAfterDataMove(int sourceProjectId, - QListsourceTreeItemIds, - int targetProjectId, - int targetTreeItemId); void refreshAfterTrashedStateChanged(int projectId, int treeItemId, bool newTrashedState); diff --git a/src/libskribisto-data/src/plmprojecthub.cpp b/src/libskribisto-data/src/plmprojecthub.cpp index 5884ad405..b4a81a8bf 100755 --- a/src/libskribisto-data/src/plmprojecthub.cpp +++ b/src/libskribisto-data/src/plmprojecthub.cpp @@ -230,10 +230,10 @@ SKRResult PLMProjectHub::backupAProject(int projectId, result.addData("projectId", projectId); } - if (projectPath.scheme() == "qrc") { - result = SKRResult(SKRResult::Warning, this, "qrc_projects_cant_back_up"); - result.addData("projectId", projectId); - } +// if (projectPath.scheme() == "qrc") { +// result = SKRResult(SKRResult::Warning, this, "qrc_projects_cant_back_up"); +// result.addData("projectId", projectId); +// } IFOK(result) { @@ -260,7 +260,15 @@ SKRResult PLMProjectHub::backupAProject(int projectId, // determine file base QFileInfo info(projectPath.toLocalFile()); QFileInfo backupFolderInfo(folderPath.toLocalFile()); - QString backupFile = backupFolderInfo.filePath() + "/" + info.completeBaseName(); + + QString backupFile; + if(projectPath.scheme() == "qrc"){ + backupFile = backupFolderInfo.filePath() + "/" + projectPath.path().split("/").last(); + backupFile.remove(".skrib"); + } + else{ + backupFile = backupFolderInfo.filePath() + "/" + info.completeBaseName(); + } // add date and time : QDateTime now = QDateTime::currentDateTime(); @@ -272,7 +280,9 @@ SKRResult PLMProjectHub::backupAProject(int projectId, backupFile = backupFile + "." + type; // firstly, save the project - IFOKDO(result, this->saveProject(projectId)); + if(projectPath.scheme() != "qrc"){ + IFOKDO(result, this->saveProject(projectId)); + } // then create a copy IFOK(result) { diff --git a/src/libskribisto-data/src/skrtreehub.cpp b/src/libskribisto-data/src/skrtreehub.cpp index 8ebbef59f..7b46b5d02 100755 --- a/src/libskribisto-data/src/skrtreehub.cpp +++ b/src/libskribisto-data/src/skrtreehub.cpp @@ -1045,6 +1045,8 @@ SKRResult SKRTreeHub::moveTreeItemAsChildOf(int projectId, int noteId, int targe IFKO(result) { emit errorSent(result); } + + return result; } @@ -1745,26 +1747,52 @@ void SKRTreeHub::copy(int projectId, QListtreeItemIds) SKRResult SKRTreeHub::paste(int projectId, int parentTreeItemId) { SKRResult result(this); + QList treeItemIdList; + int targetProjectId; if (m_cutCopy.type != CutCopy::Type::None) { if (m_cutCopy.type == CutCopy::Type::Cut) { for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { result = this->moveTreeItemAsChildOf(m_cutCopy.projectId, treeItemId, parentTreeItemId); + treeItemIdList << treeItemId; } + targetProjectId = m_cutCopy.projectId; + + // become a Copy after first paste + m_cutCopy.treeItemIds = treeItemIdList; + m_cutCopy.type = CutCopy::Type::Copy; } else if (m_cutCopy.type == CutCopy::Type::Copy) { for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { result = this->duplicateTreeItem(m_cutCopy.projectId, treeItemId); int newTreeItemId = result.getData("treeItemId", -2).toInt(); - + treeItemIdList << newTreeItemId; IFOKDO(result, this->moveTreeItemAsChildOf(m_cutCopy.projectId, newTreeItemId, parentTreeItemId)) } + targetProjectId = m_cutCopy.projectId; } + + + } + IFOK(result){ + result.addData("treeItemIdList", QVariant::fromValue>(treeItemIdList)); + + result = this->renumberSortOrders(projectId); } IFKO(result) { emit errorSent(result); } + IFOK(result) { + if (m_cutCopy.type == CutCopy::Type::Cut) { + emit treeItemMoved(projectId, treeItemIdList, targetProjectId, parentTreeItemId); + } + else if (m_cutCopy.type == CutCopy::Type::Copy) { + emit treeItemsAdded(projectId, treeItemIdList); + } + + emit projectModified(projectId); + } return result; } diff --git a/src/libskribisto-data/src/skrtreehub.h b/src/libskribisto-data/src/skrtreehub.h index 298ce5a4f..43b8af32b 100755 --- a/src/libskribisto-data/src/skrtreehub.h +++ b/src/libskribisto-data/src/skrtreehub.h @@ -309,6 +309,8 @@ private slots: const QDateTime& newDate); void treeItemAdded(int projectId, int treeItemId); + void treeItemsAdded(int projectId, + QList treeItemIds); void treeItemRemoved(int projectId, int treeItemId); void treeItemMoved(int sourceProjectId, diff --git a/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp b/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp index de6625a7b..d7072df22 100755 --- a/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp +++ b/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp @@ -69,6 +69,14 @@ private Q_SLOTS: void cleanUpHtml(); void sortAlphabetically(); + // cut /copy /paste + void simpleCutPaste(); + void multipleCutPaste(); + void simpleCutPasteInTheSameFolder(); + void simpleCopyPaste(); + void multipleCopyPaste(); + void simpleCopyPasteInTheSameFolder(); + private: SKRData *m_data; @@ -194,9 +202,9 @@ void WriteCase::setTitle() void WriteCase::getTitle() { - QString title = skrdata->treeHub()->getTitle(m_currentProjectId, 1); + QString title = skrdata->treeHub()->getTitle(m_currentProjectId, 4); - QCOMPARE(title, QString("First title")); + QCOMPARE(title, QString("Sol")); } void WriteCase::setIndent() @@ -231,21 +239,21 @@ void WriteCase::setTrashed() QSignalSpy spy(skrdata->treeHub(), SIGNAL(trashedChanged(int,int,bool))); SKRResult result = skrdata->treeHub()->setTrashedWithChildren(m_currentProjectId, - 55, + 5, true); QCOMPARE(result.isSuccess(), true); - QVERIFY(spy.count() == 3); + QVERIFY(spy.count() == 5); // make sure the signal was emitted exactly one time QList arguments = spy.takeFirst(); // take the first signal QVERIFY(arguments.at(2).toBool() == true); - bool value = skrdata->treeHub()->getTrashed(m_currentProjectId, 55); + bool value = skrdata->treeHub()->getTrashed(m_currentProjectId, 5); QCOMPARE(value, true); - QDateTime date = skrdata->treeHub()->getTrashedDate(m_currentProjectId, 55); + QDateTime date = skrdata->treeHub()->getTrashedDate(m_currentProjectId, 5); QCOMPARE(date.isValid(), true); } @@ -259,7 +267,7 @@ void WriteCase::restoring() // restoring QSignalSpy spy(skrdata->treeHub(), SIGNAL(trashedChanged(int,int,bool))); - SKRResult result = skrdata->treeHub()->untrashOnlyOneTreeItem(m_currentProjectId, 2); + SKRResult result = skrdata->treeHub()->untrashOnlyOneTreeItem(m_currentProjectId, 5); QCOMPARE(result.isSuccess(), true); @@ -268,11 +276,11 @@ void WriteCase::restoring() QVERIFY(arguments.at(2).toBool() == false); - bool value = skrdata->treeHub()->getTrashed(m_currentProjectId, 2); + bool value = skrdata->treeHub()->getTrashed(m_currentProjectId, 5); QCOMPARE(value, false); - QDateTime date = skrdata->treeHub()->getTrashedDate(m_currentProjectId, 2); + QDateTime date = skrdata->treeHub()->getTrashedDate(m_currentProjectId, 5); QCOMPARE(date.isNull(), true); } @@ -281,7 +289,7 @@ void WriteCase::restoring() void WriteCase::getTrashed() { - bool value = skrdata->treeHub()->getTrashed(m_currentProjectId, 1); + bool value = skrdata->treeHub()->getTrashed(m_currentProjectId, 2); QCOMPARE(value, false); } @@ -308,14 +316,14 @@ void WriteCase::setPrimaryContent() void WriteCase::getPrimaryContent() { - QString value = skrdata->treeHub()->getPrimaryContent(m_currentProjectId, 1); + QString value = skrdata->treeHub()->getPrimaryContent(m_currentProjectId, 16); QTextDocument doc; doc.setHtml(value); - QCOMPARE(doc.toPlainText(), QString("first content test_project_dict_word badword")); + QCOMPARE(doc.toPlainText(), QString("second content test_project_dict_word badword")); // lorem ipsum : - value = skrdata->treeHub()->getPrimaryContent(m_currentProjectId, 8); + value = skrdata->treeHub()->getPrimaryContent(m_currentProjectId, 14); QVERIFY(value.size() > 5000); } @@ -342,9 +350,9 @@ void WriteCase::setCreationDate() void WriteCase::getCreationDate() { - QDateTime value = skrdata->treeHub()->getCreationDate(m_currentProjectId, 1); + QDateTime value = skrdata->treeHub()->getCreationDate(m_currentProjectId, 2); - QCOMPARE(value, QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0))); + QCOMPARE(value, QDateTime(QDate(2010, 1, 1), QTime(1, 1, 1))); } // ------------------------------------------------------------------------------------ @@ -370,7 +378,7 @@ void WriteCase::setUpdateDate() void WriteCase::getUpdateDate() { - QDateTime value = skrdata->treeHub()->getUpdateDate(m_currentProjectId, 1); + QDateTime value = skrdata->treeHub()->getUpdateDate(m_currentProjectId, 2); QCOMPARE(value, QDateTime(QDate(2010, 1, 1), QTime(1, 1, 1))); } @@ -379,9 +387,9 @@ void WriteCase::getUpdateDate() void WriteCase::queue() { - QString value = skrdata->treeHub()->getTitle(m_currentProjectId, 1); + QString value = skrdata->treeHub()->getTitle(m_currentProjectId, 4); - QCOMPARE(value, QString("First title")); + QCOMPARE(value, QString("Sol")); QSignalSpy spy(skrdata->treeHub(), SIGNAL(titleChanged(int,int,QString))); skrdata->treeHub()->setTitle(m_currentProjectId, 1, "new_title1"); @@ -454,9 +462,9 @@ void WriteCase::property() QVERIFY(spy.count() == 1); QString value = skrdata->treePropertyHub()->getProperty(m_currentProjectId, 1, - "test0"); + "test1"); - QCOMPARE(value, QString("value0")); + QCOMPARE(value, QString("value1")); QHash hash = skrdata->treePropertyHub()->getAllIsSystems( m_currentProjectId); @@ -489,7 +497,7 @@ void WriteCase::property_replace() void WriteCase::getTreeLabel() { QString value = skrdata->treePropertyHub()->getProperty(m_currentProjectId, - 1, + 5, "label"); QCOMPARE(value, "this is a label"); @@ -529,10 +537,157 @@ void WriteCase::sortAlphabetically() { QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); QList wantedIds; - wantedIds << 0 << 1 << 2 << 55 << 4 << 57 << 54 << 5 << 6 << 7 << 50 << 51 << 56 << 52 << 3 << 8 << 24; + wantedIds << 0 << 3 << 21 << 22 << 23 << 1 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12; + QCOMPARE(ids, wantedIds); +} + +//----------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------- + +void WriteCase::simpleCutPaste(){ + + + skrdata->treeHub()->cut(m_currentProjectId, QList() << 14); + skrdata->treeHub()->paste(m_currentProjectId, 6); + + + QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: + //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + QList wantedIds; + wantedIds << 0 << 2 << 4 << 5 << 13 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 14 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + QCOMPARE(ids, wantedIds); + + + int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); + + QCOMPARE(indent, 3); + + +} + +void WriteCase::simpleCutPasteInTheSameFolder(){ + skrdata->treeHub()->cut(m_currentProjectId, QList() << 14); + skrdata->treeHub()->paste(m_currentProjectId, 5); + + + QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: + //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + QList wantedIds; + wantedIds << 0 << 2 << 4 << 5 << 13 << 15 << 16 << 14 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + QCOMPARE(ids, wantedIds); + + + int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); + + QCOMPARE(indent, 3); + + +} + +void WriteCase::multipleCutPaste(){ + + + skrdata->treeHub()->cut(m_currentProjectId, QList() << 14 << 15 << 16); + skrdata->treeHub()->paste(m_currentProjectId, 6); + + + QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: + //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + QList wantedIds; + wantedIds << 0 << 2 << 4 << 5 << 13 << 6 << 17 << 18 << 19 << 20 << 14 << 15 << 16 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + QCOMPARE(ids, wantedIds); + + + int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); + QCOMPARE(indent, 3); + indent = skrdata->treeHub()->getIndent(m_currentProjectId, 15); + QCOMPARE(indent, 3); + indent = skrdata->treeHub()->getIndent(m_currentProjectId, 16); + QCOMPARE(indent, 3); +} + + +void WriteCase::simpleCopyPaste(){ + + + skrdata->treeHub()->copy(m_currentProjectId, QList() << 14); + SKRResult result = skrdata->treeHub()->paste(m_currentProjectId, 6); + + QCOMPARE(result.isSuccess(), true); + QList newIdList = result.getData("treeItemIdList", QVariant::fromValue>(QList())).value>(); + + QCOMPARE(newIdList.isEmpty(), false); + + QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: + //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + QList wantedIds; + wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << newIdList.first() << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; QCOMPARE(ids, wantedIds); + + + int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); + + QCOMPARE(indent, 3); + } +void WriteCase::simpleCopyPasteInTheSameFolder(){ + + + skrdata->treeHub()->copy(m_currentProjectId, QList() << 14); + SKRResult result = skrdata->treeHub()->paste(m_currentProjectId, 5); + + QCOMPARE(result.isSuccess(), true); + QList newIdList = result.getData("treeItemIdList", QVariant::fromValue>(QList())).value>(); + + QCOMPARE(newIdList.isEmpty(), false); + + QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: + //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + QList wantedIds; + wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << newIdList.first() << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + QCOMPARE(ids, wantedIds); + + + int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); + + QCOMPARE(indent, 3); + +} + +void WriteCase::multipleCopyPaste(){ + + skrdata->treeHub()->copy(m_currentProjectId, QList() << 14 << 15 << 16); + SKRResult result = skrdata->treeHub()->paste(m_currentProjectId, 6); + + QCOMPARE(result.isSuccess(), true); + QList newIdList = result.getData("treeItemIdList", QVariant::fromValue>(QList())).value>(); + + QCOMPARE(newIdList.count(), 3); + + QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: + //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + QList wantedIds; + wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << newIdList.first() << newIdList.at(1) << newIdList.at(2) << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + QCOMPARE(ids, wantedIds); + + + int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); + QCOMPARE(indent, 3); + indent = skrdata->treeHub()->getIndent(m_currentProjectId, 15); + QCOMPARE(indent, 3); + indent = skrdata->treeHub()->getIndent(m_currentProjectId, 16); + QCOMPARE(indent, 3); +} + + QTEST_GUILESS_MAIN(WriteCase) #include "tst_writecase.moc" diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml index 5c2c0567c..cf449209d 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml @@ -210,7 +210,6 @@ NavigationListForm { } //enabled: onTriggered: { - console.log("depth", navigationListStackView.depth) navigationListStackView.pop(priv.transitionOperation) navigationListStackView.currentItem.setCurrent() diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml index 1b22b7491..81e56f9f3 100644 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml @@ -644,6 +644,7 @@ Item { && event.key === Qt.Key_X && swipeDelegate.state !== "edit_name" && swipeDelegate.state !== "edit_label") { + swipeDelegate.configureMenu() cutAction.trigger() event.accepted = true } @@ -654,6 +655,7 @@ Item { && event.key === Qt.Key_C && swipeDelegate.state !== "edit_name" && swipeDelegate.state !== "edit_label") { + swipeDelegate.configureMenu() copyAction.trigger() event.accepted = true } @@ -664,6 +666,7 @@ Item { && event.key === Qt.Key_V && swipeDelegate.state !== "edit_name" && swipeDelegate.state !== "edit_label") { + swipeDelegate.configureMenu() pasteAction.trigger() event.accepted = true } @@ -1061,10 +1064,22 @@ Item { model.treeItemId, "can_add_child_paper", "true") === "true") { - swipeDelegate.ListView.view.currentIndex = model.index - goToChildTimer.start() + + if( root.popupId >= 0){ + setCurrentTreeItemParentIdCalled(model.projectId , model.treeItemId) + } + else{ + swipeDelegate.ListView.view.currentIndex = model.index + goToChildTimer.start() + } + } else { - swipeDelegate.ListView.view.currentIndex = model.index + if( root.popupId >= 0){ + openDocumentTimer.start() + } + else { + swipeDelegate.ListView.view.currentIndex = model.index + } } swipeDelegate.forceActiveFocus() @@ -1092,7 +1107,7 @@ Item { } onGrabChanged: function(transition, point) { - point.accepted = true + point.accepted = false } grabPermissions: PointerHandler.TakeOverForbidden @@ -1360,6 +1375,7 @@ Item { onCheckStateChanged: { model.checkState = selectionCheckBox.checkState determineSelectedTreeItems() + console.log("onCheckStateChanged") } Binding on checkState { @@ -1374,7 +1390,7 @@ Item { = navigationProxyModel.getCheckedIdsList() priv.selectedProjectId = currentProjectId - console.log(selectedTreeItemsIds) + console.log(priv.selectedTreeItemsIds) } } @@ -2121,13 +2137,13 @@ Item { onTriggered: { - if (selectedTreeItemsIds.length > 0) { + if (priv.selectedTreeItemsIds.length > 0) { console.log("cut action", menu.projectId, - selectedTreeItemsIds) + priv.selectedTreeItemsIds) skrData.treeHub().cut( menu.projectId, - selectedTreeItemsIds) + priv.selectedTreeItemsIds) } else { console.log("cut action", menu.projectId, @@ -2154,13 +2170,13 @@ Item { enabled: listView.enabled onTriggered: { - if (selectedTreeItemsIds.length > 0) { + if (priv.selectedTreeItemsIds.length > 0) { console.log("copy action", menu.projectId, - selectedTreeItemsIds) + priv.selectedTreeItemsIds) skrData.treeHub().copy( menu.projectId, - selectedTreeItemsIds) + priv.selectedTreeItemsIds) } else { console.log("copy action", menu.projectId, From 9a9d38c1290829c2f6a4c095e887a7b63a11e226 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Wed, 4 Aug 2021 01:58:08 +0200 Subject: [PATCH 06/13] Tags, View Tags: forbid clicks passing through a color square View: fix lack of drop visual clue --- src/app/src/qml/Commons/ColorChooser.qml | 6 ++++++ src/app/src/qml/Items/SkrBasePage.qml | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/app/src/qml/Commons/ColorChooser.qml b/src/app/src/qml/Commons/ColorChooser.qml index 55a79faaa..fa1937930 100755 --- a/src/app/src/qml/Commons/ColorChooser.qml +++ b/src/app/src/qml/Commons/ColorChooser.qml @@ -112,6 +112,12 @@ Item { priv.textColorCode = model.textColorCode priv.colorCode = model.colorCode } + + onGrabChanged: function(transition, point) { + point.accepted = false + } + + grabPermissions: PointerHandler.TakeOverForbidden } HoverHandler{ diff --git a/src/app/src/qml/Items/SkrBasePage.qml b/src/app/src/qml/Items/SkrBasePage.qml index 4d6355d66..91499d387 100755 --- a/src/app/src/qml/Items/SkrBasePage.qml +++ b/src/app/src/qml/Items/SkrBasePage.qml @@ -42,8 +42,7 @@ FocusScope { keys: dropAreaEnabled ? ["application/skribisto-tree-item"] : [] onEntered: { - //console.debug("entered page") - if (drag.keys === ["application/skribisto-tree-item"]) { + if (drag.keys.includes("application/skribisto-tree-item")) { dropIndicator.visible = true } } @@ -99,6 +98,8 @@ FocusScope { border.width: 4 } + + Keys.onPressed: function(event) { if (event.key === Qt.Key_F2 && treeItemId != -1) { if (control.projectId !== -1) { From da58eb962a2a5f25d830b79c06baf53ad0a767fc Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Wed, 4 Aug 2021 03:45:29 +0200 Subject: [PATCH 07/13] Navigation Navigation: add visual clue when Cut and Copy items Navigation: set a smoother transition between lists --- .../src/models/skrtreeitem.cpp | 4 + .../src/models/skrtreeitem.h | 31 +++--- .../src/models/skrtreelistmodel.cpp | 13 +++ src/libskribisto-data/src/skrtreehub.cpp | 98 ++++++++++++++----- src/libskribisto-data/src/skrtreehub.h | 11 ++- .../tests/auto/writecase/tst_writecase.cpp | 3 + .../NavigationList.qml | 40 +++++++- .../NavigationListView.qml | 2 +- 8 files changed, 158 insertions(+), 44 deletions(-) diff --git a/src/libskribisto-data/src/models/skrtreeitem.cpp b/src/libskribisto-data/src/models/skrtreeitem.cpp index 26a6397f1..135052153 100755 --- a/src/libskribisto-data/src/models/skrtreeitem.cpp +++ b/src/libskribisto-data/src/models/skrtreeitem.cpp @@ -142,6 +142,10 @@ QVariant SKRTreeItem::data(int role) m_data.insert(role, m_treeHub->getUpdateDate(projectId, treeItemId)); break; + case Roles::CutCopyRole: + m_data.insert(role, m_treeHub->isCutCopy(projectId, treeItemId)); + break; + case Roles::CharCountRole: m_data.insert(role, m_propertyHub->getProperty(projectId, treeItemId, diff --git a/src/libskribisto-data/src/models/skrtreeitem.h b/src/libskribisto-data/src/models/skrtreeitem.h index c230293e2..150c1e60d 100755 --- a/src/libskribisto-data/src/models/skrtreeitem.h +++ b/src/libskribisto-data/src/models/skrtreeitem.h @@ -25,23 +25,24 @@ class EXPORT SKRTreeItem : public QObject { CreationDateRole = Qt::UserRole + 10, UpdateDateRole = Qt::UserRole + 11, ContentDateRole = Qt::UserRole + 12, + CutCopyRole = Qt::UserRole + 13, // implemented in list proxy : - HasChildrenRole = Qt::UserRole + 13, - CharCountRole = Qt::UserRole + 14, - WordCountRole = Qt::UserRole + 15, - CharCountWithChildrenRole = Qt::UserRole + 16, - WordCountWithChildrenRole = Qt::UserRole + 17, - ProjectIsBackupRole = Qt::UserRole + 18, - ProjectIsActiveRole = Qt::UserRole + 19, - IsRenamableRole = Qt::UserRole + 20, - IsMovableRole = Qt::UserRole + 21, - CanAddSiblingTreeItemRole = Qt::UserRole + 22, - CanAddChildTreeItemRole = Qt::UserRole + 23, - IsTrashableRole = Qt::UserRole + 24, - IsOpenableRole = Qt::UserRole + 25, - IsCopyableRole = Qt::UserRole + 26, - OtherPropertiesRole = Qt::UserRole + 27 + HasChildrenRole = Qt::UserRole + 14, + CharCountRole = Qt::UserRole + 15, + WordCountRole = Qt::UserRole + 16, + CharCountWithChildrenRole = Qt::UserRole + 17, + WordCountWithChildrenRole = Qt::UserRole + 18, + ProjectIsBackupRole = Qt::UserRole + 19, + ProjectIsActiveRole = Qt::UserRole + 20, + IsRenamableRole = Qt::UserRole + 21, + IsMovableRole = Qt::UserRole + 22, + CanAddSiblingTreeItemRole = Qt::UserRole + 23, + CanAddChildTreeItemRole = Qt::UserRole + 24, + IsTrashableRole = Qt::UserRole + 25, + IsOpenableRole = Qt::UserRole + 26, + IsCopyableRole = Qt::UserRole + 27, + OtherPropertiesRole = Qt::UserRole + 28 }; Q_ENUM(Roles) diff --git a/src/libskribisto-data/src/models/skrtreelistmodel.cpp b/src/libskribisto-data/src/models/skrtreelistmodel.cpp index 69e316200..f376ddb56 100755 --- a/src/libskribisto-data/src/models/skrtreelistmodel.cpp +++ b/src/libskribisto-data/src/models/skrtreelistmodel.cpp @@ -279,6 +279,10 @@ QVariant SKRTreeListModel::data(const QModelIndex& index, int role) const return item->data(role); } + if (role == SKRTreeItem::Roles::CutCopyRole) { + return item->data(role); + } + return QVariant(); } @@ -443,6 +447,7 @@ QHashSKRTreeListModel::roleNames() const { roles[SKRTreeItem::Roles::IsOpenableRole] = "isOpenable"; roles[SKRTreeItem::Roles::IsCopyableRole] = "isCopyable"; roles[SKRTreeItem::Roles::OtherPropertiesRole] = "otherProperties"; + roles[SKRTreeItem::Roles::CutCopyRole] = "cutCopy"; return roles; } @@ -662,6 +667,14 @@ void SKRTreeListModel::connectToSKRDataSignals() } }); + m_dataConnectionsList << this->connect(skrdata->treeHub(), + &SKRTreeHub::cutCopyChanged, this, + [this](int projectId, int treeItemId, bool value) { + Q_UNUSED(value) + this->exploitSignalFromSKRData(projectId, treeItemId, + SKRTreeItem::Roles::CutCopyRole); + + }); m_dataConnectionsList << this->connect(m_propertyHub, &SKRPropertyHub::propertyChanged, this, diff --git a/src/libskribisto-data/src/skrtreehub.cpp b/src/libskribisto-data/src/skrtreehub.cpp index 7b46b5d02..58ffcefc4 100755 --- a/src/libskribisto-data/src/skrtreehub.cpp +++ b/src/libskribisto-data/src/skrtreehub.cpp @@ -27,7 +27,7 @@ #include SKRTreeHub::SKRTreeHub(QObject *parent) : QObject(parent), m_tableName("tbl_tree"), m_last_added_id(-1), m_cutCopy( - CutCopy()) + CutCopy()) { connect(this, &SKRTreeHub::errorSent, this, &SKRTreeHub::setError, Qt::DirectConnection); @@ -871,7 +871,7 @@ SKRResult SKRTreeHub::moveTreeItem(int sourceProjectId, int sourceTreeItemId, in IFOKDO(result, queries.renumberSortOrder()) - IFKO(result) { + IFKO(result) { queries.rollback(); emit errorSent(result); } @@ -927,7 +927,7 @@ SKRResult SKRTreeHub::moveTreeItemUp(int projectId, int treeItemId) if (this->getIndent(projectId, possibleTargetTreeItemId) == - this->getIndent(projectId, treeItemId)) { + this->getIndent(projectId, treeItemId)) { targetTreeItemId = possibleTargetTreeItemId; break; } @@ -948,7 +948,7 @@ SKRResult SKRTreeHub::moveTreeItemUp(int projectId, int treeItemId) IFOKDO(result, this->moveTreeItem(projectId, treeItemId, targetTreeItemId)) - IFKO(result) { + IFKO(result) { emit errorSent(result); } return result; @@ -991,7 +991,7 @@ SKRResult SKRTreeHub::moveTreeItemDown(int projectId, int treeItemId) if (this->getIndent(projectId, possibleTargetTreeItemId) == - this->getIndent(projectId, treeItemId)) { + this->getIndent(projectId, treeItemId)) { targetTreeItemId = possibleTargetTreeItemId; break; } @@ -1012,7 +1012,7 @@ SKRResult SKRTreeHub::moveTreeItemDown(int projectId, int treeItemId) IFOKDO(result, this->moveTreeItem(projectId, treeItemId, targetTreeItemId, true)) - IFKO(result) { + IFKO(result) { emit errorSent(result); } return result; @@ -1447,7 +1447,7 @@ QListSKRTreeHub::getAllSiblings(int projectId, int treeItemId, bool treeIte // alone, so no siblings if ((minSiblingIndex == treeItemSortedIdIndex) && - (maxSiblingIndex == treeItemSortedIdIndex)) { + (maxSiblingIndex == treeItemSortedIdIndex)) { return siblingsList; } @@ -1731,45 +1731,76 @@ SKRResult SKRTreeHub::duplicateTreeItem(int projectId, int treeItemId) // ---------------------------------------------------------------------------------------- void SKRTreeHub::cut(int projectId, QListtreeItemIds) -{ +{ + // unset old treeItems + m_cutCopy.hasRunOnce = true; + int oldProjectId = m_cutCopy.projectId; + for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { + emit cutCopyChanged(oldProjectId, treeItemId, false); + } + + // set new treeItems m_cutCopy = CutCopy(CutCopy::Cut, projectId, treeItemIds); + + for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { + emit cutCopyChanged(projectId, treeItemId, true); + } } // ---------------------------------------------------------------------------------------- void SKRTreeHub::copy(int projectId, QListtreeItemIds) { + // unset old treeItems + m_cutCopy.hasRunOnce = true; + int oldProjectId = m_cutCopy.projectId; + for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { + emit cutCopyChanged(oldProjectId, treeItemId, false); + } + + // set new treeItems m_cutCopy = CutCopy(CutCopy::Copy, projectId, treeItemIds); + + for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { + emit cutCopyChanged(projectId, treeItemId, true); + } } // ---------------------------------------------------------------------------------------- -SKRResult SKRTreeHub::paste(int projectId, int parentTreeItemId) +SKRResult SKRTreeHub::paste(int targetProjectId, int parentTreeItemId) { SKRResult result(this); QList treeItemIdList; - int targetProjectId; + QList originalTreeItemIdList = m_cutCopy.treeItemIds; + int sourceProjectId = m_cutCopy.projectId; if (m_cutCopy.type != CutCopy::Type::None) { if (m_cutCopy.type == CutCopy::Type::Cut) { - for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { - result = this->moveTreeItemAsChildOf(m_cutCopy.projectId, treeItemId, parentTreeItemId); - treeItemIdList << treeItemId; + + if(targetProjectId == sourceProjectId){ + for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { + result = this->moveTreeItemAsChildOf(sourceProjectId, treeItemId, parentTreeItemId); + treeItemIdList << treeItemId; + } } - targetProjectId = m_cutCopy.projectId; + //TODO: case if projects are different // become a Copy after first paste + m_cutCopy.projectId = targetProjectId; m_cutCopy.treeItemIds = treeItemIdList; m_cutCopy.type = CutCopy::Type::Copy; } else if (m_cutCopy.type == CutCopy::Type::Copy) { for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { - result = this->duplicateTreeItem(m_cutCopy.projectId, treeItemId); - int newTreeItemId = result.getData("treeItemId", -2).toInt(); - treeItemIdList << newTreeItemId; - IFOKDO(result, this->moveTreeItemAsChildOf(m_cutCopy.projectId, newTreeItemId, parentTreeItemId)) + if(targetProjectId == sourceProjectId){ + result = this->duplicateTreeItem(sourceProjectId, treeItemId); + int newTreeItemId = result.getData("treeItemId", -2).toInt(); + treeItemIdList << newTreeItemId; + IFOKDO(result, this->moveTreeItemAsChildOf(sourceProjectId, newTreeItemId, parentTreeItemId)) + } + //TODO: case if projects are different } - targetProjectId = m_cutCopy.projectId; } @@ -1777,7 +1808,15 @@ SKRResult SKRTreeHub::paste(int projectId, int parentTreeItemId) IFOK(result){ result.addData("treeItemIdList", QVariant::fromValue>(treeItemIdList)); - result = this->renumberSortOrders(projectId); + result = this->renumberSortOrders(targetProjectId); + + // unset old treeItems + m_cutCopy.hasRunOnce = true; + for (int treeItemId : qAsConst(originalTreeItemIdList)) { + emit cutCopyChanged(sourceProjectId, treeItemId, false); + } + + } IFKO(result) { emit errorSent(result); @@ -1785,18 +1824,29 @@ SKRResult SKRTreeHub::paste(int projectId, int parentTreeItemId) IFOK(result) { if (m_cutCopy.type == CutCopy::Type::Cut) { - emit treeItemMoved(projectId, treeItemIdList, targetProjectId, parentTreeItemId); + emit treeItemMoved(sourceProjectId, treeItemIdList, targetProjectId, parentTreeItemId); } - else if (m_cutCopy.type == CutCopy::Type::Copy) { - emit treeItemsAdded(projectId, treeItemIdList); + else if (m_cutCopy.type == CutCopy::Type::Copy) { + emit treeItemsAdded(targetProjectId, treeItemIdList); } - emit projectModified(projectId); + emit projectModified(targetProjectId); } return result; } +bool SKRTreeHub::isCutCopy(int projectId, int treeItemId) const{ + + if(m_cutCopy.projectId == projectId && !m_cutCopy.hasRunOnce){ + + return m_cutCopy.treeItemIds.contains(treeItemId); + } + + return false; +} + + // ---------------------------------------------------------------------------------------- SKRResult SKRTreeHub::setTrashedDateToNow(int projectId, int treeItemId) diff --git a/src/libskribisto-data/src/skrtreehub.h b/src/libskribisto-data/src/skrtreehub.h index 43b8af32b..0e54a20c6 100755 --- a/src/libskribisto-data/src/skrtreehub.h +++ b/src/libskribisto-data/src/skrtreehub.h @@ -49,15 +49,19 @@ struct CutCopy this->type = type; this->projectId = projectId; this->treeItemIds = treeItemIds; + this->hasRunOnce = false; } CutCopy() { this->type = Type::None; + this->projectId = -2; + this->hasRunOnce = false; } CutCopy::Type type; int projectId; QList treeItemIds; + bool hasRunOnce; }; Q_DECLARE_METATYPE(CutCopy) @@ -251,7 +255,7 @@ class EXPORT SKRTreeHub : public QObject { QListtreeItemIds); Q_INVOKABLE void copy(int projectId, QListtreeItemIds); - Q_INVOKABLE SKRResult paste(int projectId, + Q_INVOKABLE SKRResult paste(int targetProjectId, int parentTreeItemId); Q_INVOKABLE SKRResult addQuickNote(int projectId, @@ -259,6 +263,7 @@ class EXPORT SKRTreeHub : public QObject { const QString& type, const QString& noteName); + Q_INVOKABLE bool isCutCopy(int projectId, int treeItemId) const; private: SKRResult setTrashedDateToNow(int projectId, @@ -329,6 +334,10 @@ private slots: int sourceTreeItemId, int receiverTreeItemId); + void cutCopyChanged(int projectId, + int treeItemId, + bool value); + protected: SKRResult m_error; diff --git a/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp b/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp index d7072df22..d14ec9e25 100755 --- a/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp +++ b/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp @@ -591,7 +591,10 @@ void WriteCase::multipleCutPaste(){ skrdata->treeHub()->cut(m_currentProjectId, QList() << 14 << 15 << 16); + QCOMPARE(skrdata->treeHub()->isCutCopy(m_currentProjectId, 14), true); + skrdata->treeHub()->paste(m_currentProjectId, 6); + QCOMPARE(skrdata->treeHub()->isCutCopy(m_currentProjectId, 14), false); QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml index cf449209d..c193e83f6 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml @@ -60,7 +60,7 @@ NavigationListForm { Component.onCompleted: { - var newItem = navigationListStackView.push("NavigationListView.qml") + var newItem = navigationListStackView.push("NavigationListView.qml", priv.transitionOperation) newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) newItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) newItem.openDocument.connect(openDocument) @@ -71,6 +71,7 @@ NavigationListForm { + function setCurrentTreeItemParentId(projectId, treeItemParentId) { sidePopupListModel.clear() @@ -113,7 +114,7 @@ NavigationListForm { "projectId": projectId, "treeItemId": ancestorsList[i] }, - StackView.Immediate) + priv.transitionOperation) newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) @@ -176,7 +177,7 @@ NavigationListForm { "projectId": projectId, "treeItemId": ancestorsList[i] }, - StackView.Immediate) + priv.transitionOperation) newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) newItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) newItem.openDocument.connect(openDocument) @@ -464,7 +465,40 @@ NavigationListForm { //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- + navigationListStackView.popEnter: Transition { + XAnimator { + from: (navigationListStackView.mirrored ? -1 : 1) * -navigationListStackView.width + to: 0 + duration: 200 + easing.type: Easing.OutCubic + } + } + navigationListStackView.popExit: Transition { + XAnimator { + from: 0 + to: (navigationListStackView.mirrored ? -1 : 1) * navigationListStackView.width + duration: 200 + easing.type: Easing.OutCubic + } + } + + navigationListStackView.pushEnter: Transition { + XAnimator { + from: (navigationListStackView.mirrored ? -1 : 1) * navigationListStackView.width + to: 0 + duration: 200 + easing.type: Easing.OutCubic + } + } + navigationListStackView.pushExit: Transition { + XAnimator { + from: 0 + to: (navigationListStackView.mirrored ? -1 : 1) * -navigationListStackView.width + duration: 200 + easing.type: Easing.OutCubic + } + } //------------------------------------------------------------------------------------- //---------side Navigation Popup--------------------------------------------------------- //------------------------------------------------------------------------------------- diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml index 81e56f9f3..c43286026 100644 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml @@ -942,7 +942,7 @@ Item { Drag.supportedActions: Qt.MoveAction - opacity: mouseDragHandler.active | touchDragHandler.active ? 0.2 : 1.0 + opacity: mouseDragHandler.active | touchDragHandler.active | model.cutCopy ? 0.2 : 1.0 //sDrag.dragType: Drag.Internal borderWidth: 2 borderColor: mouseDragHandler.active | content.dragging ? SkrTheme.accent : "transparent" From 6c537208468c64b7850a3dc7f60ea9d0b0199fdd Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Wed, 4 Aug 2021 13:17:48 +0200 Subject: [PATCH 08/13] Navigation, Drag drop Navigation: fix transitions Drag Drop: switch to a new way to drag drop in and from the navigation list --- .../NavigationList.qml | 104 +++-- .../NavigationListView.qml | 422 ++++++++++++++---- .../SectionPage/SectionPageForm.ui.qml | 2 +- 3 files changed, 418 insertions(+), 110 deletions(-) diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml index c193e83f6..ba59a079b 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml @@ -34,6 +34,10 @@ NavigationListForm { property int currentProjectId: -2 property int currentTreeItemId: -2 property bool dragging: false + onDraggingChanged: if(dragging){ + sidePopupListModel.clear() + } + property bool renaming: false property bool selecting: false property bool animationEnabled: SkrSettings.ePaperSettings.animationEnabled @@ -48,6 +52,8 @@ NavigationListForm { property int selectedProjectId: -2 property bool devModeEnabled: SkrSettings.devSettings.devModeEnabled + + property bool goingUp: false } onCurrentParentIdChanged: { @@ -81,6 +87,8 @@ NavigationListForm { if (projectId > -1 & treeItemParentId > -1) { var ancestorsList = skrData.treeHub().getAllAncestors( projectId, treeItemParentId) + var currentAncestorsList = skrData.treeHub().getAllAncestors( + projectId, root.currentParentId) } //compare with current parent id @@ -97,6 +105,23 @@ NavigationListForm { navigationListStackView.get(0).init() navigationListStackView.get(0).setCurrent() } + else if (projectId > 0 && treeItemParentId >= 0 && root.currentParentId === ancestorsList[0]) { + + var newItem = navigationListStackView.push("NavigationListView.qml", { + "projectId": projectId, + "parentId": treeItemParentId + }, + priv.transitionOperation) + + + newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) + newItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) + newItem.openDocument.connect(openDocument) + newItem.openDocumentInAnotherView.connect(openDocumentInAnotherView) + newItem.openDocumentInNewWindow.connect(openDocumentInNewWindow) + newItem.init() + newItem.setCurrent() + } else { navigationListStackView.pop(null, priv.transitionOperation) ancestorsList.reverse() @@ -114,7 +139,7 @@ NavigationListForm { "projectId": projectId, "treeItemId": ancestorsList[i] }, - priv.transitionOperation) + StackView.Immediate) newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) @@ -173,11 +198,16 @@ NavigationListForm { navigationListStackView.get(0).setCurrent() for (var i = 1; i < ancestorsList.length; i++) { + var transition = StackView.Immediate + if(i === ancestorsList.length - 1){ + transition = StackView.Transition + } + var newItem = navigationListStackView.push("NavigationListView.qml", { "projectId": projectId, "treeItemId": ancestorsList[i] }, - priv.transitionOperation) + transition) newItem.setCurrentTreeItemParentIdCalled.connect(setCurrentTreeItemParentId) newItem.setCurrentTreeItemIdCalled.connect(setCurrentTreeItemId) newItem.openDocument.connect(openDocument) @@ -211,22 +241,25 @@ NavigationListForm { } //enabled: onTriggered: { + console.log("goUpAction triggered") + priv.goingUp = true navigationListStackView.pop(priv.transitionOperation) navigationListStackView.currentItem.setCurrent() + priv.goingUp = false -// if(navigationListStackView.currentItem.listView.currentItem){ -// navigationListStackView.currentItem.listView.currentItem.forceActiveFocus() -// } + // if(navigationListStackView.currentItem.listView.currentItem){ + // navigationListStackView.currentItem.listView.currentItem.forceActiveFocus() + // } -// var index = navigationListStackView.currentItem.listView.currentIndex -// var item = navigationListStackView.currentItem.listView.itemAtIndex(index) + // var index = navigationListStackView.currentItem.listView.currentIndex + // var item = navigationListStackView.currentItem.listView.itemAtIndex(index) -// if (item) { -// item.forceActiveFocus() -// } else { -// navigationListStackView.currentItem.listView.forceActiveFocus() -// } + // if (item) { + // item.forceActiveFocus() + // } else { + // navigationListStackView.currentItem.listView.forceActiveFocus() + // } priv.currentProjectId = navigationListStackView.currentItem.projectId priv.currentParentId = navigationListStackView.currentItem.parentId @@ -466,6 +499,10 @@ NavigationListForm { //---------------------------------------------------------------------------- navigationListStackView.popEnter: Transition { + onRunningChanged: { + if(running) + console.log("popEnter") + } XAnimator { from: (navigationListStackView.mirrored ? -1 : 1) * -navigationListStackView.width to: 0 @@ -473,17 +510,26 @@ NavigationListForm { easing.type: Easing.OutCubic } } - navigationListStackView.popExit: Transition { - XAnimator { - from: 0 - to: (navigationListStackView.mirrored ? -1 : 1) * navigationListStackView.width - duration: 200 - easing.type: Easing.OutCubic - } + navigationListStackView.popExit: Transition { + onRunningChanged: { + if(running) + console.log("popExit") } + XAnimator { + from: 0 + to: /*(priv.goingUp ? -1 : 1) **/ (navigationListStackView.mirrored ? -1 : 1) * navigationListStackView.width + duration: 200 + easing.type: Easing.OutCubic + } + } + navigationListStackView.pushEnter: Transition { + onRunningChanged: { + if(running) + console.log("pushEnter") + } XAnimator { from: (navigationListStackView.mirrored ? -1 : 1) * navigationListStackView.width to: 0 @@ -491,14 +537,20 @@ NavigationListForm { easing.type: Easing.OutCubic } } - navigationListStackView.pushExit: Transition { - XAnimator { - from: 0 - to: (navigationListStackView.mirrored ? -1 : 1) * -navigationListStackView.width - duration: 200 - easing.type: Easing.OutCubic - } + navigationListStackView.pushExit: Transition { + onRunningChanged: { + if(running) + console.log("pushExit") } + XAnimator { + from: 0 + to: (navigationListStackView.mirrored ? -1 : 1) * -navigationListStackView.width + duration: 200 + easing.type: Easing.OutCubic + } + } + + //------------------------------------------------------------------------------------- //---------side Navigation Popup--------------------------------------------------------- //------------------------------------------------------------------------------------- diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml index c43286026..2a097225b 100644 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml @@ -215,9 +215,9 @@ Item { //console.log("entered") //content.sourceIndex = drag.source.visualIndex - visualModel.items.move( - drag.source.visualIndex, - visualModel.items.count - 1) + // visualModel.items.move( + // drag.source.visualIndex, + // visualModel.items.count - 1) } onExited: { @@ -240,8 +240,8 @@ Item { id: dropTimer interval: 20 onTriggered: { - navigationProxyModel.moveItem(moveSourceInt, - visualModel.items.count - 1) + // navigationProxyModel.moveItem(moveSourceInt, + // visualModel.items.count - 1) } } @@ -471,7 +471,9 @@ Item { id: swipeDelegate property int indent: model.indent property alias content: content - property alias dropArea: dropArea + property alias topDropIndicator: topDropIndicator + property alias middleDropIndicator: middleDropIndicator + property alias bottomDropIndicator: bottomDropIndicator property alias checkState: selectionCheckBox.checkState focus: true @@ -874,47 +876,278 @@ Item { } } - contentItem: DropArea { - id: dropArea + contentItem: Item { + id: dropAreaHolder - keys: ["application/skribisto-tree-item"] - onEntered: { + property int dropAreaSizeDivider: 4 - console.log("entered", content.visualIndex) - if(content.visualIndex === 0){ + Rectangle{ + id: topDropIndicator + z: 1 + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: parent.height / dropAreaHolder.dropAreaSizeDivider + visible : false + onVisibleChanged: { + var previousItem = swipeDelegate.ListView.view.itemAtIndex(model.index -1) + if(previousItem){ + previousItem.bottomDropIndicator.visible = visible + } + } + + gradient: Gradient { + orientation: Gradient.Vertical + GradientStop { + position: 0.00; + color: SkrTheme.accent; + } + GradientStop { + position: 1.00; + color: "transparent"; + } + } + + } + + DropArea { + id: topDropArea + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: parent.height / dropAreaHolder.dropAreaSizeDivider + + + keys:["application/skribisto-tree-item"] + onEntered: function(drag) { + + if(!model.canAddSiblingTreeItem){ + drag.accepted = false + return + } + + if(drag.source.treeItemId === model.treeItemId){ + drag.accepted = false + return + } + + topOnEnteredTimer.start() + } + + Timer{ + id: topOnEnteredTimer + property bool visible: false + interval: 10 + onTriggered: { + + + topDropIndicator.visible = true + + } + } + + onExited: { + topOnExitedTimer.start() + + } + + Timer{ + id: topOnExitedTimer + property bool visible: false + interval: 10 + onTriggered: { + topDropIndicator.visible = false + + } + } + + onDropped: function(drop){ + topDropIndicator.visible = false + skrData.treeHub().moveTreeItem(model.projectId, drag.source.treeItemId, model.treeItemId, false) + + } + + } + + + DropArea { + id: middleDropArea + anchors.fill: parent + anchors.topMargin: topDropArea.height + anchors.bottomMargin: bottomDropArea.height + + keys: ["application/skribisto-tree-item"] + + + onEntered: function(drag) { + console.log("entered", content.visualIndex) + + if(!model.canAddChildTreeItem){ + middleDropArea.visible = false + dropAreaHolder.dropAreaSizeDivider = 2 + drag.accepted = false + return + } + + if(drag.source.treeItemId === model.treeItemId){ + drag.accepted = false + return + } + + + middleDropIndicator.visible = true + + + //content.sourceIndex = drag.source.visualIndex + // visualModel.items.move( + // drag.source.visualIndex, + // content.visualIndex) } + onExited: { + middleDropIndicator.visible = false + // visualModel.items.move( content.visualIndex, moveSourceInt) + + } + + onDropped: function(drop) { + middleDropIndicator.visible = false + // console.log("dropped") + + skrData.treeHub().moveTreeItemAsChildOf(model.projectId, drag.source.treeItemId, model.treeItemId) + + if (drop.proposedAction === Qt.MoveAction) { + + //console.log("dropped from :", moveSourceInt, "to :", content.visualIndex) + // cancelDragTimer.stop() + // listView.interactive = true + // priv.dragging = false + // dropTimer.start() + + } + } + - //content.sourceIndex = drag.source.visualIndex - visualModel.items.move( - drag.source.visualIndex, - content.visualIndex) } - onExited: { + + Rectangle{ + id: middleDropIndicator + z: 1 + + + anchors.fill: parent + anchors.topMargin: topDropArea.height + anchors.bottomMargin: bottomDropArea.height + visible : false + + gradient: Gradient { + orientation: Gradient.Vertical + GradientStop { + position: 0.00; + color: "transparent"; + } + GradientStop { + position: 0.33; + color: SkrTheme.accent; + } + GradientStop { + position: 0.66; + color: SkrTheme.accent; + } + GradientStop { + position: 1.00; + color: "transparent"; + } + } } - onDropped: { - // console.log("dropped") - if (drop.proposedAction === Qt.MoveAction) { - - //console.log("dropped from :", moveSourceInt, "to :", content.visualIndex) - cancelDragTimer.stop() - listView.interactive = true - priv.dragging = false - dropTimer.start() - //proxyModel.moveItemById(moveSourceProjectId, moveSourceTreeItemId, content.treeItemId) + DropArea { + id: bottomDropArea + + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: parent.height / dropAreaHolder.dropAreaSizeDivider + + keys: ["application/skribisto-tree-item"] + onEntered: function(drag) { + + if(!model.canAddSiblingTreeItem){ + drag.accepted = false + return + } + + if(drag.source.treeItemId === model.treeItemId){ + drag.accepted = false + return + } + + bottomOnEnteredTimer.start() + } + + + Timer{ + id: bottomOnEnteredTimer + property bool visible: false + interval: 10 + onTriggered: { + bottomDropIndicator.visible = true + + } + } + + onExited: { + bottomOnExitedTimer.start() + + } + + Timer{ + id: bottomOnExitedTimer + property bool visible: false + interval: 10 + onTriggered: { + bottomDropIndicator.visible = false + + } + } + onDropped: function(drop){ + bottomDropIndicator.visible = false + + skrData.treeHub().moveTreeItem(model.projectId, drag.source.treeItemId, model.treeItemId, true) + } + } + Rectangle{ + id: bottomDropIndicator + z: 1 - Timer{ - id: dropTimer - interval: 20 - onTriggered: { - swipeDelegate.ListView.view.proxyModel.moveItem(moveSourceInt, - content.visualIndex) + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: parent.height / dropAreaHolder.dropAreaSizeDivider + visible : false + onVisibleChanged: { + var nextItem = swipeDelegate.ListView.view.itemAtIndex(model.index + 1) + if(nextItem){ + nextItem.topDropIndicator.visible = visible + } + } + gradient: Gradient { + orientation: Gradient.Vertical + GradientStop { + position: 0.00; + color: "transparent"; + } + GradientStop { + position: 1.00; + color: SkrTheme.accent; + } } + } @@ -924,26 +1157,27 @@ Item { property int sourceIndex: -2 property int projectId: model.projectId property int treeItemId: model.treeItemId + property point dragPoint: Qt.point(width / 2, height / 2) property bool isCurrent: model.index === listView.currentIndex ? true : false - anchors { - horizontalCenter: priv.dragging ? undefined : parent.horizontalCenter - verticalCenter: priv.dragging ? undefined : parent.verticalCenter - } - width: parent.width - height: swipeDelegate.height + anchors.fill: parent + opacity: model.cutCopy ? 0.2 : 1.0 Drag.active: mouseDragHandler.active | touchDragHandler.active Drag.source: content - Drag.hotSpot.x: width / 2 - Drag.hotSpot.y: height / 2 + Drag.hotSpot.x: dragPoint.x + Drag.hotSpot.y: dragPoint.y Drag.keys: ["application/skribisto-tree-item"] Drag.supportedActions: Qt.MoveAction - opacity: mouseDragHandler.active | touchDragHandler.active | model.cutCopy ? 0.2 : 1.0 - //sDrag.dragType: Drag.Internal + Drag.dragType: Drag.Automatic + Drag.mimeData: { + "application/skribisto-tree-item": "Copied text" + } + + borderWidth: 2 borderColor: mouseDragHandler.active | content.dragging ? SkrTheme.accent : "transparent" Behavior on borderColor { @@ -963,28 +1197,33 @@ Item { //grabPermissions: PointerHandler.TakeOverForbidden onActiveChanged: { if (active) { - swipeDelegate.ListView.view.enableSection(false) + //swipeDelegate.ListView.view.enableSection(false) + + Globals.touchUsed = false listView.interactive = false moveSourceInt = content.visualIndex moveSourceTreeItemId = content.treeItemId moveSourceProjectId = content.projectId priv.dragging = true - cancelDragTimer.stop() + + + //cancelDragTimer.stop() } else { - cancelDragTimer.stop() + //cancelDragTimer.stop() priv.dragging = false content.dragging = false content.Drag.drop() - swipeDelegate.ListView.view.enableSection(true) + //swipeDelegate.ListView.view.enableSection(true) } + } enabled: swipeDelegate.ListView.view.popupId === -1 onCanceled: { console.log("drag cancelled") - cancelDragTimer.stop() + //cancelDragTimer.stop() priv.dragging = false content.dragging = false } @@ -1004,27 +1243,27 @@ Item { //grabPermissions: PointerHandler.TakeOverForbidden onActiveChanged: { if (active) { - swipeDelegate.ListView.view.enableSection(false) + //swipeDelegate.ListView.view.enableSection(false) Globals.touchUsed = true listView.interactive = false moveSourceInt = content.visualIndex moveSourceTreeItemId = content.treeItemId moveSourceProjectId = content.projectId priv.dragging = true - cancelDragTimer.stop() + //cancelDragTimer.stop() } else { listView.interactive = true - cancelDragTimer.stop() + //cancelDragTimer.stop() priv.dragging = false content.dragging = false content.Drag.drop() - swipeDelegate.ListView.view.enableSection(true) + //swipeDelegate.ListView.view.enableSection(true) } } enabled: content.dragging && swipeDelegate.ListView.view.popupId === -1 onCanceled: { - cancelDragTimer.stop() + //cancelDragTimer.stop() priv.dragging = false content.dragging = false } @@ -1032,15 +1271,15 @@ Item { | PointerHandler.CanTakeOverFromAnything } - Timer { - id: cancelDragTimer - repeat: false - interval: 3000 - onTriggered: { - priv.dragging = false - content.dragging = false - } - } + // Timer { + // id: cancelDragTimer + // repeat: false + // interval: 3000 + // onTriggered: { + // priv.dragging = false + // content.dragging = false + // } + // } TapHandler { id: tapHandler @@ -1071,7 +1310,7 @@ Item { else{ swipeDelegate.ListView.view.currentIndex = model.index goToChildTimer.start() - } + } } else { if( root.popupId >= 0){ @@ -1110,6 +1349,15 @@ Item { point.accepted = false } + onPressedChanged: { + content.opacity = 0.2 + content.grabToImage(function(result) { + content.Drag.imageSource = result.url + }) + content.opacity = 1.0 + content.dragPoint = point.pressPosition + } + grabPermissions: PointerHandler.TakeOverForbidden } @@ -1180,6 +1428,14 @@ Item { point.accepted = true } + onPressedChanged: { + content.grabToImage(function(result) { + content.Drag.imageSource = result.url + }) + + content.dragPoint = point.pressPosition + } + grabPermissions: PointerHandler.TakeOverForbidden } Timer { @@ -1808,26 +2064,26 @@ Item { property int animationDuration: 150 states: [ - State { - name: "drag_active" - when: content.Drag.active - - // ParentChange { - // target: swipeDelegate - // parent: Overlay.overlay - // } - PropertyChanges { - target: swipeDelegate - z: 3 - } - AnchorChanges { - target: content - anchors { - horizontalCenter: undefined - verticalCenter: undefined - } - } - }, + // State { + // name: "drag_active" + // when: content.Drag.active + + // // ParentChange { + // // target: swipeDelegate + // // parent: Overlay.overlay + // // } + // PropertyChanges { + // target: swipeDelegate + // z: 3 + // } + // AnchorChanges { + // target: content + // anchors { + // horizontalCenter: undefined + // verticalCenter: undefined + // } + // } + // }, State { name: "edit_name" PropertyChanges { diff --git a/src/plugins/sectionPage/SectionPage/SectionPageForm.ui.qml b/src/plugins/sectionPage/SectionPage/SectionPageForm.ui.qml index cb08e73ed..ed3cd87f7 100755 --- a/src/plugins/sectionPage/SectionPage/SectionPageForm.ui.qml +++ b/src/plugins/sectionPage/SectionPage/SectionPageForm.ui.qml @@ -116,7 +116,7 @@ SkrBasePage { wheelEnabled: true //visible: SkrSettings.accessibilitySettings.accessibilityEnabled model: ListModel { - ListElement{ value: "book-beginning "; text: qsTr("Beginning of a book") } + ListElement{ value: "book-beginning"; text: qsTr("Beginning of a book") } ListElement{ value: "chapter"; text: qsTr("Chapter") } ListElement{ value: "separator"; text: qsTr("Separator") } ListElement{ value: "book-end"; text: qsTr("End of a book") } From 013ca33d50f56124af091ad7d1e08f499ce937db Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Wed, 4 Aug 2021 17:07:21 +0200 Subject: [PATCH 09/13] Navigation Navigation: fixed drag drop into another item Navigation: forbid drop into itself Navigation: add drop zone in the empty space at the bottom of the list --- src/app/src/qml/Commons/RelationshipPanel.qml | 14 +- src/app/src/qml/WelcomePage/ExporterPage.qml | 2 +- src/libskribisto-data/src/skrtreehub.cpp | 151 +++++++++----- src/libskribisto-data/src/skrtreehub.h | 28 +-- src/libskribisto-data/tests/CMakeLists.txt | 2 +- .../{writecase => treehubcase}/CMakeLists.txt | 2 +- .../tst_treehubcase.cpp} | 196 +++++++++++------- .../NavigationList.qml | 34 ++- .../NavigationListForm.ui.qml | 9 +- .../NavigationListView.qml | 83 ++++---- src/translations/skribisto_en_US.ts | 12 +- 11 files changed, 332 insertions(+), 201 deletions(-) rename src/libskribisto-data/tests/auto/{writecase => treehubcase}/CMakeLists.txt (95%) rename src/libskribisto-data/tests/auto/{writecase/tst_writecase.cpp => treehubcase/tst_treehubcase.cpp} (78%) diff --git a/src/app/src/qml/Commons/RelationshipPanel.qml b/src/app/src/qml/Commons/RelationshipPanel.qml index 755819e32..902bb0512 100644 --- a/src/app/src/qml/Commons/RelationshipPanel.qml +++ b/src/app/src/qml/Commons/RelationshipPanel.qml @@ -974,7 +974,17 @@ RelationshipPanelForm { keys: ["application/skribisto-tree-item"] - onEntered: { + onEntered: function(drag) { + + if(drag.source.projectId === root.projectId){ + drag.accepted = false + return + } + if(drag.source.treeItemId === root.treeItemId){ + drag.accepted = false + return + } + dropIndicator.visible = true } onExited: { @@ -982,7 +992,7 @@ RelationshipPanelForm { } - onDropped: { + onDropped: function(drop) { if(drop.proposedAction === Qt.MoveAction){ skrData.treeHub().setTreeRelationship(drag.source.projectId, drag.source.treeItemId, root.treeItemId) } diff --git a/src/app/src/qml/WelcomePage/ExporterPage.qml b/src/app/src/qml/WelcomePage/ExporterPage.qml index fbd3b3539..49aa07738 100755 --- a/src/app/src/qml/WelcomePage/ExporterPage.qml +++ b/src/app/src/qml/WelcomePage/ExporterPage.qml @@ -75,7 +75,7 @@ ExporterPageForm { //shortcut: "Ctrl+Shift+Del" icon.source: selectAllTreeItemsAction.checked ? "qrc:///icons/backup/edit-select-none.svg" : "qrc:///icons/backup/edit-select-all.svg" checkable: true - onTriggered: { + onCheckedChanged: { if(selectAllTreeItemsAction.checked){ treeProxyModel.checkAll() diff --git a/src/libskribisto-data/src/skrtreehub.cpp b/src/libskribisto-data/src/skrtreehub.cpp index 58ffcefc4..473ae38e2 100755 --- a/src/libskribisto-data/src/skrtreehub.cpp +++ b/src/libskribisto-data/src/skrtreehub.cpp @@ -27,7 +27,7 @@ #include SKRTreeHub::SKRTreeHub(QObject *parent) : QObject(parent), m_tableName("tbl_tree"), m_last_added_id(-1), m_cutCopy( - CutCopy()) + CutCopy()) { connect(this, &SKRTreeHub::errorSent, this, &SKRTreeHub::setError, Qt::DirectConnection); @@ -871,7 +871,7 @@ SKRResult SKRTreeHub::moveTreeItem(int sourceProjectId, int sourceTreeItemId, in IFOKDO(result, queries.renumberSortOrder()) - IFKO(result) { + IFKO(result) { queries.rollback(); emit errorSent(result); } @@ -927,7 +927,7 @@ SKRResult SKRTreeHub::moveTreeItemUp(int projectId, int treeItemId) if (this->getIndent(projectId, possibleTargetTreeItemId) == - this->getIndent(projectId, treeItemId)) { + this->getIndent(projectId, treeItemId)) { targetTreeItemId = possibleTargetTreeItemId; break; } @@ -948,7 +948,7 @@ SKRResult SKRTreeHub::moveTreeItemUp(int projectId, int treeItemId) IFOKDO(result, this->moveTreeItem(projectId, treeItemId, targetTreeItemId)) - IFKO(result) { + IFKO(result) { emit errorSent(result); } return result; @@ -991,7 +991,7 @@ SKRResult SKRTreeHub::moveTreeItemDown(int projectId, int treeItemId) if (this->getIndent(projectId, possibleTargetTreeItemId) == - this->getIndent(projectId, treeItemId)) { + this->getIndent(projectId, treeItemId)) { targetTreeItemId = possibleTargetTreeItemId; break; } @@ -1012,7 +1012,7 @@ SKRResult SKRTreeHub::moveTreeItemDown(int projectId, int treeItemId) IFOKDO(result, this->moveTreeItem(projectId, treeItemId, targetTreeItemId, true)) - IFKO(result) { + IFKO(result) { emit errorSent(result); } return result; @@ -1020,31 +1020,71 @@ SKRResult SKRTreeHub::moveTreeItemDown(int projectId, int treeItemId) // ---------------------------------------------------------------------------------------- -SKRResult SKRTreeHub::moveTreeItemAsChildOf(int projectId, int noteId, int targetParentId, int wantedSortOrder) +SKRResult SKRTreeHub::moveTreeItemAsChildOf(int sourceProjectId, + int sourceTreeItemId, + int targetProjectId, + int targetParentId, + bool sendSignal, + int wantedSortOrder) { SKRResult result(this); - int validSortOrder = getValidSortOrderAfterTree(projectId, targetParentId); - if (wantedSortOrder == -1) { - wantedSortOrder = validSortOrder; - } + QList childrenList = this->getAllChildren(sourceProjectId, sourceTreeItemId); + int originalSourceIndent = this->getIndent(sourceProjectId, sourceTreeItemId); - if (wantedSortOrder > validSortOrder) { - result = SKRResult(SKRResult::Critical, this, "wantedSortOrder_is_outside_scope_of_parent"); - } - IFOK(result) { - result = this->setSortOrder(projectId, noteId, wantedSortOrder); - } - IFOK(result) { - int parentIndent = this->getIndent(projectId, targetParentId); + if (sourceProjectId == targetProjectId) { + int validSortOrder = getValidSortOrderAfterTree(targetProjectId, targetParentId); + + + if (wantedSortOrder == -1) { + wantedSortOrder = validSortOrder; + } + + if (wantedSortOrder > validSortOrder) { + result = SKRResult(SKRResult::Critical, this, "wantedSortOrder_is_outside_scope_of_parent"); + } + IFOK(result) { + result = this->setSortOrder(sourceProjectId, sourceTreeItemId, wantedSortOrder - childrenList.count() - 1); + } + IFOK(result) { + int parentIndent = this->getIndent(targetProjectId, targetParentId); + + result = this->setIndent(sourceProjectId, sourceTreeItemId, parentIndent + 1); + + int i = 1; - result = this->setIndent(projectId, noteId, parentIndent + 1); + for (int childId : qAsConst(childrenList)) { + result = this->setSortOrder(sourceProjectId, childId, wantedSortOrder - childrenList.count() - 1 + i); + i++; + + int orignalSourceChildIndent = this->getIndent(sourceProjectId, childId); + int delta = orignalSourceChildIndent - originalSourceIndent; + + result = this->setIndent(sourceProjectId, childId, parentIndent + 1 + delta); + } + } + } + else { + // TODO: move between different projects } IFKO(result) { emit errorSent(result); } + IFOK(result) { + this->renumberSortOrders(sourceProjectId); + emit treeItemMoved(sourceProjectId, + QList() << sourceTreeItemId << childrenList, + targetProjectId, + targetParentId); + emit projectModified(sourceProjectId); + + if (sourceProjectId != targetProjectId) { + this->renumberSortOrders(targetProjectId); + emit projectModified(targetProjectId); + } + } return result; @@ -1447,7 +1487,7 @@ QListSKRTreeHub::getAllSiblings(int projectId, int treeItemId, bool treeIte // alone, so no siblings if ((minSiblingIndex == treeItemSortedIdIndex) && - (maxSiblingIndex == treeItemSortedIdIndex)) { + (maxSiblingIndex == treeItemSortedIdIndex)) { return siblingsList; } @@ -1731,10 +1771,11 @@ SKRResult SKRTreeHub::duplicateTreeItem(int projectId, int treeItemId) // ---------------------------------------------------------------------------------------- void SKRTreeHub::cut(int projectId, QListtreeItemIds) -{ +{ // unset old treeItems m_cutCopy.hasRunOnce = true; int oldProjectId = m_cutCopy.projectId; + for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { emit cutCopyChanged(oldProjectId, treeItemId, false); } @@ -1754,6 +1795,7 @@ void SKRTreeHub::copy(int projectId, QListtreeItemIds) // unset old treeItems m_cutCopy.hasRunOnce = true; int oldProjectId = m_cutCopy.projectId; + for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { emit cutCopyChanged(oldProjectId, treeItemId, false); } @@ -1770,53 +1812,58 @@ void SKRTreeHub::copy(int projectId, QListtreeItemIds) SKRResult SKRTreeHub::paste(int targetProjectId, int parentTreeItemId) { - SKRResult result(this); + SKRResult result(this); QList treeItemIdList; QList originalTreeItemIdList = m_cutCopy.treeItemIds; - int sourceProjectId = m_cutCopy.projectId; + int sourceProjectId = m_cutCopy.projectId; if (m_cutCopy.type != CutCopy::Type::None) { if (m_cutCopy.type == CutCopy::Type::Cut) { - - if(targetProjectId == sourceProjectId){ + if (targetProjectId == sourceProjectId) { for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { - result = this->moveTreeItemAsChildOf(sourceProjectId, treeItemId, parentTreeItemId); + result = this->moveTreeItemAsChildOf(sourceProjectId, + treeItemId, + targetProjectId, + parentTreeItemId, + false); + result = this->renumberSortOrders(targetProjectId); treeItemIdList << treeItemId; } } - //TODO: case if projects are different + + // TODO: case if projects are different // become a Copy after first paste - m_cutCopy.projectId = targetProjectId; + m_cutCopy.projectId = targetProjectId; m_cutCopy.treeItemIds = treeItemIdList; - m_cutCopy.type = CutCopy::Type::Copy; + m_cutCopy.type = CutCopy::Type::Copy; } else if (m_cutCopy.type == CutCopy::Type::Copy) { for (int treeItemId : qAsConst(m_cutCopy.treeItemIds)) { - if(targetProjectId == sourceProjectId){ + if (targetProjectId == sourceProjectId) { result = this->duplicateTreeItem(sourceProjectId, treeItemId); int newTreeItemId = result.getData("treeItemId", -2).toInt(); treeItemIdList << newTreeItemId; - IFOKDO(result, this->moveTreeItemAsChildOf(sourceProjectId, newTreeItemId, parentTreeItemId)) + IFOKDO(result, + this->moveTreeItemAsChildOf(sourceProjectId, newTreeItemId, targetProjectId, + parentTreeItemId, + false)) + result = this->renumberSortOrders(targetProjectId); } - //TODO: case if projects are different + + // TODO: case if projects are different } } - - } - IFOK(result){ - result.addData("treeItemIdList", QVariant::fromValue>(treeItemIdList)); - - result = this->renumberSortOrders(targetProjectId); + IFOK(result) { + result.addData("treeItemIdList", QVariant::fromValue >(treeItemIdList)); // unset old treeItems m_cutCopy.hasRunOnce = true; + for (int treeItemId : qAsConst(originalTreeItemIdList)) { emit cutCopyChanged(sourceProjectId, treeItemId, false); } - - } IFKO(result) { emit errorSent(result); @@ -1825,28 +1872,36 @@ SKRResult SKRTreeHub::paste(int targetProjectId, int parentTreeItemId) IFOK(result) { if (m_cutCopy.type == CutCopy::Type::Cut) { emit treeItemMoved(sourceProjectId, treeItemIdList, targetProjectId, parentTreeItemId); + + emit projectModified(sourceProjectId); + + if (sourceProjectId != targetProjectId) { + emit projectModified(targetProjectId); + } } else if (m_cutCopy.type == CutCopy::Type::Copy) { emit treeItemsAdded(targetProjectId, treeItemIdList); - } - emit projectModified(targetProjectId); + if (sourceProjectId == targetProjectId) { + emit projectModified(sourceProjectId); + } + else { + emit projectModified(targetProjectId); + } + } } return result; } -bool SKRTreeHub::isCutCopy(int projectId, int treeItemId) const{ - - if(m_cutCopy.projectId == projectId && !m_cutCopy.hasRunOnce){ - +bool SKRTreeHub::isCutCopy(int projectId, int treeItemId) const { + if ((m_cutCopy.projectId == projectId) && !m_cutCopy.hasRunOnce) { return m_cutCopy.treeItemIds.contains(treeItemId); } return false; } - // ---------------------------------------------------------------------------------------- SKRResult SKRTreeHub::setTrashedDateToNow(int projectId, int treeItemId) diff --git a/src/libskribisto-data/src/skrtreehub.h b/src/libskribisto-data/src/skrtreehub.h index 0e54a20c6..179c403dd 100755 --- a/src/libskribisto-data/src/skrtreehub.h +++ b/src/libskribisto-data/src/skrtreehub.h @@ -53,9 +53,9 @@ struct CutCopy } CutCopy() { - this->type = Type::None; - this->projectId = -2; - this->hasRunOnce = false; + this->type = Type::None; + this->projectId = -2; + this->hasRunOnce = false; } CutCopy::Type type; @@ -201,10 +201,12 @@ class EXPORT SKRTreeHub : public QObject { int treeItemId); Q_INVOKABLE SKRResult moveTreeItemDown(int projectId, int treeItemId); - Q_INVOKABLE SKRResult moveTreeItemAsChildOf(int projectId, - int noteId, - int targetParentId, - int wantedSortOrder = -1); + Q_INVOKABLE SKRResult moveTreeItemAsChildOf(int sourceProjectId, + int sourceTreeItemId, + int targetProjectId, + int targetParentId, + bool sendSignal = true, + int wantedSortOrder = -1); Q_INVOKABLE int getParentId(int projectId, int treeItemId); @@ -263,7 +265,9 @@ class EXPORT SKRTreeHub : public QObject { const QString& type, const QString& noteName); - Q_INVOKABLE bool isCutCopy(int projectId, int treeItemId) const; + Q_INVOKABLE bool isCutCopy(int projectId, + int treeItemId) const; + private: SKRResult setTrashedDateToNow(int projectId, @@ -314,8 +318,8 @@ private slots: const QDateTime& newDate); void treeItemAdded(int projectId, int treeItemId); - void treeItemsAdded(int projectId, - QList treeItemIds); + void treeItemsAdded(int projectId, + QListtreeItemIds); void treeItemRemoved(int projectId, int treeItemId); void treeItemMoved(int sourceProjectId, @@ -334,8 +338,8 @@ private slots: int sourceTreeItemId, int receiverTreeItemId); - void cutCopyChanged(int projectId, - int treeItemId, + void cutCopyChanged(int projectId, + int treeItemId, bool value); protected: diff --git a/src/libskribisto-data/tests/CMakeLists.txt b/src/libskribisto-data/tests/CMakeLists.txt index 4c9bb48b8..c90f5727f 100755 --- a/src/libskribisto-data/tests/CMakeLists.txt +++ b/src/libskribisto-data/tests/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(auto/openprojectcase) add_subdirectory(auto/settingscase) -add_subdirectory(auto/writecase) +add_subdirectory(auto/treehubcase) add_subdirectory(auto/treelistmodelcase) diff --git a/src/libskribisto-data/tests/auto/writecase/CMakeLists.txt b/src/libskribisto-data/tests/auto/treehubcase/CMakeLists.txt similarity index 95% rename from src/libskribisto-data/tests/auto/writecase/CMakeLists.txt rename to src/libskribisto-data/tests/auto/treehubcase/CMakeLists.txt index 8752fdb10..251a9439a 100755 --- a/src/libskribisto-data/tests/auto/writecase/CMakeLists.txt +++ b/src/libskribisto-data/tests/auto/treehubcase/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5.0) -set(PROJECT_NAME "tst_writecase") +set(PROJECT_NAME "tst_treehubcase") project(${PROJECT_NAME}) diff --git a/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp b/src/libskribisto-data/tests/auto/treehubcase/tst_treehubcase.cpp similarity index 78% rename from src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp rename to src/libskribisto-data/tests/auto/treehubcase/tst_treehubcase.cpp index d14ec9e25..673adf268 100755 --- a/src/libskribisto-data/tests/auto/writecase/tst_writecase.cpp +++ b/src/libskribisto-data/tests/auto/treehubcase/tst_treehubcase.cpp @@ -14,13 +14,13 @@ #include "skrresult.h" #include "models/skrtreelistmodel.h" -class WriteCase : public QObject { +class TreeHubCase : public QObject { Q_OBJECT public: - WriteCase(); - ~WriteCase(); + TreeHubCase(); + ~TreeHubCase(); public slots: @@ -73,10 +73,13 @@ private Q_SLOTS: void simpleCutPaste(); void multipleCutPaste(); void simpleCutPasteInTheSameFolder(); + void folderCutPaste(); void simpleCopyPaste(); void multipleCopyPaste(); void simpleCopyPasteInTheSameFolder(); + // move + private: SKRData *m_data; @@ -84,22 +87,22 @@ private Q_SLOTS: int m_currentProjectId; }; -WriteCase::WriteCase() +TreeHubCase::TreeHubCase() {} -WriteCase::~WriteCase() +TreeHubCase::~TreeHubCase() {} -void WriteCase::initTestCase() +void TreeHubCase::initTestCase() { m_data = new SKRData(this); m_testProjectPath = "qrc:/testfiles/skribisto_test_project.skrib"; } -void WriteCase::cleanupTestCase() +void TreeHubCase::cleanupTestCase() {} -void WriteCase::init() +void TreeHubCase::init() { QSignalSpy spy(skrdata->projectHub(), SIGNAL(projectLoaded(int))); @@ -116,7 +119,7 @@ void WriteCase::init() m_currentProjectId = idList.first(); } -void WriteCase::cleanup() +void TreeHubCase::cleanup() { QSignalSpy spy(skrdata->projectHub(), SIGNAL(projectClosed(int))); @@ -134,7 +137,7 @@ void WriteCase::cleanup() // ------------------------------------------------------------------------------------ -// void WriteCase::getAll() +// void TreeHubCase::getAll() // { // // QList > list = // skrdata->treeHub()->getAll(1); @@ -149,7 +152,7 @@ void WriteCase::cleanup() // ------------------------------------------------------------------------------------ -void WriteCase::getAllIndents() +void TreeHubCase::getAllIndents() { QHash hash = skrdata->treeHub()->getAllIndents(m_currentProjectId); @@ -161,7 +164,7 @@ void WriteCase::getAllIndents() // ------------------------------------------------------------------------------------ -void WriteCase::getAllSortOrders() +void TreeHubCase::getAllSortOrders() { QHash hash = skrdata->treeHub()->getAllSortOrders(m_currentProjectId); @@ -173,7 +176,7 @@ void WriteCase::getAllSortOrders() // ------------------------------------------------------------------------------------ -void WriteCase::getAllIds() +void TreeHubCase::getAllIds() { QList list = skrdata->treeHub()->getAllIds(m_currentProjectId); @@ -182,7 +185,7 @@ void WriteCase::getAllIds() // ------------------------------------------------------------------------------------ -void WriteCase::setTitle() +void TreeHubCase::setTitle() { QSignalSpy spy(skrdata->treeHub(), SIGNAL(titleChanged(int,int,QString))); @@ -200,14 +203,14 @@ void WriteCase::setTitle() // ------------------------------------------------------------------------------------ -void WriteCase::getTitle() +void TreeHubCase::getTitle() { QString title = skrdata->treeHub()->getTitle(m_currentProjectId, 4); QCOMPARE(title, QString("Sol")); } -void WriteCase::setIndent() +void TreeHubCase::setIndent() { QSignalSpy spy(skrdata->treeHub(), SIGNAL(indentChanged(int,int,int))); @@ -225,7 +228,7 @@ void WriteCase::setIndent() // ------------------------------------------------------------------------------------ -void WriteCase::getIndent() +void TreeHubCase::getIndent() { int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 1); @@ -234,7 +237,7 @@ void WriteCase::getIndent() // ------------------------------------------------------------------------------------ -void WriteCase::setTrashed() +void TreeHubCase::setTrashed() { QSignalSpy spy(skrdata->treeHub(), SIGNAL(trashedChanged(int,int,bool))); @@ -260,7 +263,7 @@ void WriteCase::setTrashed() // ------------------------------------------------------------------------------------ -void WriteCase::restoring() +void TreeHubCase::restoring() { setTrashed(); @@ -287,7 +290,7 @@ void WriteCase::restoring() // ------------------------------------------------------------------------------------ -void WriteCase::getTrashed() +void TreeHubCase::getTrashed() { bool value = skrdata->treeHub()->getTrashed(m_currentProjectId, 2); @@ -296,7 +299,7 @@ void WriteCase::getTrashed() // ------------------------------------------------------------------------------------ -void WriteCase::setPrimaryContent() +void TreeHubCase::setPrimaryContent() { QSignalSpy spy(skrdata->treeHub(), SIGNAL(primaryContentChanged(int,int,QString))); @@ -314,7 +317,7 @@ void WriteCase::setPrimaryContent() // ------------------------------------------------------------------------------------ -void WriteCase::getPrimaryContent() +void TreeHubCase::getPrimaryContent() { QString value = skrdata->treeHub()->getPrimaryContent(m_currentProjectId, 16); QTextDocument doc; @@ -329,7 +332,7 @@ void WriteCase::getPrimaryContent() // ------------------------------------------------------------------------------------ -void WriteCase::setCreationDate() +void TreeHubCase::setCreationDate() { QSignalSpy spy(skrdata->treeHub(), SIGNAL(creationDateChanged(int,int,QDateTime))); QDateTime date(QDate(2010, 1, 1), QTime(1, 0, 0)); @@ -348,7 +351,7 @@ void WriteCase::setCreationDate() // ------------------------------------------------------------------------------------ -void WriteCase::getCreationDate() +void TreeHubCase::getCreationDate() { QDateTime value = skrdata->treeHub()->getCreationDate(m_currentProjectId, 2); @@ -357,7 +360,7 @@ void WriteCase::getCreationDate() // ------------------------------------------------------------------------------------ -void WriteCase::setUpdateDate() +void TreeHubCase::setUpdateDate() { QSignalSpy spy(skrdata->treeHub(), SIGNAL(updateDateChanged(int,int,QDateTime))); QDateTime date(QDate(2010, 1, 1), QTime(1, 0, 0)); @@ -376,7 +379,7 @@ void WriteCase::setUpdateDate() // ------------------------------------------------------------------------------------ -void WriteCase::getUpdateDate() +void TreeHubCase::getUpdateDate() { QDateTime value = skrdata->treeHub()->getUpdateDate(m_currentProjectId, 2); @@ -385,7 +388,7 @@ void WriteCase::getUpdateDate() // ------------------------------------------------------------------------------------ -void WriteCase::queue() +void TreeHubCase::queue() { QString value = skrdata->treeHub()->getTitle(m_currentProjectId, 4); @@ -407,7 +410,7 @@ void WriteCase::queue() // ------------------------------------------------------------------------------------ -void WriteCase::missingProjectError() +void TreeHubCase::missingProjectError() { // QSignalSpy spy(skrdata->errorHub(), SIGNAL(errorSent())); // skrdata->treeHub()->getTitle(9999, 1); @@ -416,7 +419,7 @@ void WriteCase::missingProjectError() // ------------------------------------------------------------------------------------ -void WriteCase::addTreeItem() +void TreeHubCase::addTreeItem() { SKRResult result = skrdata->treeHub()->addTreeItemBelow(m_currentProjectId, 1, "TEXT"); int lastId = skrdata->treeHub()->getLastAddedId(); @@ -444,7 +447,7 @@ void WriteCase::addTreeItem() // ------------------------------------------------------------------------------------ -void WriteCase::removeTreeItem() +void TreeHubCase::removeTreeItem() { SKRResult result = skrdata->treeHub()->removeTreeItem(m_currentProjectId, 1); @@ -453,7 +456,7 @@ void WriteCase::removeTreeItem() // ------------------------------------------------------------------------------------ -void WriteCase::property() +void TreeHubCase::property() { QSignalSpy spy(skrdata->treePropertyHub(), SIGNAL(propertyChanged(int,int,int,QString,QString))); @@ -474,7 +477,7 @@ void WriteCase::property() QVERIFY(keyList.length() > 0); } -void WriteCase::property_replace() +void TreeHubCase::property_replace() { QSignalSpy spy(skrdata->treePropertyHub(), SIGNAL(propertyChanged(int,int,int,QString,QString))); @@ -494,7 +497,7 @@ void WriteCase::property_replace() QCOMPARE(arguments.at(1).toInt(), id); } -void WriteCase::getTreeLabel() +void TreeHubCase::getTreeLabel() { QString value = skrdata->treePropertyHub()->getProperty(m_currentProjectId, 5, @@ -503,7 +506,7 @@ void WriteCase::getTreeLabel() QCOMPARE(value, "this is a label"); } -void WriteCase::setTreeLabel() +void TreeHubCase::setTreeLabel() { skrdata->treePropertyHub()->setProperty(m_currentProjectId, 1, "label", "new"); QString value = skrdata->treePropertyHub()->getProperty(m_currentProjectId, @@ -513,7 +516,7 @@ void WriteCase::setTreeLabel() QCOMPARE(value, "new"); } -void WriteCase::setTag() +void TreeHubCase::setTag() { SKRResult result = skrdata->tagHub()->setTagRelationship(m_currentProjectId, 2, 1); @@ -522,7 +525,7 @@ void WriteCase::setTag() QCOMPARE(result.isSuccess(), true); } -void WriteCase::cleanUpHtml() { +void TreeHubCase::cleanUpHtml() { QString html = "style=\" font-family:'Cantarell'; font-size:16pt; font-weight:400; font-style:normal;\""; html.remove(QRegularExpression(" font-family:.*?;")); @@ -530,66 +533,65 @@ void WriteCase::cleanUpHtml() { qDebug() << html; } -void WriteCase::sortAlphabetically() { +void TreeHubCase::sortAlphabetically() { SKRResult result = skrdata->treeHub()->sortAlphabetically(m_currentProjectId, 0); QCOMPARE(result.isSuccess(), true); QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); QList wantedIds; - wantedIds << 0 << 3 << 21 << 22 << 23 << 1 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12; + wantedIds << 0 << 3 << 21 << 22 << 23 << 1 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + 7 << 8 << 9 << 10 << 11 << 12; QCOMPARE(ids, wantedIds); } -//----------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------- -//----------------------------------------------------------------------------------- - -void WriteCase::simpleCutPaste(){ - +// ----------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------- +void TreeHubCase::simpleCutPaste() { skrdata->treeHub()->cut(m_currentProjectId, QList() << 14); skrdata->treeHub()->paste(m_currentProjectId, 6); QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: - //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + // 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + // 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 QList wantedIds; - wantedIds << 0 << 2 << 4 << 5 << 13 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 14 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + wantedIds << 0 << 2 << 4 << 5 << 13 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 14 << 7 << 8 << 9 << 10 << 11 << + 12 << 1 << 3 << 21 << 22 << 23; QCOMPARE(ids, wantedIds); int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); QCOMPARE(indent, 3); - - } -void WriteCase::simpleCutPasteInTheSameFolder(){ +void TreeHubCase::simpleCutPasteInTheSameFolder() { skrdata->treeHub()->cut(m_currentProjectId, QList() << 14); skrdata->treeHub()->paste(m_currentProjectId, 5); QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: - //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + // 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + // 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 QList wantedIds; - wantedIds << 0 << 2 << 4 << 5 << 13 << 15 << 16 << 14 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + wantedIds << 0 << 2 << 4 << 5 << 13 << 15 << 16 << 14 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << + 12 << 1 << 3 << 21 << 22 << 23; QCOMPARE(ids, wantedIds); int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); QCOMPARE(indent, 3); - - } -void WriteCase::multipleCutPaste(){ - - +void TreeHubCase::multipleCutPaste() { skrdata->treeHub()->cut(m_currentProjectId, QList() << 14 << 15 << 16); QCOMPARE(skrdata->treeHub()->isCutCopy(m_currentProjectId, 14), true); @@ -598,10 +600,13 @@ void WriteCase::multipleCutPaste(){ QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: - //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + // 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + // 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 QList wantedIds; - wantedIds << 0 << 2 << 4 << 5 << 13 << 6 << 17 << 18 << 19 << 20 << 14 << 15 << 16 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + wantedIds << 0 << 2 << 4 << 5 << 13 << 6 << 17 << 18 << 19 << 20 << 14 << 15 << 16 << 7 << 8 << 9 << 10 << 11 << + 12 << 1 << 3 << 21 << 22 << 23; QCOMPARE(ids, wantedIds); @@ -613,72 +618,108 @@ void WriteCase::multipleCutPaste(){ QCOMPARE(indent, 3); } +void TreeHubCase::folderCutPaste() { + skrdata->treeHub()->cut(m_currentProjectId, QList() << 2); + QCOMPARE(skrdata->treeHub()->isCutCopy(m_currentProjectId, 2), true); + + SKRResult result = skrdata->treeHub()->paste(m_currentProjectId, 3); + QCOMPARE(skrdata->treeHub()->isCutCopy(m_currentProjectId, 2), false); + + QCOMPARE(result.isSuccess(), true); + + QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + + // original: + // 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + // 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + qDebug() << "found:" << ids; + QList wantedIds; + wantedIds << 0 << 1 << 3 << 21 << 22 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + 7 << + 8 << 9 << 10 << 11 << 12 << 23; + QCOMPARE(ids, wantedIds); -void WriteCase::simpleCopyPaste(){ + int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); + QCOMPARE(indent, 4); + indent = skrdata->treeHub()->getIndent(m_currentProjectId, 15); + QCOMPARE(indent, 4); + indent = skrdata->treeHub()->getIndent(m_currentProjectId, 16); + QCOMPARE(indent, 4); + indent = skrdata->treeHub()->getIndent(m_currentProjectId, 12); + QCOMPARE(indent, 3); +} +void TreeHubCase::simpleCopyPaste() { skrdata->treeHub()->copy(m_currentProjectId, QList() << 14); SKRResult result = skrdata->treeHub()->paste(m_currentProjectId, 6); QCOMPARE(result.isSuccess(), true); - QList newIdList = result.getData("treeItemIdList", QVariant::fromValue>(QList())).value>(); + QList newIdList = result.getData("treeItemIdList", + QVariant::fromValue >(QList())).value >(); QCOMPARE(newIdList.isEmpty(), false); QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: - //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + // 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + // 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 QList wantedIds; - wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << newIdList.first() << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << newIdList.first() << 7 << 8 << + 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; QCOMPARE(ids, wantedIds); int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); QCOMPARE(indent, 3); - } -void WriteCase::simpleCopyPasteInTheSameFolder(){ - - +void TreeHubCase::simpleCopyPasteInTheSameFolder() { skrdata->treeHub()->copy(m_currentProjectId, QList() << 14); SKRResult result = skrdata->treeHub()->paste(m_currentProjectId, 5); QCOMPARE(result.isSuccess(), true); - QList newIdList = result.getData("treeItemIdList", QVariant::fromValue>(QList())).value>(); + QList newIdList = result.getData("treeItemIdList", + QVariant::fromValue >(QList())).value >(); QCOMPARE(newIdList.isEmpty(), false); QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: - //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + // 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + // 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 QList wantedIds; - wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << newIdList.first() << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << newIdList.first() << 6 << 17 << 18 << 19 << 20 << 7 << 8 << + 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; QCOMPARE(ids, wantedIds); int indent = skrdata->treeHub()->getIndent(m_currentProjectId, 14); QCOMPARE(indent, 3); - } -void WriteCase::multipleCopyPaste(){ - +void TreeHubCase::multipleCopyPaste() { skrdata->treeHub()->copy(m_currentProjectId, QList() << 14 << 15 << 16); SKRResult result = skrdata->treeHub()->paste(m_currentProjectId, 6); QCOMPARE(result.isSuccess(), true); - QList newIdList = result.getData("treeItemIdList", QVariant::fromValue>(QList())).value>(); + QList newIdList = result.getData("treeItemIdList", + QVariant::fromValue >(QList())).value >(); QCOMPARE(newIdList.count(), 3); QList ids = skrdata->treeHub()->getAllIds(m_currentProjectId); + // original: - //0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 + // 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << + // 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23 QList wantedIds; - wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << newIdList.first() << newIdList.at(1) << newIdList.at(2) << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; + wantedIds << 0 << 2 << 4 << 5 << 13 << 14 << 15 << 16 << 6 << 17 << 18 << 19 << 20 << newIdList.first() << + newIdList.at(1) << newIdList.at(2) << 7 << 8 << 9 << 10 << 11 << 12 << 1 << 3 << 21 << 22 << 23; QCOMPARE(ids, wantedIds); @@ -690,7 +731,6 @@ void WriteCase::multipleCopyPaste(){ QCOMPARE(indent, 3); } +QTEST_GUILESS_MAIN(TreeHubCase) -QTEST_GUILESS_MAIN(WriteCase) - -#include "tst_writecase.moc" +#include "tst_treehubcase.moc" diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml index ba59a079b..e5472c5dc 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationList.qml @@ -53,7 +53,6 @@ NavigationListForm { property bool devModeEnabled: SkrSettings.devSettings.devModeEnabled - property bool goingUp: false } onCurrentParentIdChanged: { @@ -243,10 +242,8 @@ NavigationListForm { onTriggered: { console.log("goUpAction triggered") - priv.goingUp = true navigationListStackView.pop(priv.transitionOperation) navigationListStackView.currentItem.setCurrent() - priv.goingUp = false // if(navigationListStackView.currentItem.listView.currentItem){ // navigationListStackView.currentItem.listView.currentItem.forceActiveFocus() @@ -339,6 +336,31 @@ NavigationListForm { } } + //---------------------------------------------------------------------------- + //------------------ select all button :------------------------------------------ + //---------------------------------------------------------------------------- + Action { + id: selectAllTreeItemsAction + text: selectAllTreeItemsAction.checked ? qsTr("Select none") : qsTr("Select all") + enabled: root.enabled && currentParentId !== -2 + checkable: true + icon { + source: selectAllTreeItemsAction.checked ? "qrc:///icons/backup/edit-select-none.svg" : "qrc:///icons/backup/edit-select-all.svg" + } + onCheckedChanged: { + if(selectAllTreeItemsAction.checked){ + navigationListStackView.currentItem.checkAll() + } + else{ + navigationListStackView.currentItem.checkNone() + + } + } + } + + selectAllToolButton.action: selectAllTreeItemsAction + selectAllToolButton.visible: selectTreeItemAction.checked + //---------------------------------------------------------------------------- //------------------ select button :------------------------------------------ //---------------------------------------------------------------------------- @@ -349,8 +371,6 @@ NavigationListForm { checkable: true icon { source: "qrc:///icons/backup/dialog-ok-apply.svg" - height: 100 - width: 100 } onCheckedChanged: { priv.selecting = selectTreeItemAction.checked @@ -369,8 +389,6 @@ NavigationListForm { enabled: root.enabled && currentParentId !== -2 icon { source: "qrc:///icons/backup/list-add.svg" - height: 100 - width: 100 } onTriggered: { addItemMenu.open() @@ -518,7 +536,7 @@ NavigationListForm { XAnimator { from: 0 - to: /*(priv.goingUp ? -1 : 1) **/ (navigationListStackView.mirrored ? -1 : 1) * navigationListStackView.width + to: (navigationListStackView.mirrored ? -1 : 1) * navigationListStackView.width duration: 200 easing.type: Easing.OutCubic } diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml index 04e242965..df16eeab1 100755 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListForm.ui.qml @@ -9,6 +9,7 @@ FocusScope { property int scrollBarVerticalPolicy: ScrollBar.AlwaysOff property alias goUpToolButton: goUpToolButton property alias selectToolButton: selectToolButton + property alias selectAllToolButton: selectAllToolButton property alias addToolButton: addToolButton property alias treeMenuToolButton: treeMenuToolButton property alias navigationListStackView: navigationListStackView @@ -46,17 +47,21 @@ FocusScope { Layout.fillWidth: true } + SkrToolButton { + id: selectAllToolButton + flat: true + display: AbstractButton.IconOnly + } + SkrToolButton { id: selectToolButton flat: true - text: qsTr("Select") display: AbstractButton.IconOnly } SkrToolButton { id: addToolButton flat: true - text: qsTr("Add a document") display: AbstractButton.IconOnly } diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml index 2a097225b..4974cd022 100644 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml @@ -94,6 +94,15 @@ Item { } } + function checkAll(){ + navigationProxyModel.checkAll() + } + + function checkNone(){ + navigationProxyModel.checkNone() + } + + NewItemPopup { id: newItemPopup } @@ -126,12 +135,6 @@ Item { } } - - - function checkNone(){ - navigationProxyModel.checkNone() - } - Item { id: topDraggingMover anchors.top: scrollView.top @@ -212,39 +215,31 @@ Item { keys: ["application/skribisto-tree-item"] onEntered: { + if(!model.canAddChildTreeItem){ + drag.accepted = false + return + } + + if(skrData.treeHub().getAllAncestors(projectId, parentId).includes(drag.source.treeItemId)){ + drag.accepted = false + return + } - //console.log("entered") - //content.sourceIndex = drag.source.visualIndex - // visualModel.items.move( - // drag.source.visualIndex, - // visualModel.items.count - 1) } onExited: { } onDropped: { - // console.log("dropped") if (drop.proposedAction === Qt.MoveAction) { - //console.log("dropped from :", moveSourceInt, "to :", content.visualIndex) listView.interactive = true priv.dragging = false - dropTimer.start() - //proxyModel.moveItemById(moveSourceProjectId, moveSourceTreeItemId, content.treeItemId) + skrData.treeHub().moveTreeItemAsChildOf(drag.source.projectId, drag.source.treeItemId, projectId, parentId) } } - Timer{ - id: dropTimer - interval: 20 - onTriggered: { - // navigationProxyModel.moveItem(moveSourceInt, - // visualModel.items.count - 1) - } - } - TapHandler { @@ -933,6 +928,12 @@ Item { return } + if(skrData.treeHub().getAllAncestors(projectId, parentId).includes(drag.source.treeItemId)){ + drag.accepted = false + return + } + + topOnEnteredTimer.start() } @@ -997,33 +998,26 @@ Item { } - middleDropIndicator.visible = true + if(skrData.treeHub().getAllAncestors(projectId, parentId).includes(drag.source.treeItemId)){ + drag.accepted = false + return + } + middleDropIndicator.visible = true - //content.sourceIndex = drag.source.visualIndex - // visualModel.items.move( - // drag.source.visualIndex, - // content.visualIndex) } onExited: { middleDropIndicator.visible = false - // visualModel.items.move( content.visualIndex, moveSourceInt) } onDropped: function(drop) { middleDropIndicator.visible = false - // console.log("dropped") - skrData.treeHub().moveTreeItemAsChildOf(model.projectId, drag.source.treeItemId, model.treeItemId) + skrData.treeHub().moveTreeItemAsChildOf(drag.source.projectId, drag.source.treeItemId, model.projectId, model.treeItemId) if (drop.proposedAction === Qt.MoveAction) { - //console.log("dropped from :", moveSourceInt, "to :", content.visualIndex) - // cancelDragTimer.stop() - // listView.interactive = true - // priv.dragging = false - // dropTimer.start() } } @@ -1083,6 +1077,12 @@ Item { return } + + if(skrData.treeHub().getAllAncestors(projectId, parentId).includes(drag.source.treeItemId)){ + drag.accepted = false + return + } + bottomOnEnteredTimer.start() } @@ -1166,8 +1166,8 @@ Item { Drag.active: mouseDragHandler.active | touchDragHandler.active Drag.source: content - Drag.hotSpot.x: dragPoint.x - Drag.hotSpot.y: dragPoint.y + Drag.hotSpot.x: dragPoint.x / 1.5 + Drag.hotSpot.y: dragPoint.y / 1.5 Drag.keys: ["application/skribisto-tree-item"] Drag.supportedActions: Qt.MoveAction @@ -1350,11 +1350,10 @@ Item { } onPressedChanged: { - content.opacity = 0.2 content.grabToImage(function(result) { content.Drag.imageSource = result.url - }) - content.opacity = 1.0 + }, Qt.size(content.width / 1.5, content.height / 1.5)) + content.dragPoint = point.pressPosition } diff --git a/src/translations/skribisto_en_US.ts b/src/translations/skribisto_en_US.ts index 9c191dfd3..d0742ab17 100644 --- a/src/translations/skribisto_en_US.ts +++ b/src/translations/skribisto_en_US.ts @@ -925,19 +925,19 @@ New item's parameters - - - NavigationListForm.ui - Add a document + Select none - Navigation menu + Select all + + + NavigationListForm.ui - Select + Navigation menu From 4cb9b811beeded7857b6ee6440850844262ba3dc Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Fri, 6 Aug 2021 07:51:04 +0200 Subject: [PATCH 10/13] Plume Creator importer Plume Creator importer: fix completely importer with new approach Plume Creator importer: add test --- src/app/src/qml/Commons/RelationshipPanel.qml | 5 +- src/app/src/qml/Items/SkrToolButton.qml | 12 +- src/app/src/qml/main.qml | 2 +- src/libskribisto-data/src/skrtaghub.cpp | 46 ++ src/libskribisto-data/src/skrtaghub.h | 3 + .../src/tasks/sql/plmexporter.cpp | 2 +- .../src/tasks/sql/plmimporter.cpp | 2 +- .../src/tasks/sql/plmproject.cpp | 2 +- src/libskribisto-data/src/tasks/sql/sql.qrc | 1 + .../src/tasks/sql/sqlite_project_1_8.sql | 52 ++ .../NavigationListView.qml | 4 +- .../plumeCreatorImporter/CMakeLists.txt | 8 + .../skrplumecreatorimporter.cpp | 638 +++++++++++------- .../skrplumecreatorimporter.h | 31 +- .../plumeCreatorImporter/tests/CMakeLists.txt | 32 + .../tests/plume-test.plume | Bin 0 -> 27955 bytes .../plumeCreatorImporter/tests/testfiles.qrc | 5 + .../tests/tst_plumecreatorimporter.cpp | 174 +++++ src/translations/skribisto_en_US.ts | 21 +- 19 files changed, 785 insertions(+), 255 deletions(-) create mode 100755 src/libskribisto-data/src/tasks/sql/sqlite_project_1_8.sql create mode 100755 src/plugins/plumeCreatorImporter/tests/CMakeLists.txt create mode 100644 src/plugins/plumeCreatorImporter/tests/plume-test.plume create mode 100755 src/plugins/plumeCreatorImporter/tests/testfiles.qrc create mode 100644 src/plugins/plumeCreatorImporter/tests/tst_plumecreatorimporter.cpp diff --git a/src/app/src/qml/Commons/RelationshipPanel.qml b/src/app/src/qml/Commons/RelationshipPanel.qml index 902bb0512..09a92efc9 100644 --- a/src/app/src/qml/Commons/RelationshipPanel.qml +++ b/src/app/src/qml/Commons/RelationshipPanel.qml @@ -976,7 +976,7 @@ RelationshipPanelForm { keys: ["application/skribisto-tree-item"] onEntered: function(drag) { - if(drag.source.projectId === root.projectId){ + if(drag.source.projectId !== root.projectId){ drag.accepted = false return } @@ -998,9 +998,6 @@ RelationshipPanelForm { } dropIndicator.visible = false } - - - } diff --git a/src/app/src/qml/Items/SkrToolButton.qml b/src/app/src/qml/Items/SkrToolButton.qml index 2610b6d6a..ba0729e64 100755 --- a/src/app/src/qml/Items/SkrToolButton.qml +++ b/src/app/src/qml/Items/SkrToolButton.qml @@ -5,11 +5,10 @@ import ".." ToolButton { id: control - icon.color: control.action === null ? (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled) : - (control.action.icon.color === "transparent"? - (enabled ? control.action.icon.color: SkrTheme.buttonIconDisabled) : - (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled)) - + icon.color: control.action ? (control.action.icon.color === "transparent"? + (enabled ? control.action.icon.color: SkrTheme.buttonIconDisabled) : + (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled)) : + (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled) Material.background: SkrTheme.buttonBackground Material.foreground: control.activeFocus ? SkrTheme.accent : SkrTheme.buttonForeground @@ -58,6 +57,7 @@ ToolButton { property string finalShortcutText: shortcutText ? " (" + shortcutText +")" : "" } - property string shortcutText: action === null ? "" : action.shortcutText + property string shortcutText: action ? (action.shortcutText ? action.shortcutText : "") : "" + } diff --git a/src/app/src/qml/main.qml b/src/app/src/qml/main.qml index 9c75a6488..63be14c90 100755 --- a/src/app/src/qml/main.qml +++ b/src/app/src/qml/main.qml @@ -1021,7 +1021,7 @@ ApplicationWindow { Layout.alignment: Qt.AlignHCenter - text: "

" + qsTr("Loading a projects") + "

" + text: "

" + qsTr("Loading a project") + "

" focus: true } diff --git a/src/libskribisto-data/src/skrtaghub.cpp b/src/libskribisto-data/src/skrtaghub.cpp index 9e8719884..045902627 100755 --- a/src/libskribisto-data/src/skrtaghub.cpp +++ b/src/libskribisto-data/src/skrtaghub.cpp @@ -246,6 +246,52 @@ SKRResult SKRTagHub::setTagTextColor(int projectId, int tagId, const QString& co return result; } +// -------------------------------------------------------------------------------- + +SKRResult SKRTagHub::setTagRandomColors(int projectId, int tagId) +{ + int randColorIndex = QRandomGenerator::global()->bounded(27); + + QMap colorMap; + + colorMap.insert("#FFFFFF", "#000000"); + colorMap.insert("#FFFAFA", "#000000"); + colorMap.insert("#F0FFFF", "#000000"); + colorMap.insert("#F5F5DC", "#000000"); + colorMap.insert("#000000", "#FFFFFF"); + colorMap.insert("#FF0000", "#000000"); + colorMap.insert("#8B0000", "#FFFFFF"); + colorMap.insert("#98FB98", "#000000"); + colorMap.insert("#7FFF00", "#000000"); + colorMap.insert("#008000", "#FFFFFF"); + colorMap.insert("#006400", "#FFFFFF"); + colorMap.insert("#E0FFFF", "#000000"); + colorMap.insert("#00FFFF", "#000000"); + colorMap.insert("#008B8B", "#FFFFFF"); + colorMap.insert("#0000FF", "#FFFFFF"); + colorMap.insert("#00008B", "#FFFFFF"); + colorMap.insert("#FFB6C1", "#000000"); + colorMap.insert("#FFC0CB", "#000000"); + colorMap.insert("#FF69B4", "#000000"); + colorMap.insert("#FF00FF", "#000000"); + colorMap.insert("#8B008B", "#FFFFFF"); + colorMap.insert("#FFFFE0", "#000000"); + colorMap.insert("#FFFF00", "#000000"); + colorMap.insert("#FFD700", "#000000"); + colorMap.insert("#FFA500", "#000000"); + colorMap.insert("#FF8C00", "#FFFFFF"); + colorMap.insert("#808080", "#FFFFFF"); + colorMap.insert("#A9A9A9", "#FFFFFF"); + + + SKRResult result = setTagColor(projectId, tagId, colorMap.keys().at(randColorIndex)); + + result = setTagTextColor(projectId, tagId, colorMap.values().at(randColorIndex)); + + + return result; +} + // ------------------------------------------------------------ SKRResult SKRTagHub::setCreationDate(int projectId, int tagId, diff --git a/src/libskribisto-data/src/skrtaghub.h b/src/libskribisto-data/src/skrtaghub.h index 339a9b5fc..0296ddb4d 100755 --- a/src/libskribisto-data/src/skrtaghub.h +++ b/src/libskribisto-data/src/skrtaghub.h @@ -93,6 +93,9 @@ class EXPORT SKRTagHub : public QObject { int treeItemId, int tagId); + Q_INVOKABLE SKRResult setTagRandomColors(int projectId, + int tagId); + signals: void errorSent(const SKRResult& result) const; diff --git a/src/libskribisto-data/src/tasks/sql/plmexporter.cpp b/src/libskribisto-data/src/tasks/sql/plmexporter.cpp index cc7679bd9..005ad96e0 100755 --- a/src/libskribisto-data/src/tasks/sql/plmexporter.cpp +++ b/src/libskribisto-data/src/tasks/sql/plmexporter.cpp @@ -47,7 +47,7 @@ SKRResult PLMExporter::exportWholeSQLiteDbTo(PLMProject *db, // copy db temp to file QFile tempFile(databaseTempFileName); - QFile file(fileName.toLocalFile()); + QFile file(fileName.path()); if (!tempFile.open(QIODevice::ReadOnly)) { result = SKRResult(SKRResult::Critical, this, "tempfile_cant_be_opened"); diff --git a/src/libskribisto-data/src/tasks/sql/plmimporter.cpp b/src/libskribisto-data/src/tasks/sql/plmimporter.cpp index 806f85bf8..194340831 100755 --- a/src/libskribisto-data/src/tasks/sql/plmimporter.cpp +++ b/src/libskribisto-data/src/tasks/sql/plmimporter.cpp @@ -52,7 +52,7 @@ QSqlDatabase PLMImporter::createSQLiteDbFrom(const QString& type, fileNameString = ":" + fileNameString; } else { - fileNameString = fileName.toLocalFile(); + fileNameString = fileName.path(); } QFile file(fileNameString); diff --git a/src/libskribisto-data/src/tasks/sql/plmproject.cpp b/src/libskribisto-data/src/tasks/sql/plmproject.cpp index d184856d4..b764fba0f 100755 --- a/src/libskribisto-data/src/tasks/sql/plmproject.cpp +++ b/src/libskribisto-data/src/tasks/sql/plmproject.cpp @@ -48,7 +48,7 @@ PLMProject::PLMProject(QObject *parent, int projectId, const QUrl& fileName, SKR info.setFile(fileName.toString().replace("qrc:", ":")); } else { - info.setFile(fileName.toLocalFile()); + info.setFile(fileName.path()); } if (!info.exists()) { diff --git a/src/libskribisto-data/src/tasks/sql/sql.qrc b/src/libskribisto-data/src/tasks/sql/sql.qrc index 74568917f..1b15f6fc1 100755 --- a/src/libskribisto-data/src/tasks/sql/sql.qrc +++ b/src/libskribisto-data/src/tasks/sql/sql.qrc @@ -2,6 +2,7 @@ sqlite_project.sql sqlite_project_1_6.sql + sqlite_project_1_8.sql diff --git a/src/libskribisto-data/src/tasks/sql/sqlite_project_1_8.sql b/src/libskribisto-data/src/tasks/sql/sqlite_project_1_8.sql new file mode 100755 index 000000000..ce2070161 --- /dev/null +++ b/src/libskribisto-data/src/tasks/sql/sqlite_project_1_8.sql @@ -0,0 +1,52 @@ +-- +-- File generated with SQLiteStudio v3.2.1 on jeu. janv. 30 22:52:23 2020 +-- +-- Text encoding used: UTF-8 +-- +-- skribisto_db_version:1.8 + +PRAGMA foreign_keys = off; +BEGIN TRANSACTION; + +-- Table: tbl_stat_history +CREATE TABLE tbl_stat_history (l_stat_history_id INTEGER PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT UNIQUE ON CONFLICT ROLLBACK NOT NULL ON CONFLICT ROLLBACK, dt_saved DATETIME, l_sheet_char_count INTEGER, l_sheet_word_count INTEGER, l_note_char_count INTEGER, l_note_word_count INTEGER); + +-- Table: tbl_project +CREATE TABLE tbl_project (l_project_id INTEGER PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT NOT NULL ON CONFLICT ROLLBACK UNIQUE ON CONFLICT ROLLBACK, t_project_name TEXT, dbl_database_version DOUBLE NOT NULL DEFAULT (0), dt_created DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL, dt_updated DATETIME NOT NULL DEFAULT (CURRENT_TIMESTAMP), t_author TEXT, t_project_unique_identifier TEXT, t_spell_check_lang TEXT NOT NULL ON CONFLICT ROLLBACK DEFAULT ""); + +-- Table: tbl_project_dict +CREATE TABLE tbl_project_dict (l_project_dict_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ON CONFLICT ROLLBACK UNIQUE ON CONFLICT ROLLBACK, t_word TEXT UNIQUE ON CONFLICT REPLACE NOT NULL ON CONFLICT ROLLBACK); + +-- Table: tbl_tree_relationship +CREATE TABLE tbl_tree_relationship (l_tree_relationship_id INTEGER PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT UNIQUE ON CONFLICT ROLLBACK NOT NULL ON CONFLICT ROLLBACK, l_tree_source_code INTEGER REFERENCES tbl_tree (l_tree_id) NOT NULL ON CONFLICT ROLLBACK, l_tree_receiver_code INTEGER REFERENCES tbl_tree (l_tree_id) NOT NULL ON CONFLICT ROLLBACK); + +-- Table: tbl_tree +CREATE TABLE tbl_tree (l_tree_id INTEGER PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT UNIQUE ON CONFLICT ROLLBACK NOT NULL ON CONFLICT ROLLBACK, t_title TEXT, t_internal_title TEXT, l_sort_order INTEGER NOT NULL ON CONFLICT ROLLBACK DEFAULT (9999999999), l_indent INTEGER NOT NULL ON CONFLICT ROLLBACK DEFAULT (0), t_type TEXT, m_primary_content BLOB, m_secondary_content BLOB, dt_created DATETIME NOT NULL ON CONFLICT ROLLBACK DEFAULT (CURRENT_TIMESTAMP), dt_updated DATETIME NOT NULL ON CONFLICT ROLLBACK DEFAULT (CURRENT_TIMESTAMP), dt_trashed DATETIME, b_trashed BOOLEAN NOT NULL ON CONFLICT ROLLBACK DEFAULT (0)); + +-- Table: tbl_tree_property +CREATE TABLE tbl_tree_property (l_tree_property_id INTEGER PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT UNIQUE ON CONFLICT ROLLBACK NOT NULL ON CONFLICT ROLLBACK, l_tree_code INTEGER REFERENCES tbl_tree (l_tree_id), t_name TEXT, t_value_type TEXT NOT NULL ON CONFLICT ROLLBACK DEFAULT STRING, m_value BLOB, dt_created DATETIME NOT NULL DEFAULT (CURRENT_TIMESTAMP), dt_updated DATETIME NOT NULL ON CONFLICT ROLLBACK DEFAULT (CURRENT_TIMESTAMP), b_system BOOLEAN DEFAULT (0) NOT NULL ON CONFLICT ROLLBACK, b_silent BOOLEAN DEFAULT (0) NOT NULL ON CONFLICT ROLLBACK); + +-- Table: tbl_tag +CREATE TABLE tbl_tag (l_tag_id INTEGER PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT UNIQUE ON CONFLICT ROLLBACK NOT NULL ON CONFLICT ROLLBACK, t_name TEXT NOT NULL UNIQUE ON CONFLICT ROLLBACK, t_color TEXT, t_text_color TEXT, dt_created DATETIME NOT NULL ON CONFLICT ROLLBACK DEFAULT (CURRENT_TIMESTAMP), dt_updated DATETIME NOT NULL ON CONFLICT ROLLBACK DEFAULT (CURRENT_TIMESTAMP)); + +-- Table: tbl_tag_relationship +CREATE TABLE tbl_tag_relationship (l_tag_relationship_id INTEGER PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT NOT NULL ON CONFLICT ROLLBACK, l_tree_code INTEGER REFERENCES tbl_tree (l_tree_id), l_tag_code INTEGER REFERENCES tbl_tag (l_tag_id), dt_created DATETIME NOT NULL ON CONFLICT ROLLBACK DEFAULT (CURRENT_TIMESTAMP)); + + +-- project item +INSERT INTO tbl_tree (l_tree_id, l_sort_order, l_indent, t_type) VALUES (0, -1, 0, 'PROJECT'); +INSERT INTO tbl_tree_property (l_tree_code, t_name, t_value_type, m_value) VALUES (0, 'is_renamable', 'BOOL', 'true'); +INSERT INTO tbl_tree_property (l_tree_code, t_name, t_value_type, m_value) VALUES (0, 'is_movable', 'BOOL', 'false'); +INSERT INTO tbl_tree_property (l_tree_code, t_name, t_value_type, m_value) VALUES (0, 'can_add_sibling_paper', 'BOOL', 'false'); +INSERT INTO tbl_tree_property (l_tree_code, t_name, t_value_type, m_value) VALUES (0, 'can_add_child_paper', 'BOOL', 'true'); +INSERT INTO tbl_tree_property (l_tree_code, t_name, t_value_type, m_value) VALUES (0, 'is_trashable', 'BOOL', 'false'); +INSERT INTO tbl_tree_property (l_tree_code, t_name, t_value_type, m_value) VALUES (0, 'is_openable', 'BOOL', 'true'); +INSERT INTO tbl_tree_property (l_tree_code, t_name, t_value_type, m_value) VALUES (0, 'is_copyable', 'BOOL', 'false'); + + + + + + +COMMIT TRANSACTION; +PRAGMA foreign_keys = on; diff --git a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml index 4974cd022..fc72da1e8 100644 --- a/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml +++ b/src/plugins/navigationProjectToolbox/NavigationProjectToolbox/NavigationListView.qml @@ -1419,7 +1419,6 @@ Item { content.dragging = true listView.interactive = false - cancelDragTimer.start() priv.selecting = false } @@ -1428,9 +1427,10 @@ Item { } onPressedChanged: { + content.grabToImage(function(result) { content.Drag.imageSource = result.url - }) + }, Qt.size(content.width / 1.5, content.height / 1.5)) content.dragPoint = point.pressPosition } diff --git a/src/plugins/plumeCreatorImporter/CMakeLists.txt b/src/plugins/plumeCreatorImporter/CMakeLists.txt index a8a9aee84..d7071ad7c 100755 --- a/src/plugins/plumeCreatorImporter/CMakeLists.txt +++ b/src/plugins/plumeCreatorImporter/CMakeLists.txt @@ -89,3 +89,11 @@ endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION "bin/plugins") endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + + + + + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_subdirectory(tests) +endif() diff --git a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp index 8b0ede2e4..1f5a10a41 100644 --- a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp +++ b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.cpp @@ -17,7 +17,7 @@ SKRPlumeCreatorImporter::SKRPlumeCreatorImporter(QObject *parent) : QObject(pare SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFileName, const QUrl& skribistoFileName) { - QString sqlFile = ":/sql/sqlite_project_1_6.sql"; + QString sqlFile = ":/sql/sqlite_project_1_8.sql"; SKRResult result = skrdata->projectHub()->createSilentlyNewSpecificEmptyProject(skribistoFileName, sqlFile); int projectId = -2; @@ -45,27 +45,8 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi // extract zip - JlCompress::extractDir(plumeFileName.toLocalFile(), tempDirPath); + JlCompress::extractDir(plumeFileName.path(), tempDirPath); - // QFile qf(plumeFileName.toLocalFile()); - // qf.open(QIODevice::ReadOnly); - // int fd = qf.handle(); - // FILE *plumeFile = fdopen(dup(fd), "rb"); - - - // // QFile tf(plumeFileName.toLocalFile()); - // // tf.open(QIODevice::ReadOnly); - // FILE *fp; - // QByteArray ba = tempDirPath.toLocal8Bit(); - // const char *c_str2 = ba.data(); - // fp = fopen(c_str2, "w"); - - // // int fd2 = tf.handle(); - // // FILE *t = fdopen(dup(fd2), "rb"); - - // decompress(plumeFile, fp); - // fclose(plumeFile); // correct - // fclose(fp); // ----------- create text folder--------------------------------------- @@ -78,7 +59,7 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi int textFolderId = result.getData("treeItemId", -2).toInt(); IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, textFolderId, 1000)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, textFolderId, "text")); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, textFolderId, tr("Text"))); // ----------- create attend folder--------------------------------------- @@ -92,7 +73,7 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi int attendFolderId = result.getData("treeItemId", -2).toInt(); IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, attendFolderId, 80000000)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, attendFolderId, "attendance")); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, attendFolderId, tr("Attendance"))); // ----------- create note folder--------------------------------------- @@ -106,7 +87,7 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi int noteFolderId = result.getData("treeItemId", -2).toInt(); IFOKDO(result, skrdata->treeHub()->setSortOrder(projectId, noteFolderId, 90000000)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteFolderId, "note")); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteFolderId, tr("Notes"))); // ----------------------------- read @@ -134,59 +115,78 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi m_attendanceConversionHash.clear(); + QStringList rolesNames; + QStringList levelsNames; + QStringList box_1Names; + QStringList box_2Names; + QStringList box_3Names; + QStringList spinBox_1Names; + + while (attendXml.readNext() != QXmlStreamReader::EndDocument) { + qDebug() << "xml.name().toString()" << attendXml.name().toString() << + attendXml.attributes().value("name").toString(); + + if ((attendXml.tokenType() == QXmlStreamReader::StartElement) && + (attendXml.name().toString() == "plume-attendance")) { + rolesNames = attendXml.attributes().value("rolesNames").toString().split("--", + Qt::SkipEmptyParts); + levelsNames = + attendXml.attributes().value("levelsNames").toString().split("--", Qt::SkipEmptyParts); + box_1Names = attendXml.attributes().value("box_1").toString().split("--", Qt::SkipEmptyParts); + box_2Names = attendXml.attributes().value("box_2").toString().split("--", Qt::SkipEmptyParts); + box_3Names = attendXml.attributes().value("box_3").toString().split("--", Qt::SkipEmptyParts); + spinBox_1Names = + attendXml.attributes().value("spinBox_1").toString().split("--", Qt::SkipEmptyParts); + } + if ((attendXml.tokenType() == QXmlStreamReader::StartElement) && (attendXml.name() == "group")) { + result = this->createAttendFolder(projectId, 2, + attendXml, + tempDirPath, + attendFolderId); - while (attendXml.readNextStartElement() && attendXml.name().toString() == "plume-attendance") { - QStringList rolesNames = attendXml.attributes().value("rolesNames").toString().split("--", Qt::SkipEmptyParts); - QStringList levelsNames = - attendXml.attributes().value("levelsNames").toString().split("--", Qt::SkipEmptyParts); - - QStringList box_1Names = attendXml.attributes().value("box_1").toString().split("--", Qt::SkipEmptyParts); - QStringList box_2Names = attendXml.attributes().value("box_2").toString().split("--", Qt::SkipEmptyParts); - QStringList box_3Names = attendXml.attributes().value("box_3").toString().split("--", Qt::SkipEmptyParts); - - QStringList spinBox_1Names = - attendXml.attributes().value("spinBox_1").toString().split("--", Qt::SkipEmptyParts); - - - while (attendXml.readNextStartElement() && attendXml.name().toString() == "group") { - int attendNumber = attendXml.attributes().value("number").toInt(); - QString groupName = attendXml.attributes().value("name").toString(); - result = this->createNote(projectId, 1, attendNumber, groupName, tempDirPath + "/attend/A", attendFolderId); IFOK(result) { int groupNoteId = result.getData("treeItemId", -2).toInt(); + int attendNumber = attendXml.attributes().value("number").toInt(); + m_attendanceConversionHash.insert(attendNumber, groupNoteId); + result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "rolesNames", rolesNames); + result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "levelsNames", rolesNames); result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_1", box_1Names); result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_2", box_2Names); result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "box_3", box_3Names); result = this->createTagsFromAttend(projectId, groupNoteId, attendXml, "spinBox_1", spinBox_1Names); } + } - while (attendXml.readNextStartElement() && attendXml.name().toString() == "obj") { - int attendNumber = attendXml.attributes().value("number").toInt(); - QString objName = attendXml.attributes().value("name").toString(); - result = - this->createNote(projectId, 2, attendNumber, objName, tempDirPath + "/attend/A", attendFolderId); - IFOK(result) { - int objNoteId = result.getData("treeItemId", -2).toInt(); + if ((attendXml.tokenType() == QXmlStreamReader::StartElement) && (attendXml.name() == "obj")) { + result = this->createAttendObject(projectId, 3, + attendXml, + tempDirPath, + attendFolderId); + + IFOK(result) { + int objNoteId = result.getData("treeItemId", -2).toInt(); - m_attendanceConversionHash.insert(attendNumber, objNoteId); + int attendNumber = attendXml.attributes().value("number").toInt(); + m_attendanceConversionHash.insert(attendNumber, objNoteId); - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_1", box_1Names); - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_2", box_2Names); - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_3", box_3Names); - result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "spinBox_1", spinBox_1Names); - QString objQuickDetail = attendXml.attributes().value("quickDetails").toString(); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "rolesNames", rolesNames); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "levelsNames", rolesNames); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_1", box_1Names); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_2", box_2Names); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "box_3", box_3Names); + result = this->createTagsFromAttend(projectId, objNoteId, attendXml, "spinBox_1", spinBox_1Names); - skrdata->treePropertyHub()->setProperty(projectId, objNoteId, "label", objQuickDetail); - } - attendXml.readElementText(); + QString objQuickDetail = attendXml.attributes().value("quickDetails").toString(); + skrdata->treePropertyHub()->setProperty(projectId, objNoteId, "label", objQuickDetail, true, true, + false); } } } @@ -218,28 +218,85 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi QXmlStreamReader xml(lines); + bool isInAct = false; + bool isInChapter = false; - while (xml.readNextStartElement() && xml.name().toString() == "plume-tree") { - // pick project name - QString projectName = xml.attributes().value("projectName").toString(); - IFOKDO(result, skrdata->projectHub()->setProjectName(projectId, projectName)); + while (xml.readNext() != QXmlStreamReader::EndDocument) { + qDebug() << "xml.name().toString()" << xml.name().toString() << + xml.attributes().value("name").toString(); + int indent = 0; - while (xml.readNextStartElement()) { - if (xml.name().toString() == "trash") { - xml.skipCurrentElement(); - continue; - } + if ((xml.tokenType() == QXmlStreamReader::StartElement) && (xml.name() == "plume-tree")) { + skrdata->projectHub()->setProjectName(projectId, xml.attributes().value("projectName").toString()); + } - if (xml.name().toString() == "book") { - IFOKDO(result, - this->readXMLRecursivelyAndCreatePaper(projectId, 1, &xml, tempDirPath, textFolderId, - noteFolderId)); - } + if ((xml.tokenType() == QXmlStreamReader::StartElement) && (xml.name() == "book")) { + indent = 2; + result = this->createFolderAndAssociations(projectId, indent, xml, tempDirPath, textFolderId, noteFolderId); + + + result = this->createSection(projectId, indent + 1, textFolderId, "book-beginning"); + } + else if ((xml.tokenType() == QXmlStreamReader::EndElement) && (xml.name() == "book")) { + indent = 3; + result = this->createSection(projectId, indent, textFolderId, "book-end"); + } + else if ((xml.tokenType() == QXmlStreamReader::StartElement) && (xml.name() == "act")) { + isInAct = true; + indent = 3; + this->createFolderAndAssociations(projectId, indent, xml, tempDirPath, textFolderId, noteFolderId); } + else if ((xml.tokenType() == QXmlStreamReader::EndElement) && (xml.name() == "act")) { + isInAct = false; + } + else if ((xml.tokenType() == QXmlStreamReader::StartElement) && (xml.name() == "chapter")) { + isInChapter = true; + indent = 3 + (isInAct ? 1 : 0); + this->createFolderAndAssociations(projectId, indent, xml, tempDirPath, textFolderId, noteFolderId); - while (xml.readNext() != QXmlStreamReader::EndDocument) {} - } + result = this->createSection(projectId, indent + 1, textFolderId, "chapter"); + } + else if ((xml.tokenType() == QXmlStreamReader::EndElement) && (xml.name() == "chapter")) { + isInChapter = false; + } + else if ((xml.tokenType() == QXmlStreamReader::StartElement) && (xml.name() == "scene")) { + indent = 3 + (isInAct ? 1 : 0) + (isInChapter ? 1 : 0); + this->createPapersAndAssociations(projectId, indent, xml, tempDirPath, textFolderId, noteFolderId); + } + else if ((xml.tokenType() == QXmlStreamReader::StartElement) && (xml.name() == "separator")) { + indent = 3 + (isInAct ? 1 : 0) + (isInChapter ? 1 : 0); + + result = this->createSection(projectId, indent, textFolderId, "separator"); + } + + + // while (xml.readNextStartElement()) { + // if (xml.name().toString() == "trash") { + // xml.skipCurrentElement(); + // continue; + // } + // qDebug() << "xml.name().toString()" << + // xml.name().toString() << + // xml.attributes().value("name").toString(); + + // if (xml.name().toString() == "book") { + // IFOKDO(result, + // + // + // + // this->readXMLRecursivelyAndCreatePaper(projectId, + // 1, &xml, tempDirPath, textFolderId, + // + // + // + // + // noteFolderId)); + // } + // } + } + + // } IFKO(result) { result = SKRResult(SKRResult::Critical, this, "error_while_exploiting_tree"); @@ -291,7 +348,7 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi // transform texts with children in folders - IFOKDO(result, transformParentsToFolder(projectId)); + // IFOKDO(result, transformParentsToFolder(projectId)); // save @@ -308,189 +365,166 @@ SKRResult SKRPlumeCreatorImporter::importPlumeCreatorProject(const QUrl& plumeFi } // ----------------------------------------------------------- +SKRResult SKRPlumeCreatorImporter::createSection(int projectId, + int indent, + int textFolderId, + const QString& section_type) { + SKRResult result(this); + result = skrdata->treeHub()->addChildTreeItem(projectId, textFolderId, "SECTION"); + int newItemId = result.getData("treeItemId", -2).toInt(); -SKRResult SKRPlumeCreatorImporter::transformParentsToFolder(int projectId) { - SKRResult result; - QSqlDatabase sqlDb = plmProjectManager->project(projectId)->getSqlDb(); - - - // retrieve sorted id with indent list - - QList treeIdList; - QList treeIndentList; - QList treeSortOrderList; - QList typeList; - QSqlQuery query(sqlDb); - QString queryStr = "SELECT l_tree_id, l_indent, l_sort_order, t_type FROM tbl_tree ORDER BY l_sort_order"; + if (newItemId != -2) { + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, newItemId, indent)); + result = skrdata->treePropertyHub()->setProperty(projectId, + newItemId, + "section_type", + section_type, + false); + } + return result; +} - query.prepare(queryStr); +// --------------------------------------------------- +SKRResult SKRPlumeCreatorImporter::createFolderAndAssociations(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString& tempDirPath, + int textFolderId, int noteFolderId) +{ + SKRResult result(this); - query.exec(); + int plumeId = xml.attributes().value("number").toInt(); + QString name = xml.attributes().value("name").toString(); + bool isTrashed = xml.attributes().value("isTrashed").toString() == "yes"; + QString badge = xml.attributes().value("badge").toString(); - if (query.lastError().isValid()) { - result = SKRResult(SKRResult::Critical, this, "sql_error"); - result.addData("SQLError", query.lastError().text()); - result.addData("SQL string", queryStr); + // create folder + result = skrdata->treeHub()->addChildTreeItem(projectId, textFolderId, "FOLDER"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "text_creation"); return result; } + int folderId = result.getData("treeItemId", -2).toInt(); - while (query.next()) { - treeIdList.append(query.value(0).toInt()); - treeIndentList.append(query.value(1).toInt()); - treeSortOrderList.append(query.value(2).toInt()); - typeList.append(query.value(2).toString()); - } + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, folderId, indent)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, folderId, name)); + IFOKDO(result, skrdata->treeHub()->setTrashedWithChildren(projectId, folderId, isTrashed)); + IFOKDO(result, skrdata->treePropertyHub()->setProperty(projectId, folderId, "label", badge, true, true, false)); - // remove project item (the first one) - treeIdList.takeFirst(); - treeIndentList.takeFirst(); - treeSortOrderList.takeFirst(); - typeList.takeFirst(); - // remove folders + // - fetch text - QList indexesToRemove; + QFileInfo textFileInfo(tempDirPath + "/text/T" + QString::number(plumeId) + ".html"); - for (int k = 0; k < typeList.count(); k++) { - if (typeList.at(k) == "TEXT") { - indexesToRemove.append(k); - } + if (!textFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); + return result; } - for (int m = indexesToRemove.count() - 1; m >= 0; m++) { - treeIdList.removeAt(m); - treeIndentList.removeAt(m); - treeSortOrderList.removeAt(m); - typeList.removeAt(m); + + QFile textFile(textFileInfo.absoluteFilePath()); + + if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); + return result; } + QByteArray textLines = textFile.readAll(); - // create folders - sqlDb.transaction(); + QTextDocument folderDoc; - QString treeQueryStr = "INSERT INTO tbl_tree (t_title, l_indent, l_sort_order, t_type, dt_trashed, b_trashed)" - " VALUES (" - " (SELECT t_title FROM tbl_tree WHERE l_tree_id = :treeId)," - " (SELECT l_indent FROM tbl_tree WHERE l_tree_id = :treeId)," - " :newSortOrder," - " 'FOLDER'," - " (SELECT dt_trashed FROM tbl_tree WHERE l_tree_id = :treeId)," - " (SELECT b_trashed FROM tbl_tree WHERE l_tree_id = :treeId)" - ")"; + folderDoc.setHtml(QString::fromUtf8(textLines)); - QString incrementIndentQueryStr = "UPDATE tbl_tree SET l_indent = :newIndent WHERE l_tree_id = :treeId"; + // create note - int previousIdIndent = -1; - for (int i = treeIdList.count() - 1; i >= 0; i--) { - int currentTreeId = treeIdList.at(i); - int currentIndent = treeIndentList.at(i); + result = this->createNote(projectId, 2, plumeId, name, tempDirPath + "/text/N", noteFolderId); + bool ok; + int noteId = result.getData("treeItemId", -2).toInt(&ok); - if (i < treeIdList.count() - 1) { - previousIdIndent = treeIndentList.at(i + 1); - } + if (!ok) { + result = SKRResult(SKRResult::Critical, this, "bad_conversion_to_int"); + return result; + } + if (result.getData("hasContent", false).toBool()) { + result = skrdata->treeHub()->setTreeRelationship(projectId, noteId, folderId); + } - if (previousIdIndent > currentIndent) { - int currentSortOrder = treeSortOrderList.at(i); + // create synopsis - // create folder - query.prepare(treeQueryStr); + QFileInfo synFileInfo(tempDirPath + "/text/S" + QString::number(plumeId) + ".html"); - query.bindValue(":treeId", currentTreeId); - query.bindValue(":newSortOrder", currentSortOrder - 1); + if (!synFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_synopsis_file"); + result.addData("filePath", synFileInfo.absoluteFilePath()); + return result; + } - query.exec(); - if (query.lastError().isValid()) { - result = SKRResult(SKRResult::Critical, this, "sql_error"); - result.addData("SQLError", query.lastError().text()); - result.addData("SQL string", queryStr); - result.addData("treeId", currentTreeId); - result.addData("newSortOrder", currentSortOrder - 1); - sqlDb.rollback(); + QFile synFile(synFileInfo.absoluteFilePath()); - return result; - } + if (!synFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_synopsis_file"); + result.addData("filePath", synFileInfo.absoluteFilePath()); + return result; + } - int newFolderTreeId = query.lastInsertId().toInt(); + QByteArray lines = synFile.readAll(); - // increment current item's indent - query.prepare(incrementIndentQueryStr); + QTextDocument synDoc; - query.bindValue(":treeId", currentTreeId); - query.bindValue(":newIndent", currentIndent + 1); + synDoc.setHtml(QString::fromUtf8(lines)); - query.exec(); + IFOKDO(result, skrdata->treeHub()->setSecondaryContent(projectId, folderId, synDoc.toHtml())); - if (query.lastError().isValid()) { - result = SKRResult(SKRResult::Critical, this, "sql_error"); - result.addData("SQLError", query.lastError().text()); - result.addData("SQL string", queryStr); - result.addData("treeId", currentTreeId); - result.addData("sortOrder", currentSortOrder); - result.addData("newIndent", currentIndent + 1); - sqlDb.rollback(); - return result; - } - } - } + // create sheet - IFOK(result) { - sqlDb.commit(); + result = skrdata->treeHub()->addChildTreeItem(projectId, textFolderId, "TEXT"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "text_creation_after_folder"); + return result; } + int sheetId = result.getData("treeItemId", -2).toInt(); - return result; -} + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, sheetId, indent + 1)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, sheetId, tr("%1 (content)").arg(name))); -// ----------------------------------------------------------- -SKRResult SKRPlumeCreatorImporter::readXMLRecursivelyAndCreatePaper(int projectId, - int indent, - QXmlStreamReader *xml, - const QString& tempDirPath, - int textFolderId, int noteFolderId) -{ - SKRResult result; + // + IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, sheetId, folderDoc.toHtml())); - if (!xml->readNextStartElement()) { - xml->readElementText(); - return result; - } - else { - if (xml->name().toString() == "separator") { - xml->skipCurrentElement(); - } - else { - IFOKDO(result, - this->createPapersAndAssociations(projectId, indent, *xml, tempDirPath, textFolderId, noteFolderId)); - IFOKDO(result, - readXMLRecursivelyAndCreatePaper(projectId, indent + 1, xml, tempDirPath, textFolderId, - noteFolderId)); - } - } - while (xml->readNextStartElement()) { - if (xml->name().toString() == "separator") { - xml->skipCurrentElement(); + // associate "attendance" notes + + QStringList attendIds = xml.attributes().value("attend").toString().split("-", Qt::SkipEmptyParts); + + for (const QString& attendIdString : qAsConst(attendIds)) { + int attendId = attendIdString.toInt(); + + if (attendId == 0) { continue; } - IFOKDO(result, - this->createPapersAndAssociations(projectId, indent, *xml, tempDirPath, textFolderId, noteFolderId)); - IFOKDO(result, - readXMLRecursivelyAndCreatePaper(projectId, indent + 1, xml, tempDirPath, textFolderId, noteFolderId)); + + int skrNoteId = m_attendanceConversionHash.value(attendId); + + // associate : + result = skrdata->treeHub()->setTreeRelationship(projectId, skrNoteId, folderId); } + return result; } @@ -502,8 +536,10 @@ SKRResult SKRPlumeCreatorImporter::createPapersAndAssociations(int projectId, { SKRResult result(this); - int plumeId = xml.attributes().value("number").toInt(); - QString name = xml.attributes().value("name").toString(); + int plumeId = xml.attributes().value("number").toInt(); + QString name = xml.attributes().value("name").toString(); + bool isTrashed = xml.attributes().value("isTrashed").toString() == "yes"; + QString badge = xml.attributes().value("badge").toString(); // create sheet @@ -514,11 +550,11 @@ SKRResult SKRPlumeCreatorImporter::createPapersAndAssociations(int projectId, } int sheetId = result.getData("treeItemId", -2).toInt(); - int textFolderIndent = skrdata->treeHub()->getIndent(projectId, textFolderId); - IFOKDO(result, skrdata->treeHub()->setIndent(projectId, sheetId, textFolderIndent + indent)); + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, sheetId, indent)); IFOKDO(result, skrdata->treeHub()->setTitle(projectId, sheetId, name)); - IFOKDO(result, skrdata->treeHub()->setType(projectId, sheetId, "TEXT")); + IFOKDO(result, skrdata->treeHub()->setTrashedWithChildren(projectId, sheetId, isTrashed)); + IFOKDO(result, skrdata->treePropertyHub()->setProperty(projectId, sheetId, "label", badge, true, true, false)); // - fetch text @@ -557,7 +593,7 @@ SKRResult SKRPlumeCreatorImporter::createPapersAndAssociations(int projectId, // create note - result = this->createNote(projectId, indent, plumeId, name, tempDirPath + "/text/N", noteFolderId); + result = this->createNote(projectId, 2, plumeId, name, tempDirPath + "/text/N", noteFolderId); bool ok; int noteId = result.getData("treeItemId", -2).toInt(&ok); @@ -630,62 +666,201 @@ SKRResult SKRPlumeCreatorImporter::createPapersAndAssociations(int projectId, // ----------------------------------------------------------------------------------------------- -SKRResult SKRPlumeCreatorImporter::createNote(int projectId, int indent, int plumeId, const QString& name, - const QString& tempDirPath, int parentFolderId) +SKRResult SKRPlumeCreatorImporter::createAttendFolder(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString & tempDirPath, + int attendFolderId) { SKRResult result(this); + int plumeId = xml.attributes().value("number").toInt(); + QString name = xml.attributes().value("name").toString(); - result = skrdata->treeHub()->addChildTreeItem(projectId, parentFolderId, "TEXT"); + + // create folder + + result = skrdata->treeHub()->addChildTreeItem(projectId, attendFolderId, "FOLDER"); IFKO(result) { - result = SKRResult(SKRResult::Critical, this, "note_creation"); + result = SKRResult(SKRResult::Critical, this, "text_creation"); + return result; + } + int folderId = result.getData("treeItemId", -2).toInt(); + + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, folderId, indent)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, folderId, name)); + + + // - fetch text + + QFileInfo textFileInfo(tempDirPath + "/attend/A" + QString::number(plumeId) + ".html"); + + if (!textFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); + return result; + } + + + QFile textFile(textFileInfo.absoluteFilePath()); + + if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); + return result; + } + + QByteArray textLines = textFile.readAll(); + + + QTextDocument folderDoc; + + folderDoc.setHtml(QString::fromUtf8(textLines)); + + + // create sheet + + result = skrdata->treeHub()->addChildTreeItem(projectId, attendFolderId, "TEXT"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "text_creation_after_folder"); + return result; + } + int sheetId = result.getData("treeItemId", -2).toInt(); + + + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, sheetId, indent + 1)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, sheetId, tr("%1 (content)").arg(name))); + + + // + IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, sheetId, folderDoc.toHtml())); + + result.addData("treeItemId", folderId); + + return result; +} + +// ----------------------------------------------------------------------------------------------- + + +SKRResult SKRPlumeCreatorImporter::createAttendObject(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString & tempDirPath, + int attendFolderId) +{ + SKRResult result(this); + + int plumeId = xml.attributes().value("number").toInt(); + QString name = xml.attributes().value("name").toString(); + + + QFileInfo textFileInfo(tempDirPath + "/attend/A" + QString::number(plumeId) + ".html"); + + if (!textFileInfo.exists()) { + result = SKRResult(SKRResult::Critical, this, "no_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); + return result; + } + + + QFile textFile(textFileInfo.absoluteFilePath()); + + if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + result = SKRResult(SKRResult::Critical, this, "cant_open_text_file"); + result.addData("filePath", textFileInfo.absoluteFilePath()); return result; } - int noteId = result.getData("treeItemId", -2).toInt(); + QByteArray textLines = textFile.readAll(); + + + QTextDocument attendDoc; - int parentFolderIndent = skrdata->treeHub()->getIndent(projectId, parentFolderId); + attendDoc.setHtml(QString::fromUtf8(textLines)); - IFOKDO(result, skrdata->treeHub()->setIndent(projectId, noteId, parentFolderIndent + indent)); - IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteId, name)); - IFOKDO(result, skrdata->treeHub()->setType(projectId, noteId, "TEXT")); + + // create sheet + + result = skrdata->treeHub()->addChildTreeItem(projectId, attendFolderId, "TEXT"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "text_creation_after_folder"); + return result; + } + int sheetId = result.getData("treeItemId", -2).toInt(); + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, sheetId, indent)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, sheetId, name)); + + IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, sheetId, attendDoc.toHtml())); + + + return result; +} + +// ----------------------------------------------------------------------------------------------- + +SKRResult SKRPlumeCreatorImporter::createNote(int projectId, int indent, int plumeId, const QString& name, + const QString& tempDirPath, int parentFolderId) +{ + SKRResult result(this); + // fetch text - QFileInfo attendFileInfo(tempDirPath + QString::number(plumeId) + ".html"); + QFileInfo noteFileInfo(tempDirPath + QString::number(plumeId) + ".html"); - if (!attendFileInfo.exists()) { + if (!noteFileInfo.exists()) { result = SKRResult(SKRResult::Critical, this, "no_attend_file"); - result.addData("filePath", attendFileInfo.absoluteFilePath()); + result.addData("filePath", noteFileInfo.absoluteFilePath()); return result; } - QFile attendFile(attendFileInfo.absoluteFilePath()); + QFile noteFile(noteFileInfo.absoluteFilePath()); - if (!attendFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (!noteFile.open(QIODevice::ReadOnly | QIODevice::Text)) { result = SKRResult(SKRResult::Critical, this, "cant_open_attend_file"); - result.addData("filePath", attendFileInfo.absoluteFilePath()); + result.addData("filePath", noteFileInfo.absoluteFilePath()); return result; } - QByteArray lines = attendFile.readAll(); + QByteArray lines = noteFile.readAll(); QTextDocument noteDoc; noteDoc.setHtml(QString::fromUtf8(lines)); - IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, noteId, noteDoc.toHtml())); + if (noteDoc.characterCount() > 1) { + result.addData("hasContent", true); + + result = skrdata->treeHub()->addChildTreeItem(projectId, parentFolderId, "TEXT"); + IFKO(result) { + result = SKRResult(SKRResult::Critical, this, "note_creation"); + return result; + } + int noteId = result.getData("treeItemId", -2).toInt(); + + + IFOKDO(result, skrdata->treeHub()->setIndent(projectId, noteId, indent)); + IFOKDO(result, skrdata->treeHub()->setTitle(projectId, noteId, name)); + IFOKDO(result, skrdata->treeHub()->setType(projectId, noteId, "TEXT")); + + + IFOKDO(result, skrdata->treeHub()->setPrimaryContent(projectId, noteId, noteDoc.toHtml())); + } + else { + result.addData("hasContent", false); + } return result; } // ----------------------------------------------------------------------------------------------- SKRResult SKRPlumeCreatorImporter::createTagsFromAttend(int projectId, - int noteId, + int attendId, const QXmlStreamReader& xml, const QString & attributeName, const QStringList & values) @@ -713,11 +888,16 @@ SKRResult SKRPlumeCreatorImporter::createTagsFromAttend(int IFOK(result) { result = skrdata->tagHub()->addTag(projectId, values.at(index)); IFOK(result) { + int tagId = result.getData("tagId", -2).toInt(); + result = skrdata->tagHub()->setTagRelationship(projectId, - noteId, - result.getData("tagId", -2).toInt()); + attendId, + tagId); + + skrdata->tagHub()->setTagRandomColors(projectId, tagId); } } + return result; } diff --git a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h index 2b03c5633..72eaa3524 100644 --- a/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h +++ b/src/plugins/plumeCreatorImporter/skrplumecreatorimporter.h @@ -23,7 +23,6 @@ class SKRPlumeCreatorImporter : public QObject { // QSqlDatabase copySQLiteDbToMemory(QSqlDatabase sourceSqlDb, int // projectId, SKRResult &result); - SKRResult transformParentsToFolder(int projectId); SKRResult createPapersAndAssociations(int projectId, int indent, @@ -39,18 +38,32 @@ class SKRPlumeCreatorImporter : public QObject { int parentFolderId); SKRResult createTagsFromAttend(int projectId, - int noteId, + int attendId, const QXmlStreamReader& xml, const QString & attributeName, const QStringList & values); - SKRResult readXMLRecursivelyAndCreatePaper(int projectId, - int indent, - QXmlStreamReader *xml, - const QString & tempDirPath, - int textFolderId, - int noteFolderId); - QHashm_attendanceConversionHash; + SKRResult createSection(int projectId, + int indent, + int textFolderId, + const QString& section_type); + + SKRResult createFolderAndAssociations(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString & tempDirPath, + int textFolderId, + int noteFolderId); + SKRResult createAttendFolder(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString & tempDirPath, + int attendFolderId); + SKRResult createAttendObject(int projectId, + int indent, + const QXmlStreamReader& xml, + const QString & tempDirPath, + int attendFolderId); }; #endif // SKRPLUMECREATORIMPORTER_H diff --git a/src/plugins/plumeCreatorImporter/tests/CMakeLists.txt b/src/plugins/plumeCreatorImporter/tests/CMakeLists.txt new file mode 100755 index 000000000..bcb4c03f0 --- /dev/null +++ b/src/plugins/plumeCreatorImporter/tests/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.5.0) + +set(PROJECT_NAME "tst_plumecreatorimporter") + +project(${PROJECT_NAME}) + +enable_testing() + +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) + +# As moc files are generated in the binary dir, tell CMake +# to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Test Core CONFIG REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Test Core REQUIRED) + +set(QRC testfiles.qrc) +qt_add_resources(RESOURCES ${QRC}) + + + +add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp + ${RESOURCES}) +add_test(${PROJECT_NAME} ${PROJECT_NAME}) + + +target_link_libraries(${PROJECT_NAME} PRIVATE skribisto-data Qt${QT_VERSION_MAJOR}::Test Qt${QT_VERSION_MAJOR}::Core) +include_directories("${CMAKE_SOURCE_DIR}/src/libskribisto-data/src/") +target_link_libraries(${PROJECT_NAME} PRIVATE skribisto-plugin-plumeCreatorImporter) +include_directories("${CMAKE_SOURCE_DIR}/src/plugins/plumeCreatorImporter") diff --git a/src/plugins/plumeCreatorImporter/tests/plume-test.plume b/src/plugins/plumeCreatorImporter/tests/plume-test.plume new file mode 100644 index 0000000000000000000000000000000000000000..eaedcebb12764eaad79b207dbb7b72c9056a5fc0 GIT binary patch literal 27955 zcmeHQ2{@E()E@gTyFn;Rb{Rxjv+rXWJK23%vM-gT$dU??lBC3ESCIxO`<5ues8HFJ zy;7D!|1dN5e0~1k_v^Z5x@5WD`#R6_+|PZ^dCvRJ87(y|Y%0LwE20Dkvh)M;KRoz% zOHWT*XPbjsOBZ1R@Bt1cI1sLIC+`aw0Dv1N06@BOk&H0Eou|_YC!Gf8P64vwA3O!c z{S@)4y>dykFY8Hl$~A)IWtv&}?(wlus6aZootw`df>vim(fvy@D8S$srS}_qkcznE^o`r!3L|oY2I;28HvvO@Fn)_UIk_COS2qbV8+9QNmE%_8Z|IwUoj|tKlNo< zy{;dQW9(4Eh4Po2vhPidEQw$3XHI|;%-u@0N_Y+d9qEYs4q&|sdh@+?M&CP2vYK`} zlvqOLojDuiZ-X*W=|x_~5sbF`NvFsaIAo(PVQV*KbCe;6~DP;xG0gNHbm{EouX4 zBwtaam74hT)L;K`$dA9wxqD%#b#RF^KM4K&*nxC~f215P9G0R9w4J$64q+U!@oQ=TPJMKZy2jO6s>N|q*T9DfNbYYZS zqur-w?w_%_8~43f!g%idD$>&7qyd*mQ(8w_0h}~V1Zlo@)N|*Ow1bNa^D2RN<)jYV zQ_e`L)f~Q&ZsiU=^o?AR(>ixD{qwC+UElDWabmhtS09(DrYYu}vGQXy5^KZC@|XK8 zc(E~fAEjfTSo?wfVm}OuVw`h?IjsBYO4&d7IEJa8#K?d2H&JNdh96Q_(x3F zzF6`eyxz`Nx;dsp9mjz`UR2B#6_fG~Rz1-9*#%Q{YF<XRz!4WkQ6jojh0f*#8Xsm#tv#ye&&K&i= zZe`r3WFlh*|63Fl<~HOot4aa)9QL}Dz8&92?DcItL3St&(Oy`8KE-4mOAktw-B zeGBKm-*{j(Jo&}qGqX34Mn*C)EBd}tJyQe2f*aL-1-`+=I*4N?PjE+emIyV3b!_M* zOO$~Gp`QOlPX0ua-Y23^Me;B2>j|xMNoTuLG`gpGsrzhhUwqbF3&R5@g;%^vtS!ug zjO{OdbjYS#W_W?+27QT7)r?@C=UqJAC!xIxRJZVD4!o-M%F~eboXT7o-j^9Pu1x@qmQGG2{^~QyCP;)@f0>m77?fcKPR}MkoD}wY) zFEhuA3V9|X2H2;tnf1z)ul>3TlkpC*O)Y>y`=GqW zR#KI7v5bF>b{`?>VE|=>AAntb;E3BRPbhwvsWh9Jjwem(;ANVhmggjr)y%g+NKwbe=r&2lv&tO?Vq@JR{(b; zY7e*r`%IG#2t1e>x7SI-KbX88I67zV$06Rv)R!$Ths%^Yo^vI$IXe+(uJnH(HG)m0 z*0+^^T+C3`rz^PdzuDMZdwMKoD};-e0D|GfG6k{P5Ws(4{Gx+@A+GWAuyvP*U(LVx ztGVk5FDF}1TMtihZZ>g3#KwSMy1@nx{BNGNr#x3BL`85D&97cxKMPm920S4mMqQ$N zWNGKcDX#On!G-COp&v1?t@0{teCkbZT9K9*+Fr3sI2h0$@?RN?_QX8Vl^?( z_8qXM7Wu~APkN8r)yztz)oApY@cpB)_HO~IE%mq*K6{0!D;acjKGG!B1oLzDa70gA zxcX`55WGn9jh)6_fc&r=FH}m&Nbw4I=CN?ipZ@xtO8S=c(zaSI+DU?^q(A8KNiTsX zLp$*n^8X7iFKszx;=kd z1Qf4L5T+PB$S2l!OA+=0FDq2?%)Zf&f`X%ACk#e_nM7tDGEsAOa&;195Qzh+nd7+P4<||mP811>C@@H5TTJ0Id3$>wKOCO( zN;+@ubycWe!$e_!&uqgKKkHSug~^l897iF7zJ) z?lp%w%0A3&`trW~>g zf2>?p_c6PsMVr*j-dqJ&hPR;=1Exx?;gE@aG-^TVkFmTmwC+}ZQvXDl+*T0n)n6q{ z+pO3Tj;HD?C#KE_t%O-$EZI;QMzbF z?JA;veT>?kqR^+^9Xaj(((!>2g?w~!zT1;?T{Ka<%JKbDQIPGG`OrDv4&{97Bh+#Q z2@FDqs6V07rwc;g;n>x#Z&^{gAOZAwcQ@bMUAf7zy$TXKJO59z^HqY{9#80GVuzB6 z^;zFCQ;=;Y6KJOXd#2DqYFDQd%Rvf)cr+AUo7knbiB--I&D5@v)D~yn?Uj+xiQkSS zep~Rgy~GdA(~j`8KJ8l$QMzcyx2qiAdZJc^sO>%0htALcqx^hDQistsq(7sEv_0R4 z4pIMYh=QOU->wc(D~=CM)Sp2V1bshhR}-}=>D%6E7oGI&P|~;lyu0lCz}rju(7D8a zoJ*kfw5vVevYvF&A!=6>wH%^!L1;<+DNLdB^FMOAs-Q8niIZT1K_ihe!+P5QV z-}>x)IY@!GDJV2g|20qOlI$*iqIucvLAH0=MVBRZs4VfzyN0@;?FkCa)DAJVMTpwo zQ+?oi!u!7+#|8ivI|L%y8(TVC+vZ=baIO>} z4xNxt81&`j%dLX#dr;vs-iPTMal9iQ=Gi!XBVEI$Hg6H1blss z{CPjH(b|SyWe9SAVH+M}c8ZHUP@Jmf+;KyJQv;bPUm30SY`+Dzgj~Uw=LvjDk*)O@ zR{u%<8B{>e|HcPZgY!7CiXJjUx=as`dS_hHRlyBfV2x59llI5G>TTFQ_j1rNk-CHD zE-;~*;kn1X0w6;})ePjEm|$KNtp?*^sOU*rzBXM!XF zl?wGl;$flr6lD#<3-L)knXWf{O4Xc2h>OKOrJXrzEr=m67S~bf63>tT0^w%~^j+b| zFrarUZ7MSDi?6`AWUh>tmFyHmCmgpxm*!L$NpJC!;!RtE8Y3h2S+U&r9B0aECd^`M zzK8{!KZ(aw5D>%=AlJkH8z z(UBN_a56ICT@~Sc0PLOrav9Ur(5yU4Rx-)+N|N?_AW)9;LKquc09#wo)6`qNmEMKz zq?`*rg-|LP%-h9wzIG?4#%|saJ=j?^Ed5@(M4efgTb}?{6n+g1bZ38(-&hDV3Tk}* zIvxDCib_@+rffnkwx4kTxnRmWdXOeZ8xhOpY2^&dhPLB&-mmsCRF^nK;lzyKetuiv z(dvD(+QT%aTVrV8YEQPNle&&?eCr$YXV?v{cUhP`!uX?8mE>XkBhOt~_rd!Po?sWK zGmxXJcruiC!gddbm`c6;(m1ma-y`1-zoFaHAeN(;Uacqec!Pq8^| zEC67b62XqAyRB_W+zany0jj6d{ukA!?3@8Im!}fwItM&`(wrZsL`8zBCCGd`l;S_c z(cX+Yhf6@V_Z~w;{EyF1M1QiWm%W{fJIFNQ((GN*nLCo5=~sBF9_V9hU!dAccL0l) zwbX_|qzV|qMf7N9;Xt>QI>8$n45B%l5CS@l04u0QOc!teNg1iXbL#{no5kv8rrC@o za}0&6njM6W!szaNC_8J`igE69@JoWv+Rjww=KDX#OR+yAs(;`2BjDiiQ+4eO%u?kR z&qN)=Z^w9DHMvL`-urIwLaGpP?}*;sd7Dlzvb@6zNp0Et~Nz!uHAk@Zz{9KyU&$qdQHF-g!ivFJt8h(8KgJMs<=5i z<3s)MYqvrOSQ^7;+@&eOr_>RAYdG^P(7q4cRPJx4rQfVJXH;iZpky~vEP>N}&{DRv ztrD+GJFHIIgx{MWsR&!}Jsuqmae0D^{o^FOJF){WNiDP+YVxNZQh%&T=>drqkhBDF z3|Y2Q%HC10pvbBH$Z!sJI1~1?x$Q#+i-V+CRu7emH&tLfZN-sPe66Pw!0wzVHxseK z_fS&3nN~euCKb@n^T|Yh&0`5E$o>{4 zdkqS@M>K0G3emm{&8YA;=`YG-HmKG-Z}vHN&yTR7j7Dfab&1-gjMr-N_RQqpz|_rR zA~_QjQ%yY1rOyOY{i|O{$B_jzgPUT(_2L^ecK*QbGdM^o(y4DzwA8I&l7FM#(z zdhZ_S;X53pA1jlLdXN6hR)RXxWwgtr?sw`a5yBiZz4q92jHut?=3rI{Sto80{abut zOb`{JwuAz3@G?1!gV1+hqe8IUo~+N$yZVl@yL>i|io{S(ck9#M&M*Q*7BBikrmL?4o}H|vqTf|Z9y0~8iFba#f+knC@Ub9)lU=(&< zCI=tY8W@53bpv4(rXa9&8M8?{;jK)==eYcum!X8WCPsD@USW29V!3#xM!gCGMNklA zmtmB#MP4^MI;%JLSi6zmN1{Kp{8U+?r4(MkEsaZ*A5vp^$5} zy}0MMUR&pVsL}pgBrOXPiEMVUdWF`{bM5vkXGM(``G(2W(DlPz3$=s$Z3HdIa`f** zDL9s)h%&)y==uS#-GIfM%L86|E_pR_>+xE1FB|>mUB7m!B)mu{>TRXJZ?%wb8M<+* z>j%7cs+I>)(2WshJ#y`M2|*|%vS7A)yzAF@EfgwOIlrss6L;U2D|f2+Am;{=3@f`bAoc1c;+%`p?X)MW#!jhWl?vt0jdR z?Y~9NN~3IU42WAVZZbTfL&k}XE@|r}QkO$Qk&lwCUflIY*DledJj&=sn_D9j6p={e z<2GB_T)XQPlu^h{cKv#zYoQY=%g}!&W-auF8fvDSc+jnvS55;7Mc%SqJ=OKdwMK`* zDCBP#T?@UUwG92Y`(8{3HPww;+j@Qb;Ggz|3^N;zZaviqz2))#v+rFy)k6l$RyQU9 zo7L!6GP*89RC>Qyv0F{dX62+6+zDeuacMPTvl7P&g3uH`NO-|ROATl7V?_WG@PBvU L^YAxc{Ox}LoU}F( literal 0 HcmV?d00001 diff --git a/src/plugins/plumeCreatorImporter/tests/testfiles.qrc b/src/plugins/plumeCreatorImporter/tests/testfiles.qrc new file mode 100755 index 000000000..8402e1c20 --- /dev/null +++ b/src/plugins/plumeCreatorImporter/tests/testfiles.qrc @@ -0,0 +1,5 @@ + + + plume-test.plume + + diff --git a/src/plugins/plumeCreatorImporter/tests/tst_plumecreatorimporter.cpp b/src/plugins/plumeCreatorImporter/tests/tst_plumecreatorimporter.cpp new file mode 100644 index 000000000..211070032 --- /dev/null +++ b/src/plugins/plumeCreatorImporter/tests/tst_plumecreatorimporter.cpp @@ -0,0 +1,174 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "skrdata.h" +#include "skrresult.h" +#include "skrplumecreatorimporter.h" + +class PlumeCreatorTest : public QObject { + Q_OBJECT + +public: + + PlumeCreatorTest(); + ~PlumeCreatorTest(); + +public slots: + +private Q_SLOTS: + + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + void importAll(); + +private: + + SKRData *m_data; + QUrl m_testPlumePath; + QUrl m_testSkribistoTempFilePath; + QTemporaryFile m_testPlumeTempFile; + QTemporaryFile m_testSkribistoTempFile; + int m_currentProjectId; +}; + +PlumeCreatorTest::PlumeCreatorTest() +{} + +PlumeCreatorTest::~PlumeCreatorTest() +{} + +void PlumeCreatorTest::initTestCase() +{ + m_data = new SKRData(this); + + m_testPlumeTempFile.setAutoRemove(false); + m_testPlumeTempFile.open(); + QString testPlumeTempFileName = m_testPlumeTempFile.fileName(); + m_testPlumeTempFile.remove(); + + bool result = QFile::copy(":/testfiles/plume-test.plume", testPlumeTempFileName); + QVERIFY(result); + + m_testPlumePath = testPlumeTempFileName; + + m_testSkribistoTempFile.setAutoRemove(false); + m_testSkribistoTempFile.open(); + m_testSkribistoTempFilePath = m_testSkribistoTempFile.fileName(); +} + +void PlumeCreatorTest::cleanupTestCase() +{ + QFile testPlumeTempFile(m_testPlumePath.fileName()); + + testPlumeTempFile.remove(); + + m_testPlumeTempFile.remove(); +} + +void PlumeCreatorTest::init() {} + +void PlumeCreatorTest::cleanup() +{ + QSignalSpy spy(skrdata->projectHub(), SIGNAL(projectClosed(int))); + + skrdata->projectHub()->closeAllProjects(); + + // QCOMPARE(spy.count(), 1); + + // while (!spy.isEmpty()) { + // QList arguments = spy.takeFirst(); + + // // qDebug() << "project n°" << + // QString::number(arguments.at(0).toInt()) + // // << " closed"; + // } +} + +void PlumeCreatorTest::importAll() { + SKRPlumeCreatorImporter *importer = new SKRPlumeCreatorImporter(this); + + SKRResult result = importer->importPlumeCreatorProject(m_testPlumePath, m_testSkribistoTempFilePath); + + QVERIFY(result.isSuccess()); + + int projectId = skrdata->projectHub()->getProjectIdList().first(); + + // [Project] + // - Text [FOLDER] + // - - Book 1 [FOLDER] + // - - - Book 1 (content) + // - - - [book-beginnning] + // - - - Chapter 1 [FOLDER] + // - - - - Chapter 1 (content) + // - - - - [chapter] + // - - - - Scene 1.1 + // - - - - [separator] + // - - - - Scene 1.2 + // - - - - Scene 1.3 + // - - - Chapter 2 [FOLDER] + // - - - - Chapter 2 (content) + // - - - - [chapter] + // - - - - Scene 2.1 + // - - - - Scene 2.2 + // - - - - Scene 2.3 + // - - - - [separator] + // - - - Act 1 [FOLDER] + // - - - Act 1 (content) + // - - - - Chapter 1.1 [FOLDER] + // - - - - - Chapter 1.1 (content) + // - - - - - [chapter] + // - - - - - Scene 1.1.1 + // - - - - - Scene 1.1.2 + // - - [book-ending] + // - - Book 2 [FOLDER] + // - - - Book 2 (content) + // - - - [book-beginnning] + // - - - Act 1 [FOLDER] + // - - - - Act 1 (content) + // - - - Act 2 [FOLDER] + // - - - - Act 2 (content) + // - - [book-ending] + // - Attendance [FOLDER] + // - - group 1 [FOLDER] + // - - - group 1 (content) + // - - - obj 1 + // - - - obj 2 + // - - group 2 [FOLDER] + // - - - group 2 (content) + // - - - obj 5 + // - Notes [FOLDER] + // - - Chapter 1 + // - - Scene 1.1 + + QList allIds = skrdata->treeHub()->getAllIds(projectId); + + qDebug() << "count:" << skrdata->treeHub()->getAllIds(projectId).count(); + + for (int treeItemId : qAsConst(allIds)) { + qDebug() << "title:" << skrdata->treeHub()->getTitle(projectId, treeItemId) << skrdata->treeHub()->getType( + projectId, + treeItemId) << skrdata->treeHub()->getIndent(projectId, treeItemId); + } + + QCOMPARE(skrdata->treeHub()->getTitle(projectId, allIds.at(6)), "Chapter 1 (content)"); + QCOMPARE(skrdata->treeHub()->getTitle(projectId, allIds.at(27)), "Book 2"); + QCOMPARE(skrdata->treeHub()->getTitle(projectId, allIds.at(33)), "Act 2 (content)"); + QCOMPARE(skrdata->treeHub()->getTitle(projectId, allIds.at(34)), ""); + QCOMPARE(skrdata->treeHub()->getTitle(projectId, allIds.at(45)), "Scene 1.1"); + + QCOMPARE(skrdata->treeHub()->getAllIds(projectId).count(), 46); +} + +QTEST_GUILESS_MAIN(PlumeCreatorTest) + +#include "tst_plumecreatorimporter.moc" diff --git a/src/translations/skribisto_en_US.ts b/src/translations/skribisto_en_US.ts index d0742ab17..39dffe5c6 100644 --- a/src/translations/skribisto_en_US.ts +++ b/src/translations/skribisto_en_US.ts @@ -1941,6 +1941,25 @@ + + SKRPlumeCreatorImporter + + Text + + + + Attendance + + + + Notes + + + + %1 (content) + + + SKRRootItem @@ -2991,7 +3010,7 @@ - Loading a projects + Loading a project From d84e0efcda1af0d9c627271ad43e786cf08b7187 Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Mon, 9 Aug 2021 00:00:23 +0200 Subject: [PATCH 11/13] Test, Fixes Test: create a test for SkrFickable Fixes: Buttons stop trowing an error if no action --- src/app/src/qml.qrc | 1 + src/app/src/qml/Commons/WritingZone.qml | 89 ----- .../src/qml/Commons/WritingZoneForm.ui.qml | 83 ++-- src/app/src/qml/Items/SkrButton.qml | 19 +- src/app/src/qml/Items/SkrFlickable.qml | 288 ++++++++++++++ src/app/src/qml/Items/SkrMenuItem.qml | 8 +- src/app/src/qml/Items/SkrTabButtonForm.ui.qml | 6 +- src/app/src/qml/Items/SkrToolButton.qml | 4 +- src/app/src/qml/main.qml | 1 + src/app/tests/CMakeLists.txt | 1 + src/app/tests/flickable/CMakeLists.txt | 121 ++++++ src/app/tests/flickable/main.cpp | 361 ++++++++++++++++++ src/app/tests/flickable/test_flickable.qml | 63 +++ src/app/tests/flickable/test_qml.qrc | 5 + src/app/tests/navigationlist/CMakeLists.txt | 2 +- src/plugins/textPage/CMakeLists.txt | 2 - .../textPage/TextPage/MinimapForm.ui.qml | 1 + src/plugins/textPage/TextPage/TextPage.qml | 69 ++++ .../textPage/TextPage/TextPageForm.ui.qml | 57 ++- src/plugins/textPage/minimapcanvas.cpp | 31 -- src/plugins/textPage/minimapcanvas.h | 56 --- src/plugins/textPage/textpage.cpp | 6 - 22 files changed, 1023 insertions(+), 251 deletions(-) create mode 100755 src/app/src/qml/Items/SkrFlickable.qml create mode 100755 src/app/tests/flickable/CMakeLists.txt create mode 100755 src/app/tests/flickable/main.cpp create mode 100755 src/app/tests/flickable/test_flickable.qml create mode 100755 src/app/tests/flickable/test_qml.qrc delete mode 100644 src/plugins/textPage/minimapcanvas.cpp delete mode 100644 src/plugins/textPage/minimapcanvas.h diff --git a/src/app/src/qml.qrc b/src/app/src/qml.qrc index 9eca42f73..920de3b23 100755 --- a/src/app/src/qml.qrc +++ b/src/app/src/qml.qrc @@ -107,5 +107,6 @@ qml/Commons/RelationshipPanelForm.ui.qml qml/Items/SkrBusyIndicator.qml qml/Commons/OutlineWritingZone.qml + qml/Items/SkrFlickable.qml diff --git a/src/app/src/qml/Commons/WritingZone.qml b/src/app/src/qml/Commons/WritingZone.qml index 563cdf100..843c81e7c 100755 --- a/src/app/src/qml/Commons/WritingZone.qml +++ b/src/app/src/qml/Commons/WritingZone.qml @@ -659,95 +659,6 @@ WritingZoneForm { } } - //----------------------------------------------------------------------------- - // left scroll area : - - //textArea.onCursorRectangleChanged: flickable.ensureVisible(textArea.cursorRectangle) - leftScrollTouchArea.onUpdated: { - var deltaY = touchPoints[0].y - touchPoints[0].previousY - - // console.log("deltaY :", deltaY) - if (flickable.atYBeginning && deltaY > 0) { - flickable.returnToBounds() - return - } - if (flickable.atYEnd && deltaY < 0) { - flickable.returnToBounds() - return - } - - flickable.flick(0, deltaY * 50) - - // for (var touch in touchPoints) - // console.log("Multitouch updated touch", touchPoints[touch].pointId, - // "at", touchPoints[touch].x, ",", touchPoints[touch].y, - // ",", touchPoints[touch].previousY, ",", - // touchPoints[touch].startY) - } - - // leftScrollMouseArea.onPressAndHold: { - - // } - // leftScrollMouseArea.onWheel: function(event) { - - // var deltaY = wheel.angleDelta.y *10 - - // flickable.flick(0, deltaY) - - // if (flickable.atYBeginning && wheel.angleDelta.y > 0) { - // flickable.returnToBounds() - // return - // } - // if (flickable.atYEnd && wheel.angleDelta.y < 0) { - // flickable.returnToBounds() - // return - // } - // } - - // right scroll area : - - //textArea.onCursorRectangleChanged: flickable.ensureVisible(textArea.cursorRectangle) - rightScrollTouchArea.onUpdated: { - var deltaY = touchPoints[0].y - touchPoints[0].previousY - - // console.log("deltaY :", deltaY) - if (flickable.atYBeginning && deltaY > 0) { - flickable.returnToBounds() - return - } - if (flickable.atYEnd && deltaY < 0) { - flickable.returnToBounds() - return - } - - flickable.flick(0, deltaY * 50) - - // for (var touch in touchPoints) - // console.log("Multitouch updated touch", touchPoints[touch].pointId, - // "at", touchPoints[touch].x, ",", touchPoints[touch].y, - // ",", touchPoints[touch].previousY, ",", - // touchPoints[touch].startY) - } - - // rightScrollMouseArea.onPressAndHold: { - - // } - // rightScrollMouseArea.onWheel: function(event) { - - // var deltaY = wheel.angleDelta.y *10 - - // flickable.flick(0, deltaY) - - // if (flickable.atYBeginning && wheel.angleDelta.y > 0) { - // flickable.returnToBounds() - // return - // } - // if (flickable.atYEnd && wheel.angleDelta.y < 0) { - // flickable.returnToBounds() - // return - // } - // } - // scrollView : //-------------------------------------------------------------------------------- diff --git a/src/app/src/qml/Commons/WritingZoneForm.ui.qml b/src/app/src/qml/Commons/WritingZoneForm.ui.qml index 4977bba11..b5f77755a 100755 --- a/src/app/src/qml/Commons/WritingZoneForm.ui.qml +++ b/src/app/src/qml/Commons/WritingZoneForm.ui.qml @@ -1,4 +1,5 @@ import QtQuick 2.15 +import QtQml 2.15 import QtQuick.Controls 2.15 import QtQuick.Controls.Material 2.15 import QtQuick.Layouts 1.15 @@ -14,12 +15,11 @@ FocusScope { property alias scrollView: scrollView property alias textArea: textArea property alias flickable: textAreaFlickable + readonly property bool flicking: textAreaFlickable.flicking || leftScrollFlickable.flicking || rightScrollFlickable.flicking + readonly property bool dragging: textAreaFlickable.dragging || leftScrollFlickable.dragging || rightScrollFlickable.dragging property alias internalScrollBar: internalScrollBar property int scrollBarVerticalPolicy: ScrollBar.AsNeeded property alias leftScrollItem: leftScrollItem - property alias leftTouch1: leftTouch1 - property alias leftScrollTouchArea: leftScrollTouchArea - property alias rightScrollTouchArea: rightScrollTouchArea property alias rightScrollItem: rightScrollItem property alias placeholderText: textArea.placeholderText @@ -50,17 +50,35 @@ FocusScope { id: leftScrollItem Layout.fillHeight: true Layout.fillWidth: true - MultiPointTouchArea { - id: leftScrollTouchArea - z: 1 + + + Flickable{ + id: leftScrollFlickable anchors.fill: parent - mouseEnabled: false - maximumTouchPoints: 1 - touchPoints: [ - TouchPoint { - id: leftTouch1 - } - ] + clip: true + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds + + contentHeight: textAreaFlickable.contentHeight + contentWidth: width + + maximumFlickVelocity: 200 + flickDeceleration: 0 + Binding{ + target: leftScrollFlickable + property: "contentY" + value: textAreaFlickable.contentY + restoreMode: Binding.RestoreBindingOrValue + } + + + Binding{ + target: textAreaFlickable + property: "contentY" + value: leftScrollFlickable.contentY + restoreMode: Binding.RestoreBindingOrValue + delayed: true + } } @@ -85,6 +103,8 @@ FocusScope { flickableDirection: Flickable.VerticalFlick boundsBehavior: Flickable.StopAtBounds interactive: true + maximumFlickVelocity: 200 + flickDeceleration: 0 //clip: true ScrollBar.vertical: ScrollBar { id: internalScrollBar @@ -116,17 +136,34 @@ FocusScope { id: rightScrollItem Layout.fillHeight: true Layout.fillWidth: true - MultiPointTouchArea { - id: rightScrollTouchArea - z: 1 + + Flickable{ + id: rightScrollFlickable anchors.fill: parent - mouseEnabled: false - maximumTouchPoints: 1 - touchPoints: [ - TouchPoint { - id: rightTouch1 - } - ] + clip: true + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds + + contentHeight: textAreaFlickable.contentHeight + contentWidth: width + maximumFlickVelocity: 200 + flickDeceleration: 0 + + Binding{ + target: rightScrollFlickable + property: "contentY" + value: textAreaFlickable.contentY + restoreMode: Binding.RestoreBindingOrValue + } + + + Binding{ + target: textAreaFlickable + property: "contentY" + value: rightScrollFlickable.contentY + restoreMode: Binding.RestoreBindingOrValue + delayed: true + } } } } diff --git a/src/app/src/qml/Items/SkrButton.qml b/src/app/src/qml/Items/SkrButton.qml index 7c07af00e..8683ef32d 100755 --- a/src/app/src/qml/Items/SkrButton.qml +++ b/src/app/src/qml/Items/SkrButton.qml @@ -5,10 +5,10 @@ import ".." Button { id: control - icon.color: control.action === null ? (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled) : - (control.action.icon.color === "transparent" ? - (enabled ? control.action.icon.color : SkrTheme.buttonIconDisabled) : - (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled)) + icon.color: control.action ? (control.action.icon.color == "transparent"? + (enabled ? control.action.icon.color: SkrTheme.buttonIconDisabled) : + (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled)) : + (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled) Material.background: SkrTheme.buttonBackground Material.foreground: SkrTheme.buttonForeground @@ -32,8 +32,17 @@ Button { hoverEnabled: true SkrToolTip { - text: control.tip ? control.tip : control.text + text: (control.tip ? control.tip : control.text) + priv.finalShortcutText visible: control.hovered && text.length !== 0 } + + + QtObject{ + id: priv + property string finalShortcutText: shortcutText ? " (" + shortcutText +")" : "" + } + + property string shortcutText: action ? (action.shortcutText ? action.shortcutText : "") : "" + } diff --git a/src/app/src/qml/Items/SkrFlickable.qml b/src/app/src/qml/Items/SkrFlickable.qml new file mode 100755 index 000000000..b04efb36b --- /dev/null +++ b/src/app/src/qml/Items/SkrFlickable.qml @@ -0,0 +1,288 @@ +import QtQuick 2.15 +import QtQml 2.15 +import QtQuick.Controls 2.15 +import Qt.labs.animation 1.0 +import ".." +import "../Commons" + +Item { + id: root + + property int flickableDirection: Qt.Vertical + + clip: true + + property Item contentItem: Item { + } + + onContentItemChanged: { + root.children = [contentItem] + } + + Component.onCompleted: { + root.children = [contentItem] + contentItem.x = 0 + contentItem.y = 0 + } + + + property int contentHeight: -1 + property int contentWidth: -1 + property int contentX: 0 + property int contentY: 0 + signal flickStarted + signal flickEnded + + Binding { + target: contentItem + property: "height" + value: contentHeight + } + + Binding { + target: root + property: "contentHeight" + value: contentItem.height + delayed: true + } + + + Binding { + target: contentItem + property: "width" + value: contentWidth + } + + Binding { + target: root + property: "contentWidth" + value: contentItem.width + delayed: true + } + + Binding { + target: contentItem + property: "x" + value: - contentX + restoreMode: Binding.RestoreBinding + } + + + Binding { + target: contentItem + property: "y" + value: - contentY + restoreMode: Binding.RestoreBinding + } + + property real wheelMultiplier: 1 + + +// WheelHandler { +// id: xWheelHandler +// enabled: flickableDirection === Qt.Horizontal +// rotationScale: 15 +// target: contentItem +// property: "x" +// orientation: Qt.Horizontal +// acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad +// onWheel: function(event){ +// root.contentX = contentItem.x +// } +// activeTimeout: 20 + +// onActiveChanged: +// { +// root.contentX = contentItem.x +// // emitting signals in both instances is redundant but hard to avoid +// // when the touchpad is flicking along both axes +// if (active) { +// momentumAnimation.stop() +// root.flickStarted() +// } else { +// xbr.returnToBounds() +// root.flickEnded() +// } + +// } +// } +// WheelHandler { +// id: yWheelHandler +// enabled: flickableDirection === Qt.Vertical +// rotationScale: 15 +// target: contentItem +// property: "y" +// orientation: Qt.Vertical +// acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad +//// onWheel: function(event){ +//// root.contentY = contentItem.y +//// } +// //activeTimeout: 20 + +// onActiveChanged: +// { +//// root.contentY = contentItem.y + +// if (active) { +// momentumAnimation.stop() +// root.flickStarted() +// } else { +// console.log("y:", contentItem.y) +// itemYbr.returnToBounds() +// root.flickEnded() +// } + +// } +// } + +// onContentYChanged: console.log("contentY", contentY) +// onContentXChanged: console.log("contentX", contentX) + + WheelHandler{ + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + onActiveChanged: { + if (active) { + momentumAnimation.stop() + root.flickStarted() + } else { + ybr.returnToBounds() + root.flickEnded() + } + } + + onWheel: function(event){ + var futureValue = 0 + if(flickableDirection === Qt.Vertical){ + futureValue = contentItem.y + event.angleDelta.y * wheelMultiplier + console.log(futureValue) + if(futureValue < 0){ + contentY = 0 + } + else if(futureValue > contentHeight - root.height){ + contentY = contentHeight - root.height + } + else{ + contentY = futureValue + } + } + else{ + futureValue = contentItem.x + event.angleDelta.y * wheelMultiplier + if(futureValue < 0){ + contentX = 0 + } + else if(futureValue > contentWidth - root.width){ + contentX = contentWidth - root.width + } + else{ + contentX = futureValue + } + + + } + } + } + + BoundaryRule on contentX { + id: xbr + minimum: 0 + maximum: contentWidth - root.width >= 0 ? contentWidth - root.width : 0 + } + + BoundaryRule on contentY { + id: ybr + minimum: 0 + maximum: contentHeight - root.height >= 0 ? contentHeight - root.height : 0 + onMaximumChanged: { + console.log("maximum", maximum) + } + } + +// BoundaryRule on contentItem.x { +// id: itemXbr +// minimum: contentItem.width - root.width >= 0 ? root.width - contentItem.width : 0 +// maximum: 0 +// } + +// BoundaryRule on contentItem.y { + +// id: itemYbr +// minimum: contentItem.height - root.height >= 0 ? root.height - contentItem.height : 0 +// maximum: 0 +// } + + DragHandler { + id: dragHandler + target: contentItem + xAxis.enabled: flickableDirection === Qt.Horizontal + xAxis.minimum: root.width - contentWidth ? root.width - contentWidth : - contentWidth + xAxis.maximum: 0 + yAxis.enabled: flickableDirection === Qt.Vertical + yAxis.minimum: root.height - contentHeight ? root.height - contentHeight : - contentWidth + yAxis.maximum: 0 + + onActiveChanged: + if (active) { + momentumAnimation.stop() + root.flickStarted() + } else { + root.contentX = contentItem.x + root.contentY = contentItem.y + + var vel = centroid.velocity + if (xbr.returnToBounds()) + vel.x = 0 + if (ybr.returnToBounds()) + vel.y = 0 + if (vel.x !== 0 || vel.y !== 0) + momentumAnimation.restart(vel) + else + root.flickEnded() + } + } + + + ParallelAnimation { + id: momentumAnimation + + + + + + + onStarted: root.flickStarted() + onStopped: { + xbr.returnToBounds() + ybr.returnToBounds() + root.flickEnded() + } + + + + + + property Item target: null + property int duration: 500 + property vector2d velocity: Qt.vector2d(0,0) + + function restart(vel) { + stop() + velocity = vel + start() + } + + NumberAnimation { + id: xAnim + property: "contentX" + to: flickableDirection === Qt.Horizontal ? contentX + momentumAnimation.velocity.x / duration * 100 : 0 + duration: momentumAnimation.duration + easing.type: Easing.OutQuad + } + NumberAnimation { + id: yAnim + property: "contentY" + to: flickableDirection === Qt.Vertical ? contentY + momentumAnimation.velocity.y / duration * 100 : 0 + duration: momentumAnimation.duration + easing.type: Easing.OutQuad + } + } +} diff --git a/src/app/src/qml/Items/SkrMenuItem.qml b/src/app/src/qml/Items/SkrMenuItem.qml index 8b2ec1969..906c4a719 100755 --- a/src/app/src/qml/Items/SkrMenuItem.qml +++ b/src/app/src/qml/Items/SkrMenuItem.qml @@ -4,10 +4,10 @@ import ".." MenuItem { id: control - icon.color: control.action === null ? (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled) : - (control.action.icon.color === "transparent" ? - (enabled ? control.action.icon.color : SkrTheme.buttonIconDisabled) : - (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled)) + icon.color: control.action ? (control.action.icon.color == "transparent"? + (enabled ? control.action.icon.color: SkrTheme.buttonIconDisabled) : + (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled)) : + (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled) //Material.background: SkrTheme.menuBackground Material.foreground: SkrTheme.buttonForeground diff --git a/src/app/src/qml/Items/SkrTabButtonForm.ui.qml b/src/app/src/qml/Items/SkrTabButtonForm.ui.qml index 036c73da3..ca087e284 100755 --- a/src/app/src/qml/Items/SkrTabButtonForm.ui.qml +++ b/src/app/src/qml/Items/SkrTabButtonForm.ui.qml @@ -12,9 +12,9 @@ TabButton { property alias tabLabel: tabLabel property bool closable: true property bool fillTabBarWidth: false - property string iconSource: base.action === null ? "" : base.action.icon.source - property string iconName: base.action === null ? "" : base.action.icon.name - property string iconColor: base.action === null ? SkrTheme.buttonIcon : base.action.icon.color + property string iconSource: base.action ? base.action.icon.source : "" + property string iconName: base.action ? base.action.icon.name : "" + property string iconColor: base.action ? base.action.icon.color : SkrTheme.buttonIcon padding: 2 diff --git a/src/app/src/qml/Items/SkrToolButton.qml b/src/app/src/qml/Items/SkrToolButton.qml index ba0729e64..5ab0d1a83 100755 --- a/src/app/src/qml/Items/SkrToolButton.qml +++ b/src/app/src/qml/Items/SkrToolButton.qml @@ -5,7 +5,7 @@ import ".." ToolButton { id: control - icon.color: control.action ? (control.action.icon.color === "transparent"? + icon.color: control.action ? (control.action.icon.color == "transparent"? (enabled ? control.action.icon.color: SkrTheme.buttonIconDisabled) : (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled)) : (enabled ? SkrTheme.buttonIcon : SkrTheme.buttonIconDisabled) @@ -36,7 +36,7 @@ ToolButton { display: AbstractButton.IconOnly SkrToolTip { - text: control.tip ? control.tip + priv.finalShortcutText : control.text + priv.finalShortcutText + text: (control.tip ? control.tip : control.text) + priv.finalShortcutText visible: control.hovered && text.length !== 0 } diff --git a/src/app/src/qml/main.qml b/src/app/src/qml/main.qml index 63be14c90..e92096bb2 100755 --- a/src/app/src/qml/main.qml +++ b/src/app/src/qml/main.qml @@ -17,6 +17,7 @@ ApplicationWindow { objectName: "rootWindow" minimumHeight: 500 minimumWidth: 600 + visible: true color: SkrTheme.pageBackground diff --git a/src/app/tests/CMakeLists.txt b/src/app/tests/CMakeLists.txt index c084a6adc..d51bd6bdb 100755 --- a/src/app/tests/CMakeLists.txt +++ b/src/app/tests/CMakeLists.txt @@ -2,5 +2,6 @@ if (SKR_TEST_APP) add_subdirectory(checkabletree) add_subdirectory(navigationlist) + add_subdirectory(flickable) add_subdirectory(overview) endif() diff --git a/src/app/tests/flickable/CMakeLists.txt b/src/app/tests/flickable/CMakeLists.txt new file mode 100755 index 000000000..c457b0352 --- /dev/null +++ b/src/app/tests/flickable/CMakeLists.txt @@ -0,0 +1,121 @@ +cmake_minimum_required(VERSION 3.5.0) + +# Populate a CMake variable with the sources + +project(flickable LANGUAGES CXX VERSION ${VERSION}) + +if(ANDROID) + set(SKR_PRINT_SUPPORT false) + add_compile_definitions(SKR_PRINT_SUPPORT=false) +else() + set(SKR_PRINT_SUPPORT true) + add_compile_definitions(SKR_PRINT_SUPPORT=true) +endif() + + +if(${SKR_PRINT_SUPPORT}) + set(PRINTPACKAGE PrintSupport) +else() + set(PRINTPACKAGE "") +endif() + +# Find the QtWidgets library +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Quick Network Gui Widgets LinguistTools Svg QuickControls2 ${PRINTPACKAGE} REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Quick Network Gui Widgets LinguistTools Svg QuickControls2 ${PRINTPACKAGE} REQUIRED) +find_package(hunspell REQUIRED) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +#foreach(plugin ${Qt5Gui_PLUGINS}) +# get_target_property(_loc ${plugin} LOCATION) +# message("Plugin ${plugin} is at location ${_loc}") +#endforeach() + +# Include GNUInstallDirs, which sets sensible defaults for install directories. +# See https://cmake.org/cmake/help/v3.0/module/GNUInstallDirs.html for further information. +# These values can be easily overridden if required. +# Some defaults are set for OpenBSD as well (info and man pages). +include(GNUInstallDirs) +include(FeatureSummary) + +set(app_SRCS + main.cpp +) + +set(QML ${CMAKE_SOURCE_DIR}/src/app/src/qml.qrc + test_qml.qrc + ) +#ADD_CUSTOM_TARGET(qml.qrc SOURCES qml.qrc) + +#FILE(GLOB QML_SRC "qml/*.qml") +# Add QML files to project tree without building/compiling them +#ADD_CUSTOM_TARGET(qml SOURCES ${QML_SRC} ) + +# QtCreator supports the following variables for Android, which are identical to qmake Android variables. +# Check http://doc.qt.io/qt-5/deployment-android.html for more information. +# They need to be set before the find_package(Qt5 ...) call. + +#if(ANDROID) +# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") +# if (ANDROID_ABI STREQUAL "armeabi-v7a") +# set(ANDROID_EXTRA_LIBS +# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so +# ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so) +# endif() +#endif() + +set(QRC ${CMAKE_SOURCE_DIR}/src/app/src/pics.qrc + ${CMAKE_SOURCE_DIR}/src/app/src/controls.qrc + ${CMAKE_SOURCE_DIR}/src/app/src/themes.qrc + ${CMAKE_SOURCE_DIR}/3rdparty/icons.qrc + ${CMAKE_SOURCE_DIR}/resources/test/testfiles.qrc + ${CMAKE_SOURCE_DIR}/readme.qrc + + ) + +# needed to allow qML debugging : +if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + find_package(Qt${QT_VERSION_MAJOR}QuickCompiler) + qtquick_compiler_add_resources(RESOURCES ${QML}) + qt_add_resources(RESOURCES ${QRC}) +endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + qt_add_resources(RESOURCES ${QRC} ${QML}) +endif (CMAKE_BUILD_TYPE STREQUAL "Debug") + +# Tell CMake to create the executable + +if(ANDROID AND NOT SKR_TEST_APP) + add_library(flickable SHARED ${app_SRCS} ${RESOURCES} ${QRC} ) +else() + add_executable(flickable ${app_SRCS} ${RESOURCES} ${QRC} ) +endif() + +target_compile_definitions(flickable + PRIVATE $<$,$>:QT_QML_DEBUG>) + +if(${SKR_PRINT_SUPPORT}) + set(PRINT Qt${QT_VERSION_MAJOR}::PrintSupport) +else() + set(PRINT "") +endif() + +if(SKR_TEST_APP) +target_link_libraries(flickable PRIVATE skribisto skribisto-data Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::QuickControls2 + ${PRINT} ${HUNSPELL_LIBRARIES}) +endif() + +include_directories("${CMAKE_SOURCE_DIR}/src/libskribisto-data/src/" "${CMAKE_SOURCE_DIR}/src/app/src/") +target_include_directories(flickable SYSTEM PUBLIC ${HUNSPELL_INCLUDE_DIRS}) +target_include_directories(flickable SYSTEM PUBLIC "${CMAKE_SOURCE_DIR}/src/app/src/") + +install(TARGETS flickable RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +#set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/data/Info.plist) diff --git a/src/app/tests/flickable/main.cpp b/src/app/tests/flickable/main.cpp new file mode 100755 index 000000000..fa05139ae --- /dev/null +++ b/src/app/tests/flickable/main.cpp @@ -0,0 +1,361 @@ +#include "iostream" +#include +#include +#include + +using namespace std; + +// for translator +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "skrpluginloader.h" +#include "skrdata.h" +#include "skrtreehub.h" +#include "skrtaghub.h" +#include "skrresult.h" +#include "plmprojecthub.h" +#include "skrprojectdicthub.h" +#include "skrerrorhub.h" +#include "skrpropertyhub.h" +#include "skrstathub.h" +#include "documenthandler.h" +#include "skrhighlighter.h" +#include "skrspellchecker.h" +#include "plmutils.h" +#include "skrthemes.h" +#include "skrexporter.h" +#include "skrclipboard.h" +#include "skr.h" +#include "models/skrtaglistmodel.h" +#include "models/skrsearchtreelistproxymodel.h" +#include "models/skrsearchtaglistproxymodel.h" +#include "models/skrmodels.h" +#include "skrrecentprojectlistmodel.h" +#include "skrusersettings.h" +#include "skrfonts.h" +#include "skreditmenusignalhub.h" +#include "skrqmltools.h" +#include "skrrootitem.h" +#include "skrtextbridge.h" +#include "skrwindowmanager.h" +#include "skrviewmanager.h" +#include "skrtreemanager.h" + +#ifdef QT_DEBUG +# include +#endif // QT_DEBUG +// ------------------------------------------------------- +void startCore() +{ + // new PLMPluginLoader(qApp); + + // UTF-8 codec + QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); + + // Names for the QSettings + QCoreApplication::setOrganizationName("skribisto"); + QCoreApplication::setOrganizationDomain("skribisto.eu"); + + QCoreApplication::setApplicationVersion(QString::number( + SKR_VERSION_MAJOR) + "." + QString::number( + SKR_VERSION_MINOR)); + qDebug() << QCoreApplication::applicationVersion(); + QString appName = "Skribisto"; + + QCoreApplication::setApplicationName(appName); + QSettings::setDefaultFormat(QSettings::IniFormat); +} + +// ------------------------------------------------------- + + +//// ------------------------------------------------------- + +// void openProjectInArgument(SKRData *data) +// { +// // open directly a project if *.skribisto path is the first argument : +// // TODO: add ignore --qml +// QStringList args = qApp->arguments(); + +// if (args.count() > 1) { +// QString argument; + +// for (int i = 1; i <= args.count() - 1; ++i) { +// if (QFileInfo(args.at(i)).exists()) { +// argument = args.at(i); +// break; +// } +// } + +// if (!argument.isEmpty()) { +// # ifdef Q_OS_WIN32 +// QTextCodec *codec = +// QTextCodec::codecForUtfText(argument.toUtf8()); +// argument = codec->toUnicode(argument.toUtf8()); +// # endif // ifdef Q_OS_WIN32 +// argument = QDir::fromNativeSeparators(argument); + +// data->projectHub()->loadProject(argument); +// } +// } +// } + + +// ------------------------------------------------------- + + +// ------------------------------------------------------- + +// ------------------------------------------------------- + +int main(int argc, char *argv[]) +{ +#ifdef QT_DEBUG + QQmlDebuggingEnabler enabler; + + // QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true); +#endif // QT_DEBUG + + // Allows qml styling + qputenv("QT_STYLE_OVERRIDE", ""); + + + // QIcon::setFallbackSearchPaths(QIcon::fallbackSearchPaths() << ":icons"); + + // TODO : add option for UI scale + +#if QT_VERSION >= 0x051400 + QGuiApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); +#else // if QT_VERSION >= 0x051400 + + // qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", QByteArray("0")); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif // if QT_VERSION >= 0x051400 + + + QApplication app(argc, argv); + + // icons : + // qDebug() << "icon search paths :" << QIcon::themeSearchPaths(); + + // if Gnome desktop : + // if(qgetenv("XDG_CURRENT_DESKTOP") == "GNOME"){ + + // QIcon::setThemeName("Adwaita"); + // } + // else { + + // BUG preventing the use of basic breeze theme + // https://bugreports.qt.io/browse/QTBUG-87583 + // instead, I am picking "actions" and "animations" folders from Breeze + // QIcon::setThemeName(QStringLiteral("breeze")); + + // QIcon::setThemeName(QStringLiteral("Adwaita")); + + // } + + startCore(); + + + // QQuickStyle::setStyle("org.kde.desktop"); + + // ----------------------------------------------------------------------- + + + // Language : + + // install translation of plugins: + // PLMPluginLoader::instance()->installPluginTranslations(); + + + // ----------------------------------------------------------------------- + + QQmlApplicationEngine engine(qApp); + + SKRData *data = new SKRData(&engine); + SKRRootItem *rootItem = new SKRRootItem(&engine); + rootItem->applyLanguageFromSettings(); + + + SKRModels *models = new SKRModels(&engine); + SKRFonts *skrFonts = new SKRFonts(&engine); + SKREditMenuSignalHub *skrEditMenuSignalHub = new SKREditMenuSignalHub(&engine); + SKRQMLTools *skrQMLTools = new SKRQMLTools(&engine); + SKRTextBridge *skrTextBridge = new SKRTextBridge(&engine); + SKRUserSettings *skrUserSettings = new SKRUserSettings(&engine); + SKRTreeManager *skrTreeManager = new SKRTreeManager(&engine); + + qmlRegisterUncreatableType("eu.skribisto.result", + 1, + 0, + "SKRResult", + "Can't instantiate SKRResult"); + + + qmlRegisterUncreatableType("eu.skribisto.projecthub", + 1, + 0, + "PLMProjectHub", + "Can't instantiate PLMProjectHub"); + + qmlRegisterUncreatableType("eu.skribisto.treehub", + 1, + 0, + "SKRTreeHub", + "Can't instantiate SKRTreeHub"); + + qmlRegisterUncreatableType("eu.skribisto.taghub", + 1, + 0, + "SKRTagHub", + "Can't instantiate SKRTagHub"); + + qmlRegisterUncreatableType("eu.skribisto.projectdicthub", + 1, + 0, + "SKRProjectDictHub", + "Can't instantiate SKRProjectDictHub"); + + qmlRegisterUncreatableType("eu.skribisto.propertyhub", + 1, + 0, + "SKRProjectDictHub", + "Can't instantiate SKRPropertyHub"); + + qmlRegisterUncreatableType("eu.skribisto.stathub", + 1, + 0, + "SKRStatHub", + "Can't instantiate SKRStatHub"); + + qmlRegisterUncreatableType("eu.skribisto.errorhub", + 1, + 0, + "SKRStatHub", + "Can't instantiate SKRErrorHub"); + + + qmlRegisterUncreatableType("eu.skribisto.skr", + 1, + 0, + "SKR", + "Can't instantiate SKR"); + + qmlRegisterUncreatableType("eu.skribisto.models", + 1, + 0, + "SKRModels", + "Can't instantiate SKRModels"); + + + qmlRegisterUncreatableType( + "eu.skribisto.writedocumentlistmodel", + 1, + 0, + "PLMWriteDocumentListModel", + "Can't instantiate PLMWriteDocumentListModel"); + + qmlRegisterType("eu.skribisto.searchtreelistproxymodel", + 1, + 0, + "SKRSearchTreeListProxyModel"); + + + qmlRegisterType("eu.skribisto.searchtaglistproxymodel", + 1, + 0, + "SKRSearchTagListProxyModel"); + + qmlRegisterType("eu.skribisto.recentprojectlistmodel", + 1, + 0, + "SKRRecentProjectListModel"); + + qmlRegisterType("eu.skribisto.documenthandler", + 1, + 0, + "DocumentHandler"); + + qmlRegisterUncreatableType("eu.skribisto.highlighter", + 1, + 0, + "Highlighter", + "Can't instantiate SKRHighlighter"); + + qmlRegisterType("eu.skribisto.spellchecker", + 1, + 0, + "SKRSpellChecker"); + + qmlRegisterType("eu.skribisto.usersettings", + 1, + 0, + "SKRUserSettings"); + + qmlRegisterType("eu.skribisto.themes", + 1, + 0, + "SKRThemes"); + + qmlRegisterType("eu.skribisto.exporter", + 1, + 0, + "SKRExporter"); + + qmlRegisterType("eu.skribisto.clipboard", + 1, + 0, + "SKRClipboard"); + + qmlRegisterType("eu.skribisto.viewmanager", + 1, + 0, + "SKRViewManager"); + + + const QUrl url(QStringLiteral("qrc:/qml/test/test_flickable.qml")); + + SKRWindowManager *skrWindowManager = new SKRWindowManager(qApp, &engine, url); + + engine.rootContext()->setContextProperty("skrData", data); + engine.rootContext()->setContextProperty("skrRootItem", rootItem); + engine.rootContext()->setContextProperty("skrModels", models); + engine.rootContext()->setContextProperty("skrFonts", skrFonts); + engine.rootContext()->setContextProperty("skrQMLTools", skrQMLTools); + engine.rootContext()->setContextProperty("skrEditMenuSignalHub", skrEditMenuSignalHub); + engine.rootContext()->setContextProperty("skrTextBridge", skrTextBridge); + engine.rootContext()->setContextProperty("skrWindowManager", skrWindowManager); + engine.rootContext()->setContextProperty("skrUserSettings", skrUserSettings); + engine.rootContext()->setContextProperty("skrTreeManager", skrTreeManager); + + + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl& objUrl) { + if (!obj && (url == objUrl)) QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + + skrWindowManager->restoreWindows(); + + + // QCoreApplication *app = qApp; + // engine->connect(engine, &QQmlApplicationEngine::objectCreated, + // [app](QObject *object, const QUrl &url){ + // if(object == nullptr){ + // app->quit(); + // } + // }); + + if (engine.rootObjects().isEmpty()) return -1; + + + return app.exec(); +} diff --git a/src/app/tests/flickable/test_flickable.qml b/src/app/tests/flickable/test_flickable.qml new file mode 100755 index 000000000..d3d2ba964 --- /dev/null +++ b/src/app/tests/flickable/test_flickable.qml @@ -0,0 +1,63 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Window 2.15 +import QtQml.Models 2.15 +import QtQml 2.15 +import Qt.labs.settings 1.1 +import eu.skribisto.result 1.0 +import eu.skribisto.projecthub 1.0 +import eu.skribisto.searchtreelistproxymodel 1.0 +import QtQuick.Controls.Material 2.15 +import "qrc:///qml/Commons" +import "qrc:///qml/Items" +import "qrc:///qml/" + +ApplicationWindow { + + id: rootWindow + objectName: "rootWindow" + minimumHeight: 500 + minimumWidth: 500 + visible: true + + + Component.onCompleted:{ + + } + + Rectangle{ + id: base + //anchors.fill: parent + height: 500 + width: 500 + + border.color: "black" + border.width: 1 + + SkrFlickable{ + id: flickable + anchors.fill: parent + + contentWidth: 400 + + contentItem: Rectangle{ + width: flickable.contentWidth + height: 1000 + + gradient: Gradient { + GradientStop { + position: 0.00; + color: "#ffffff"; + } + GradientStop { + position: 1.00; + color: "#000000"; + } + } + + } + } + + } + +} diff --git a/src/app/tests/flickable/test_qml.qrc b/src/app/tests/flickable/test_qml.qrc new file mode 100755 index 000000000..bb1a8e64e --- /dev/null +++ b/src/app/tests/flickable/test_qml.qrc @@ -0,0 +1,5 @@ + + + test_flickable.qml + + diff --git a/src/app/tests/navigationlist/CMakeLists.txt b/src/app/tests/navigationlist/CMakeLists.txt index ebce3acfd..861ef1472 100755 --- a/src/app/tests/navigationlist/CMakeLists.txt +++ b/src/app/tests/navigationlist/CMakeLists.txt @@ -98,7 +98,7 @@ else() add_executable(navigationlist ${app_SRCS} ${RESOURCES} ${QRC} ) endif() -target_compile_definitions(checkabletree +target_compile_definitions(navigationlist PRIVATE $<$,$>:QT_QML_DEBUG>) if(${SKR_PRINT_SUPPORT}) diff --git a/src/plugins/textPage/CMakeLists.txt b/src/plugins/textPage/CMakeLists.txt index 08bf40236..44ec8edb6 100755 --- a/src/plugins/textPage/CMakeLists.txt +++ b/src/plugins/textPage/CMakeLists.txt @@ -19,8 +19,6 @@ set(SRCS plugin_info.json textpage.cpp textpage.h - minimapcanvas.h - minimapcanvas.cpp ) set(QRC diff --git a/src/plugins/textPage/TextPage/MinimapForm.ui.qml b/src/plugins/textPage/TextPage/MinimapForm.ui.qml index 0318c9002..78b5e0c96 100644 --- a/src/plugins/textPage/TextPage/MinimapForm.ui.qml +++ b/src/plugins/textPage/TextPage/MinimapForm.ui.qml @@ -100,6 +100,7 @@ Item { } WheelHandler { + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad id: wheelHandler } diff --git a/src/plugins/textPage/TextPage/TextPage.qml b/src/plugins/textPage/TextPage/TextPage.qml index bb60557d5..2f41f29f9 100755 --- a/src/plugins/textPage/TextPage/TextPage.qml +++ b/src/plugins/textPage/TextPage/TextPage.qml @@ -572,6 +572,47 @@ TextPageForm { writingZone.highlighter.otherHighlightColor_2: SkrTheme.otherHighlight_2 writingZone.highlighter.otherHighlightColor_3: SkrTheme.otherHighlight_3 + //------------------------------------------------------------------------ + //----- scrollarea------------------------------------------------------------ + //------------------------------------------------------------------------ + + Binding{ + target: leftScrollFlickable + property: "contentY" + value: writingZone.flickable.contentY + restoreMode: Binding.RestoreBindingOrValue + } + + + Binding{ + target: writingZone.flickable + property: "contentY" + value: leftScrollFlickable.contentY + restoreMode: Binding.RestoreBindingOrValue + delayed: true + } + + + //leftScrollWheel.grabPermissions: PointerHandler.TakeOverForbidden + + + //--------------------------- + + Binding{ + target: rightScrollFlickable + property: "contentY" + value: writingZone.flickable.contentY + restoreMode: Binding.RestoreBindingOrValue + } + + + Binding{ + target: writingZone.flickable + property: "contentY" + value: rightScrollFlickable.contentY + restoreMode: Binding.RestoreBindingOrValue + delayed: true + } //------------------------------------------------------------------------ //-----minimap------------------------------------------------------------ @@ -581,6 +622,30 @@ TextPageForm { minimapLoader.active: minimapVisibility minimapLoader.sourceComponent: minimapComponent + QtObject{ + id: minimapPriv + property bool minimapBindingDelayed: true + } + + writingZone.onWidthChanged: { + minimapPriv.minimapBindingDelayed = false + + if(minimapBindingDelayedTimer.running){ + minimapBindingDelayedTimer.stop() + } + + minimapBindingDelayedTimer.start() + } + + Timer{ + id: minimapBindingDelayedTimer + interval: 50 + onTriggered: { + minimapPriv.minimapBindingDelayed = true + + } + } + Component{ id: minimapComponent @@ -606,6 +671,7 @@ TextPageForm { Binding on sourceViewWidth { value: writingZone.textArea.width restoreMode: Binding.RestoreBindingOrValue + delayed: minimapPriv.minimapBindingDelayed } @@ -623,6 +689,9 @@ TextPageForm { property: "contentY" value: minimap.value restoreMode: Binding.RestoreBindingOrValue + when: !(leftScrollFlickable.flicking || leftScrollFlickable.dragging || + writingZone.flicking || writingZone.dragging || + rightScrollFlickable.flicking || rightScrollFlickable.dragging) delayed: true } dragHandler.onActiveChanged: { diff --git a/src/plugins/textPage/TextPage/TextPageForm.ui.qml b/src/plugins/textPage/TextPage/TextPageForm.ui.qml index cad0ff4f4..1d3d43f03 100755 --- a/src/plugins/textPage/TextPage/TextPageForm.ui.qml +++ b/src/plugins/textPage/TextPage/TextPageForm.ui.qml @@ -18,6 +18,8 @@ SkrBasePage { property alias viewButtons: viewButtons property alias pageMenuToolButton: pageMenuToolButton property alias titleLabel: titleLabel + property alias leftScrollFlickable: leftScrollFlickable + property alias rightScrollFlickable: rightScrollFlickable property alias countLabel: countLabel @@ -97,35 +99,15 @@ SkrBasePage { z: 1 - // RowLayout { - // spacing: 0 - // anchors.fill: parent - - // SkrPane { - // id: leftPane - // Layout.fillHeight: true - // Layout.fillWidth: true - - // MultiPointTouchArea { - // id: leftPaneScrollTouchArea - // z: 1 - // anchors.fill: parent - // mouseEnabled: false - // maximumTouchPoints: 1 - // touchPoints: [ - // TouchPoint { - // id: leftTouch1 - // } - // ] - // } - - // MouseArea { - // id: leftPaneScrollMouseArea - // z: 0 - // anchors.fill: parent - // } - // } - // } + + SkrFlickable{ + id: leftScrollFlickable + anchors.fill: parent + flickableDirection: Qt.Vertical + contentHeight: writingZone.flickable.contentHeight + contentWidth: width + } + } Item { @@ -172,6 +154,23 @@ SkrBasePage { //Layout.maximumWidth: 300 z: 1 + Flickable{ + id: rightScrollFlickable + anchors.fill: parent + anchors.rightMargin: rightBaseLayout.width + clip: true + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds + + contentHeight: writingZone.flickable.contentHeight + contentWidth: width + + maximumFlickVelocity: 200 + flickDeceleration: 0 + interactive: true + + } + RowLayout { id: rightBaseLayout spacing: 0 diff --git a/src/plugins/textPage/minimapcanvas.cpp b/src/plugins/textPage/minimapcanvas.cpp deleted file mode 100644 index 6d6ef410f..000000000 --- a/src/plugins/textPage/minimapcanvas.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "minimapcanvas.h" - - -MinimapCanvas::MinimapCanvas(QQuickItem *parent) : QQuickPaintedItem(parent) -{} - -// ------------------------------------------------------------------------- - -QQuickTextDocument * MinimapCanvas::textDocument() const -{ - return m_textDoc; -} - -void MinimapCanvas::setTextDocument(QQuickTextDocument *textDocument) -{ - m_textDoc = textDocument; - emit textDocumentChanged(); - - if (m_textDoc) { - m_textCursor = QTextCursor(m_textDoc->textDocument()); - } -} - -// ----------------------------------------------------------- - -void MinimapCanvas::paint(QPainter *painter) -{ - if (!this->isComponentComplete()) { - return; - } -} diff --git a/src/plugins/textPage/minimapcanvas.h b/src/plugins/textPage/minimapcanvas.h deleted file mode 100644 index d2276f0ce..000000000 --- a/src/plugins/textPage/minimapcanvas.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef MINIMAPCANVAS_H -#define MINIMAPCANVAS_H - -#include -#include -#include - -class MinimapCanvas : public QQuickPaintedItem { - Q_OBJECT - Q_PROPERTY( - QQuickTextDocument * - textDocument READ textDocument WRITE setTextDocument NOTIFY textDocumentChanged) - - // Q_PROPERTY(QString fontFamily READ fontFamily WRITE setFontFamily - // NOTIFY formatChanged) - // Q_PROPERTY(qreal fontSize READ fontSize WRITE setFontSize NOTIFY - // formatChanged) - // Q_PROPERTY( - // qreal topMargin READ topMargin WRITE setTopMargin NOTIFY - // formatChanged) - // Q_PROPERTY( - // qreal textIndent READ textIndent WRITE setTextIndent NOTIFY - // formatChanged) - -public: - - MinimapCanvas(QQuickItem *parent = nullptr); - - QQuickTextDocument* textDocument() const; - void setTextDocument(QQuickTextDocument *textDocument); - - // QString fontFamily() const; - // void setFontFamily(const QString& fontFamily); - - // qreal fontSize() const; - // void setFontSize(qreal fontSize); - - // qreal topMargin() const; - // void setTopMargin(qreal topMargin); - // qreal textIndent() const; - // void setTextIndent(qreal textIndent); - - void paint(QPainter *painter); - -signals: - - void textDocumentChanged(); - void formatChanged(); - -private: - - QQuickTextDocument *m_textDoc; - QTextCursor m_textCursor; -}; - -#endif // MINIMAPCANVAS_H diff --git a/src/plugins/textPage/textpage.cpp b/src/plugins/textPage/textpage.cpp index 192d9853c..34f15a195 100755 --- a/src/plugins/textPage/textpage.cpp +++ b/src/plugins/textPage/textpage.cpp @@ -20,15 +20,9 @@ ***************************************************************************/ #include "textpage.h" #include "skrdata.h" -#include "minimapcanvas.h" TextPage::TextPage(QObject *parent) : QObject(parent) { - qmlRegisterType("eu.skribisto.minimapcanvas", - 1, - 0, - "MinimapCanvas"); - m_wordMeter = new SKRWordMeter(this); connect(m_wordMeter, &SKRWordMeter::characterCountCalculated, skrdata->statHub(), From 6d93eeb6893795639e38c145f12f96d1a89d473a Mon Sep 17 00:00:00 2001 From: Cyril Jacquet Date: Mon, 9 Aug 2021 00:09:26 +0200 Subject: [PATCH 12/13] Update news.yml --- NEWS.yml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/NEWS.yml b/NEWS.yml index 88cd248f3..d2b83ad61 100644 --- a/NEWS.yml +++ b/NEWS.yml @@ -71,4 +71,23 @@ Description: - "Section: add Section page type" - "Parameters: add a way to add custom parameters to new pages" - +Version: 1.9.28 +Date: 2021-08-09 +Type: stable +Description: +- "Fix Windows support" +- "Save: add a save button at the top of the window" +- "Shortcuts: display shortcuts in tips" +- "Minimap: fix wobbling of minimap width and text area width" +- "Navigation: disable animation for side lists" +- "Navigation: better navigation with cleaner code and more usable" +- "Navigation: fix keyboard navigation" +- "Navigation: add visual clue when Cut and Copy items" +- "Navigation: set a smoother transition between lists" +- "Navigation: fix clicking in side popups doing nothing" +- "Plume Creator importer: fix completely importer with new approach" +- "Drag Drop: switch to a new way to drag drop in and from the navigation list" +- "Notes: fix setting a note folder in Project page" +- "Section : fix section icons not displaying correctly" +- "Tags: forbid clicks passing through a color square" +- "View: fix lack of drop visual clue" From d9770f800a2f66409d2867d3a85e2e6819c69a29 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 8 Aug 2021 22:09:59 +0000 Subject: [PATCH 13/13] releases in AppData generated --- eu.skribisto.skribisto.appdata.xml | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/eu.skribisto.skribisto.appdata.xml b/eu.skribisto.skribisto.appdata.xml index 7e4739599..5e72bb995 100644 --- a/eu.skribisto.skribisto.appdata.xml +++ b/eu.skribisto.skribisto.appdata.xml @@ -28,19 +28,25 @@ - +
    -
  • Minimap: new minimap scrollbar
  • -
  • Icons: use bigger and sharper icons
  • -
  • Pages: creation list of items is sorted
  • -
  • Druide Antidote: add plugin to use Druide Antidote with text page
  • -
  • Breadcrumb: make buttons action to go to the beginning/end smoother
  • -
  • Docks: toolbox top buttons are now in a list
  • -
  • Notes: fix set note folder does nothing
  • -
  • Notes: add a quick button to add Notes folder
  • -
  • Section: add Section page type
  • -
  • Parameters: add a way to add custom parameters to new pages
  • +
  • Fix Windows support
  • +
  • Save: add a save button at the top of the window
  • +
  • Shortcuts: display shortcuts in tips
  • +
  • Minimap: fix wobbling of minimap width and text area width
  • +
  • Navigation: disable animation for side lists
  • +
  • Navigation: better navigation with cleaner code and more usable
  • +
  • Navigation: fix keyboard navigation
  • +
  • Navigation: add visual clue when Cut and Copy items
  • +
  • Navigation: set a smoother transition between lists
  • +
  • Navigation: fix clicking in side popups doing nothing
  • +
  • Plume Creator importer: fix completely importer with new approach
  • +
  • Drag Drop: switch to a new way to drag drop in and from the navigation list
  • +
  • Notes: fix setting a note folder in Project page
  • +
  • Section : fix section icons not displaying correctly
  • +
  • Tags: forbid clicks passing through a color square
  • +
  • View: fix lack of drop visual clue