Warning, /frameworks/qqc2-desktop-style/org.kde.desktop/private/TextFieldContextMenu.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com> 0003 SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> 0004 SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 pragma Singleton 0010 0011 import QtQml.Models 0012 import QtQuick 0013 import QtQuick.Controls as QQC2 0014 import org.kde.kirigami as Kirigami 0015 import org.kde.sonnet as Sonnet 0016 0017 QQC2.Menu { 0018 id: root 0019 0020 property Item target 0021 property bool deselectWhenMenuClosed: true 0022 property int restoredCursorPosition: 0 0023 property int restoredSelectionStart 0024 property int restoredSelectionEnd 0025 property bool persistentSelectionSetting 0026 0027 // assuming that Instantiator::active is bound to target.Kirigami.SpellCheck.enabled 0028 property Instantiator/*<Sonnet.SpellcheckHighlighter>*/ spellcheckHighlighterInstantiator 0029 0030 // assuming that spellchecker's active state is not writable, use target.Kirigami.SpellCheck.enabled instead. 0031 readonly property Sonnet.SpellcheckHighlighter spellcheckHighlighter: 0032 spellcheckHighlighterInstantiator?.object as Sonnet.SpellcheckHighlighter 0033 0034 property /*list<string>*/var spellcheckSuggestions: [] 0035 0036 Component.onCompleted: persistentSelectionSetting = persistentSelectionSetting // break binding 0037 0038 property var runOnMenuClose: () => {} 0039 0040 function storeCursorAndSelection() { 0041 restoredCursorPosition = target.cursorPosition; 0042 restoredSelectionStart = target.selectionStart; 0043 restoredSelectionEnd = target.selectionEnd; 0044 } 0045 0046 // target is pressed with mouse 0047 function targetClick( 0048 handlerPoint, 0049 target, 0050 spellcheckHighlighterInstantiator, 0051 mousePosition, 0052 ) { 0053 if (!(target instanceof TextInput || target instanceof TextEdit)) { 0054 console.warn("Target not supported by standard context menu:", target); 0055 return; 0056 } 0057 if (handlerPoint.pressedButtons === Qt.RightButton) { // only accept just right click 0058 if (visible) { 0059 deselectWhenMenuClosed = false; // don't deselect text if menu closed by right click on textfield 0060 dismiss(); 0061 } else { 0062 this.target = target; 0063 target.persistentSelection = true; // persist selection when menu is opened 0064 0065 this.spellcheckHighlighterInstantiator = spellcheckHighlighterInstantiator; 0066 0067 spellcheckSuggestions = (spellcheckHighlighter && mousePosition) 0068 ? spellcheckHighlighter.suggestions(mousePosition) 0069 : []; 0070 0071 storeCursorAndSelection(); 0072 popup(target); 0073 // slightly locate context menu away from mouse so no item is selected when menu is opened 0074 x += 1 0075 y += 1 0076 } 0077 } else { 0078 dismiss(); 0079 } 0080 } 0081 0082 // context menu keyboard key 0083 function targetKeyPressed(event, target) { 0084 if (event.modifiers === Qt.NoModifier && event.key === Qt.Key_Menu) { 0085 this.target = target; 0086 target.persistentSelection = true; // persist selection when menu is opened 0087 storeCursorAndSelection(); 0088 popup(target); 0089 } 0090 } 0091 0092 function __hasSelectedText(): bool { 0093 return target !== null 0094 && target.selectedText !== ""; 0095 } 0096 0097 function __editable(): bool { 0098 return target !== null 0099 && !target.readOnly; 0100 } 0101 0102 function __hasSpellcheckCapability(): bool { 0103 return __editable() 0104 && spellcheckHighlighterInstantiator !== null; 0105 } 0106 0107 function __showSpellcheckActions(): bool { 0108 return __editable() 0109 && spellcheckHighlighter !== null 0110 && spellcheckHighlighter.active 0111 && spellcheckHighlighter.wordIsMisspelled; 0112 } 0113 0114 // Show actions which should normally be hidden for password field 0115 function __showPasswordRestrictedActions(): bool { 0116 return target !== null 0117 && target.echoMode !== TextInput.PasswordEchoOnEdit 0118 && target.echoMode !== TextInput.Password; 0119 } 0120 0121 // Show text editing actions which should normally be hidden for password field 0122 function __showPasswordRestrictedEditingActions(): bool { 0123 return __showPasswordRestrictedActions() && !target.readOnly; 0124 } 0125 0126 modal: true 0127 0128 // deal with whether text should be deselected 0129 onClosed: { 0130 // reset parent, so OverlayZStacking could refresh z order next time 0131 // this menu is about to open for the same item that might have been 0132 // reparented to a different popup. 0133 parent = null; 0134 0135 // restore text field's original persistent selection setting 0136 target.persistentSelection = persistentSelectionSetting 0137 // deselect text field text if menu is closed not because of a right click on the text field 0138 if (deselectWhenMenuClosed) { 0139 target.deselect(); 0140 } 0141 deselectWhenMenuClosed = true; 0142 0143 // restore cursor position 0144 target.forceActiveFocus(); 0145 target.cursorPosition = restoredCursorPosition; 0146 target.select(restoredSelectionStart, restoredSelectionEnd); 0147 0148 // run action, and free memory 0149 try { 0150 runOnMenuClose(); 0151 } catch (e) { 0152 console.error(e); 0153 console.trace(); 0154 } 0155 runOnMenuClose = () => {}; 0156 0157 // clean up spellchecker 0158 spellcheckHighlighterInstantiator = null; 0159 spellcheckSuggestions = []; 0160 } 0161 0162 onOpened: { 0163 runOnMenuClose = () => {}; 0164 } 0165 0166 Instantiator { 0167 active: root.__showSpellcheckActions() 0168 0169 model: root.spellcheckSuggestions 0170 delegate: QQC2.MenuItem { 0171 required property string modelData 0172 0173 text: modelData 0174 0175 onClicked: { 0176 root.deselectWhenMenuClosed = false; 0177 root.runOnMenuClose = () => { 0178 root.spellcheckHighlighter.replaceWord(modelData); 0179 }; 0180 } 0181 } 0182 onObjectAdded: (index, object) => { 0183 root.insertItem(0, object); 0184 } 0185 onObjectRemoved: (index, object) => { 0186 root.removeItem(object); 0187 } 0188 } 0189 0190 QQC2.MenuItem { 0191 visible: root.__showSpellcheckActions() && root.spellcheckSuggestions.length === 0 0192 action: QQC2.Action { 0193 enabled: false 0194 text: root.spellcheckHighlighter 0195 ? qsTr('No Suggestions for "%1"') 0196 .arg(root.spellcheckHighlighter.wordUnderMouse) 0197 : "" 0198 } 0199 } 0200 0201 QQC2.MenuSeparator { 0202 visible: root.__showSpellcheckActions() 0203 } 0204 0205 QQC2.MenuItem { 0206 visible: root.__showSpellcheckActions() 0207 action: QQC2.Action { 0208 text: root.spellcheckHighlighter 0209 ? qsTr('Add "%1" to Dictionary') 0210 .arg(root.spellcheckHighlighter.wordUnderMouse) 0211 : "" 0212 0213 onTriggered: { 0214 root.deselectWhenMenuClosed = false; 0215 root.runOnMenuClose = () => { 0216 root.spellcheckHighlighter.addWordToDictionary(root.spellcheckHighlighter.wordUnderMouse); 0217 }; 0218 } 0219 } 0220 } 0221 0222 QQC2.MenuItem { 0223 visible: root.__showSpellcheckActions() 0224 action: QQC2.Action { 0225 text: qsTr("Ignore") 0226 onTriggered: { 0227 root.deselectWhenMenuClosed = false; 0228 root.runOnMenuClose = () => { 0229 root.spellcheckHighlighter.ignoreWord(root.spellcheckHighlighter.wordUnderMouse); 0230 }; 0231 } 0232 } 0233 } 0234 0235 QQC2.MenuItem { 0236 visible: root.__hasSpellcheckCapability() 0237 0238 checkable: true 0239 checked: root.target?.Kirigami.SpellCheck.enabled ?? false 0240 text: qsTr("Spell Check") 0241 0242 onToggled: { 0243 if (root.target) { 0244 root.target.Kirigami.SpellCheck.enabled = checked; 0245 } 0246 } 0247 } 0248 0249 QQC2.MenuSeparator { 0250 visible: root.__hasSpellcheckCapability() 0251 && (root.__editable() || root.__showPasswordRestrictedActions()) 0252 } 0253 0254 QQC2.MenuItem { 0255 action: QQC2.Action { 0256 icon.name: "edit-undo-symbolic" 0257 text: qsTr("Undo") 0258 shortcut: StandardKey.Undo 0259 } 0260 visible: root.__showPasswordRestrictedEditingActions() 0261 enabled: root.target?.canUndo ?? false 0262 onTriggered: { 0263 root.deselectWhenMenuClosed = false; 0264 root.runOnMenuClose = () => { 0265 root.target.undo(); 0266 }; 0267 } 0268 } 0269 QQC2.MenuItem { 0270 action: QQC2.Action { 0271 icon.name: "edit-redo-symbolic" 0272 text: qsTr("Redo") 0273 shortcut: StandardKey.Redo 0274 } 0275 visible: root.__showPasswordRestrictedEditingActions() 0276 enabled: root.target?.canRedo ?? false 0277 onTriggered: { 0278 root.deselectWhenMenuClosed = false; 0279 root.runOnMenuClose = () => { 0280 root.target.redo(); 0281 }; 0282 } 0283 } 0284 QQC2.MenuSeparator { 0285 visible: root.__showPasswordRestrictedEditingActions() 0286 } 0287 QQC2.MenuItem { 0288 action: QQC2.Action { 0289 icon.name: "edit-cut-symbolic" 0290 text: qsTr("Cut") 0291 shortcut: StandardKey.Cut 0292 } 0293 visible: root.__showPasswordRestrictedEditingActions() 0294 enabled: root.__hasSelectedText() 0295 onTriggered: { 0296 root.deselectWhenMenuClosed = false; 0297 root.runOnMenuClose = () => { 0298 root.target.cut(); 0299 }; 0300 } 0301 } 0302 QQC2.MenuItem { 0303 action: QQC2.Action { 0304 icon.name: "edit-copy-symbolic" 0305 text: qsTr("Copy") 0306 shortcut: StandardKey.Copy 0307 } 0308 visible: root.__showPasswordRestrictedActions() 0309 enabled: root.__hasSelectedText() 0310 onTriggered: { 0311 root.deselectWhenMenuClosed = false; 0312 root.runOnMenuClose = () => { 0313 root.target.copy(); 0314 }; 0315 } 0316 } 0317 QQC2.MenuItem { 0318 action: QQC2.Action { 0319 icon.name: "edit-paste-symbolic" 0320 text: qsTr("Paste") 0321 shortcut: StandardKey.Paste 0322 } 0323 visible: root.__editable() 0324 enabled: target?.canPaste ?? false 0325 onTriggered: { 0326 root.deselectWhenMenuClosed = false; 0327 root.runOnMenuClose = () => { 0328 root.target.paste(); 0329 }; 0330 } 0331 } 0332 QQC2.MenuItem { 0333 action: QQC2.Action { 0334 icon.name: "edit-delete-symbolic" 0335 text: qsTr("Delete") 0336 shortcut: StandardKey.Delete 0337 } 0338 visible: root.__editable() 0339 enabled: root.__hasSelectedText() 0340 onTriggered: { 0341 root.deselectWhenMenuClosed = false; 0342 root.runOnMenuClose = () => { 0343 root.target.remove(root.target.selectionStart, root.target.selectionEnd); 0344 }; 0345 } 0346 } 0347 QQC2.MenuSeparator { 0348 visible: root.target !== null 0349 && (root.__editable() || root.__showPasswordRestrictedActions()) 0350 } 0351 QQC2.MenuItem { 0352 action: QQC2.Action { 0353 icon.name: "edit-select-all-symbolic" 0354 text: qsTr("Select All") 0355 shortcut: StandardKey.SelectAll 0356 } 0357 visible: root.target !== null 0358 onTriggered: { 0359 root.deselectWhenMenuClosed = false; 0360 root.runOnMenuClose = () => { 0361 root.target.selectAll(); 0362 }; 0363 } 0364 } 0365 }