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 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 pragma Singleton
0009 
0010 import QtQuick 2.6
0011 import QtQml 2.2
0012 import QtQuick.Controls 2.15
0013 import org.kde.kirigami 2.5 as Kirigami
0014 
0015 Menu {
0016     id: contextMenu
0017 
0018     property Item target
0019     property bool deselectWhenMenuClosed: true
0020     property int restoredCursorPosition: 0
0021     property int restoredSelectionStart
0022     property int restoredSelectionEnd
0023     property bool persistentSelectionSetting
0024     property var spellcheckhighlighter: null
0025     property var spellcheckhighlighterLoader: null
0026     property var suggestions: []
0027     Component.onCompleted: persistentSelectionSetting = persistentSelectionSetting // break binding
0028 
0029     property var runOnMenuClose: () => {}
0030 
0031     parent: Overlay.overlay
0032 
0033     function storeCursorAndSelection() {
0034         contextMenu.restoredCursorPosition = target.cursorPosition;
0035         contextMenu.restoredSelectionStart = target.selectionStart;
0036         contextMenu.restoredSelectionEnd = target.selectionEnd;
0037     }
0038 
0039     // target is pressed with mouse
0040     function targetClick(handlerPoint, newTarget, spellcheckhighlighter, mousePosition) {
0041         if (handlerPoint.pressedButtons === Qt.RightButton) { // only accept just right click
0042             if (contextMenu.visible) {
0043                 deselectWhenMenuClosed = false; // don't deselect text if menu closed by right click on textfield
0044                 dismiss();
0045             } else {
0046                 contextMenu.target = newTarget;
0047                 contextMenu.target.persistentSelection = true; // persist selection when menu is opened
0048                 contextMenu.spellcheckhighlighterLoader = spellcheckhighlighter;
0049                 if (spellcheckhighlighter && spellcheckhighlighter.active) {
0050                     contextMenu.spellcheckhighlighter = spellcheckhighlighter.item;
0051                     contextMenu.suggestions = mousePosition ? spellcheckhighlighter.item.suggestions(mousePosition) : [];
0052                 } else {
0053                     contextMenu.spellcheckhighlighter = null;
0054                     contextMenu.suggestions = [];
0055                 }
0056                 storeCursorAndSelection();
0057                 popup(contextMenu.target);
0058                 // slightly locate context menu away from mouse so no item is selected when menu is opened
0059                 x += 1
0060                 y += 1
0061             }
0062         } else {
0063             dismiss();
0064         }
0065     }
0066 
0067     // context menu keyboard key
0068     function targetKeyPressed(event, newTarget) {
0069         if (event.modifiers === Qt.NoModifier && event.key === Qt.Key_Menu) {
0070             contextMenu.target = newTarget;
0071             target.persistentSelection = true; // persist selection when menu is opened
0072             storeCursorAndSelection();
0073             popup(contextMenu.target);
0074         }
0075     }
0076 
0077     readonly property bool targetIsPassword: target !== null && (target.echoMode === TextInput.PasswordEchoOnEdit || target.echoMode === TextInput.Password)
0078 
0079     onAboutToShow: {
0080         if (Overlay.overlay) {
0081             let tempZ = 0
0082             for (let i in Overlay.overlay.visibleChildren) {
0083                 tempZ = Math.max(tempZ, Overlay.overlay.visibleChildren[i].z)
0084             }
0085             z = tempZ + 1
0086         }
0087     }
0088 
0089     // deal with whether or not text should be deselected
0090     onClosed: {
0091         // restore text field's original persistent selection setting
0092         target.persistentSelection = persistentSelectionSetting
0093         // deselect text field text if menu is closed not because of a right click on the text field
0094         if (deselectWhenMenuClosed) {
0095             target.deselect();
0096         }
0097         deselectWhenMenuClosed = true;
0098 
0099         // restore cursor position
0100         target.forceActiveFocus();
0101         target.cursorPosition = restoredCursorPosition;
0102         target.select(restoredSelectionStart, restoredSelectionEnd);
0103 
0104         // run action, and free memory
0105         runOnMenuClose();
0106         runOnMenuClose = () => {};
0107     }
0108 
0109     onOpened: {
0110         runOnMenuClose = () => {};
0111     }
0112 
0113     Instantiator {
0114         active: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.active && spellcheckhighlighter.wordIsMisspelled
0115         model: suggestions
0116         delegate: MenuItem {
0117             text: modelData
0118             onClicked: {
0119                 deselectWhenMenuClosed = false;
0120                 runOnMenuClose = () => spellcheckhighlighter.replaceWord(modelData);
0121             }
0122         }
0123         onObjectAdded: {
0124             contextMenu.insertItem(0, object)
0125         }
0126         onObjectRemoved: contextMenu.removeItem(0)
0127     }
0128 
0129     MenuItem {
0130         visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.active && spellcheckhighlighter.wordIsMisspelled && suggestions.length === 0
0131         action: Action {
0132             text: spellcheckhighlighter ? qsTr("No suggestions for \"%1\"").arg(spellcheckhighlighter.wordUnderMouse) : ''
0133             enabled: false
0134         }
0135     }
0136 
0137     MenuItem {
0138         visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.active && spellcheckhighlighter.wordIsMisspelled
0139         action: Action {
0140             text: spellcheckhighlighter ? qsTr("Add \"%1\" to dictionary").arg(spellcheckhighlighter.wordUnderMouse) : ''
0141             onTriggered: {
0142                 deselectWhenMenuClosed = false;
0143                 runOnMenuClose = () => spellcheckhighlighter.addWordToDictionary(spellcheckhighlighter.wordUnderMouse);
0144             }
0145         }
0146     }
0147 
0148     MenuItem {
0149         visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.active && spellcheckhighlighter.wordIsMisspelled
0150         action: Action {
0151             text: qsTr("Ignore")
0152             onTriggered: {
0153                 deselectWhenMenuClosed = false;
0154                 runOnMenuClose = () => spellcheckhighlighter.ignoreWord(spellcheckhighlighter.wordUnderMouse);
0155             }
0156         }
0157     }
0158 
0159     MenuItem {
0160         visible: target !== null && !target.readOnly && spellcheckhighlighterLoader && spellcheckhighlighterLoader.activable
0161         checkable: true
0162         checked: spellcheckhighlighter ? spellcheckhighlighter.active : false
0163         text: qsTr("Enable Spellchecker")
0164         onCheckedChanged: {
0165             spellcheckhighlighterLoader.active = checked;
0166             spellcheckhighlighter = spellcheckhighlighterLoader.item;
0167         }
0168     }
0169 
0170     MenuSeparator {
0171         visible: target !== null && !target.readOnly && ((spellcheckhighlighter !== null && spellcheckhighlighter.active && spellcheckhighlighter.wordIsMisspelled) || (spellcheckhighlighterLoader && spellcheckhighlighterLoader.activable))
0172     }
0173 
0174     MenuItem {
0175         visible: target !== null && !target.readOnly && !targetIsPassword
0176         action: Action {
0177             icon.name: "edit-undo-symbolic"
0178             text: qsTr("Undo")
0179             shortcut: StandardKey.Undo
0180         }
0181         enabled: target !== null && target.canUndo
0182         onTriggered: {
0183             deselectWhenMenuClosed = false;
0184             runOnMenuClose = () => target.undo();
0185         }
0186     }
0187     MenuItem {
0188         visible: target !== null && !target.readOnly && !targetIsPassword
0189         action: Action {
0190             icon.name: "edit-redo-symbolic"
0191             text: qsTr("Redo")
0192             shortcut: StandardKey.Redo
0193         }
0194         enabled: target !== null && target.canRedo
0195         onTriggered: {
0196             deselectWhenMenuClosed = false;
0197             runOnMenuClose = () => target.redo();
0198         }
0199     }
0200     MenuSeparator {
0201         visible: target !== null && !target.readOnly && !targetIsPassword
0202     }
0203     MenuItem {
0204         visible: target !== null && !target.readOnly && !targetIsPassword
0205         action: Action {
0206             icon.name: "edit-cut-symbolic"
0207             text: qsTr("Cut")
0208             shortcut: StandardKey.Cut
0209         }
0210         enabled: target !== null && target.selectedText
0211         onTriggered: {
0212             deselectWhenMenuClosed = false;
0213             runOnMenuClose = () => target.cut();
0214         }
0215     }
0216     MenuItem {
0217         action: Action {
0218             icon.name: "edit-copy-symbolic"
0219             text: qsTr("Copy")
0220             shortcut: StandardKey.Copy
0221         }
0222         enabled: target !== null && target.selectedText
0223         visible: !targetIsPassword
0224         onTriggered: {
0225             deselectWhenMenuClosed = false;
0226             runOnMenuClose = () => target.copy();
0227         }
0228     }
0229     MenuItem {
0230         visible: target !== null && !target.readOnly
0231         action: Action {
0232             icon.name: "edit-paste-symbolic"
0233             text: qsTr("Paste")
0234             shortcut: StandardKey.Paste
0235         }
0236         enabled: target !== null && target.canPaste
0237         onTriggered: {
0238             deselectWhenMenuClosed = false;
0239             runOnMenuClose = () => target.paste();
0240         }
0241     }
0242     MenuItem {
0243         visible: target !== null && !target.readOnly
0244         action: Action {
0245             icon.name: "edit-delete-symbolic"
0246             text: qsTr("Delete")
0247             shortcut: StandardKey.Delete
0248         }
0249         enabled: target !== null && target.selectedText
0250         onTriggered: {
0251             deselectWhenMenuClosed = false;
0252             runOnMenuClose = () => target.remove(target.selectionStart, target.selectionEnd);
0253         }
0254     }
0255     MenuSeparator {
0256         visible: !targetIsPassword
0257     }
0258     MenuItem {
0259         action: Action {
0260             icon.name: "edit-select-all-symbolic"
0261             text: qsTr("Select All")
0262             shortcut: StandardKey.SelectAll
0263         }
0264         visible: !targetIsPassword
0265         onTriggered: {
0266             deselectWhenMenuClosed = false;
0267             runOnMenuClose = () => target.selectAll();
0268         }
0269     }
0270 }