1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the Qt Components project.
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
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
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
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."
39 ****************************************************************************/
48 property alias font: textEdit.font
49 property alias cursorPosition: textEdit.cursorPosition
50 property alias horizontalAlignment: textEdit.horizontalAlignment
51 property alias inputMethodHints: textEdit.inputMethodHints
52 property alias verticalAlignment: textEdit.verticalAlignment
53 property alias readOnly: textEdit.readOnly
54 property alias selectedText: textEdit.selectedText
55 property alias selectionEnd: textEdit.selectionEnd
56 property alias selectionStart: textEdit.selectionStart
57 property alias text: textEdit.text
58 property alias textFormat: textEdit.textFormat
59 property alias wrapMode: textEdit.wrapMode
60 property bool errorHighlight: false
74 function select(start, end) {
75 textEdit.select(start, end)
78 function selectAll() {
82 function selectWord() {
86 function positionAt(x, y) {
87 var p = mapToItem(textEdit, x, y);
88 return textEdit.positionAt(p.x, p.y)
91 function positionToRectangle(pos) {
92 var rect = textEdit.positionToRectangle(pos)
93 var point = mapFromItem(textEdit, rect.x, rect.y)
94 rect.x = point.x; rect.y = point.y
98 function openSoftwareInputPanel() {
99 textEdit.openSoftwareInputPanel()
102 function closeSoftwareInputPanel() {
103 textEdit.closeSoftwareInputPanel()
107 property alias placeholderText: placeholder.text
108 // TODO: Refactor implicit size when following bugs are resolved
109 // http://bugreports.qt.nokia.com/browse/QTBUG-14957
110 // http://bugreports.qt.nokia.com/browse/QTBUG-16665
111 // http://bugreports.qt.nokia.com/browse/QTBUG-16710 (fixed in Qt 4.7.2)
112 // http://bugreports.qt.nokia.com/browse/QTBUG-12305 (fixed in QtQuick1.1)
113 property real platformMaxImplicitWidth: -1
114 property real platformMaxImplicitHeight: -1
115 property bool platformInverted: false
116 property bool enabled: true // overriding due to QTBUG-15797 and related bugs
119 var preferredWidth = placeholder.visible ? placeholder.model.paintedWidth
121 preferredWidth = Math.max(preferredWidth, privy.minImplicitWidth)
122 preferredWidth += container.horizontalMargins
123 if (root.platformMaxImplicitWidth >= 0)
124 return Math.min(preferredWidth, root.platformMaxImplicitWidth)
125 return preferredWidth
129 var preferredHeight = placeholder.visible ? placeholder.model.paintedHeight
130 : flick.contentHeight
131 preferredHeight += container.verticalMargins
132 // layout spec gives minimum height (textFieldHeight) which includes required padding
133 preferredHeight = Math.max(privateStyle.textFieldHeight, preferredHeight)
134 if (root.platformMaxImplicitHeight >= 0)
135 return Math.min(preferredHeight, root.platformMaxImplicitHeight)
136 return preferredHeight
140 // Detect when a width has been explicitly set. Needed to determine if the TextEdit should
141 // grow horizontally or wrap. I.e. in determining the model's width. There's no way to get
142 // notified of having an explicit width set. Therefore it's polled in widthChanged.
143 privy.widthExplicit = root.widthExplicit()
148 onCurrentOrientationChanged: {
149 delayedEnsureVisible.start()
157 // TODO: More consistent minimum width for empty TextArea than 20 * " " on current font?
158 property real minImplicitWidth: privateStyle.textWidth(" ", textEdit.font)
159 property bool widthExplicit: false
160 property bool wrap: privy.widthExplicit || root.platformMaxImplicitWidth >= 0
161 property real wrapWidth: privy.widthExplicit ? root.width - container.horizontalMargins
162 : root.platformMaxImplicitWidth - container.horizontalMargins
163 function bg_postfix() {
164 if (root.errorHighlight)
166 else if (root.readOnly || !root.enabled)
168 else if (textEdit.activeFocus)
178 source: privateStyle.imagePath("qtg_fr_textfield_" + privy.bg_postfix(), root.platformInverted)
180 left: platformStyle.borderSizeMedium
181 top: platformStyle.borderSizeMedium
182 right: platformStyle.borderSizeMedium
183 bottom: platformStyle.borderSizeMedium
191 property real horizontalMargins: container.anchors.leftMargin
192 + container.anchors.rightMargin
193 + flick.anchors.leftMargin
194 + flick.anchors.rightMargin
196 property real verticalMargins: container.anchors.topMargin
197 + container.anchors.bottomMargin
198 + flick.anchors.topMargin
199 + flick.anchors.bottomMargin
203 leftMargin: platformStyle.paddingSmall; rightMargin: platformStyle.paddingSmall
204 topMargin: platformStyle.paddingMedium; bottomMargin: platformStyle.paddingMedium
209 // TODO: Should placeholder also be scrollable?
212 objectName: "placeholder"
214 // TODO: See TODO: Refactor implicit size...
215 property variant model: Text {
217 text: placeholder.text
219 wrapMode: textEdit.wrapMode
220 horizontalAlignment: textEdit.horizontalAlignment
221 verticalAlignment: textEdit.verticalAlignment
226 target: placeholder.model
228 value: privy.wrapWidth
232 color: root.platformInverted ? platformStyle.colorNormalMidInverted
233 : platformStyle.colorNormalMid
235 horizontalAlignment: textEdit.horizontalAlignment
236 verticalAlignment: textEdit.verticalAlignment
238 if (text && (textEdit.model.paintedWidth == 0 && textEdit.model.paintedHeight <= textEdit.cursorRectangle.height))
239 return (readOnly || !textEdit.activeFocus)
243 wrapMode: textEdit.wrapMode
244 x: flick.x; y: flick.y
245 height: flick.height; width: flick.width
251 property real tiny: Math.round(platformStyle.borderSizeMedium / 2)
253 function ensureVisible(rect) {
254 if (Math.round(contentX) > Math.round(rect.x))
256 else if (Math.round(contentX + width) < Math.round(rect.x + rect.width))
257 contentX = rect.x + rect.width - width
259 if (Math.round(contentY) > Math.round(rect.y))
261 else if (Math.round(contentY + height) < Math.round(rect.y + rect.height))
262 contentY = rect.y + rect.height - height
270 bottomMargin: tiny / 2
273 boundsBehavior: Flickable.StopAtBounds
274 contentHeight: textEdit.model.paintedHeight
275 contentWidth: textEdit.model.paintedWidth +
276 (textEdit.wrapMode == TextEdit.NoWrap ? textEdit.cursorRectangle.width : 0)
277 interactive: root.enabled
280 if(textEdit.cursorVisible || textEdit.cursorPosition == textEdit.selectionEnd)
281 ensureVisible(textEdit.cursorRectangle)
285 if(textEdit.cursorVisible || textEdit.cursorPosition == textEdit.selectionEnd)
286 ensureVisible(textEdit.cursorRectangle)
291 objectName: "textEdit"
293 // TODO: See TODO: Refactor implicit size...
294 property variant model: TextEdit {
297 horizontalAlignment: textEdit.horizontalAlignment
298 verticalAlignment: textEdit.verticalAlignment
299 wrapMode: textEdit.wrapMode
303 // In Wrap mode, if the width is bound the text will always get wrapped at the
304 // set width. If the width is not bound the text won't get wrapped but
305 // paintedWidth will increase.
308 target: textEdit.model
310 value: privy.wrapWidth
313 activeFocusOnPress: false
314 enabled: root.enabled
316 font { family: platformStyle.fontFamilyRegular; pixelSize: platformStyle.fontSizeMedium }
317 color: root.platformInverted ? platformStyle.colorNormalLightInverted
318 : platformStyle.colorNormalDark
319 cursorVisible: activeFocus && !selectedText
320 selectedTextColor: root.platformInverted ? platformStyle.colorNormalDarkInverted
321 : platformStyle.colorNormalLight
322 selectionColor: root.platformInverted ? platformStyle.colorTextSelectionInverted
323 : platformStyle.colorTextSelection
324 textFormat: TextEdit.AutoText
325 wrapMode: TextEdit.Wrap
328 // TODO: Make bug report?
329 // Called too early (before TextEdit size is adjusted) delay ensureVisible call a bit
330 onCursorRectangleChanged: delayedEnsureVisible.start()
331 onActiveFocusChanged: {
340 // De-focusing requires setting focus elsewhere, in this case editor's parent
342 root.parent.forceActiveFocus()
346 Keys.forwardTo: touchController
348 TextTouchController {
351 // selection handles require touch area geometry to differ from TextEdit's geometry
353 top: editor.top; topMargin: -container.verticalMargins / 2
354 left: editor.left; leftMargin: -container.horizontalMargins / 2
356 height: Math.max(root.height, flick.contentHeight + container.verticalMargins * 2)
357 width: Math.max(root.width, flick.contentWidth + container.horizontalMargins * 2)
358 editorScrolledX: flick.contentX - (container.horizontalMargins / 2)
359 editorScrolledY: flick.contentY - (container.verticalMargins / 2)
360 copyEnabled: textEdit.selectedText
361 cutEnabled: !textEdit.readOnly && textEdit.selectedText
362 platformInverted: root.platformInverted
363 Component.onCompleted: flick.movementEnded.connect(touchController.flickEnded)
364 Connections { target: screen; onCurrentOrientationChanged: touchController.updateGeometry() }
367 onHeightChanged: touchController.updateGeometry()
368 onWidthChanged: touchController.updateGeometry()
369 onHorizontalAlignmentChanged: touchController.updateGeometry()
370 onVerticalAlignmentChanged: touchController.updateGeometry()
371 onWrapModeChanged: touchController.updateGeometry()
372 onFontChanged: touchController.updateGeometry()
377 PropertyAnimation { id: fade; target: textEdit; property: "opacity"; from: 0; to: 1; duration: 250 }
378 PropertyAnimation { id: scroll; target: flick; property: "contentY"; duration: 250 }
385 topMargin: -flick.tiny / 2;
390 orientation: Qt.Vertical
391 height: container.height
392 platformInverted: root.platformInverted
399 leftMargin: -flick.tiny;
400 bottom: container.bottom;
401 bottomMargin: -flick.tiny / 2
402 rightMargin: vertical.opacity ? vertical.width : 0
406 orientation: Qt.Horizontal
407 width: container.width
408 platformInverted: root.platformInverted
412 id: delayedEnsureVisible
414 onTriggered: flick.ensureVisible(textEdit.cursorRectangle)