diff --git a/HIG/source/layout/gestures.rst b/HIG/source/layout/gestures.rst new file mode 100644 index 0000000..545523f --- /dev/null +++ b/HIG/source/layout/gestures.rst @@ -0,0 +1,144 @@ +Gestures +======== + +Purpose +------- + +Gestures are a way to perform common tasks faster und more intuitively. They +should never be the only way to perform an action, but rather be treated like a +keybord shortcut or a context menu item. + +Principles +---------- + +Unless you are creating a video game, avoid using non-standard gestures. +Don't abuse the standard gestures to trigger non-standard actions. For example +don't use a pinch gesture to delete an item; only use that gesture for zooming or scaling content. + +Systemwide gestures +-------------------- + +Avoid interfering with systemwide screen edge gestures like pulling down the +top panel on Plasma Mobile. + +Desktop +~~~~~~~ + +================== ================================= +Gesture Action +================== ================================= +Switch workspace +Show app overview +================== ================================= + + +Mobile +~~~~~~ + +================== ================================= +Gesture Action +================== ================================= +Edge swipe top Pull down the top panel +================== ================================= + + +Application gestures +-------------------- + +Gestures are commonly used both on desktop and on mobile. They +can be used to navigate, trigger actions, or manipulate content. + +Tap +~~~ + +.. raw:: html + + + +A tap is handled like a mouse click. It can be used to navigate, trigger +actions, or manipulate content. + +Long press +~~~~~~~~~~ + +A Long press can trigger additional actions on an item or a control or enter a different mode. + +Swipe +~~~~~ + +Swipes can execute actions on list items and +navigate between views. + +.. raw:: html + + + +Swipe navigation in a column view. + +Pinch +~~~~~ + +.. raw:: html + + + +Scales content + +Rotate +~~~~~~ + +.. raw:: html + + + +Rotate content. + + +Mobile +~~~~~~ + +Aditionally to the common gestures, there a gestures especially for Plamsa +Mobile. + +Swipe to reveal +^^^^^^^^^^^^^^^ + +.. raw:: html + + + +Reveal on-demand actions for list items. + +Edge swipe left +^^^^^^^^^^^^^^^ + +.. raw:: html + + + +Open the global drawer. + +Edge swipe right +^^^^^^^^^^^^^^^^ + +.. raw:: html + + + +Open the context drawer. + + diff --git a/HIG/source/layout/index.rst b/HIG/source/layout/index.rst index 1c7a84b..b4aed63 100644 --- a/HIG/source/layout/index.rst +++ b/HIG/source/layout/index.rst @@ -1,19 +1,21 @@ Layout ====== .. toctree:: :maxdepth: 1 :caption: Contents: :titlesonly: :hidden: units metrics alignment onehand + gestures * :doc:`units` * :doc:`metrics` * :doc:`alignment` * :doc:`onehand` +* :doc:`gestures` diff --git a/HIG/source/qml/files/Folder.qml b/HIG/source/qml/files/Folder.qml index 1bd07be..7128d7a 100644 --- a/HIG/source/qml/files/Folder.qml +++ b/HIG/source/qml/files/Folder.qml @@ -1,86 +1,73 @@ /* * Copyright 2018 Fabian Riethmayer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami Kirigami.ScrollablePage { property alias currentIndex: list.currentIndex; property var pagemodel; property var pageroot; Kirigami.Theme.colorSet: Kirigami.Theme.View title: pagemodel.name id: page background: Rectangle { color: Kirigami.Theme.backgroundColor } /*header: Rectangle { clip: true id: header color: "#3daee9" height: searchField.implicitHeight + 2 * Kirigami.Units.largeSpacing width: root.width //border.width: 1 //border.color: "#bdc3c7" TextField { id: searchField placeholderText: "Search" anchors.centerIn: parent anchors.margins: Kirigami.Units.largeSpacing width: parent.width - 2 * Kirigami.Units.largeSpacing } }*/ ListView { currentIndex: -1 id: list model: pagemodel.subfolder delegate: Kirigami.BasicListItem { onClicked: { - pageroot.pageStack.push(Qt.resolvedUrl("Folder.qml"), {"pagemodel": pagemodel.subfolder.get(index), "pageroot": pageroot}); - } - - contentItem: Row { - spacing: 2 * Kirigami.Units.largeSpacing - - Item { - width: Kirigami.Units.iconSizes.medium - height: width - - Kirigami.Icon { - width: Kirigami.Units.iconSizes.medium - height: width - source: model.icon - } - } - Label { - anchors.verticalCenter: parent.verticalCenter - text: model.name - } + loadSubFolder(index); } + icon: model.icon + label: model.name } } + + function loadSubFolder(index) { + pageroot.pageStack.push(Qt.resolvedUrl("Folder.qml"), {"pagemodel": pagemodel.subfolder.get(index), "pageroot": pageroot}); + } } diff --git a/HIG/source/qml/layout/gestures/Pinch.qml b/HIG/source/qml/layout/gestures/Pinch.qml new file mode 100644 index 0000000..8a8a9ad --- /dev/null +++ b/HIG/source/qml/layout/gestures/Pinch.qml @@ -0,0 +1,79 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.3 +import org.kde.kirigami 2.7 as Kirigami +import "../../lib/annotate.js" as A +import "../../addr/" as Addr +import "../../lib/" as HIG + +Rectangle { + width: 320 + height: 600 + id: root + + Image { + z: 2 + anchors.fill: parent + id: myImage + source: "../../../img/wallpaper/next.png" + fillMode: Image.PreserveAspectCrop + smooth: true + + Behavior on rotation { + id: behave + enabled: false + NumberAnimation {duration: 300; easing.type: Easing.Linear} + } + Behavior on scale { + enabled: behave.enabled + NumberAnimation {duration: 300; easing.type: Easing.Linear} + } + + PinchArea { + id: pinchArea + anchors.fill: parent + pinch.target: myImage + + pinch.minimumRotation: 0 + pinch.maximumRotation: 0 + pinch.minimumScale: 0.1 + pinch.maximumScale: 3 + pinch.dragAxis: Pinch.XAndYAxis + + onPinchFinished: function(event) { + behave.enabled = true + myImage.rotation = 0 + myImage.scale = 1 + } + } + } + + HIG.FTimer { + running: true + onTick: function(frameCounter) { + if (frameCounter == 120) { + var a = new A.An(pinchArea); + a.pinch({ from: Qt.rect(0, 0, 20, 20), distance: 100}); + } + } + } +} diff --git a/HIG/source/qml/layout/gestures/Rotate.qml b/HIG/source/qml/layout/gestures/Rotate.qml new file mode 100644 index 0000000..22d19ea --- /dev/null +++ b/HIG/source/qml/layout/gestures/Rotate.qml @@ -0,0 +1,80 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.13 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import org.kde.kirigami 2.4 as Kirigami +import "../../lib/annotate.js" as A +import "../../addr/" as Addr +import "../../lib/" as HIG + +Rectangle { + width: 320 + height: 600 + id: root + + Image { + anchors.fill: root + id: myImage + source: "../../../img/wallpaper/next.png" + fillMode: Image.PreserveAspectCrop + smooth: true + + Behavior on rotation { + id: behave + enabled: false + NumberAnimation {duration: 300; easing.type: Easing.Linear} + } + Behavior on scale { + enabled: behave.enabled + NumberAnimation {duration: 300; easing.type: Easing.Linear} + } + + PinchArea { + id: pinchArea + anchors.fill: parent + pinch.target: myImage + + pinch.minimumRotation: -360 + pinch.maximumRotation: 360 + pinch.minimumScale: 1 + pinch.maximumScale: 1 + pinch.dragAxis: Pinch.XAndYAxis + + onPinchFinished: function(event) { + behave.enabled = true + myImage.rotation = 0 + myImage.scale = 1 + } + } + } + + + // HACK coordinates are only final after a small delay + HIG.FTimer { + running: true + onTick: function(frameCounter) { + if (frameCounter == 90 ) { + var a = new A.An(pinchArea); + a.rotate({ from: Qt.point(0, 0), angle: -90}); + } + } + } +} diff --git a/HIG/source/qml/layout/gestures/ShowContentDrawer.qml b/HIG/source/qml/layout/gestures/ShowContentDrawer.qml new file mode 100644 index 0000000..e35c299 --- /dev/null +++ b/HIG/source/qml/layout/gestures/ShowContentDrawer.qml @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import org.kde.kirigami 2.4 as Kirigami +import "../../lib/annotate.js" as A +import "../../addr/" as Addr +import "../../lib/" as HIG + +Rectangle { + width: 320 + height: 600 + + + + Addr.Addressbook { + id: root + index: 2 + } + + HIG.FTimer { + running: true + onTick: function(frameCounter) { + if (frameCounter == 120) { + var a = new A.An(root); + a.swipe({fromX: 160, fromY: 120, toX: -160, toY: 0}); + } + if (frameCounter == 240) { + var a = new A.An(root); + a.touch({x: -120, y: 120}); + } + } + } +} diff --git a/HIG/source/qml/layout/gestures/ShowGlobalDrawer.qml b/HIG/source/qml/layout/gestures/ShowGlobalDrawer.qml new file mode 100644 index 0000000..799783d --- /dev/null +++ b/HIG/source/qml/layout/gestures/ShowGlobalDrawer.qml @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import org.kde.kirigami 2.4 as Kirigami +import "../../lib/annotate.js" as A +import "../../addr/" as Addr +import "../../lib/" as HIG + +Rectangle { + width: 320 + height: 600 + + Addr.Addressbook { + id: root + } + + HIG.FTimer { + running: true + onTick: function(frameCounter) { + if (frameCounter == 120 ) { + var a = new A.An(root); + //a.tree(); + a.swipe({fromX: -155, fromY: 120, toX: 120, toY: 0}); + } + if (frameCounter == 300 ) { + var a = new A.An(root); + a.touch({x: 100, y: 120}); + } + } + } +} diff --git a/HIG/source/qml/layout/gestures/Swipe.qml b/HIG/source/qml/layout/gestures/Swipe.qml new file mode 100644 index 0000000..03f011f --- /dev/null +++ b/HIG/source/qml/layout/gestures/Swipe.qml @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import org.kde.kirigami 2.4 as Kirigami +import "../../lib/annotate.js" as A +import "../../addr/" as Addr +import "../../lib/" as HIG + +Rectangle { + width: 320 + height: 600 + + Addr.Addressbook { + id: root + } + + HIG.FTimer { + running: true + onTick: function(frameCounter) { + if (frameCounter == 120) { + var a = new A.An(root); + //a.tree(); + a.find("swipelistitem").first().swipe({fromX: +130, fromY: 0, toX: -120, toY: 0}); + } + if (frameCounter == 240) { + var a = new A.An(root); + a.find("swipelistitem").first().touch({x: -50, y: 0}); + } + } + } +} diff --git a/HIG/source/qml/layout/gestures/SwipeNavigate.qml b/HIG/source/qml/layout/gestures/SwipeNavigate.qml new file mode 100644 index 0000000..769084e --- /dev/null +++ b/HIG/source/qml/layout/gestures/SwipeNavigate.qml @@ -0,0 +1,67 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.4 as Kirigami +import "../../models/" as Models +import "../../files/" as Files +import "../../lib/annotate.js" as A +import "../../lib/" as HIG + +Kirigami.ApplicationItem { + width: 320 + height: 600 + id: root + + property var mydata : Models.Files { + Component.onCompleted: { + rootFolder.pagemodel = mydata.get(0) + } + } + + pageStack.initialPage: Files.Folder { + id: rootFolder + pageroot: root + } + + pageStack.defaultColumnWidth: root.width < 320 ? root.width : 320 + pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Breadcrumb + + HIG.FTimer { + running: true + onTick: function(frameCounter) { + if (frameCounter == 20 ) { + root.focus = true + var b = new A.An(root); + //b.touch({fromX: -120, fromY: 220}); + rootFolder.loadSubFolder(1) + } + if (frameCounter == 120 ) { + var b = new A.An(root); + b.swipe({fromX: -120, toX: 220}); + } + if (frameCounter == 300) { + var b = new A.An(root); + b.swipe({fromX: 120, toX: -220}); + } + } + } +} diff --git a/HIG/source/qml/layout/gestures/Touch.qml b/HIG/source/qml/layout/gestures/Touch.qml new file mode 100644 index 0000000..e3b72a4 --- /dev/null +++ b/HIG/source/qml/layout/gestures/Touch.qml @@ -0,0 +1,69 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.2 +import org.kde.kirigami 2.4 as Kirigami +import "../../models/" as Models +import "../../files/" as Files +import "../../lib/annotate.js" as A +import "../../lib/" as HIG + +Kirigami.ApplicationItem { + width: 320 + height: 600 + id: root + + property var mydata : Models.Files { + Component.onCompleted: { + rootFolder.pagemodel = mydata.get(0) + } + } + + pageStack.initialPage: Files.Folder { + id: rootFolder + pageroot: root + } + + pageStack.defaultColumnWidth: root.width < 320 ? root.width : 320 + pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Breadcrumb + + HIG.FTimer { + running: true + onTick: function(frameCounter) { + if (frameCounter == 20) { + root.focus = true + } + + if (frameCounter == 120) { + var a = new A.An(root); + a.find("basiclistitem").eq(0).touch(); + } + if (frameCounter == 300) { + var node = root; + while (node.parent) { + node = node.parent + } + a = new A.An(node);; + a.find("privateactiontoolbutton").first().touch(); + } + } + } +} diff --git a/HIG/source/qml/layout/gestures/config.json b/HIG/source/qml/layout/gestures/config.json new file mode 100644 index 0000000..ae40141 --- /dev/null +++ b/HIG/source/qml/layout/gestures/config.json @@ -0,0 +1,44 @@ +{ + "Pinch.qml": { + "type": "webm", + "controls": "mobile", + "delay": 1, + "duration": 4 + }, + "Rotate.qml": { + "type": "webm", + "controls": "mobile", + "delay": 1, + "duration": 4 + }, + "ShowGlobalDrawer.qml": { + "type": "webm", + "controls": "mobile", + "delay": 1, + "duration": 6 + }, + "Swipe.qml": { + "type": "webm", + "controls": "mobile", + "delay": 1, + "duration": 8 + }, + "Touch.qml": { + "type": "webm", + "controls": "mobile", + "delay": 1, + "duration": 6 + }, + "SwipeNavigate.qml": { + "type": "webm", + "controls": "mobile", + "delay": 1, + "duration": 7 + }, + "ShowContentDrawer.qml": { + "type": "webm", + "controls": "mobile", + "delay": 2, + "duration": 5 + } +} diff --git a/HIG/source/qml/lib/DemoTouch.qml b/HIG/source/qml/lib/DemoTouch.qml index 0b50e3e..f07176a 100644 --- a/HIG/source/qml/lib/DemoTouch.qml +++ b/HIG/source/qml/lib/DemoTouch.qml @@ -1,118 +1,118 @@ /* * Copyright 2018 Fabian Riethmayer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.2 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 import org.kde.kirigami 2.4 as Kirigami import "tools.js" as T import "annotate.js" as A Rectangle { - width: 380 + width: 640 height: 320 color: "white" id: root Row { anchors.fill: parent spacing: Kirigami.Units.largeSpacing anchors.margins: Kirigami.Units.gridUnit * 2 ComboBox { model: [ "Item1", "Item2", "Item3" ] } Component { id: delegateComponent Kirigami.SwipeListItem { id: listItem contentItem: RowLayout { Label { Layout.fillWidth: true height: Math.max(implicitHeight, Kirigami.Units.iconSizes.smallMedium) text: model.title color: listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) ? listItem.activeTextColor : listItem.textColor } } actions: [ Kirigami.Action { iconName: "document-decrypt" text: "Action 1" onTriggered: showPassiveNotification(model.text + " Action 1 clicked") }, Kirigami.Action { iconName: "mail-reply-sender" text: "Action 2" onTriggered: showPassiveNotification(model.text + " Action 2 clicked") }] } } ListView { width: 200 height: 200 id: mainList Timer { id: refreshRequestTimer interval: 3000 onTriggered: page.refreshing = false } model: ListModel { id: listModel Component.onCompleted: { for (var i = 0; i < 200; ++i) { listModel.append({"title": "Item " + i, "actions": [{text: "Action 1", icon: "document-decrypt"}, {text: "Action 2", icon: "mail-reply-sender"}] }) } } } moveDisplaced: Transition { YAnimator { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } delegate: Kirigami.DelegateRecycler { width: parent ? parent.width : implicitWidth sourceComponent: delegateComponent } } } // HACK coordinates are only final after a small delay Timer { interval: 1000 repeat: false running: true onTriggered: { var a = new A.An(root); //a.tree(); a.find("swipelistitem").first().swipe({fromX: +90, fromY: 0, toX: -80, toY: 0}); } } // Draw helpers and anotation Raster { base: Kirigami.Units.gridUnit desktop: true } } diff --git a/HIG/source/qml/lib/FTimer.qml b/HIG/source/qml/lib/FTimer.qml new file mode 100644 index 0000000..5011ae8 --- /dev/null +++ b/HIG/source/qml/lib/FTimer.qml @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 + +Item { + width: 1 + height: 1 + visible: false + signal tick(int frameCounter) + + property int frameCounter: 0 + property bool running : false + + NumberAnimation on rotation { + from:0 + to: 360 + duration: 800 + loops: Animation.Infinite + } + onRotationChanged: function() { + if (running) { + frameCounter++; + tick(frameCounter); + } + } + + function start() { + frameCounter = 0; + running = true; + } + + function stop() { + running = false; + } +} diff --git a/HIG/source/qml/lib/Pinch.qml b/HIG/source/qml/lib/Pinch.qml new file mode 100644 index 0000000..3b38b4f --- /dev/null +++ b/HIG/source/qml/lib/Pinch.qml @@ -0,0 +1,134 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.kirigami 2.4 as Kirigami +import QtTest 1.2 + +import "tools.js" as T + +// Show a touch event +Item { + id: canvas + anchors.fill: parent; + z: 10000000000 + + // Define the pinch movement + property rect from + property rect to + property int duration : 300; + + // For internal + property int i: 0 + property real aStepX; + property real aStepY; + property real bStepX; + property real bStepY; + property var sequence; + property int frames: 0 + + VisualTouchPoint { + id: touchPointA; + x: from.right + y: from.top + dur: duration + } + + VisualTouchPoint { + id: touchPointB + x: from.left + y: from.bottom + dur: duration + } + + TestEvent { + id: event + } + + FTimer { + id: swipeTimer + onTick: function(frameCounter) { + // Move both touch pointer + sequence.move(1, canvas.parent, from.right + frameCounter * aStepX, from.top + frameCounter * aStepY); + sequence.move(2, canvas.parent, from.left + frameCounter * bStepX, from.bottom + frameCounter * bStepY); + sequence.commit(); + //console.log("M " + (from.right + frameCounter * aStepX) + " x " + (from.top + frameCounter * aStepY) + " - " + (from.left + frameCounter * bStepX) + " x " + (from.bottom + frameCounter * bStepY)); + + if (frameCounter >= frames) { + swipeTimer.stop(); + touchPointA.moved(); + } + } + } + + function pinch() { + // Calculate how many frames the animation is running + frames = Math.floor(60 / 1000 * duration); + //console.log(frames) + + touchPointA.animate = true; + touchPointB.animate = true; + + // Calculate step size + // aStepX and aStepX should both > 0 + // and bStepX and bStepY both < 0 + // (or the other way around) + aStepX = -1 * (from.right - to.right) / frames + aStepY = -1 * (from.top - to.top) / frames + bStepX = -1 * (from.left - to.left) / frames + bStepY = -1 * (from.bottom - to.bottom) / frames + + + // Wait till both touch points are pressed + T.join([touchPointA.pressed, touchPointB.pressed], function() { + sequence = event.touchEvent(canvas.parent); + sequence.press(1, canvas.parent, from.right, from.top); + sequence.press(2, canvas.parent, from.left, from.bottom); + sequence.commit(); + //console.log("P " + from.right + " x " + from.top + " - " + from.left + " x " + from.bottom); + + // Move the visual touchpointer to the end + touchPointA.x = to.right; + touchPointA.y = to.top; + touchPointB.x = to.left; + touchPointB.y = to.bottom; + + + // Start the gesture + i = 0; + swipeTimer.start(); + }); + + // Release after swipes are done + touchPointA.moved.connect(function() { + touchPointA.state = "RELEASED"; + touchPointB.state = "RELEASED"; + }); + + touchPointA.released.connect(function() { + sequence.release(1, canvas.parent, to.right, to.top); + sequence.release(2, canvas.parent, to.left, to.bottom); + sequence.commit(); + //console.log("R " + to.right + " x " + to.top + " - " + to.left + " x " + to.bottom); + }); + + touchPointA.state = "PRESSED" + touchPointB.state = "PRESSED" + } +} diff --git a/HIG/source/qml/lib/Rotate.qml b/HIG/source/qml/lib/Rotate.qml new file mode 100644 index 0000000..e7a8b62 --- /dev/null +++ b/HIG/source/qml/lib/Rotate.qml @@ -0,0 +1,152 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.kirigami 2.4 as Kirigami +import QtTest 1.2 + +import "tools.js" as T + +// Show a touch event +Item { + id: canvas + anchors.fill: parent; + // Define the pinch movement + property rect from; + property int angle; + property int duration : 300; + + // For internal + property int i: 0; + property real step; + property real offsetX : Math.floor(canvas.width / 2); + property real offsetY : Math.floor(canvas.height / 2); + property var sequence; + property int frames: 0; + + Item { + + anchors.fill: parent + + VisualTouchPoint { + id: touchPointA; + x: from.right + y: from.top + dur: duration + } + + VisualTouchPoint { + id: touchPointB + x: from.left + y: from.bottom + dur: duration + } + + transform: Rotation { + id: rotateHelper + + origin.x: canvas.width / 2 + origin.y: canvas.height / 2 + + Behavior on angle { + NumberAnimation {duration: canvas.duration; easing.type: Easing.Linear} + } + } + + } + + TestEvent { + id: event + } + + FTimer { + id: swipeTimer + onTick: function(frameCounter) { + // Move both touch pointer + var p = rotatePoint(Qt.point(from.right, from.top), frameCounter * step); + sequence.move(1, canvas.parent, p.x, p.y); + p = rotatePoint(Qt.point(from.left, from.bottom), frameCounter * step); + sequence.move(2, canvas.parent, p.x, p.y); + sequence.commit(); + + if (frameCounter >= frames) { + running = false; + touchPointA.moved(); + } + } + } + + // Rotate a point around the center of the canvas + function rotatePoint(point, angle) { + // to radian + angle = 2 * Math.PI / 360 * angle + // Translate point to 0/0 for rotation + var tPoint = Qt.point( + point.x - offsetX, + point.y - offsetY + ); + tPoint = Qt.point( + tPoint.x * Math.cos(angle) - tPoint.y * Math.sin(angle), + tPoint.x * Math.sin(angle) + tPoint.y * Math.cos(angle) + ); + return Qt.point(tPoint.x + offsetX, tPoint.y + offsetY); + } + + function rotate() { + touchPointA.animate = true; + touchPointB.animate = true; + + // Calculate how many frames the animation is running + frames = Math.floor(60 / 1000 * duration); + + step = canvas.angle / frames; + + // Wait till both touch points are pressed + T.join([touchPointA.pressed, touchPointB.pressed], function() { + sequence = event.touchEvent(canvas.parent); + sequence.press(1, canvas.parent, from.right, from.top); + sequence.press(2, canvas.parent, from.left, from.bottom); + sequence.commit(); + + // Rotate + rotateHelper.angle = canvas.angle + + // Start the gesture + i = 0; + swipeTimer.start(); + }); + + // Release after swipes are done + touchPointA.moved.connect(function() { + touchPointA.state = "RELEASED"; + touchPointB.state = "RELEASED"; + }); + + touchPointA.released.connect(function() { + var p = rotatePoint(Qt.point(from.right, from.top), canvas.angle); + sequence.release(1, canvas.parent, p.x, p.y); + p = rotatePoint(Qt.point(from.left, from.bottom), canvas.angle); + sequence.release(2, canvas.parent, p.x, p.y); + sequence.commit(); + }); + + touchPointA.state = "PRESSED" + touchPointB.state = "PRESSED" + } +} diff --git a/HIG/source/qml/lib/Touch.qml b/HIG/source/qml/lib/Touch.qml index 1ed2253..f9c7c5f 100644 --- a/HIG/source/qml/lib/Touch.qml +++ b/HIG/source/qml/lib/Touch.qml @@ -1,160 +1,118 @@ /* * Copyright 2018 Fabian Riethmayer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.2 import org.kde.kirigami 2.4 as Kirigami import QtTest 1.2 -// Drawing a brace between to obejcts to show the distance between them +// Show a touch event Item { id: canvas anchors.fill: parent; property int fromX property int fromY property int toX property int toY property int dur: 300 property var sequence; - property int i: 0 - - /*MouseArea { - anchors.fill: parent - onPressed: { - console.log(mouse.x + "x" + mouse.y) - } - }*/ - - Rectangle { - id: ind - z: 1 - width: height - height: Kirigami.Units.iconSizes.smallMedium - color: "#331d99f3" - radius: height / 2 - visible: false - x: cursor.x - - NumberAnimation on width { - id: indAnim - duration: dur - running: false - } - } - - Image { - id: cursor - source: "../../img/transform-browse.svg" - visible: false - width: Kirigami.Units.iconSizes.smallMedium - height: Kirigami.Units.iconSizes.smallMedium - z: 2 - - NumberAnimation on x { - id: xAnim - duration: dur - running: false - } - NumberAnimation on y { - id: yAnim - running: false - duration: dur - onStopped: { - timer.start() - } - } + property int touchId: 0 + property int frames: 0 + z: 10000000000 + + VisualTouchPoint { + x: fromX; + y: fromY; + id: touchPoint } TestEvent { id: event } - Timer { - id: timer - interval: 300 - repeat: false - running: false - onTriggered: { - ind.visible = false; - cursor.visible = false; - swipeTimer.stop(); - } - } - - Timer { + // needs to be synced with the animations of the touchPoint + FTimer { id: swipeTimer - interval: 30 - repeat: true - running: false - onTriggered: { - i++; - var stepX = (toX - fromX) / timer.interval * swipeTimer.interval - var stepY = (toY - fromY) / timer.interval * swipeTimer.interval - sequence.move(1, canvas.parent, fromX + i * stepX, toY + i * stepY); - //console.log("move: " + (fromX + i * stepX) + "x" + (toY + i * stepY)) + onTick: function(frameCounter) { + // Move the touch pointer + var stepX = (toX - fromX) / frames; + var stepY = (toY - fromY) / frames; + sequence.move(touchId, canvas.parent, fromX + frameCounter * stepX, toY + frameCounter * stepY); sequence.commit(); + //console.log("M " + (fromX + frameCounter * stepX) + " x " + ( toY + frameCounter * stepY)); + + if (frameCounter >= frames) { + swipeTimer.stop(); + touchPoint.moved(); + } } } // Animate swipe function swipe() { - cursor.x = fromX - Kirigami.Units.iconSizes.smallMedium; - cursor.y = fromY - Kirigami.Units.iconSizes.smallMedium; - cursor.visible = true; - - ind.y = fromY; - ind.visible = true; - - xAnim.to = toX; - xAnim.start(); - yAnim.to = toY; - yAnim.start(); - indAnim.to = Math.abs(fromX - toX); - indAnim.start(); - - sequence = event.touchEvent(canvas.parent); - sequence.press(1, canvas.parent, fromX, fromY); - //console.log("press: " + fromX + "x" + fromY) - sequence.commit(); - i = 0; - - swipeTimer.start(); - timer.start(); - - timer.triggered.connect(function() { - sequence.release(1, canvas.parent, toX, toY); - //console.log("release: " + toX + "x" + toY) + // Calculate how many frames the animation is running + frames = Math.floor(60 / 1000 * dur); + + touchPoint.animate = true; + // Start swipe after animation is done + touchPoint.pressed.connect(function() { + touchPoint.x = toX; + touchPoint.y = toY; + if (!sequence) { + // Only create a new sequence if not 1 exists already + sequence = event.touchEvent(canvas.parent); + } + sequence.press(touchId, canvas.parent, fromX, fromY); sequence.commit(); + //console.log("P " + fromX + " x " + fromY); + + swipeTimer.start(); }); - + + // Finish touch event + // Release touch pointer + touchPoint.moved.connect(function() { + sequence.release(touchId, canvas.parent, toX, toY); + sequence.commit(); + //console.log("R " + toX + " x " + toY); + touchPoint.state = "RELEASED"; + }); + touchPoint.state = "PRESSED" } function touch() { - cursor.x = toX; - cursor.y = toY; - cursor.visible = true; - timer.start() - timer.triggered.connect(function() { - sequence = event.touchEvent(canvas); - sequence.press(1, canvas, toX, toY); + touchPoint.x = toX; + touchPoint.y = toY; + + // Emit touch event after animation is done + touchPoint.pressed.connect(function() { + if (!sequence) { + sequence = event.touchEvent(canvas); + } + sequence.press(touchId, canvas, toX, toY); sequence.commit(); - sequence.release(1, canvas, toX, toY); + touchPoint.state = "RELEASED" + }); + + touchPoint.released.connect(function() { + sequence.release(touchId, canvas, toX, toY); sequence.commit(); }); + touchPoint.state = "PRESSED" } } diff --git a/HIG/source/qml/lib/VisualTouchPoint.qml b/HIG/source/qml/lib/VisualTouchPoint.qml new file mode 100644 index 0000000..15658be --- /dev/null +++ b/HIG/source/qml/lib/VisualTouchPoint.qml @@ -0,0 +1,85 @@ +/* + * Copyright 2018 Fabian Riethmayer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.2 +import org.kde.kirigami 2.4 as Kirigami +import QtTest 1.2 + +// Visual touch point with pressed and release state +Rectangle { + id: touchPoint + + property int dur: 300 + property alias animate : behave.enabled; + + signal released() + signal pressed() + signal moved() + + state: "RELEASED" + + height: Kirigami.Units.iconSizes.small + width: height + color: "#668e44ad" + radius: height / 2 + border.width: 1 + border.color: "#8e44ad" + + Behavior on x { + id: behave + enabled: false + NumberAnimation {duration: dur; easing.type: Easing.Linear;} + } + Behavior on y { + enabled: behave.enabled + NumberAnimation {duration: dur; easing.type: Easing.Linear} + } + + states: [ + State { + name: "PRESSED" + PropertyChanges { target: touchPoint; color: "#FF8e44ad"; visible: true } + } + ] + + transitions: [ + Transition { + from: "PRESSED" + to: "RELEASED" + SequentialAnimation { + ColorAnimation {target: touchPoint; duration: 300} + PropertyAction {target: touchPoint; property: "visible"; value: false} + ScriptAction { + script: touchPoint.released() + } + } + }, + Transition { + from: "RELEASED" + to: "PRESSED" + SequentialAnimation { + PropertyAction {target: touchPoint; property: "visible"; value: true} + ColorAnimation {target: touchPoint; duration: 300} + ScriptAction { + script: touchPoint.pressed() + } + } + } + ] +} diff --git a/HIG/source/qml/lib/annotate.js b/HIG/source/qml/lib/annotate.js index 0b2bfa4..f8725ba 100644 --- a/HIG/source/qml/lib/annotate.js +++ b/HIG/source/qml/lib/annotate.js @@ -1,346 +1,411 @@ var ruler = Qt.createComponent("Ruler.qml"); var brace = Qt.createComponent("Brace.qml"); var outline = Qt.createComponent("Outline.qml"); var messure = Qt.createComponent("Messure.qml"); var padding = Qt.createComponent("Padding.qml"); var mouse = Qt.createComponent("Mouse.qml"); var touch = Qt.createComponent("Touch.qml"); +var pinch = Qt.createComponent("Pinch.qml"); +var rotate = Qt.createComponent("Rotate.qml"); // get classname and strip _QML of the name function getClassName(obj) { var str = obj.toString(); str = str.substring(0, str.indexOf("(")); if (str.search(/_QML/) !== -1) { str = str.substring(0, str.indexOf("_QML")); } return str.toLowerCase(); } // Merge 2 objects of options function getOpts(opts, choices) { for (var choice in choices) { opts[choice] = choices[choice]; } return opts; } // An extended array of QML elements function An(node) { this.nodes = []; if (typeof node === "undefined") { this.nodes = [] } else if (typeof node === "Array") { this.nodes = node; } else { this.nodes = [node]; } } // Find an An of QML elements from this point down the subtrees An.prototype.find = function(selector) { var result = new An(); if (typeof selector === "string") { selector = new Select(selector); } /* for debugging for (var member in this) { if (typeof this[member] !== "function") { console.log(member + ": " + this[member]); } }*/ // iterate threw the children // apply the selector and traverse down the tree for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; for (var i = 0; i < node.children.length; i++) { if (selector.match(node.children[i])) { // Add matching element to result result.nodes.push(node.children[i]); } if (node.children[i].children.length) { // Merge matching results of subrtree var child = new An(node.children[i]); result.concat(child.find(selector)); } } } return result; } An.prototype.inspect = function() { for (var member in this.nodes[0]) { if (typeof this[member] !== "function") { console.log(member + ": " + this[member]); } } return this; } // Search only direct children An.prototype.children = function(selector) { var result = new An(); if (typeof selector === "string") { selector = new Select(selector); } for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; for (var i = 0; i < node.children.length; i++) { if (selector.match(node.children[i])) { // Add matching element to result result.nodes.push(node.children[i]); } } } return result; } An.prototype.concat = function(n) { this.nodes = this.nodes.concat(n.nodes); } An.prototype.first = function() { if (this.nodes.length > 0) { return new An(this.nodes[0]); } return new An(); } An.prototype.last = function() { if (this.nodes.length > 0) { return new An(this.nodes[this.nodes.length - 1]); } return new An(); } An.prototype.eq = function(n) { if (this.nodes.length > n) { return new An(this.nodes[n]); } return new An(); } /** * Simulate a mouse click on the nodes */ An.prototype.click = function(opt) { var options = getOpts({ x: 0, y: 0 }, opt); for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; var x = node.mapToItem(null, 0, 0).x + Math.floor(node.width / 2) + options.x; var y = node.mapToItem(null, 0, 0).y + Math.floor(node.height / 2) + options.y; var m = mouse.createObject(root, {px: x, py: y}); m.click(); } return this; } /** - * Simulate a touch the nodes + * Simulate a swipe the nodes */ An.prototype.touch = function(opt) { var options = getOpts({ x: 0, y: 0 }, opt); - for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; var x = node.mapToItem(null, 0, 0).x + Math.floor(node.width / 2) + options.x; var y = node.mapToItem(null, 0, 0).y + Math.floor(node.height / 2) + options.y; var m = touch.createObject(root, {toX: x, toY: y}); m.touch(); } return this; } +/** + * Simulate a pinch the nodes + */ +An.prototype.pinch = function(opt) { + for (var n = 0; n < this.nodes.length; n++) { + var node = this.nodes[n]; + // Translate rectangle to center of node + var oX = node.mapToItem(null, 0, 0).x + Math.floor(node.width / 2); + var oY = node.mapToItem(null, 0, 0).x + Math.floor(node.height / 2); + var width = opt.from.right - opt.from.left; + var height = opt.from.bottom - opt.from.top; + + var from = Qt.rect( + opt.from.left + oX, + opt.from.top + oY, + width, + height + ); + // Calculate to from the distance + var dig = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); + var x = (opt.distance + dig) / dig; + var to = Qt.rect( + from.left - (width * x) / 2, + from.top - (height * x) / 2, + width * x, + height * x + ); + // TODO add option for explicit to +// var to = Qt.rect( +// opt.to.left + oX, +// opt.to.top + oY, +// opt.to.right - opt.to.left, +// opt.to.bottom - opt.to.top +// ); + var m = pinch.createObject(root, {from: from, to: to}); + m.pinch(); + } + return this; +} + +/** + * Simulate a pinch/rotate the nodes + */ +An.prototype.rotate = function(opt) { + for (var n = 0; n < this.nodes.length; n++) { + var node = this.nodes[n]; + // Translate rectangle to center of node + var oX = node.mapToItem(null, 0, 0).x + Math.floor(node.width / 2); + var oY = node.mapToItem(null, 0, 0).x + Math.floor(node.height / 2); + var width = opt.from.right - opt.from.left; + var height = opt.from.bottom - opt.from.top; + + var from = Qt.rect( + oX - 40, + oY - 40, + 80, + 80 + ); + var m = rotate.createObject(root, {from: from, angle: opt.angle}); + m.rotate(); + } + return this; +} + /** * Simulate a mouse hover on the nodes */ An.prototype.hover = function(opt) { var options = getOpts({ x: 0, y: 0, animate: true }, opt); for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; var x = node.mapToItem(null, 0, 0).x + Math.floor(node.width / 2) + options.x; var y = node.mapToItem(null, 0, 0).y + Math.floor(node.height / 2) + options.y; var m = mouse.createObject(root, {px: x, py: y, animate: options.animate}); m.hover(); } return this; } /** * Simulate a touch */ An.prototype.swipe = function(opt) { var options = getOpts({ fromX: 0, fromY: 0, toX: 0, toY: 0 }, opt); for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; var x = node.mapToItem(null, 0, 0).x + Math.floor(node.width / 2) + options.fromX; var y = node.mapToItem(null, 0, 0).y + Math.floor(node.height / 2) - Kirigami.Units.iconSizes.smallMedium / 2 + options.fromY; var t = touch.createObject(root, {fromX: x, fromY: y, toX: x + options.toX, toY: y + options.toY}); t.swipe(); } return this; } /** * Draw a tree of all the elements */ An.prototype.tree = function(lvl) { if (typeof lvl === "undefined") { lvl = "" } /* for debug for (var member in this.nodes) { if (typeof this[member] !== "function") { console.log("|" + lvl + " " + member + ": " + (typeof this[member])); } }*/ for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; console.log("|" + lvl + " " + getClassName(node) + " " + node.toString() + " " + node.children.length); for (var i = 0; i < node.children.length; i++) { var child = new An(node.children[i]); child.tree(lvl + "--"); } } } /** * Drawing annotation on the nodes */ An.prototype.draw = function(obj) { //console.log(this.nodes) for (var n = 0; n < this.nodes.length; n++) { var node = this.nodes[n]; var opt; for (var type in obj) { if (Array.isArray(obj[type])) { for (var i = 0; i < obj[type].length; i++) { this._draw(node, type, obj[type][i]); } } else { this._draw(node, type, obj[type]); } } } return this; } /** * Internal method to draw */ An.prototype._draw = function(node, type, opt) { //console.log("drawing " + type) switch (type) { case "outline": // Create outline object outline.createObject(root, {item: node, label: opt.label, aspectratio: opt.aspectratio}); break case "ruler": // Draw ruler to show alignment if (opt.offset) { // No need for left or top since these are the defaults switch (opt.offset) { case "center": opt.offset = opt.horizontal ? node.mapToItem(null, 0, 0).y + node.height / 2 : node.mapToItem(null, 0, 0).x + node.width / 2 break; case "bottom": opt.offset = node.mapToItem(null, 0, 0).y + node.height break; case "right": opt.offset = node.mapToItem(null, 0, 0).x + node.width break; } } var options = getOpts({ offset: opt.horizontal ? node.mapToItem(null, 0, 0).y : node.mapToItem(null, 0, 0).x, horizontal: false }, opt); ruler.createObject(root, options); break case "padding": // Show padding around an object var options = getOpts({ padding: opt.padding }, opt); padding.createObject(root, {item: node, padding: options.padding}); break case "brace": // Create a brace between two objects brace.createObject(root, {"from": node, "to": opt.to.nodes[0], "text": opt.text, "center": opt.center, "horizontal": opt.horizontal}); break case "messure": // Messure distance between two objects messure.createObject(root, {"from": node, "to": opt.to.nodes[0], "type": opt.type}); break } } /** * Selector for qml elements */ function Select(str) { // TODO support more complex syntax // - multiple nodenames, hirachy, ... if (str.search(/\{/) !== -1) { this.nodeName = str.substring(0, str.indexOf("{")); var members = str.match(/\{.+\}/); try { this.attrs = JSON.parse(members[0]); } catch(e) { console.log("Could not parse attributes"); console.log(e); } } else { this.nodeName = str; } } /** * Check if the node matches the selector */ Select.prototype.match = function(node) { if (this.nodeName === "*" || getClassName(node) === this.nodeName) { if (typeof this.attrs !== "undefined") { // TODO only return true if all attributes match for (var attr in this.attrs) { if (typeof node[attr] !== "undefined" && node[attr].toString() === this.attrs[attr]) { return true; } } } else { return true; } } return false; } diff --git a/HIG/source/qml/lib/tools.js b/HIG/source/qml/lib/tools.js index 4f6ea18..f853d9b 100644 --- a/HIG/source/qml/lib/tools.js +++ b/HIG/source/qml/lib/tools.js @@ -1,9 +1,52 @@ // get scale because e.g. annotation should not be scaled function getScale(node) { var scale = 1 while (node !== null) { scale = scale * node.scale node = node.parent } return scale; } + + function Timer() { + return Qt.createQmlObject("import QtQuick 2.2; Timer {}", root); +} + +function sleep(delayTime, cb) { + var timer = new Timer(); + timer.interval = delayTime; + timer.repeat = false; + timer.triggered.connect(cb); + timer.start(); +} + +function JoinSignals(signals, cb) { + this.signals = []; + this.cb = cb; + for (var i = 0; i < signals.length; i++) { + this.signals.push(new Signal(signals[i], this)); + } +} + +JoinSignals.prototype.check = function() { + for (var i = 0; i < this.signals.length; i++) { + if (!this.signals[i].fired) { + return; + } + } + this.cb(); +} + +function Signal(signal, join) { + this.fired = false; + var that = this; + + signal.connect(function() { + that.fired = true; + join.check(); + }); +} + +function join(signals, cb) { + var join = new JoinSignals(signals, cb); +} diff --git a/HIG/source/qml/models/Files.qml b/HIG/source/qml/models/Files.qml index 64a5d9d..a0319d7 100644 --- a/HIG/source/qml/models/Files.qml +++ b/HIG/source/qml/models/Files.qml @@ -1,105 +1,112 @@ /* * Copyright 2018 Fabian Riethmayer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.6 ListModel { id: model // Pattern ListElement { name: "" icon: "" subfolder: [ ListElement { name: "" icon: "" subfolder: ListElement { name: "" icon: "" subfolder: [ ListElement { name: "" icon: "" subfolder: ListElement { name: "" icon: "" } } ] } } ] } Component.onCompleted: { model.clear() var data = [{ "name": "HIG", "icon": "folder-development", "subfolder": [{ "name": "build", - "icon": "folder-blue" + "icon": "folder", + "subfolder": [{ + "name": "doctrees", + "icon": "folder-text" + }, { + "name": "html", + "icon": "folder-internet" + }] }, { "name": "source", "icon": "folder-development", "subfolder": [{ "name": "components", "icon": "folder-text" }, { "name": "img", "icon": "folder-picture" }, { "name": "introduction", "icon": "folder-text" }, { "name": "layout", "icon": "folder-text" }, { "name": "patterns", "icon": "folder-text" }, { "name": "qml", - "icon": "folder-blue", + "icon": "folder", "subfolder": [{ "name": "ui", "icon": "folder-text" }, { "name": "lib", "icon": "folder-text" }, { "name": "models", "icon": "folder-text" }] }, { "name": "resources", "icon": "folder-text" }, { "name": "style", - "icon": "folder-blue" + "icon": "folder" }, { "name": "video", "icon": "folder-video" }] }] }] model.insert(0, data) } }