1/*
2    SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org>
3
4    SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7import QtQuick 2.6
8import QtQuick.Window 2.2
9import QtQuick.Controls @QQC2_VERSION@
10import QtQuick.Templates @QQC2_VERSION@ as T
11import org.kde.plasma.core 2.0 as PlasmaCore
12import org.kde.kconfig 1.0
13import "private" as Private
14import org.kde.kirigami 2.5 as Kirigami
15import "mobiletextselection" as MobileTextSelection
16
17T.TextField {
18    id: control
19
20    /**
21     * Whether the button to clear the text from TextField is visible.
22     * @since 5.73
23     */
24    property bool clearButtonShown: false
25
26    /**
27     * Whether to show a button that allows the user to reveal the password in
28     * plain text. This only makes sense if the echoMode is set to Password.
29     * @since 5.73
30     */
31    property bool revealPasswordButtonShown: false
32
33    // this takes into account kiosk restriction
34    readonly property bool __effectiveRevealPasswordButtonShown: revealPasswordButtonShown
35                                                              && KAuthorized.authorize("lineedit_reveal_password")
36                                                              && (echoMode == TextInput.Normal || control.text.length > 0)
37
38    // Can't guarantee that background will always be present or have the margins property
39    readonly property bool __hasBackgroundAndMargins: background && background.hasOwnProperty("margins")
40
41    // TextField doesn't have this property by default for whatever reason
42    property bool visualFocus: control.activeFocus && (
43        control.focusReason == Qt.TabFocusReason ||
44        control.focusReason == Qt.BacktabFocusReason ||
45        control.focusReason == Qt.ShortcutFocusReason
46    )
47
48    /* It might be preferable to do background width OR content width if we
49     * want content to stay within the background rather than expanding the
50     * control, but this is maintaining compatibility with the pre-existing
51     * behavior. Use the following 2 lines if you want text to stay within the
52     * background:
53    implicitBackgroundWidth + leftInset + rightInset
54    || Math.ceil(Math.max(contentWidth, placeholder.implicitWidth)) + leftPadding + rightPadding
55     */
56    implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
57                            Math.ceil(Math.max(contentWidth, placeholder.implicitWidth)) + leftPadding + rightPadding)
58    implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
59                             contentHeight + topPadding + bottomPadding,
60                             placeholder.implicitHeight + topPadding + bottomPadding)
61
62    leftPadding: (__hasBackgroundAndMargins ? background.margins.left : 0) + (control.mirrored ? inlineButtonRow.width : 0)
63    topPadding: __hasBackgroundAndMargins ? background.margins.top : 0
64    rightPadding: (__hasBackgroundAndMargins ? background.margins.right : 0) + (control.mirrored ? 0 : inlineButtonRow.width)
65    bottomPadding: __hasBackgroundAndMargins ? background.margins.bottom : 0
66
67    PlasmaCore.ColorScope.inherit: !background || !background.visible
68    PlasmaCore.ColorScope.colorGroup: PlasmaCore.Theme.ViewColorGroup
69
70    color: control.backgroundVisible ? PlasmaCore.Theme.viewTextColor : PlasmaCore.ColorScope.textColor
71    selectionColor: control.backgroundVisible ? PlasmaCore.Theme.viewFocusColor : PlasmaCore.ColorScope.highlightColor
72    selectedTextColor: control.backgroundVisible ? PlasmaCore.Theme.viewHighlightedTextColor : PlasmaCore.ColorScope.highlightedTextColor
73    placeholderTextColor: PlasmaCore.Theme.disabledTextColor
74
75    verticalAlignment: TextInput.AlignVCenter
76    // Manually setting this fixes alignment in RTL layouts
77    horizontalAlignment: TextInput.AlignLeft
78    opacity: control.enabled ? 1 : 0.6
79    hoverEnabled: !Kirigami.Settings.tabletMode
80
81    // Work around Qt bug where NativeRendering breaks for non-integer scale factors
82    // https://bugreports.qt.io/browse/QTBUG-70481
83    renderType: Screen.devicePixelRatio % 1 !== 0 ? Text.QtRendering : Text.NativeRendering
84
85    selectByMouse: !Kirigami.Settings.tabletMode
86
87    cursorDelegate: Kirigami.Settings.tabletMode ? mobileCursor : null
88    Component {
89        id: mobileCursor
90        MobileTextSelection.MobileCursor {
91            target: control
92        }
93    }
94    onFocusChanged: {
95        if (focus) {
96            MobileTextSelection.MobileTextActionsToolBar.controlRoot = control;
97        }
98    }
99
100    onTextChanged: MobileTextSelection.MobileTextActionsToolBar.shouldBeVisible = false;
101    onPressed: MobileTextSelection.MobileTextActionsToolBar.shouldBeVisible = true;
102
103    onPressAndHold: {
104        if (!Kirigami.Settings.tabletMode) {
105            return;
106        }
107        forceActiveFocus();
108        cursorPosition = positionAt(event.x, event.y);
109        selectWord();
110    }
111    MobileTextSelection.MobileCursor {
112        target: control
113        selectionStartHandle: true
114        property var rect: target.positionToRectangle(target.selectionStart)
115        //FIXME: this magic values seem to be always valid, for every font,every dpi, every scaling
116        x: rect.x + 5
117        y: rect.y + 6
118    }
119
120    Label {
121        id: placeholder
122        enabled: false
123        x: control.leftPadding
124        y: control.topPadding
125        width: control.availableWidth
126        height: control.availableHeight
127
128        text: control.placeholderText
129        font: control.font
130        color: control.placeholderTextColor
131        horizontalAlignment: control.horizontalAlignment
132        verticalAlignment: control.verticalAlignment
133        visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
134        elide: Text.ElideRight
135        renderType: control.renderType
136    }
137
138    Row {
139        id: inlineButtonRow
140        anchors.right: control.right
141        anchors.rightMargin: control.__hasBackgroundAndMargins ? background.margins.right : 0
142        anchors.verticalCenter: control.verticalCenter
143
144        PlasmaCore.IconItem {
145            id: showPasswordButton
146            source: __effectiveRevealPasswordButtonShown ? (control.echoMode === TextInput.Normal ? "visibility": "hint") : ""
147            height: PlasmaCore.Units.iconSizes.small
148            width: height
149            opacity: (__effectiveRevealPasswordButtonShown && control.enabled) ? 1 : 0
150            visible: opacity > 0
151            Behavior on opacity {
152                NumberAnimation {
153                    duration: PlasmaCore.Units.longDuration
154                    easing.type: Easing.InOutQuad
155                }
156            }
157            MouseArea {
158                anchors.fill: parent
159                enabled: __effectiveRevealPasswordButtonShown
160                onClicked: {
161                    control.echoMode = (control.echoMode == TextInput.Normal ? TextInput.Password : TextInput.Normal)
162                    control.forceActiveFocus()
163                }
164            }
165        }
166
167        PlasmaCore.IconItem {
168            id: clearButton
169            //ltr confusingly refers to the direction of the arrow in the icon, not the text direction which it should be used in
170            source: clearButtonShown ? (LayoutMirroring.enabled ? "edit-clear-locationbar-ltr" : "edit-clear-locationbar-rtl") : ""
171            height: PlasmaCore.Units.iconSizes.small
172            width: height
173            opacity: (control.length > 0 && clearButtonShown && control.enabled) ? 1 : 0
174            visible: opacity > 0
175            Behavior on opacity {
176                NumberAnimation {
177                    duration: PlasmaCore.Units.longDuration
178                    easing.type: Easing.InOutQuad
179                }
180            }
181            MouseArea {
182                anchors.fill: parent
183                onClicked: {
184                    control.clear()
185                    control.forceActiveFocus()
186                }
187            }
188        }
189    }
190
191    background: PlasmaCore.FrameSvgItem {
192        implicitWidth: PlasmaCore.Units.gridUnit * 8 + margins.left + margins.right
193        implicitHeight: PlasmaCore.Units.gridUnit + margins.top + margins.bottom
194        imagePath: "widgets/lineedit"
195        prefix: "base"
196
197        PlasmaCore.FrameSvgItem {
198            anchors {
199                fill: parent
200                leftMargin: -margins.left
201                topMargin: -margins.top
202                rightMargin: -margins.right
203                bottomMargin: -margins.bottom
204            }
205            imagePath: "widgets/lineedit"
206            prefix: "hover"
207            visible: opacity > 0
208            opacity: control.hovered
209            Behavior on opacity {
210                enabled: control.hovered
211                NumberAnimation {
212                    duration: PlasmaCore.Units.longDuration
213                    easing.type: Easing.OutCubic
214                }
215            }
216        }
217        PlasmaCore.FrameSvgItem {
218            z: lineEditSvg.hasElement("hint-focus-over-base") ? 0 : -1
219            anchors {
220                fill: parent
221                leftMargin: -margins.left
222                topMargin: -margins.top
223                rightMargin: -margins.right
224                bottomMargin: -margins.bottom
225            }
226            imagePath: "widgets/lineedit"
227            prefix: control.visualFocus && lineEditSvg.hasElement("focusframe-center") ? "focusframe" : "focus"
228            visible: opacity > 0
229            opacity: control.visualFocus || control.activeFocus
230            Behavior on opacity {
231                NumberAnimation {
232                    duration: PlasmaCore.Units.longDuration
233                    easing.type: Easing.OutCubic
234                }
235            }
236        }
237    }
238
239    PlasmaCore.Svg {
240        id: lineEditSvg
241        imagePath: "widgets/lineedit"
242    }
243}
244