From 5efca48b3b68999a138637f6389da526234e51f8 Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Tue, 3 Nov 2015 02:32:21 +0100 Subject: [PATCH] Make it possible to do some TextField-related actions Introduces copy, paste and select all when a text field is selected. Uses the __editMenu property introduced in Qt 5.6. Integrates the text field custom menu, as shown in the demo. --- demo/TextFieldDemo.qml | 12 ++ modules/Material/Button.qml | 2 +- .../Controls/Styles/Material/ButtonStyle.qml | 1 + .../Styles/Material/MaterialEditMenu.qml | 188 ++++++++++++++++++ .../Styles/Material/TextFieldStyle.qml | 16 ++ .../Controls/Styles/Material/TextHandle.qml | 64 ++++++ 6 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 modules/QtQuick/Controls/Styles/Material/MaterialEditMenu.qml create mode 100644 modules/QtQuick/Controls/Styles/Material/TextHandle.qml diff --git a/demo/TextFieldDemo.qml b/demo/TextFieldDemo.qml index 71095121..c68d1cff 100644 --- a/demo/TextFieldDemo.qml +++ b/demo/TextFieldDemo.qml @@ -1,5 +1,6 @@ import QtQuick 2.0 import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.1 as Controls import Material 0.1 Item { @@ -35,6 +36,17 @@ Item { anchors.horizontalCenter: parent.horizontalCenter } + TextField { + placeholderText: "Text Field with Menu" + anchors.horizontalCenter: parent.horizontalCenter + menu: Controls.Menu { + Controls.MenuItem { + text: "Print \"awesome\"" + onTriggered: console.log("awesome"); + } + } + } + TextField { id: passwordField placeholderText: "Password" diff --git a/modules/Material/Button.qml b/modules/Material/Button.qml index 57787f2e..3b7ad74d 100644 --- a/modules/Material/Button.qml +++ b/modules/Material/Button.qml @@ -67,7 +67,7 @@ Controls.Button { The context of the button, which is used to control special styling of buttons in dialogs or snackbars. */ - property string context: "default" // or "dialog" or "snackbar" + property string context: "default" // "dialog", "snackbar" or "editmenu" /*! Set to \c true if the button is on a dark background diff --git a/modules/QtQuick/Controls/Styles/Material/ButtonStyle.qml b/modules/QtQuick/Controls/Styles/Material/ButtonStyle.qml index cc69d852..94244622 100644 --- a/modules/QtQuick/Controls/Styles/Material/ButtonStyle.qml +++ b/modules/QtQuick/Controls/Styles/Material/ButtonStyle.qml @@ -92,6 +92,7 @@ ButtonStyle { implicitWidth: context == "dialog" ? Math.max(Units.dp(64), label.width + Units.dp(16)) : context == "snackbar" ? label.width + Units.dp(16) + : context == "editmenu" ? label.width : Math.max(Units.dp(88), label.width + Units.dp(32)) Label { diff --git a/modules/QtQuick/Controls/Styles/Material/MaterialEditMenu.qml b/modules/QtQuick/Controls/Styles/Material/MaterialEditMenu.qml new file mode 100644 index 00000000..908b942a --- /dev/null +++ b/modules/QtQuick/Controls/Styles/Material/MaterialEditMenu.qml @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2015 by Aleix Pol Gonzalez + * Copyright (C) 2015 by Marco Martin + * + * 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 2.010-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.0 +import Material 0.1 + +Item { + id: root + anchors.fill: parent + + property bool editing: true + onEditingChanged: { + updateCursorOpacity() + } + function updateCursorOpacity() { + var opacity = root.editing ? 0 : 1; + cursorHandle.opacity = opacity; + selectionHandle.opacity = opacity; + } + Component.onCompleted: updateCursorOpacity() + +// https://www.google.com/design/spec/patterns/selection.html#selection-text-selection + Component { + id: editControls + Item { + id: popup + visible: input.activeFocus && !root.editing + z: 9999 + + Behavior on x { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } } + + Component.onCompleted: { + var par = control + //heuristic: if a flickable is found in the parents, + //reparent to it, so it scrolls together + while (par.parent && par.parent.contentY === undefined) { + par = par.parent + } + + popup.parent = par + } + + function popup(startRect) { +// var selectedTextTopPadding = Units.dp(8); + var mapped = parent.mapFromItem(input, startRect.x, startRect.y); + var mapWidget = parent.mapFromItem(input.parent, input.x, input.y, input.width, input.height); + + popup.x = Math.min(Math.max(mapWidget.x, mapped.x), mapWidget.x+mapWidget.width-bg.width); + popup.y = Math.max(0, mapped.y - bg.height); + } + function dismiss() { + popup.visible = false; + } + + View { + id: bg + elevation: 1 + anchors { + fill: buttons + topMargin: -Units.dp(12) + bottomMargin: -Units.dp(14) + leftMargin: -Units.dp(24) + rightMargin: -Units.dp(16) + } + } + + RowLayout { + id: buttons + spacing: Units.dp(32) + Button { + text: qsTr("Cut") + visible: input.selectedText != "" + context: "editmenu" + onClicked: { + control.cut(); + select(input.cursorPosition, input.cursorPosition); + } + } + Button { + text: qsTr("Copy") + visible: input.selectedText != "" + context: "editmenu" + onClicked: { + control.copy(); + select(input.cursorPosition, input.cursorPosition); + } + } + Button { + text: qsTr("Paste") + visible: input.canPaste + context: "editmenu" + onClicked: { + control.paste(); + } + } + Button { + text: qsTr("Select All") + visible: input.text != "" + context: "editmenu" + onClicked: { + control.selectAll(); + } + } + + IconButton { + iconName: "navigation/more_vert" + visible: control.menu !== null + onClicked: { + getMenuInstance().popup() + } + } + } + } + } + + Connections { + target: mouseArea + + onClicked: { + var pos = input.positionAt(mouse.x, mouse.y) + input.moveHandles(pos, pos) + input.activate() + } + onPressAndHold: { + root.editing = false; + var pos = input.positionAt(mouse.x, mouse.y) + input.moveHandles(pos, control.selectByMouse ? -1 : pos) + input.activate() + } + } + + Connections { + target: input + onSelectionStartChanged: popupTimer.restart() + onSelectionEndChanged: popupTimer.restart() + onActiveFocusChanged: { + popupTimer.restart() + root.editing = true; + } + onTextChanged: root.editing = true; + } + + Connections { + target: flickable + onMovingChanged: popupTimer.restart() + } + + property Item editControlsInstance: null + function getEditControlsInstance() { + // Lazy load the view when first requested + if (!editControlsInstance) { + editControlsInstance = editControls.createObject(control); + } + return editControlsInstance; + } + + Timer { + id: popupTimer + interval: 200 + onTriggered: { + if (input.canPaste || selectionStart !== selectionEnd) { + var startRect = input.positionToRectangle(input.selectionStart); + + getEditControlsInstance().popup(startRect); + } + } + } +} diff --git a/modules/QtQuick/Controls/Styles/Material/TextFieldStyle.qml b/modules/QtQuick/Controls/Styles/Material/TextFieldStyle.qml index 1bba70ea..95772cb9 100644 --- a/modules/QtQuick/Controls/Styles/Material/TextFieldStyle.qml +++ b/modules/QtQuick/Controls/Styles/Material/TextFieldStyle.qml @@ -42,6 +42,22 @@ TextFieldStyle { selectionColor: control.hasOwnProperty("color") ? control.color : Theme.accentColor textColor: Theme.light.textColor + //make sure to QT_QUICK_CONTROLS_MOBILE=ON to properly test this + property Component __editMenu: MaterialEditMenu { + id: menu + } + + property Component __cursorHandle: TextHandle { + side: control.selectionEnd - control.selectionStart + color: Palette.colors["blue"]["400"] + } + + property Component __selectionHandle: TextHandle { + side: control.selectionStart - control.selectionEnd + color: Palette.colors["blue"]["400"] + } + + background : Item { id: background diff --git a/modules/QtQuick/Controls/Styles/Material/TextHandle.qml b/modules/QtQuick/Controls/Styles/Material/TextHandle.qml new file mode 100644 index 00000000..3c666a99 --- /dev/null +++ b/modules/QtQuick/Controls/Styles/Material/TextHandle.qml @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 by Aleix Pol Gonzalez + * + * 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 2.010-1301, USA. + */ + +import QtQuick 2.1 +import Material 0.1 + +Rectangle { + id: root + property int side: 0 //-1 left, 0 center, 1 right + + width: Units.dp(22) + height: Units.dp(22) + + radius: width + y: styleData.lineHeight + + Rectangle { + id: rect + width: parent.width/2 + height: parent.height/2 + color: parent.color + } + + states: [ + State { + when: side<0 + name: "right" + PropertyChanges { target: root; x: -width} + AnchorChanges { target: rect; anchors.right: parent.right } + }, + State { + when: side>0 + name: "left" + AnchorChanges { target: rect; anchors.left: parent.left } + }, + State { + when: side==0 + name: "center" + PropertyChanges { target: rect; rotation: 45 } + PropertyChanges { target: rect; width: root.width/Math.SQRT2 } + PropertyChanges { target: rect; height: rect.width } + PropertyChanges { target: rect; x: (root.width-rect.width)/2 } + + PropertyChanges { target: root; y: styleData.lineHeight/*+root.height/2*/ } + PropertyChanges { target: root; x: -root.width/2 } + } + ] +}