diff --git a/source/qml/lib/Outline.qml b/source/qml/lib/Outline.qml index 78923bb..4fc8eee 100644 --- a/source/qml/lib/Outline.qml +++ b/source/qml/lib/Outline.qml @@ -1,120 +1,120 @@ /* * 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 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 Label { x: Kirigami.Units.smallSpacing y: Kirigami.Units.smallSpacing id: dim color: "#000" font.pointSize: 8 lineHeight: 8 height: 8 - + renderType: Text.QtRendering } } 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 index 73188d2..fdfb40c 100644 --- a/source/qml/lib/Padding.qml +++ b/source/qml/lib/Padding.qml @@ -1,130 +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 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: "" + property var padding: [] z: 10 id: container Text { id: top color: "#da4453" font.pointSize: 8 lineHeight: 8 height: 8 z: 2 + renderType: Text.QtRendering } Text { id: right color: "#da4453" font.pointSize: 8 lineHeight: 8 height: 8 z: 2 + renderType: Text.QtRendering } Text { id: bottom color: "#da4453" font.pointSize: 8 lineHeight: 8 height: 8 z: 2 + renderType: Text.QtRendering } Text { id: left color: "#da4453" font.pointSize: 8 lineHeight: 8 height: 8 z: 2 + renderType: Text.QtRendering } Canvas { anchors.fill: parent; id: canvas onPaint: { // Determen padding - var padding = { - "top": item.topPadding, - "right": item.rightPadding, - "bottom": item.bottomPadding, - "left": item.leftPadding + var padding; + if (typeof container.padding === "number") { + padding = { + "top": container.padding, + "right": container.padding, + "bottom": container.padding, + "left": container.padding + } + } + else if (typeof container.padding === "Array" && container.padding.length == 4) { + padding = { + "top": container.padding[0], + "right": container.padding[1], + "bottom": container.padding[2], + "left": container.padding[3] + } + } + else { + 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; + right.y = cItem.y + item.height / 2 - right.height; 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; + left.y = cItem.x + item.height / 2 - left.height; } } } diff --git a/source/qml/lib/annotate.js b/source/qml/lib/annotate.js index 27f6bc4..f1a1c9f 100644 --- a/source/qml/lib/annotate.js +++ b/source/qml/lib/annotate.js @@ -1,322 +1,325 @@ 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"); // 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 */ 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 mouse hover on the nodes */ An.prototype.hover = 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.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()); 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": 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": - padding.createObject(root, {item: node}); + var options = getOpts({ + padding: opt.padding + }, opt); + padding.createObject(root, {item: node, padding: options.padding}); 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; }