diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/env
build
*.qmlc
+*.jsc
diff --git a/source/img/edit-select.svg b/source/img/edit-select.svg
new file mode 100644
--- /dev/null
+++ b/source/img/edit-select.svg
@@ -0,0 +1,14 @@
+
diff --git a/source/img/video-display.svg b/source/img/video-display.svg
new file mode 100644
--- /dev/null
+++ b/source/img/video-display.svg
@@ -0,0 +1,125 @@
+
+
+
+
diff --git a/source/qml/lib/Brace.qml b/source/qml/lib/Brace.qml
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/Brace.qml
@@ -0,0 +1,150 @@
+/*
+ * 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.Controls 2.2
+import org.kde.kirigami 2.4 as Kirigami
+
+// Drawing a brace between to obejcts to show the distance between them
+Item {
+ id: canvas
+ anchors.fill: parent;
+ property Item from
+ property Item to
+ property bool flip: false
+ property bool center: true
+ property string text
+ property string color: "rgba(236, 161, 169, 0.8)"
+ property bool horizontal: true;
+
+ Rectangle {
+ id: prot
+ color: "#cc93cee9"
+ width: childrenRect.width + 10
+ height: childrenRect.height + 10
+ z: 2
+
+ Text {
+ x: 4
+ id: label
+ color: "#000"
+ text: canvas.text
+ font.pointSize: 8
+ lineHeight: 8
+ height: 8
+ }
+ }
+
+
+ Canvas {
+ anchors.fill: parent;
+ onPaint: {
+ // it might be necessary to calculate scale
+ // because annotation should not be scaled
+ // var scale = T.getScale(canvas.parent)
+
+ var ctx = getContext("2d");
+ ctx.strokeStyle = canvas.color;
+ ctx.beginPath();
+
+ var cfrom = from.mapToItem(canvas.parent, 0, 0);
+ var cto = to.mapToItem(canvas.parent, 0, 0);
+
+ // Determine direction
+ // draw either horizontal or vertical
+ if (horizontal) {
+
+ // Calculate anchor points for braces
+ if (!canvas.center) {
+ // Draw from closest borders
+ if (cfrom.x > cto.x) {
+ // If no label was provided calulate it
+ if (canvas.text == "") {
+ canvas.text = cfrom.x - cto.x - to.width;
+ }
+ cfrom.x = cfrom.x + Math.min(Math.floor(from.width / 2), 2 * Kirigami.Units.smallSpacing)
+ cto.x = cto.x + to.width - Math.min(Math.floor(to.width / 2), 2 * Kirigami.Units.smallSpacing)
+ }
+ else {
+ // If no label was provided calulate it
+ if (canvas.text == "") {
+ canvas.text = (cto.x - cfrom.x - from.width);
+ }
+ cfrom.x = cfrom.x + from.width - Math.min(Math.floor(from.width / 2), 2 * Kirigami.Units.smallSpacing)
+ cto.x = cto.x + Math.min(Math.floor(to.width / 2), 2 * Kirigami.Units.smallSpacing)
+ }
+ }
+ else {
+ // Draw from the center
+ cfrom.x = cfrom.x + from.width / 2;
+ cto.x = cto.x + to.width / 2;
+ }
+
+ // Draw the brace
+ ctx.moveTo(cfrom.x, cfrom.y);
+ ctx.lineTo(cfrom.x, cfrom.y - Kirigami.Units.smallSpacing);
+ ctx.lineTo(cto.x, cfrom.y - Kirigami.Units.smallSpacing);
+ ctx.lineTo(cto.x, cfrom.y);
+
+ // Position label
+ prot.x = (cfrom.x + cto.x) / 2 - prot.width / 2
+ prot.y = cfrom.y - prot.height - 2 * Kirigami.Units.smallSpacing
+ }
+ else {
+ if (!canvas.center) {
+ // Draw from closest borders
+ if (cfrom.y > cto.y) {
+ // If no label was provided calulate it
+ if (canvas.text == "") {
+ canvas.text = (cto.y - cfrom.y + from.height);
+ }
+ cfrom.y = cfrom.y + Math.min(Math.floor(from.height / 2), 2 * Kirigami.Units.smallSpacing)
+ cto.y = cto.y + to.height - Math.min(Math.floor(to.height / 2), 2 * Kirigami.Units.smallSpacing)
+
+ }
+ else {
+ // If no label was provided calulate it
+ if (canvas.text == "") {
+ canvas.text = (cto.y - cfrom.y - from.height);
+ }
+ cfrom.y = cfrom.y + from.height - Math.min(Math.floor(from.height / 2), 2 * Kirigami.Units.smallSpacing)
+ cto.y = cto.y + Math.min(Math.floor(to.height / 2), 2 * Kirigami.Units.smallSpacing)
+ }
+ }
+ else {
+ // Draw from the center
+ cfrom.y = cfrom.y + from.height / 2;
+ cto.y = cto.y + to.height / 2;
+ }
+
+ // Draw the brace
+ ctx.moveTo(cfrom.x, cfrom.y);
+ ctx.lineTo(cfrom.x - Kirigami.Units.smallSpacing, cfrom.y);
+ ctx.lineTo(cfrom.x - Kirigami.Units.smallSpacing, cto.y);
+ ctx.lineTo(cfrom.x, cto.y);
+
+ // Position label
+ prot.x = cfrom.x - prot.width - 2 * Kirigami.Units.smallSpacing
+ prot.y = (cfrom.y + cto.y ) / 2 - prot.height / 2 - 4
+ }
+
+ ctx.stroke();
+ }
+ }
+}
diff --git a/source/qml/lib/Demo.qml b/source/qml/lib/Demo.qml
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/Demo.qml
@@ -0,0 +1,119 @@
+/*
+ * 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.Controls 2.2
+import org.kde.kirigami 2.4 as Kirigami
+import "tools.js" as T
+import "annotate.js" as A
+
+Rectangle {
+ width: 320
+ height: 280
+ color: "white"
+
+ Item {
+ anchors.fill: parent
+ id: root
+
+ Row {
+ spacing: Kirigami.Units.largeSpacing * 2
+ x: Kirigami.Units.gridUnit * 2
+ y: Kirigami.Units.gridUnit * 2
+
+ Rectangle {
+ id: left1
+ width: Kirigami.Units.largeSpacing * 8
+ height: Kirigami.Units.largeSpacing * 8
+ color: "#27ae60"
+ }
+
+ Rectangle {
+ id: right1
+ width: Kirigami.Units.largeSpacing * 8
+ height: Kirigami.Units.largeSpacing * 8
+ color: "#27ae60"
+ anchors.top: parent.top;
+ }
+ }
+
+ Row {
+ spacing: Kirigami.Units.smallSpacing * 8
+ x: Kirigami.Units.gridUnit * 2
+ y: Kirigami.Units.gridUnit * 8
+
+ Rectangle {
+ id: left2
+ width: Kirigami.Units.largeSpacing * 8
+ height: Kirigami.Units.largeSpacing * 8
+ color: "#27ae60"
+ }
+
+ Rectangle {
+ id: right2
+ width: Kirigami.Units.largeSpacing * 12
+ height: Kirigami.Units.largeSpacing * 12
+ color: "#27ae60"
+ anchors.top: parent.top;
+
+ Rectangle {
+ id: right3
+ width: Kirigami.Units.largeSpacing * 4
+ height: Kirigami.Units.largeSpacing * 4
+ color: "black"
+ anchors.top: parent.top;
+ anchors.right: parent.right;
+ anchors.margins: Kirigami.Units.smallSpacing
+ }
+ }
+ }
+
+ }
+
+ // 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("qquickrectangle").first().draw({
+ outline: {},
+ ruler: {},
+ brace: {to: new A.An(right2), text: "16px", center: false}
+ });
+
+ a.find("qquickrectangle").eq(2).draw({
+ outline: {aspectratio: true},
+ });
+
+ a.find("qquickrectangle").eq(3).draw({
+ messure: { to: a.find("qquickrectangle").eq(4)}
+ });
+ }
+ }
+
+ // Draw helpers and anotation
+ Raster {
+ base: Kirigami.Units.gridUnit
+ mobile: true
+ desktop: true
+ }
+}
diff --git a/source/qml/lib/Messure.qml b/source/qml/lib/Messure.qml
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/Messure.qml
@@ -0,0 +1,125 @@
+/*
+ * 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.Controls 2.2
+import org.kde.kirigami 2.4 as Kirigami
+
+//window containing the application
+Canvas {
+ anchors.fill: parent;
+ id: canvas
+ property string color: "rgba(218, 68, 83, 0.8)"
+ property Item from
+ property Item to
+ property string type: "left"
+ property int rx;
+ property int ry;
+ z: 3
+ property string text
+
+ Rectangle {
+ id: prot
+ color: "#cc93cee9"
+ width: childrenRect.width + 10
+ height: childrenRect.height + 10
+ z: 2
+
+ Text {
+ x: 4
+ id: label
+ color: "#000"
+ text: canvas.text
+ font.pointSize: 8
+ lineHeight: 8
+ height: 8
+
+ }
+ }
+
+ onPaint: {
+ var ctx = getContext("2d");
+ ctx.strokeStyle = canvas.color;
+ ctx.lineWidth = 1
+ ctx.beginPath();
+
+ var cFrom = from.mapToItem(canvas.parent, 0, 0);
+ var cTo = to.mapToItem(canvas.parent, 0, 0);
+
+ // Horizontal messure
+ if (type == "left" || type == "right") {
+ var y;
+ if (canvas.ry) {
+ y = ry;
+ }
+ else {
+ if (from.height < to.height) {
+ y = cFrom.y + from.height / 2;
+ }
+ else {
+ y = cTo.y + to.height / 2;
+ }
+ }
+ if (type == "right") {
+ cFrom.x += from.width
+ cTo.x += to.width
+ }
+ ctx.moveTo(cFrom.x + 4, y);
+ ctx.lineTo(cTo.x - 4, y);
+
+ // Write distance
+ // TODO center it for real
+ if (canvas.text == "") {
+ label.text = Math.abs(cTo.x - cFrom.x);
+ }
+ prot.x = cFrom.x + (cTo.x - cFrom.x) / 2 - 8
+ prot.y = y - 16
+ }
+ else {
+ var x;
+ if (canvas.rx) {
+ x = rx;
+ }
+ else {
+ if (from.width < to.width) {
+ x = cFrom.x + from.width / 2;
+ }
+ else {
+ x = cTo.x + to.width / 2;
+ }
+
+ }
+ if (type == "bottom") {
+ cFrom.y += from.height
+ cTo.y += to.height
+ }
+ ctx.moveTo(x, cFrom.y + 4);
+ ctx.lineTo(x, cTo.y - 4);
+
+ // Write distance
+ if (canvas.text == "") {
+ label.text = Math.abs(cTo.y - cFrom.y);
+ }
+ prot.y = cFrom.y + (cTo.y - cFrom.y) / 2 - 8
+ prot.x = x + 8
+ }
+
+ ctx.stroke();
+ }
+}
diff --git a/source/qml/lib/Outline.qml b/source/qml/lib/Outline.qml
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/Outline.qml
@@ -0,0 +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.7
+import QtQuick.Controls 2.2
+import "tools.js" as T
+
+// Draw a frame around an element
+Item {
+ anchors.fill: parent;
+ property string color: "rgba(236, 161, 169, 0.6)"
+ property bool label: true
+ property bool aspectratio: false
+ property Item item
+ property Item root : container.parent
+ z: 10
+ id: container
+
+ Rectangle {
+ id: prot
+ color: "#cc93cee9"
+ width: childrenRect.width + 10
+ height: childrenRect.height + 10
+ visible: false
+ z: 2
+
+ Text {
+ x: 4
+ id: dim
+ color: "#000"
+ font.pointSize: 8
+ lineHeight: 8
+ height: 8
+
+ }
+ }
+
+ Canvas {
+ anchors.fill: parent;
+ id: canvas
+
+ onPaint: {
+ // get scale because annotation should not be scaled
+ var scale = T.getScale(container.parent)
+
+ var ctx = getContext("2d");
+ ctx.strokeStyle = container.color;
+ ctx.lineWidth = 4 / scale
+ ctx.beginPath();
+
+ // Draw an rectangle around the element
+ var offset = ctx.lineWidth / 2;
+ var cItem = item.mapToItem(container.root, 0, 0);
+ ctx.moveTo(cItem.x + offset, cItem.y + offset);
+ ctx.lineTo(cItem.x + offset, cItem.y + item.height - offset);
+ ctx.lineTo(cItem.x + item.width - offset, cItem.y + item.height - offset);
+ ctx.lineTo(cItem.x + item.width - offset, cItem.y + offset);
+ ctx.lineTo(cItem.x + offset, cItem.y + offset);
+
+ if (container.aspectratio) {
+ // Write proportions of an object instead of dimensions / px
+ ctx.moveTo(cItem.x + offset, cItem.y + offset);
+ ctx.lineTo(cItem.x + item.width - offset, cItem.y + item.height - offset);
+ ctx.moveTo(cItem.x + item.width - offset, cItem.y + offset);
+ ctx.lineTo(cItem.x + offset, cItem.y + item.height - offset);
+
+ var aspect = Math.round(item.width / item.height * 100) / 100
+ // Well known aspect ratios
+ switch (aspect) {
+ case 1:
+ aspect = "1 x 1"
+ break;
+ case 1.33:
+ aspect = "4 x 3"
+ break;
+ case 1.5:
+ aspect = "3 x 2"
+ break;
+ case 1.78:
+ aspect = "16 x 9"
+ break;
+ }
+ dim.text = aspect
+ prot.visible = true;
+ prot.x = cItem.x + item.width / 2 - prot.width / 2
+ prot.y = cItem.y + item.height / 2 - prot.height / 2
+
+ }
+ else {
+ // Write dimensions / px
+ if (container.label) {
+ dim.text = Math.round(item.width * 100) / 100 + " x " + Math.round(item.height * 100) / 100;
+ prot.visible = true;
+ prot.x = cItem.x + item.width / 2 - prot.width / 2
+ prot.y = cItem.y + item.height / 2 - prot.height / 2
+ }
+ }
+
+ ctx.stroke();
+ }
+ }
+}
diff --git a/source/qml/lib/Padding.qml b/source/qml/lib/Padding.qml
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/Padding.qml
@@ -0,0 +1,130 @@
+/*
+ * 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.Controls 2.2
+import org.kde.kirigami 2.4 as Kirigami
+
+// Show the padding of an element
+Item {
+ anchors.fill: parent;
+ property string color: "rgba(147,206,233, 0.8)"
+ property bool label: false
+ property Item item
+ property Item root : container.parent
+ property string padding: ""
+ z: 10
+ id: container
+
+ Text {
+ id: top
+ color: "#da4453"
+ font.pointSize: 8
+ lineHeight: 8
+ height: 8
+ z: 2
+ }
+ Text {
+ id: right
+ color: "#da4453"
+ font.pointSize: 8
+ lineHeight: 8
+ height: 8
+ z: 2
+ }
+ Text {
+ id: bottom
+ color: "#da4453"
+ font.pointSize: 8
+ lineHeight: 8
+ height: 8
+ z: 2
+ }
+ Text {
+ id: left
+ color: "#da4453"
+ font.pointSize: 8
+ lineHeight: 8
+ height: 8
+ z: 2
+ }
+
+ Canvas {
+ anchors.fill: parent;
+ id: canvas
+
+ onPaint: {
+ // Determen padding
+ var padding = {
+ "top": item.topPadding,
+ "right": item.rightPadding,
+ "bottom": item.bottomPadding,
+ "left": item.leftPadding
+ }
+
+ // setup drawing context
+ var offset;
+ var cItem = item.mapToItem(container.root, 0, 0);
+ var ctx = getContext("2d");
+ ctx.strokeStyle = container.color;
+ ctx.beginPath();
+
+ // Draw top
+ ctx.lineWidth = padding.top;
+ offset = ctx.lineWidth / 2;
+ ctx.moveTo(cItem.x + offset, cItem.y + offset);
+ ctx.lineTo(cItem.x + item.width - offset, cItem.y + offset);
+
+ // Draw right
+ ctx.lineWidth = padding.right;
+ offset = ctx.lineWidth / 2;
+ ctx.lineTo(cItem.x + item.width - offset, cItem.y + item.height - offset);
+
+ // Draw bottom
+ ctx.lineWidth = padding.bottom;
+ offset = ctx.lineWidth / 2;
+ ctx.lineTo(cItem.x + offset, cItem.y + item.height - offset);
+
+ // Draw left
+ ctx.lineWidth = padding.bottom;
+ offset = ctx.lineWidth / 2;
+ ctx.lineTo(cItem.x + offset, cItem.y + offset);
+
+ ctx.stroke();
+
+ // Write labels
+ top.text = padding.top;
+ top.x = cItem.x + item.width / 2;
+ top.y = cItem.y - 4;
+
+ right.text = padding.right;
+ right.x = cItem.x + item.width - right.width;
+ right.y = cItem.y + item.height / 2;
+
+ bottom.text = padding.bottom;
+ bottom.x = cItem.x + item.width / 2;
+ bottom.y = cItem.y + item.height - bottom.height - 4;
+
+ left.text = padding.left;
+ left.x = cItem.x;
+ left.y = cItem.x + item.height / 2;
+
+ }
+ }
+}
diff --git a/source/qml/lib/Raster.qml b/source/qml/lib/Raster.qml
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/Raster.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.7
+import QtQuick.Controls 2.2
+import org.kde.kirigami 2.4 as Kirigami
+
+// Drawing a semi transparent raster and a legend
+Item {
+ anchors.fill: parent;
+ property int base: Kirigami.Units.gridUnit
+ property string color: "rgba(200, 200, 200, 0.7)"
+ property string label: "Units.gridUnit / 18px"
+ property bool touch: false; // Indicate touch support
+ property bool mobile: false; // Indicate mobile/small display support
+ property bool mouse: false; // Indicate mouse support
+ property bool desktop: false; // Indicate large display support
+
+ // Painting the grid on a canvas
+ Canvas {
+ anchors.fill: parent;
+ id: canvas
+ onPaint: {
+ var ctx = getContext("2d");
+ ctx.strokeStyle = color;
+ ctx.beginPath();
+
+ // Horizontal grid lines
+ var i = 0;
+ while (i < canvas.height / base) {
+ ctx.moveTo(0, i * base);
+ ctx.lineTo(canvas.width, i * base);
+ i++;
+ }
+
+ // Vertical grid lines
+ i = 0;
+ while (i < canvas.width / base) {
+ ctx.moveTo(i * base, 0);
+ ctx.lineTo(i * base, canvas.height);
+ i++;
+ }
+ ctx.stroke();
+
+ // draw scale on the bottom right
+ // |---|
+ if (label != "") {
+ ctx.strokeStyle = "#da4453";
+ ctx.beginPath();
+ var left = canvas.width - 2 * base - canvas.width % base;
+ var top;
+ if (canvas.height % base > 2 * Kirigami.Units.smallSpacing) {
+ top = canvas.height - canvas.height % base;
+ }
+ else {
+ top = canvas.height - base - canvas.height % base;
+ }
+
+ ctx.moveTo(left, top - Kirigami.Units.smallSpacing);
+ ctx.lineTo(left, top + Kirigami.Units.smallSpacing);
+
+ ctx.moveTo(left, top);
+ ctx.lineTo(left + base, top);
+
+ ctx.moveTo(left + base, top - Kirigami.Units.smallSpacing);
+ ctx.lineTo(left + base, top + Kirigami.Units.smallSpacing);
+ ctx.stroke();
+ }
+ }
+ }
+
+ Label {
+ id: lbl
+ text: label
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ anchors.bottomMargin: canvas.height % base > 2 * Kirigami.Units.smallSpacing ? canvas.height % base + Kirigami.Units.smallSpacing : base + canvas.height % base + Kirigami.Units.smallSpacing
+ anchors.rightMargin: base
+ color: "#da4453"
+ font.pixelSize: 10
+ renderType: Text.QtRendering
+ }
+
+ Row {
+ anchors.bottom: lbl.top
+ anchors.right: lbl.right
+ anchors.margins: Kirigami.Units.smallSpacing
+ spacing: Kirigami.Units.smallSpacing
+ Image {
+ visible: mouse
+ height: Kirigami.Units.iconSizes.medium;
+ width: Kirigami.Units.iconSizes.medium;
+ source: "../../img/edit-select.svg"
+ //smooth: true
+ }
+ Image {
+ visible: touch
+ height: Kirigami.Units.iconSizes.medium;
+ width: Kirigami.Units.iconSizes.medium;
+ source: "../../img/transform-browse.svg"
+ //smooth: true
+ }
+ Image {
+ visible: desktop
+ height: Kirigami.Units.iconSizes.medium;
+ width: Kirigami.Units.iconSizes.medium;
+ source: "../../img/video-display.svg"
+ //smooth: true
+ }
+ Image {
+ visible: mobile
+ height: Kirigami.Units.iconSizes.medium;
+ width: Kirigami.Units.iconSizes.medium;
+ source: "../../img/smartphone.svg"
+ //smooth: true
+ }
+ }
+}
diff --git a/source/qml/lib/Ruler.qml b/source/qml/lib/Ruler.qml
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/Ruler.qml
@@ -0,0 +1,66 @@
+/*
+ * 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.Controls 2.2
+import org.kde.kirigami 2.4 as Kirigami
+import "tools.js" as T
+
+// Draw a ruler for highlighting alignment
+Item {
+ id: canvas
+ anchors.fill: parent;
+ property int rx;
+ property int ry;
+ property bool horizontal: true;
+ property string stroke: "rgba(41,128,185, 1)"
+ property double scale: T.getScale(canvas)
+
+ // using Rectangles because they scale smooth
+ Row {
+ y: canvas.ry
+ x: 0
+ id: hackRow
+ visible: canvas.horizontal
+ spacing: Kirigami.Units.smallSpacing / scale
+ Repeater {
+ model: new Array(Math.floor(canvas.width / hackRow.spacing / 2))
+ Rectangle {
+ width: Kirigami.Units.smallSpacing / scale
+ height: 2 / scale
+ color: "#2980b9"
+ }
+ }
+ }
+ Column {
+ x: canvas.rx
+ y: 0
+ id: hackColumn
+ visible: !canvas.horizontal
+ spacing: Kirigami.Units.smallSpacing / scale
+ Repeater {
+ model: new Array(Math.floor(canvas.height / hackColumn.spacing / 2))
+ Rectangle {
+ height: Kirigami.Units.smallSpacing / scale
+ width: 1 / scale
+ color: "#2980b9"
+ }
+ }
+ }
+}
diff --git a/source/qml/lib/annotate.js b/source/qml/lib/annotate.js
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/annotate.js
@@ -0,0 +1,222 @@
+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");
+
+// 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();
+}
+
+// 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;
+}
+
+// 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();
+}
+
+
+/**
+ * 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());
+ 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":
+ outline.createObject(root, {item: node, label: opt.label, aspectratio: opt.aspectratio});
+ break
+ case "ruler":
+ ruler.createObject(root, {rx: node.mapToItem(null, 0, 0).x, horizontal: false});
+ break
+ case "padding":
+ padding.createObject(root, {item: node});
+ break
+ case "brace":
+ brace.createObject(root, {"from": node, "to": opt.to.nodes[0], "text": opt.text, "center": opt.center, "horizontal": opt.horizontal});
+ break
+ case "messure":
+ 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/source/qml/lib/tools.js b/source/qml/lib/tools.js
new file mode 100644
--- /dev/null
+++ b/source/qml/lib/tools.js
@@ -0,0 +1,9 @@
+// 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;
+}