Skip to content

Commit a433331

Browse files
committed
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.
1 parent 42da3ad commit a433331

File tree

6 files changed

+273
-1
lines changed

6 files changed

+273
-1
lines changed

demo/TextFieldDemo.qml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import QtQuick 2.0
22
import QtQuick.Layouts 1.1
3+
import QtQuick.Controls 1.1 as Controls
34
import Material 0.1
45

56
Item {
@@ -35,6 +36,17 @@ Item {
3536
anchors.horizontalCenter: parent.horizontalCenter
3637
}
3738

39+
TextField {
40+
placeholderText: "Text Field with Menu"
41+
anchors.horizontalCenter: parent.horizontalCenter
42+
menu: Controls.Menu {
43+
Controls.MenuItem {
44+
text: "Print \"awesome\""
45+
onTriggered: console.log("awesome");
46+
}
47+
}
48+
}
49+
3850
TextField {
3951
id: passwordField
4052
placeholderText: "Password"

modules/Material/Button.qml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Controls.Button {
6767
The context of the button, which is used to control special styling of
6868
buttons in dialogs or snackbars.
6969
*/
70-
property string context: "default" // or "dialog" or "snackbar"
70+
property string context: "default" // "dialog", "snackbar" or "editmenu"
7171

7272
/*!
7373
Set to \c true if the button is on a dark background

modules/QtQuick/Controls/Styles/Material/ButtonStyle.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ ButtonStyle {
9292
implicitWidth: context == "dialog"
9393
? Math.max(Units.dp(64), label.width + Units.dp(16))
9494
: context == "snackbar" ? label.width + Units.dp(16)
95+
: context == "editmenu" ? label.width
9596
: Math.max(Units.dp(88), label.width + Units.dp(32))
9697

9798
Label {
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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+
}

modules/QtQuick/Controls/Styles/Material/TextFieldStyle.qml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ TextFieldStyle {
4242
selectionColor: control.hasOwnProperty("color") ? control.color : Theme.accentColor
4343
textColor: Theme.light.textColor
4444

45+
//make sure to QT_QUICK_CONTROLS_MOBILE=ON to properly test this
46+
property Component __editMenu: MaterialEditMenu {
47+
id: menu
48+
}
49+
50+
property Component __cursorHandle: TextHandle {
51+
side: control.selectionEnd - control.selectionStart
52+
color: Palette.colors["blue"]["400"]
53+
}
54+
55+
property Component __selectionHandle: TextHandle {
56+
side: control.selectionStart - control.selectionEnd
57+
color: Palette.colors["blue"]["400"]
58+
}
59+
60+
4561
background : Item {
4662
id: background
4763

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (C) 2015 by Aleix Pol Gonzalez <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Library General Public License as
6+
* published by the Free Software Foundation; either version 2, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Library General Public License for more details
13+
*
14+
* You should have received a copy of the GNU Library General Public
15+
* License along with this program; if not, write to the
16+
* Free Software Foundation, Inc.,
17+
* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
18+
*/
19+
20+
import QtQuick 2.1
21+
import Material 0.1
22+
23+
Rectangle {
24+
id: root
25+
property int side: 0 //-1 left, 0 center, 1 right
26+
27+
width: Units.dp(22)
28+
height: Units.dp(22)
29+
30+
radius: width
31+
y: styleData.lineHeight
32+
33+
Rectangle {
34+
id: rect
35+
width: parent.width/2
36+
height: parent.height/2
37+
color: parent.color
38+
}
39+
40+
states: [
41+
State {
42+
when: side<0
43+
name: "right"
44+
PropertyChanges { target: root; x: -width}
45+
AnchorChanges { target: rect; anchors.right: parent.right }
46+
},
47+
State {
48+
when: side>0
49+
name: "left"
50+
AnchorChanges { target: rect; anchors.left: parent.left }
51+
},
52+
State {
53+
when: side==0
54+
name: "center"
55+
PropertyChanges { target: rect; rotation: 45 }
56+
PropertyChanges { target: rect; width: root.width/Math.SQRT2 }
57+
PropertyChanges { target: rect; height: rect.width }
58+
PropertyChanges { target: rect; x: (root.width-rect.width)/2 }
59+
60+
PropertyChanges { target: root; y: styleData.lineHeight/*+root.height/2*/ }
61+
PropertyChanges { target: root; x: -root.width/2 }
62+
}
63+
]
64+
}

0 commit comments

Comments
 (0)