Warning, /graphics/spectacle/src/Gui/Annotations/TextTool.qml is written in an unsupported language. File is not indexed.
0001 /* SPDX-FileCopyrightText: 2022 Marco Martin <mart@kde.org>
0002 * SPDX-FileCopyrightText: 2022 Noah Davis <noahadvs@gmail.com>
0003 * SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005
0006 import QtQuick
0007 import QtQuick.Templates as T
0008 import org.kde.kirigami as Kirigami
0009 import org.kde.spectacle.private
0010 import ".."
0011
0012 AnimatedLoader {
0013 id: root
0014 required property AnnotationViewport viewport
0015 readonly property AnnotationDocument document: viewport.document
0016 readonly property bool shouldShow: enabled
0017 && document.selectedItem.options & AnnotationTool.TextOption
0018 && (document.tool.options & AnnotationTool.TextOption
0019 || document.tool.type === AnnotationTool.SelectTool)
0020
0021 state: shouldShow ? "active" : "inactive"
0022
0023 sourceComponent: T.TextArea {
0024 id: textField
0025 readonly property bool mirrored: effectiveHorizontalAlignment === TextInput.AlignRight
0026
0027 LayoutMirroring.enabled: false
0028 LayoutMirroring.childrenInherit: true
0029
0030 Binding on implicitWidth {
0031 value: root.document.selectedItem.mousePath.boundingRect.width
0032 restoreMode: Binding.RestoreNone
0033 when: root.shouldShow
0034 }
0035 Binding on implicitHeight {
0036 value: root.document.selectedItem.mousePath.boundingRect.height
0037 restoreMode: Binding.RestoreNone
0038 when: root.shouldShow
0039 }
0040 Binding {
0041 target: root
0042 property: "x"
0043 when: root.shouldShow
0044 value: root.document.selectedItem.mousePath.boundingRect.x
0045 restoreMode: Binding.RestoreNone
0046 }
0047 Binding {
0048 target: root
0049 property: "y"
0050 when: root.shouldShow
0051 value: root.document.selectedItem.mousePath.boundingRect.y
0052 restoreMode: Binding.RestoreNone
0053 }
0054 property color textColor
0055 Binding on textColor {
0056 value: root.document.selectedItem.options & AnnotationTool.TextOption ?
0057 root.document.selectedItem.fontColor : root.document.tool.fontColor
0058 restoreMode: Binding.RestoreNone
0059 when: root.shouldShow
0060 }
0061 color: Qt.rgba(textColor.r, textColor.g, textColor.b, 0)
0062 Binding on font {
0063 value: root.document.selectedItem.options & AnnotationTool.TextOption ?
0064 root.document.selectedItem.font : root.document.tool.font
0065 restoreMode: Binding.RestoreNone
0066 when: root.shouldShow
0067 }
0068
0069 focus: true
0070 selectByMouse: true
0071 selectionColor: Qt.rgba(1-textColor.r, 1-textColor.g, 1-textColor.b, 1)
0072 selectedTextColor: Qt.rgba(textColor.r, textColor.g, textColor.b, 1)
0073 cursorPosition: {
0074 const mapped = mapFromItem(root.viewport, root.viewport.pressPosition)
0075 return positionAt(mapped.x, mapped.y)
0076 }
0077 cursorDelegate: Item {
0078 id: cursor
0079 visible: textField.cursorVisible
0080 Rectangle {
0081 // prevent the cursor from overlapping with the background
0082 x: textField.cursorPosition === textField.length && textField.length > 0 ?
0083 -width : 0
0084 width: Math.max(1 / root.viewport.zoom,
0085 contextWindow.dprRound(fontMetrics.xHeight / 12))
0086 height: parent.height
0087 color: Qt.rgba(textField.textColor.r, textField.textColor.g, textField.textColor.b, 1)
0088 }
0089 Connections {
0090 target: textField
0091 function onCursorPositionChanged() {
0092 if (textField.cursorVisible) {
0093 Qt.callLater(blinkAnimation.restart)
0094 }
0095 }
0096 }
0097 SequentialAnimation {
0098 id: blinkAnimation
0099 running: textField.cursorVisible
0100 loops: Animation.Infinite
0101 PropertyAction {
0102 target: cursor
0103 property: "visible"
0104 value: true
0105 }
0106 PauseAnimation {
0107 duration: Qt.styleHints.cursorFlashTime / 2
0108 }
0109 PropertyAction {
0110 target: cursor
0111 property: "visible"
0112 value: false
0113 }
0114 PauseAnimation {
0115 duration: Qt.styleHints.cursorFlashTime / 2
0116 }
0117 }
0118 }
0119
0120 // Keep this in sync with the value used in Traits::createTextPath
0121 tabStopDistance: Math.round(fontMetrics.advanceWidth("x") * 8)
0122 // QPainter::drawText doesn't support rich text.
0123 // We could consider using QStaticText to add rich text support.
0124 // We probably shouldn't use QTextDocument because that's unnecessarily heavy.
0125 textFormat: TextEdit.PlainText
0126 // Keep in sync with Traits::Text::textFlags
0127 horizontalAlignment: TextEdit.AlignLeft
0128 verticalAlignment: TextEdit.AlignTop
0129 wrapMode: TextEdit.NoWrap
0130
0131 // QPainter uses native antialiasing
0132 renderType: TextEdit.NativeRendering
0133
0134 Binding on text {
0135 value: root.document.selectedItem.text
0136 restoreMode: Binding.RestoreNone
0137 when: root.shouldShow
0138 }
0139 onTextChanged: {
0140 if (root.document.selectedItem.text === text) {
0141 return
0142 }
0143 let wasEmpty = root.document.selectedItem.text.length === 0
0144 root.document.selectedItem.text = text
0145 if (wasEmpty) {
0146 root.document.selectedItem.commitChanges()
0147 } else {
0148 commitChangesTimer.restart()
0149 }
0150 }
0151
0152 Keys.onDeletePressed: (event) => {
0153 event.accepted = text.length === 0
0154 if (event.accepted) {
0155 root.document.deleteSelectedItem()
0156 }
0157 }
0158
0159 Timer {
0160 id: commitChangesTimer
0161 interval: 250
0162 onTriggered: root.document.selectedItem.commitChanges()
0163 }
0164
0165 Connections {
0166 target: root.document
0167 function onSelectedItemWrapperChanged() {
0168 commitChangesTimer.stop()
0169 }
0170 }
0171
0172 leftInset: -background.effectiveStrokeWidth
0173 rightInset: -background.effectiveStrokeWidth
0174 topInset: -background.effectiveStrokeWidth
0175 bottomInset: -background.effectiveStrokeWidth
0176 background: SelectionBackground {
0177 zoom: root.viewport.effectiveZoom
0178 }
0179
0180 FontMetrics {
0181 id: fontMetrics
0182 font: textField.font
0183 }
0184
0185 Rectangle {
0186 id: handle
0187 implicitHeight: fontMetrics.height % 2 ? fontMetrics.height + 1 : fontMetrics.height
0188 implicitWidth: implicitHeight
0189 anchors.left: textField.effectiveHorizontalAlignment === TextInput.AlignRight ?
0190 parent.right : undefined
0191 anchors.right: textField.effectiveHorizontalAlignment === TextInput.AlignLeft ?
0192 parent.left : undefined
0193 anchors.margins: Kirigami.Units.mediumSpacing
0194 radius: height / 2
0195 color: Kirigami.Theme.backgroundColor
0196 Kirigami.Icon {
0197 height: Kirigami.Units.iconSizes.roundedIconSize(parent.height)
0198 width: height
0199 anchors.centerIn: parent
0200 source: "transform-move"
0201 }
0202 DragHandler {
0203 id: dragHandler
0204 target: null
0205 cursorShape: Qt.SizeAllCursor
0206 dragThreshold: 0
0207 onActiveTranslationChanged: if (active) {
0208 let dx = activeTranslation.x / viewport.effectiveZoom
0209 let dy = activeTranslation.y / viewport.effectiveZoom
0210 root.document.selectedItem.transform(dx, dy)
0211 }
0212 onActiveChanged: if (!active) {
0213 root.document.selectedItem.commitChanges()
0214 }
0215 }
0216 TapHandler {
0217 cursorShape: Qt.SizeAllCursor
0218 }
0219 HoverHandler {
0220 cursorShape: Qt.SizeAllCursor
0221 }
0222 }
0223 Component.onCompleted: forceActiveFocus()
0224 }
0225 }
0226