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