Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / examples / declarative / text / textselection / textselection.qml
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the examples of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
11 **
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
14 ** met:
15 **   * Redistributions of source code must retain the above copyright
16 **     notice, this list of conditions and the following disclaimer.
17 **   * Redistributions in binary form must reproduce the above copyright
18 **     notice, this list of conditions and the following disclaimer in
19 **     the documentation and/or other materials provided with the
20 **     distribution.
21 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 **     the names of its contributors may be used to endorse or promote
23 **     products derived from this software without specific prior written
24 **     permission.
25 **
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 import QtQuick 1.0
41
42 Rectangle {
43     id: editor
44     color: "lightGrey"
45     width: 640; height: 480
46
47     Rectangle {
48         color: "white"
49         anchors.fill: parent
50         anchors.margins: 20
51
52         BorderImage {
53             id: startHandle
54             source: "pics/startHandle.sci"
55             opacity: 0.0
56             width: 10
57             x: edit.positionToRectangle(edit.selectionStart).x - flick.contentX-width
58             y: edit.positionToRectangle(edit.selectionStart).y - flick.contentY
59             height: edit.positionToRectangle(edit.selectionStart).height
60         }
61
62         BorderImage {
63             id: endHandle
64             source: "pics/endHandle.sci"
65             opacity: 0.0
66             width: 10
67             x: edit.positionToRectangle(edit.selectionEnd).x - flick.contentX
68             y: edit.positionToRectangle(edit.selectionEnd).y - flick.contentY
69             height: edit.positionToRectangle(edit.selectionEnd).height
70         }
71
72         Flickable {
73             id: flick
74
75             anchors.fill: parent
76             contentWidth: edit.paintedWidth
77             contentHeight: edit.paintedHeight
78             interactive: true
79             clip: true
80
81             function ensureVisible(r) {
82                 if (contentX >= r.x)
83                     contentX = r.x;
84                 else if (contentX+width <= r.x+r.width)
85                     contentX = r.x+r.width-width;
86                 if (contentY >= r.y)
87                     contentY = r.y;
88                 else if (contentY+height <= r.y+r.height)
89                     contentY = r.y+r.height-height;
90             }
91
92             TextEdit {
93                 id: edit
94                 width: flick.width
95                 height: flick.height
96                 focus: true
97                 wrapMode: TextEdit.Wrap
98
99                 onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)
100
101                 text: "<h1>Text Selection</h1>"
102                     +"<p>This example is a whacky text selection mechanisms, showing how these can be implemented in the TextEdit element, to cater for whatever style is appropriate for the target platform."
103                     +"<p><b>Press-and-hold</b> to select a word, then drag the selection handles."
104                     +"<p><b>Drag outside the selection</b> to scroll the text."
105                     +"<p><b>Click inside the selection</b> to cut/copy/paste/cancel selection."
106                     +"<p>It's too whacky to let you paste if there is no current selection."
107
108                 MouseArea {
109                     property string drag: ""
110                     property int pressPos
111
112                     x: -startHandle.width
113                     y: 0
114                     width: parent.width+startHandle.width+endHandle.width
115                     height: parent.height
116
117                     onPressAndHold: {
118                         if (editor.state == "") {
119                             edit.cursorPosition = edit.positionAt(mouse.x+x,mouse.y+y);
120                             edit.selectWord();
121                             editor.state = "selection"
122                         }
123                     }
124
125                     onClicked: {
126                         if (editor.state == "") {
127                             edit.cursorPosition = edit.positionAt(mouse.x+x,mouse.y+y);
128                             if (!edit.focus)
129                                 edit.focus = true;
130                             edit.openSoftwareInputPanel();
131                         }
132                     }
133
134                     function hitHandle(h,x,y) { 
135                         return x>=h.x+flick.contentX && x<h.x+flick.contentX+h.width && y>=h.y+flick.contentY && y<h.y+flick.contentY+h.height 
136                     }
137
138                     onPressed: {
139                         if (editor.state == "selection") {
140                             if (hitHandle(startHandle,mouse.x+x,mouse.y+y)) {
141                                 drag = "start"
142                                 flick.interactive = false
143                             } else if (hitHandle(endHandle,mouse.x+x,mouse.y+y)) {
144                                 drag = "end"
145                                 flick.interactive = false
146                             } else {
147                                 var pos = edit.positionAt(mouse.x+x,mouse.y+y);
148                                 if (pos >= edit.selectionStart && pos <= edit.selectionEnd) {
149                                     drag = "selection"
150                                     flick.interactive = false
151                                 } else {
152                                     drag = ""
153                                     flick.interactive = true
154                                 }
155                             }
156                         }
157                     }
158
159                     onReleased: {
160                         if (editor.state == "selection") {
161                             if (drag == "selection") {
162                                 editor.state = "menu"
163                             }
164                             drag = ""
165                         }
166                         flick.interactive = true
167                     }
168
169                     onPositionChanged: {
170                         if (editor.state == "selection" && drag != "") {
171                             if (drag == "start") {
172                                 var pos = edit.positionAt(mouse.x+x+startHandle.width/2,mouse.y+y);
173                                 var e = edit.selectionEnd;
174                                 if (e < pos)
175                                     e = pos;
176                                 edit.select(pos,e);
177                             } else if (drag == "end") {
178                                 var pos = edit.positionAt(mouse.x+x-endHandle.width/2,mouse.y+y);
179                                 var s = edit.selectionStart;
180                                 if (s > pos)
181                                     s = pos;
182                                 edit.select(s,pos);
183                             }
184                         }
185                     }
186                 }
187             }
188         }
189
190         Item {
191             id: menu
192             opacity: 0.0
193             width: 100
194             height: 120
195             anchors.centerIn: parent
196
197             Rectangle {
198                 border.width: 1
199                 border.color: "darkBlue"
200                 radius: 15
201                 color: "#806080FF"
202                 anchors.fill: parent
203             }
204
205             Column {
206                 anchors.centerIn: parent
207                 spacing: 8
208
209                 Rectangle {
210                     border.width: 1
211                     border.color: "darkBlue"
212                     color: "#ff7090FF"
213                     width: 60
214                     height: 16
215
216                     Text { anchors.centerIn: parent; text: "Cut" }
217
218                     MouseArea { 
219                         anchors.fill: parent
220                         onClicked: { edit.cut(); editor.state = "" } 
221                     }
222                 }
223
224                 Rectangle {
225                     border.width: 1
226                     border.color: "darkBlue"
227                     color: "#ff7090FF"
228                     width: 60
229                     height: 16
230
231                     Text { anchors.centerIn: parent; text: "Copy" }
232
233                     MouseArea { 
234                         anchors.fill: parent
235                         onClicked: { edit.copy(); editor.state = "selection" } 
236                     }
237                 }
238
239                 Rectangle {
240                     border.width: 1
241                     border.color: "darkBlue"
242                     color: "#ff7090FF"
243                     width: 60
244                     height: 16
245
246                     Text { anchors.centerIn: parent; text: "Paste" }
247
248                     MouseArea { 
249                         anchors.fill: parent
250                         onClicked: { edit.paste(); edit.cursorPosition = edit.selectionEnd; editor.state = "" } 
251                     }
252                 }
253
254                 Rectangle {
255                     border.width: 1
256                     border.color: "darkBlue"
257                     color: "#ff7090FF"
258                     width: 60
259                     height: 16
260
261                     Text { anchors.centerIn: parent; text: "Deselect" }
262
263                     MouseArea { 
264                         anchors.fill: parent
265                         onClicked: { 
266                             edit.cursorPosition = edit.selectionEnd;
267                             edit.deselect();
268                             editor.state = "" 
269                         } 
270                     }
271                 }
272             }
273         }
274     }
275
276     states: [
277         State {
278             name: "selection"
279             PropertyChanges { target: startHandle; opacity: 1.0 }
280             PropertyChanges { target: endHandle; opacity: 1.0 }
281         },
282         State {
283             name: "menu"
284             PropertyChanges { target: startHandle; opacity: 0.5 }
285             PropertyChanges { target: endHandle; opacity: 0.5 }
286             PropertyChanges { target: menu; opacity: 1.0 }
287         }
288     ]
289 }