Files
neochat/imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml
2021-08-20 20:31:25 +00:00

260 lines
8.8 KiB
QML

/*
SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
pragma Singleton
import QtQuick 2.6
import QtQml 2.2
import QtQuick.Controls 2.15
import org.kde.kirigami 2.5 as Kirigami
Menu {
id: contextMenu
property Item target
property bool deselectWhenMenuClosed: true
property int restoredCursorPosition: 0
property int restoredSelectionStart
property int restoredSelectionEnd
property bool persistentSelectionSetting
property var spellcheckhighlighter: null
property var suggestions: ([])
Component.onCompleted: persistentSelectionSetting = persistentSelectionSetting // break binding
property var runOnMenuClose
parent: Overlay.overlay
function storeCursorAndSelection() {
contextMenu.restoredCursorPosition = target.cursorPosition;
contextMenu.restoredSelectionStart = target.selectionStart;
contextMenu.restoredSelectionEnd = target.selectionEnd;
}
// target is pressed with mouse
function targetClick(handlerPoint, newTarget, spellcheckhighlighter, mousePosition) {
if (handlerPoint.pressedButtons === Qt.RightButton) { // only accept just right click
if (contextMenu.visible) {
deselectWhenMenuClosed = false; // don't deselect text if menu closed by right click on textfield
dismiss();
} else {
contextMenu.target = newTarget;
contextMenu.target.persistentSelection = true; // persist selection when menu is opened
contextMenu.spellcheckhighlighter = spellcheckhighlighter
contextMenu.suggestions = spellcheckhighlighter.suggestions(mousePosition);
storeCursorAndSelection();
popup(contextMenu.target);
// slightly locate context menu away from mouse so no item is selected when menu is opened
x += 1
y += 1
}
} else {
dismiss();
}
}
// context menu keyboard key
function targetKeyPressed(event, newTarget) {
if (event.modifiers === Qt.NoModifier && event.key === Qt.Key_Menu) {
contextMenu.target = newTarget;
target.persistentSelection = true; // persist selection when menu is opened
storeCursorAndSelection();
popup(contextMenu.target);
}
}
readonly property bool targetIsPassword: target !== null && (target.echoMode === TextInput.PasswordEchoOnEdit || target.echoMode === TextInput.Password)
onAboutToShow: {
if (Overlay.overlay) {
let tempZ = 0
for (let i in Overlay.overlay.visibleChildren) {
tempZ = Math.max(tempZ, Overlay.overlay.visibleChildren[i].z)
}
z = tempZ + 1
}
}
// deal with whether or not text should be deselected
onClosed: {
// restore text field's original persistent selection setting
target.persistentSelection = persistentSelectionSetting
// deselect text field text if menu is closed not because of a right click on the text field
if (deselectWhenMenuClosed) {
target.deselect();
}
deselectWhenMenuClosed = true;
// restore cursor position
target.forceActiveFocus();
target.cursorPosition = restoredCursorPosition;
target.select(restoredSelectionStart, restoredSelectionEnd);
// run action
runOnMenuClose();
}
onOpened: {
runOnMenuClose = function() {};
}
Instantiator {
active: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.wordIsMisspelled
model: suggestions
delegate: MenuItem {
text: modelData
onClicked: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {
spellcheckhighlighter.replaceWord(modelData);
};
}
}
onObjectAdded: contextMenu.insertItem(0, object)
onObjectRemoved: contextMenu.removeItem(0)
}
MenuItem {
visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.wordIsMisspelled && suggestions.length === 0
action: Action {
text: spellcheckhighlighter ? i18nc("@action:inmenu", "No suggestions for %1", spellcheckhighlighter.wordUnderMouse) : ""
enabled: false
}
}
MenuSeparator {
visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.wordIsMisspelled
}
MenuItem {
visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.wordIsMisspelled
action: Action {
text: i18n("Add to dictionary")
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {
spellcheckhighlighter.addWordToDictionary(spellcheckhighlighter.wordUnderMouse)
};
}
}
}
MenuItem {
visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.wordIsMisspelled
action: Action {
text: i18n("Ignore")
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {
spellcheckhighlighter.ignoreWord(spellcheckhighlighter.wordUnderMouse)
};
}
}
}
MenuSeparator {
visible: target !== null && !target.readOnly && spellcheckhighlighter !== null && spellcheckhighlighter.wordIsMisspelled
}
MenuItem {
visible: target !== null && !target.readOnly
action: Action {
icon.name: "edit-undo-symbolic"
text: i18nc("@action:inmenu", "Undo")
shortcut: StandardKey.Undo
}
enabled: target !== null && target.canUndo
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {target.undo()};
}
}
MenuItem {
visible: target !== null && !target.readOnly
action: Action {
icon.name: "edit-redo-symbolic"
text: i18nc("@action:inmenu", "Redo")
shortcut: StandardKey.Redo
}
enabled: target !== null && target.canRedo
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {target.redo()};
}
}
MenuSeparator {
visible: target !== null && !target.readOnly
}
MenuItem {
visible: target !== null && !target.readOnly && !targetIsPassword
action: Action {
icon.name: "edit-cut-symbolic"
text: i18nc("@action:inmenu", "Cut")
shortcut: StandardKey.Cut
}
enabled: target !== null && target.selectedText
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {target.cut()}
}
}
MenuItem {
action: Action {
icon.name: "edit-copy-symbolic"
text: i18nc("@action:inmenu", "Copy")
shortcut: StandardKey.Copy
}
enabled: target !== null && target.selectedText
visible: !targetIsPassword
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {target.copy()}
}
}
MenuItem {
visible: target !== null && !target.readOnly
action: Action {
icon.name: "edit-paste-symbolic"
text: i18nc("@action:inmenu", "Paste")
shortcut: StandardKey.Paste
}
enabled: target !== null && target.canPaste
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {target.paste()};
}
}
MenuItem {
visible: target !== null && !target.readOnly
action: Action {
icon.name: "edit-delete-symbolic"
text: i18nc("@action:inmenu", "Delete")
shortcut: StandardKey.Delete
}
enabled: target !== null && target.selectedText
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {target.remove(target.selectionStart, target.selectionEnd)};
}
}
MenuSeparator {
visible: !targetIsPassword
}
MenuItem {
action: Action {
icon.name: "edit-select-all-symbolic"
text: i18nc("@action:inmenu", "Select All")
shortcut: StandardKey.SelectAll
}
visible: !targetIsPassword
onTriggered: {
deselectWhenMenuClosed = false;
runOnMenuClose = function() {target.selectAll()};
}
}
}