|
| 1 | +/* |
| 2 | + * Copyright (C) 2015 by Aleix Pol Gonzalez <[email protected]> |
| 3 | + * Copyright (C) 2015 by Marco Martin <[email protected]> |
| 4 | + * |
| 5 | + * This program is free software; you can redistribute it and/or modify |
| 6 | + * it under the terms of the GNU Library General Public License as |
| 7 | + * published by the Free Software Foundation; either version 2, or |
| 8 | + * (at your option) any later version. |
| 9 | + * |
| 10 | + * This program is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + * GNU Library General Public License for more details |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU Library General Public |
| 16 | + * License along with this program; if not, write to the |
| 17 | + * Free Software Foundation, Inc., |
| 18 | + * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. |
| 19 | + */ |
| 20 | + |
| 21 | +import QtQuick 2.1 |
| 22 | +import QtQuick.Controls 1.1 |
| 23 | +import QtQuick.Controls.Styles 1.1 |
| 24 | +import QtQuick.Layouts 1.0 |
| 25 | +import Material 0.1 |
| 26 | + |
| 27 | +Item { |
| 28 | + id: root |
| 29 | + anchors.fill: parent |
| 30 | + |
| 31 | + property bool editing: true |
| 32 | + |
| 33 | +// https://www.google.de/design/spec/patterns/selection.html#selection-text-selection |
| 34 | + Component { |
| 35 | + id: editControls |
| 36 | + Item { |
| 37 | + id: popup |
| 38 | + visible: input.activeFocus && !root.editing |
| 39 | + z: 9999 |
| 40 | + |
| 41 | + Behavior on x { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } } |
| 42 | + |
| 43 | + Component.onCompleted: { |
| 44 | + var par = control |
| 45 | + //heuristic: if a flickable is found in the parents, |
| 46 | + //reparent to it, so it scrolls together |
| 47 | + while (par.parent && par.parent.contentY === undefined) { |
| 48 | + par = par.parent |
| 49 | + } |
| 50 | + |
| 51 | + popup.parent = par |
| 52 | + } |
| 53 | + |
| 54 | + function popup(startRect) { |
| 55 | +// var selectedTextTopPadding = Units.dp(8); |
| 56 | + var mapped = parent.mapFromItem(input, startRect.x, startRect.y); |
| 57 | + var mapWidget = parent.mapFromItem(input.parent, input.x, input.y, input.width, input.height); |
| 58 | + |
| 59 | + popup.x = Math.min(Math.max(mapWidget.x, mapped.x), mapWidget.x+mapWidget.width-bg.width); |
| 60 | + popup.y = Math.max(0, mapped.y - bg.height); |
| 61 | + } |
| 62 | + function dismiss() { |
| 63 | + popup.visible = false; |
| 64 | + } |
| 65 | + |
| 66 | + View { |
| 67 | + id: bg |
| 68 | + elevation: 1 |
| 69 | + anchors { |
| 70 | + fill: buttons |
| 71 | + topMargin: -Units.dp(12) |
| 72 | + bottomMargin: -Units.dp(14) |
| 73 | + leftMargin: -Units.dp(24) |
| 74 | + rightMargin: -Units.dp(16) |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + RowLayout { |
| 79 | + id: buttons |
| 80 | + spacing: Units.dp(32) |
| 81 | + Button { |
| 82 | + text: qsTr("Cut") |
| 83 | + visible: input.selectedText != "" |
| 84 | + context: "editmenu" |
| 85 | + onClicked: { |
| 86 | + control.cut(); |
| 87 | + select(input.cursorPosition, input.cursorPosition); |
| 88 | + } |
| 89 | + } |
| 90 | + Button { |
| 91 | + text: qsTr("Copy") |
| 92 | + visible: input.selectedText != "" |
| 93 | + context: "editmenu" |
| 94 | + onClicked: { |
| 95 | + control.copy(); |
| 96 | + select(input.cursorPosition, input.cursorPosition); |
| 97 | + } |
| 98 | + } |
| 99 | + Button { |
| 100 | + text: qsTr("Paste") |
| 101 | + visible: input.canPaste |
| 102 | + context: "editmenu" |
| 103 | + onClicked: { |
| 104 | + control.paste(); |
| 105 | + } |
| 106 | + } |
| 107 | + Button { |
| 108 | + text: qsTr("Select All") |
| 109 | + visible: input.text != "" |
| 110 | + context: "editmenu" |
| 111 | + onClicked: { |
| 112 | + control.selectAll(); |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + IconButton { |
| 117 | + iconName: "navigation/more_vert" |
| 118 | + visible: control.menu !== null |
| 119 | + onClicked: { |
| 120 | + getMenuInstance().popup() |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + Connections { |
| 128 | + target: mouseArea |
| 129 | + |
| 130 | + onClicked: { |
| 131 | + var pos = input.positionAt(mouse.x, mouse.y) |
| 132 | + input.moveHandles(pos, pos) |
| 133 | + input.activate() |
| 134 | + } |
| 135 | + onPressAndHold: { |
| 136 | + root.editing = false; |
| 137 | + var pos = input.positionAt(mouse.x, mouse.y) |
| 138 | + input.moveHandles(pos, control.selectByMouse ? -1 : pos) |
| 139 | + input.activate() |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + Connections { |
| 144 | + target: input |
| 145 | + onSelectionStartChanged: popupTimer.restart() |
| 146 | + onSelectionEndChanged: popupTimer.restart() |
| 147 | + onActiveFocusChanged: { |
| 148 | + popupTimer.restart() |
| 149 | + root.editing = true; |
| 150 | + } |
| 151 | + onTextChanged: root.editing = true; |
| 152 | + } |
| 153 | + |
| 154 | + Connections { |
| 155 | + target: flickable |
| 156 | + onMovingChanged: popupTimer.restart() |
| 157 | + } |
| 158 | + |
| 159 | + property Item editControlsInstance: null |
| 160 | + function getEditControlsInstance() { |
| 161 | + // Lazy load the view when first requested |
| 162 | + if (!editControlsInstance) { |
| 163 | + editControlsInstance = editControls.createObject(control); |
| 164 | + } |
| 165 | + return editControlsInstance; |
| 166 | + } |
| 167 | + |
| 168 | + Timer { |
| 169 | + id: popupTimer |
| 170 | + interval: 200 |
| 171 | + onTriggered: { |
| 172 | + if (input.canPaste || selectionStart !== selectionEnd) { |
| 173 | + var startRect = input.positionToRectangle(input.selectionStart); |
| 174 | + |
| 175 | + getEditControlsInstance().popup(startRect); |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | +} |
0 commit comments