X-Git-Url: http://git.maemo.org/git/?p=mardrone;a=blobdiff_plain;f=mardrone%2Fimports%2Fcom%2Fnokia%2Fmeego%2FTextField.qml;fp=mardrone%2Fimports%2Fcom%2Fnokia%2Fmeego%2FTextField.qml;h=45ff87e323195b988e9ff4d62b7243ef76b22f81;hp=0000000000000000000000000000000000000000;hb=53abfcee86d03676ae5e3b648d6c7cee69c2f4e1;hpb=f7f1d7c4427cba2c8490f2767259b1d06f66c7ed diff --git a/mardrone/imports/com/nokia/meego/TextField.qml b/mardrone/imports/com/nokia/meego/TextField.qml new file mode 100644 index 0000000..45ff87e --- /dev/null +++ b/mardrone/imports/com/nokia/meego/TextField.qml @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Components project. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.1 +import "." 1.0 +import "UIConstants.js" as UI +import "EditBubble.js" as Popup +import "TextAreaHelper.js" as TextAreaHelper +import "Magnifier.js" as MagnifierPopup +FocusScope { + id: root + + // Common public API + property alias text: textInput.text + property alias placeholderText: prompt.text + + property alias inputMethodHints: textInput.inputMethodHints + property alias font: textInput.font + property alias cursorPosition: textInput.cursorPosition + property alias maximumLength: textInput.maximumLength + property alias readOnly: textInput.readOnly + property alias acceptableInput: textInput.acceptableInput + property alias inputMask: textInput.inputMask + property alias validator: textInput.validator + + property alias selectedText: textInput.selectedText + property alias selectionStart: textInput.selectionStart + property alias selectionEnd: textInput.selectionEnd + + property alias echoMode: textInput.echoMode // ### TODO: declare own enum { Normal, Password } + + property bool errorHighlight: !acceptableInput + // Property enableSoftwareInputPanel is DEPRECATED + property alias enableSoftwareInputPanel: textInput.activeFocusOnPress + + property Item platformSipAttributes + + property bool platformEnableEditBubble: true + + property Item platformStyle: TextFieldStyle {} + + property alias style: root.platformStyle + + property Component customSoftwareInputPanel + + property Component platformCustomSoftwareInputPanel + + property alias platformPreedit: inputMethodObserver.preedit + + signal accepted + + onPlatformSipAttributesChanged: { + platformSipAttributes.registerInputElement(textInput) + } + + onCustomSoftwareInputPanelChanged: { + console.log("TextField's property customSoftwareInputPanel is deprecated. Use property platformCustomSoftwareInputPanel instead.") + platformCustomSoftwareInputPanel = customSoftwareInputPanel + } + + onPlatformCustomSoftwareInputPanelChanged: { + textInput.activeFocusOnPress = platformCustomSoftwareInputPanel == null + } + + + + function copy() { + textInput.copy() + } + + Connections { + target: platformWindow + + onActiveChanged: { + if(platformWindow.active) { + if (!readOnly) { + if (activeFocus) { + if (platformCustomSoftwareInputPanel != null) { + platformOpenSoftwareInputPanel(); + } else { + inputContext.simulateSipOpen(); + } + repositionTimer.running = true; + } + } + } else { + if (activeFocus) { + platformCloseSoftwareInputPanel(); + Popup.close(textInput); + } + } + } + + onAnimatingChanged: { + if (!platformWindow.animating && root.activeFocus) { + TextAreaHelper.repositionFlickable(contentMovingAnimation); + } + } + } + + + function paste() { + textInput.paste() + } + + function cut() { + textInput.cut() + } + + function select(start, end) { + textInput.select(start, end) + } + + function selectAll() { + textInput.selectAll() + } + + function selectWord() { + textInput.selectWord() + } + + function positionAt(x) { + var p = mapToItem(textInput, x, 0); + return textInput.positionAt(p.x) + } + + function positionToRectangle(pos) { + var rect = textInput.positionToRectangle(pos) + rect.x = mapFromItem(textInput, rect.x, 0).x + return rect; + } + + // ensure propagation of forceActiveFocus + function forceActiveFocus() { + textInput.forceActiveFocus() + } + + function closeSoftwareInputPanel() { + console.log("TextField's function closeSoftwareInputPanel is deprecated. Use function platformCloseSoftwareInputPanel instead.") + platformCloseSoftwareInputPanel() + } + + function platformCloseSoftwareInputPanel() { + inputContext.simulateSipClose(); + if (inputContext.customSoftwareInputPanelVisible) { + inputContext.customSoftwareInputPanelVisible = false + inputContext.customSoftwareInputPanelComponent = null + inputContext.customSoftwareInputPanelTextField = null + } else { + textInput.closeSoftwareInputPanel(); + } + } + + function openSoftwareInputPanel() { + console.log("TextField's function openSoftwareInputPanel is deprecated. Use function platformOpenSoftwareInputPanel instead.") + platformOpenSoftwareInputPanel() + } + + function platformOpenSoftwareInputPanel() { + inputContext.simulateSipOpen(); + if (platformCustomSoftwareInputPanel != null && !inputContext.customSoftwareInputPanelVisible) { + inputContext.customSoftwareInputPanelTextField = root + inputContext.customSoftwareInputPanelComponent = platformCustomSoftwareInputPanel + inputContext.customSoftwareInputPanelVisible = true + } else { + textInput.openSoftwareInputPanel(); + } + } + + // private + property bool __expanding: true // Layout hint used but ToolBarLayout + property int __preeditDisabledMask: Qt.ImhHiddenText| + Qt.ImhNoPredictiveText| + Qt.ImhDigitsOnly| + Qt.ImhFormattedNumbersOnly| + Qt.ImhDialableCharactersOnly| + Qt.ImhEmailCharactersOnly| + Qt.ImhUrlCharactersOnly + + implicitWidth: platformStyle.defaultWidth + implicitHeight: UI.FIELD_DEFAULT_HEIGHT + + onActiveFocusChanged: { + if (!readOnly) { + if (activeFocus) { + if (platformCustomSoftwareInputPanel != null) { + platformOpenSoftwareInputPanel(); + } else { + inputContext.simulateSipOpen(); + } + + repositionTimer.running = true; + } else { + platformCloseSoftwareInputPanel(); + Popup.close(textInput); + } + } + } + + + BorderImage { + id: background + source: errorHighlight? + platformStyle.backgroundError: + readOnly? + platformStyle.backgroundDisabled: + textInput.activeFocus? + platformStyle.backgroundSelected: + platformStyle.background + + anchors.fill: parent + border.left: root.platformStyle.backgroundCornerMargin; border.top: root.platformStyle.backgroundCornerMargin + border.right: root.platformStyle.backgroundCornerMargin; border.bottom: root.platformStyle.backgroundCornerMargin + } + + Text { + id: prompt + + anchors {verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right} + anchors.leftMargin: root.platformStyle.paddingLeft + anchors.rightMargin: root.platformStyle.paddingRight + anchors.verticalCenterOffset: root.platformStyle.baselineOffset + + font: root.platformStyle.textFont + color: root.platformStyle.promptTextColor + elide: Text.ElideRight + + // opacity for default state + opacity: 0.0 + + states: [ + State { + name: "unfocused" + // memory allocation optimization: cursorPosition is checked to minimize displayText evaluations + when: !root.activeFocus && textInput.cursorPosition == 0 && !textInput.text && prompt.text && !textInput.inputMethodComposing + PropertyChanges { target: prompt; opacity: 1.0; } + }, + State { + name: "focused" + // memory allocation optimization: cursorPosition is checked to minimize displayText evaluations + when: root.activeFocus && textInput.cursorPosition == 0 && !textInput.text && prompt.text && !textInput.inputMethodComposing + PropertyChanges { target: prompt; opacity: 0.6; } + } + ] + + transitions: [ + Transition { + from: "unfocused"; to: "focused"; + reversible: true + SequentialAnimation { + PauseAnimation { duration: 60 } + NumberAnimation { target: prompt; properties: "opacity"; duration: 150 } + } + }, + Transition { + from: "focused"; to: ""; + reversible: true + SequentialAnimation { + PauseAnimation { duration: 60 } + NumberAnimation { target: prompt; properties: "opacity"; duration: 100 } + } + } + ] + } + + MouseArea { + enabled: !textInput.activeFocus + z: enabled?1:0 + anchors.fill: parent + anchors.margins: UI.TOUCH_EXPANSION_MARGIN + onClicked: { + if (!textInput.activeFocus) { + textInput.forceActiveFocus(); + + // activate to preedit and/or move the cursor + var preeditDisabled = root.inputMethodHints & + root.__preeditDisabledMask + var injectionSucceeded = false; + var newCursorPosition = textInput.positionAt(mapToItem(textInput, mouseX, mouseY).x,TextInput.CursorOnCharacter); + if (!preeditDisabled + && !TextAreaHelper.atSpace(newCursorPosition) + && newCursorPosition != textInput.text.length + && !(newCursorPosition == 0 || TextAreaHelper.atSpace(newCursorPosition - 1))) { + injectionSucceeded = TextAreaHelper.injectWordToPreedit(newCursorPosition); + } + if (!injectionSucceeded) { + textInput.cursorPosition=newCursorPosition; + } + } + } + } + + TextInput { + id: textInput + + property alias preedit: inputMethodObserver.preedit + property alias preeditCursorPosition: inputMethodObserver.preeditCursorPosition + + anchors {verticalCenter: parent.verticalCenter; left: parent.left; right: parent.right} + anchors.leftMargin: root.platformStyle.paddingLeft + anchors.rightMargin: root.platformStyle.paddingRight + anchors.verticalCenterOffset: root.platformStyle.baselineOffset + + passwordCharacter: "\u2022" + font: root.platformStyle.textFont + color: root.platformStyle.textColor + selectByMouse: false + selectedTextColor: root.platformStyle.selectedTextColor + selectionColor: root.platformStyle.selectionColor + mouseSelectionMode: TextInput.SelectWords + focus: true + + onAccepted: { root.accepted() } + + Component.onDestruction: { + Popup.close(textInput); + } + + Connections { + target: TextAreaHelper.findFlickable(root.parent) + + onContentYChanged: if (root.activeFocus) TextAreaHelper.filteredInputContextUpdate(); + onContentXChanged: if (root.activeFocus) TextAreaHelper.filteredInputContextUpdate(); + onMovementEnded: inputContext.update(); + } + + Connections { + target: inputContext + + onSoftwareInputPanelRectChanged: { + if (activeFocus) { + repositionTimer.running = true + } + } + } + + onTextChanged: { + if(root.activeFocus) { + TextAreaHelper.repositionFlickable(contentMovingAnimation) + } + + if (Popup.isOpened(textInput) && !Popup.isChangingInput()) + Popup.close(textInput); + } + + onCursorPositionChanged: { + if (MagnifierPopup.isOpened() && + Popup.isOpened()) { + Popup.close(textInput); + } else if ((!mouseFilter.attemptToActivate || + textInput.cursorPosition == textInput.text.length) && + Popup.isOpened(textInput) && + !Popup.isChangingInput()) { + Popup.close(textInput); + Popup.open(textInput, + textInput.positionToRectangle(textInput.cursorPosition)); + } + } + + onSelectedTextChanged: { + if (Popup.isOpened(textInput) && !Popup.isChangingInput()) { + Popup.close(textInput); + } + } + + InputMethodObserver { + id: inputMethodObserver + + onPreeditChanged: { + if(root.activeFocus) { + TextAreaHelper.repositionFlickable(contentMovingAnimation) + } + + if (Popup.isOpened(textInput) && !Popup.isChangingInput()) { + Popup.close(textInput); + } + } + } + + Timer { + id: repositionTimer + interval: 350 + onTriggered: { + TextAreaHelper.repositionFlickable(contentMovingAnimation) + } + } + + PropertyAnimation { + id: contentMovingAnimation + property: "contentY" + duration: 200 + easing.type: Easing.InOutCubic + } + + MouseFilter { + id: mouseFilter + anchors.fill: parent + anchors.leftMargin: UI.TOUCH_EXPANSION_MARGIN - root.platformStyle.paddingLeft + anchors.rightMargin: UI.TOUCH_EXPANSION_MARGIN - root.platformStyle.paddingRight + anchors.topMargin: UI.TOUCH_EXPANSION_MARGIN - ((root.height - parent.height) / 2) + anchors.bottomMargin: UI.TOUCH_EXPANSION_MARGIN - ((root.height - parent.height) / 2) + + property bool attemptToActivate: false + property bool pressOnPreedit: false + property int oldCursorPosition: 0 + + property variant editBubblePosition: Qt.point(0,0) + + onPressed: { + var mousePosition = textInput.positionAt(mouse.x,TextInput.CursorOnCharacter); + pressOnPreedit = textInput.cursorPosition==mousePosition + oldCursorPosition = textInput.cursorPosition; + var preeditDisabled = root.inputMethodHints & + root.__preeditDisabledMask + + attemptToActivate = !pressOnPreedit && !root.readOnly && !preeditDisabled && root.activeFocus && + !(mousePosition == 0 || TextAreaHelper.atSpace(mousePosition - 1) || TextAreaHelper.atSpace(mousePosition)); + mouse.filtered = true; + } + + onDelayedPressSent: { + if (textInput.preedit) { + textInput.cursorPosition = oldCursorPosition; + } + } + + onHorizontalDrag: { + // possible pre-edit word have to be commited before selection + if (root.activeFocus || root.readOnly) { + inputContext.reset() + parent.selectByMouse = true + attemptToActivate = false + } + } + + onPressAndHold:{ + // possible pre-edit word have to be commited before showing the magnifier + if ((root.text != "" || inputMethodObserver.preedit != "") && root.activeFocus) { + inputContext.reset() + attemptToActivate = false + MagnifierPopup.open(root); + var magnifier = MagnifierPopup.popup; + var cursorPos = textInput.positionToRectangle(0); + var mappedPosMf = mapFromItem(parent,mouse.x,cursorPos.y+cursorPos.height); + magnifier.xCenter = mapToItem(magnifier.sourceItem,mappedPosMf.x,0).x; + var mappedPos = mapToItem(magnifier.parent, mappedPosMf.x - magnifier.width / 2, + textInput.y - 120 - UI.MARGIN_XLARGE - (height / 2)); + var yAdjustment = -mapFromItem(magnifier.__rootElement(), 0, 0).y < magnifier.height / 2.5 ? magnifier.height / 2.5 + mapFromItem(magnifier.__rootElement(), 0,0).y : 0 + magnifier.x = mappedPos.x; + magnifier.y = mappedPos.y + yAdjustment; + magnifier.yCenter = mapToItem(magnifier.sourceItem,0,mappedPosMf.y).y; + parent.cursorPosition = textInput.positionAt(mouse.x) + } + } + + onReleased: { + if (MagnifierPopup.isOpened()) { + MagnifierPopup.close(); + } + + if (attemptToActivate) + inputContext.reset(); + + var newCursorPosition = textInput.positionAt(mouse.x,TextInput.CursorOnCharacter); + editBubblePosition = textInput.positionToRectangle(newCursorPosition); + + if (attemptToActivate) { + var beforeText = textInput.text; + + textInput.cursorPosition = newCursorPosition; + var injectionSucceeded = false; + + if (!TextAreaHelper.atSpace(newCursorPosition) + && newCursorPosition != textInput.text.length) { + injectionSucceeded = TextAreaHelper.injectWordToPreedit(newCursorPosition); + } + if (injectionSucceeded) { + mouse.filtered=true; + if (textInput.preedit.length >=1 && textInput.preedit.length <= 4) + editBubblePosition = textInput.positionToRectangle(textInput.cursorPosition+1) + } else { + textInput.text=beforeText; + textInput.cursorPosition=newCursorPosition; + } + } else if (!parent.selectByMouse) { + if (!pressOnPreedit) inputContext.reset(); + textInput.cursorPosition = textInput.positionAt(mouse.x,TextInput.CursorOnCharacter); + } + parent.selectByMouse = false; + } + + onFinished: { + if (root.activeFocus && platformEnableEditBubble) { + if (textInput.preedit.length == 0) + editBubblePosition = textInput.positionToRectangle(textInput.cursorPosition); + Popup.open(textInput,editBubblePosition); + + } + attemptToActivate = false + } + + onMousePositionChanged: { + if (MagnifierPopup.isOpened() && !parent.selectByMouse) { + textInput.cursorPosition = textInput.positionAt(mouse.x) + var magnifier = MagnifierPopup.popup; + var mappedPosMf = mapFromItem(parent,mouse.x,0); + var mappedPos = mapToItem(magnifier.parent,mappedPosMf.x - magnifier.width / 2.0, 0); + magnifier.xCenter = mapToItem(magnifier.sourceItem,mappedPosMf.x,0).x; + magnifier.x = mappedPos.x; + } + } + + onDoubleClicked: { + // possible pre-edit word have to be commited before selection + inputContext.reset() + parent.selectByMouse = true + attemptToActivate = false + } + } + } + + InverseMouseArea { + anchors.fill: parent + anchors.margins: UI.TOUCH_EXPANSION_MARGIN + enabled: textInput.activeFocus + onClickedOutside: { + if (Popup.isOpened(textInput) && ((mouseX > Popup.geometry().left && mouseX < Popup.geometry().right) && + (mouseY > Popup.geometry().top && mouseY < Popup.geometry().bottom))) { + return; + } + root.parent.focus = true; + } + } +}