diff --git a/source/components/navigation/actionbutton.rst b/source/components/navigation/actionbutton.rst index ecd2e4f..07714db 100644 --- a/source/components/navigation/actionbutton.rst +++ b/source/components/navigation/actionbutton.rst @@ -1,52 +1,67 @@ Primary Action Button ===================== -.. image:: /img/Action_Buttons.png +.. figure:: /img/Actionbutton1.png + :figclass: border :alt: Primary Action Button + + Primary action, create a new address book entry. When to use ----------- Use a Primary Action Button whenever there is a primary action for a certain page of your application or for the whole application, which is executed frequently. Typical primary actions are "Create New", "Edit,", "Save" or "Send". -The Primary Action Button also serves as an additional handle to open -the :doc:`drawers `. +Aditionally you can opt to define two secondary primary actions that are +placed left and right to the main primary button. + +.. figure:: /img/Actionbutton2.png + :figclass: border + :alt: Primary Action Button with two secondary buttons + + Call, message and write an email as primary actions. If there is no primary action, you may opt to use the Primary Action Button as a shortcut to navigate back to the application's main page instead of omitting it completely. Do that if - navigating back to the main page is a frequent action in your application - or you use Primary Action buttons on some pages and would like to keep the layout consistent across pages - or drawers are frequently used - and the space occupied by the button is not urgently needed for the content If the primary action is clearly associated with a specific element on the user interface, place controls within the content instead. How to use ---------- - Provide a clear icon for the Primary Action Button since it has no text label - If the Primary Action Button changes its action within a page (for example switching to "save" when editing, change its icon as well - If you use the Primary Action Button as a shortcut for going to the main page, use the "go-home" icon from the actions category for it. Desktop-specific ~~~~~~~~~~~~~~~~ If your application is using :doc:`column-based navigation ` - If there is a global Primary Action, associate it with the first column - If there is a Primary action for the context of a specific column, associated with the respective page + +.. figure:: /img/Actionbutton3.png + :figclass: border + :alt: Primary Action Buttons on Desktop + + Primary Action Buttons are placed in a :doc:`toolbar ` diff --git a/source/components/navigation/toolbar.rst b/source/components/navigation/toolbar.rst index 3dd2feb..7adec25 100644 --- a/source/components/navigation/toolbar.rst +++ b/source/components/navigation/toolbar.rst @@ -1,57 +1,63 @@ Tool bar ======== +.. figure:: /img/Actionbutton3.png + :figclass: border + :alt: Primary Action Buttons on Desktop + + Toolbar with the most important actions :doc:`toolbar ` + Purpose ------- A *tool bar* is a graphical presentation of commands optimized for fast access. Typically, a toolbar contains buttons that correspond to items in an application's menu, providing direct access to application's most frequently used functions. A good menu bar is a comprehensive catalog of all the available top-level commands, whereas a good tool bar gives quick, convenient access to frequently used commands. Guidelines ---------- Is this the right control ~~~~~~~~~~~~~~~~~~~~~~~~~ - For standard applications, apply a tool bar by default. - Provide a tool bar in addition to the menu bar, but do not replace the menu bar. Behavior ~~~~~~~~ - A tool bar should contain only a few, frequently used operations. If the number of operations is above 5 they have to be grouped with separators. Not more than 3 of those sections should be implemented. - Do not abuse the tool bar to expose application's features. Only the most frequently functions should be add to the tool bar. - Execute operations immediately; do not require additional input from user. - Do not use :doc:`menu buttons ` in tool bars. They do not fit well the concept of fast access. - Try to avoid using :doc:`split buttons ` or :doc:`toggle buttons <../editing/togglebutton>` in order to keep the interaction with all buttons in the tool bar consistent. - Do not hide tool bars by default. If configurable, users should easily be able to make the tool bar viewable again. - Disable buttons that do not apply to the current context. - Consider to provide customization for tool bars in respect to position and content. Appearance ~~~~~~~~~~ - Do not change the button style (QToolbar::toolButtonStyle) from the default. The default is currently text beside icons. - Use and design tool bar icons with special care. Users remember location of an object but rely as well on icon properties. - A distinct association between the underlying function and its visual depiction is crucial. Follow the advices for :doc:`icon design `. - Do not simulate Microsoft's ribbon controls. KDE stays plain and simple.Microsoft's ribbon controls. KDE stays plain and simple. diff --git a/source/img/Actionbutton1.png b/source/img/Actionbutton1.png new file mode 100644 index 0000000..028d754 Binary files /dev/null and b/source/img/Actionbutton1.png differ diff --git a/source/img/Actionbutton2.png b/source/img/Actionbutton2.png new file mode 100644 index 0000000..8e695e0 Binary files /dev/null and b/source/img/Actionbutton2.png differ diff --git a/source/img/Actionbutton3.png b/source/img/Actionbutton3.png new file mode 100644 index 0000000..a21efd8 Binary files /dev/null and b/source/img/Actionbutton3.png differ diff --git a/source/patterns/navigation/breadcrumb.rst b/source/patterns/navigation/breadcrumb.rst index 8a91d09..f7978be 100644 --- a/source/patterns/navigation/breadcrumb.rst +++ b/source/patterns/navigation/breadcrumb.rst @@ -1,49 +1,57 @@ Breadcrumbs =========== .. container:: intend |desktopicon| |mobileicon| Pattern for n-deep content structure. .. image:: /img/NP-n-deep.png :alt: Breadcrumb patterns The *breadcrumbs* pattern is a navigation aid for hierarchical content structures (e.g. home > documents > business). It provides information about the current position within the hierarchy, and offers shortcut links to jump to previous positions without using the Back button. +.. raw:: html + + + +Use of breadcrumb navigation in Plasma Mobile. + When to use ----------- - Use a breadcrumb control for orientation and navigation in strictly hierarchical content. Apply other controls like tags for flat or less organized content. - Make sure a breadcrumb control has only supportive functions. Do not use it as primary and exclusive navigation pattern. - Do not use a breadcrumb control to just identify or label the position; it must be interactive. - Do not make the breadcrumb control dynamic by showing the user's past interactions (known as 'path breadcrumbs'). Breadcrumbs should show the hierarchy, not the user's history. How to use ---------- - Link all breadcrumb steps to the appropriate page or position. Show the current position at the very end of the breadcrumb control. - Keep breadcrumbs plain and textual; do not use icons or other controls. - Place a breadcrumb control above the content area, but below other navigation controls. - Do not integrate a breadcrumb control into a toolbar or titlebar. Implementation -------------- - Consider providing a dropdown context menu full of alternative options for each breadcrumb item. But always offer one-click access by default. - Think of ways to make the breadcrumb control interactive in other creative ways. For example, it might permit content to be dragged-and-dropped onto a breadcrumb item to quickly move it there. diff --git a/source/qml/lib/Mouse.qml b/source/qml/lib/Mouse.qml index d9d2ba4..c509c22 100644 --- a/source/qml/lib/Mouse.qml +++ b/source/qml/lib/Mouse.qml @@ -1,108 +1,117 @@ /* * 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 QtTest 1.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 int px property int py + z: 2 Rectangle { id: ind x: cursor.x - width / 2 y: cursor.y - width / 2 + 5 - z: 1 - width: Kirigami.Units.iconSizes.small + z: 2 + width: Kirigami.Units.iconSizes.medium height: width color: "#9911d116" radius: width / 2 visible: false NumberAnimation on width { id: indAnim duration: 300 running: false onStopped: { ind.visible = false; - if (qmlControler) { - qmlControler.click(px, py); - } - else { - console.error("Can't find qmlControler."); - } - + event.mouseClick(canvas.parent, px, py, Qt.LeftButton, Qt.NoModifier, 0) } } } + TestEvent { + id: event + } + + Image { id: cursor source: "../../img/left_ptr.png" visible: false - width: Kirigami.Units.iconSizes.smallMedium - height: Kirigami.Units.iconSizes.smallMedium - z: 2 + width: Kirigami.Units.iconSizes.medium + height: Kirigami.Units.iconSizes.medium + z: 3 - NumberAnimation on x { - id: xAnim - duration: 1000 - running: false - } - NumberAnimation on y { - id: yAnim + ParallelAnimation { running: false - duration: 1000 - onStopped: { - ind.visible = true; - indAnim.to = Kirigami.Units.iconSizes.smallMedium; - indAnim.start(); + id: cursorAnimation + NumberAnimation { + id: cursorAnimationX + target: cursor + property: "x" + duration: 1000 + } + NumberAnimation { + id: cursorAnimationY + target: cursor + property: "y" + duration: 1000 } } } // Animate mouse to x/y and then click function click() { cursor.x = px - 60; cursor.y = py + 60; cursor.visible = true; - xAnim.to = px; - xAnim.start(); - yAnim.to = py; - yAnim.start(); + cursorAnimation.onStopped.connect(function() { + ind.visible = true; + indAnim.to = Kirigami.Units.iconSizes.smallMedium; + indAnim.start(); + }); + + cursorAnimationX.to = px; + cursorAnimationY.to = py; + cursorAnimation.start(); } function hover() { - cursor.x = px; - cursor.y = py; + cursor.x = px - 60; + cursor.y = py + 60; cursor.visible = true; - if (qmlControler) { - qmlControler.hover(px, py); - } - else { - console.error("Can't find qmlControler."); - } + + cursorAnimation.onStopped.connect(function() { + event.mouseMove(canvas.parent, px, py, 0, Qt.NoButton); + }); + + cursorAnimationX.to = px; + cursorAnimationY.to = py; + cursorAnimation.start(); } } diff --git a/source/qml/lib/Touch.qml b/source/qml/lib/Touch.qml index f779657..61928f5 100644 --- a/source/qml/lib/Touch.qml +++ b/source/qml/lib/Touch.qml @@ -1,129 +1,147 @@ /* * 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 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 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() } } } TestEvent { id: event } Timer { id: timer interval: 300 repeat: false running: false onTriggered: { ind.visible = false; cursor.visible = false; + swipeTimer.stop(); + } + } + + Timer { + 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, fromX + i * stepX, toY + i * stepY); + sequence.commit(); } } // Animate swipe function swipe() { cursor.x = fromX - Kirigami.Units.iconSizes.smallMedium; cursor.y = fromY; 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(); - timer.triggered.connect(function() { - var sequence = event.touchEvent(canvas); - sequence.press(1, canvas, fromX, fromY); - sequence.commit(); + sequence = event.touchEvent(canvas); + sequence.press(1, canvas, fromX, fromY); + sequence.commit(); + i = 0; + swipeTimer.start(); - sequence.move(1, canvas, toX, toY); + timer.triggered.connect(function() { + sequence.release(1, canvas, toX, toY); sequence.commit(); }); } function touch() { cursor.x = toX; cursor.y = toY; cursor.visible = true; - console.log(toX + " " + toY); timer.start() timer.triggered.connect(function() { - var sequence = event.touchEvent(canvas); + sequence = event.touchEvent(canvas); sequence.press(1, canvas, toX, toY); sequence.commit(); sequence.release(1, canvas, toX, toY); sequence.commit(); }); } } diff --git a/source/qml/lib/annotate.js b/source/qml/lib/annotate.js index 0998546..27f6bc4 100644 --- a/source/qml/lib/annotate.js +++ b/source/qml/lib/annotate.js @@ -1,305 +1,322 @@ 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(obj) { +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); - var y = node.mapToItem(null, 0, 0).y + Math.floor(node.height / 2); + 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(obj) { +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); - var y = node.mapToItem(null, 0, 0).y + Math.floor(node.height / 2); + 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(obj) { +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) + obj.fromX; - var y = node.mapToItem(null, 0, 0).y + Math.floor(node.height / 2) - Kirigami.Units.iconSizes.smallMedium / 2 + obj.fromY; - var t = touch.createObject(root, {fromX: x, fromY: y, toX: x + obj.toX, toY: y + obj.toY}); + 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}); 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; }