Revert "Spellchecking with new Sonnet declarative API"
This reverts commit 17bbc60f6f
This commit is contained in:
@@ -9,7 +9,8 @@ import QtQuick.Templates 2.15 as T
|
|||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import QtQuick.Window 2.15
|
import QtQuick.Window 2.15
|
||||||
|
|
||||||
import org.kde.kirigami 2.18 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
ToolBar {
|
ToolBar {
|
||||||
@@ -68,7 +69,7 @@ ToolBar {
|
|||||||
font: inputField.font
|
font: inputField.font
|
||||||
}
|
}
|
||||||
|
|
||||||
TextArea {
|
T.TextArea {
|
||||||
id: inputField
|
id: inputField
|
||||||
focus: true
|
focus: true
|
||||||
/* Some QQC2 styles will have their own predefined backgrounds for TextAreas.
|
/* Some QQC2 styles will have their own predefined backgrounds for TextAreas.
|
||||||
@@ -100,9 +101,16 @@ ToolBar {
|
|||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
readOnly: currentRoom.usesEncryption
|
readOnly: currentRoom.usesEncryption
|
||||||
|
|
||||||
|
palette: Kirigami.Theme.palette
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
Kirigami.SpellChecking.enabled: true
|
|
||||||
|
implicitWidth: Math.max(contentWidth + leftPadding + rightPadding,
|
||||||
|
implicitBackgroundWidth + leftInset + rightInset,
|
||||||
|
placeholder.implicitWidth + leftPadding + rightPadding)
|
||||||
|
implicitHeight: Math.max(contentHeight + topPadding + bottomPadding,
|
||||||
|
implicitBackgroundHeight + topInset + bottomInset,
|
||||||
|
placeholder.implicitHeight + topPadding + bottomPadding)
|
||||||
|
|
||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
selectionColor: Kirigami.Theme.highlightColor
|
selectionColor: Kirigami.Theme.highlightColor
|
||||||
@@ -115,6 +123,65 @@ ToolBar {
|
|||||||
|
|
||||||
selectByMouse: !Kirigami.Settings.tabletMode
|
selectByMouse: !Kirigami.Settings.tabletMode
|
||||||
|
|
||||||
|
cursorDelegate: Loader {
|
||||||
|
visible: inputField.activeFocus && !inputField.readOnly && inputField.selectionStart === inputField.selectionEnd
|
||||||
|
active: visible
|
||||||
|
sourceComponent: CursorDelegate { target: inputField }
|
||||||
|
}
|
||||||
|
|
||||||
|
CursorHandle {
|
||||||
|
id: selectionStartHandle
|
||||||
|
target: inputField
|
||||||
|
}
|
||||||
|
|
||||||
|
CursorHandle {
|
||||||
|
id: selectionEndHandle
|
||||||
|
target: inputField
|
||||||
|
isSelectionEnd: true
|
||||||
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
|
||||||
|
// unfortunately, taphandler's pressed event only triggers when the press is lifted
|
||||||
|
// we need to use the longpress signal since it triggers when the button is first pressed
|
||||||
|
longPressThreshold: 0
|
||||||
|
onLongPressed: TextFieldContextMenu.targetClick(point, inputField, spellcheckhighlighter, inputField.positionAt(point.position.x, point.position.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressAndHold: {
|
||||||
|
if (!Kirigami.Settings.tabletMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
forceActiveFocus();
|
||||||
|
cursorPosition = positionAt(event.x, event.y);
|
||||||
|
selectWord();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocusChanged: {
|
||||||
|
if (focus) {
|
||||||
|
MobileTextActionsToolBar.controlRoot = inputField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: placeholder
|
||||||
|
x: inputField.leftPadding
|
||||||
|
y: inputField.topPadding
|
||||||
|
width: inputField.width - (inputField.leftPadding + inputField.rightPadding)
|
||||||
|
height: inputField.height - (inputField.topPadding + inputField.bottomPadding)
|
||||||
|
|
||||||
|
text: inputField.placeholderText
|
||||||
|
font: inputField.font
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
horizontalAlignment: inputField.horizontalAlignment
|
||||||
|
verticalAlignment: inputField.verticalAlignment
|
||||||
|
visible: !inputField.length && !inputField.preeditText && (!inputField.activeFocus || inputField.horizontalAlignment !== Qt.AlignHCenter)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ChatDocumentHandler {
|
ChatDocumentHandler {
|
||||||
id: documentHandler
|
id: documentHandler
|
||||||
document: inputField.textDocument
|
document: inputField.textDocument
|
||||||
@@ -124,6 +191,18 @@ ToolBar {
|
|||||||
room: currentRoom ?? null
|
room: currentRoom ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpellcheckHighlighter {
|
||||||
|
id: spellcheckhighlighter
|
||||||
|
document: inputField.textDocument
|
||||||
|
cursorPosition: inputField.cursorPosition
|
||||||
|
selectionStart: inputField.selectionStart
|
||||||
|
selectionEnd: inputField.selectionEnd
|
||||||
|
onChangeCursorPosition: {
|
||||||
|
inputField.cursorPosition = start;
|
||||||
|
inputField.moveCursorSelection(end, TextEdit.SelectCharacters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: timeoutTimer
|
id: timeoutTimer
|
||||||
repeat: false
|
repeat: false
|
||||||
@@ -164,6 +243,9 @@ ToolBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
|
// trigger if context menu button is pressed
|
||||||
|
TextFieldContextMenu.targetKeyPressed(event, inputField)
|
||||||
|
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
switchRoomDown();
|
switchRoomDown();
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
@@ -247,7 +329,10 @@ ToolBar {
|
|||||||
chatBar.complete();
|
chatBar.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPressed: MobileTextActionsToolBar.shouldBeVisible = true;
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
|
MobileTextActionsToolBar.shouldBeVisible = false;
|
||||||
timeoutTimer.restart()
|
timeoutTimer.restart()
|
||||||
repeatTimer.start()
|
repeatTimer.start()
|
||||||
currentRoom.cachedInput = text
|
currentRoom.cachedInput = text
|
||||||
|
|||||||
65
imports/NeoChat/Component/ChatBox/CursorDelegate.qml
Normal file
65
imports/NeoChat/Component/ChatBox/CursorDelegate.qml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/* SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Window 2.15
|
||||||
|
import QtQuick.Templates 2.15
|
||||||
|
import org.kde.kirigami 2.14 as Kirigami
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property alias target: root.parent
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: cursorLine
|
||||||
|
property real previousX: 0
|
||||||
|
property real previousY: 0
|
||||||
|
parent: target
|
||||||
|
implicitWidth: target.cursorRectangle.width
|
||||||
|
implicitHeight: target.cursorRectangle.height
|
||||||
|
x: Math.floor(target.cursorRectangle.x)
|
||||||
|
y: Math.floor(target.cursorRectangle.y)
|
||||||
|
|
||||||
|
color: target.color
|
||||||
|
SequentialAnimation {
|
||||||
|
id: blinkAnimation
|
||||||
|
running: root.visible && Qt.styleHints.cursorFlashTime != 0 && target.selectionStart === target.selectionEnd
|
||||||
|
PropertyAction {
|
||||||
|
target: cursorLine
|
||||||
|
property: "opacity"
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
PauseAnimation {
|
||||||
|
duration: Qt.styleHints.cursorFlashTime/2
|
||||||
|
}
|
||||||
|
SequentialAnimation {
|
||||||
|
loops: Animation.Infinite
|
||||||
|
OpacityAnimator {
|
||||||
|
target: cursorLine
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
duration: Qt.styleHints.cursorFlashTime/2
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
OpacityAnimator {
|
||||||
|
target: cursorLine
|
||||||
|
from: 0
|
||||||
|
to: 1
|
||||||
|
duration: Qt.styleHints.cursorFlashTime/2
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.target
|
||||||
|
function onCursorPositionChanged() {
|
||||||
|
blinkAnimation.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
98
imports/NeoChat/Component/ChatBox/CursorHandle.qml
Normal file
98
imports/NeoChat/Component/ChatBox/CursorHandle.qml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/* SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Window 2.15
|
||||||
|
import QtQuick.Templates 2.15
|
||||||
|
import org.kde.kirigami 2.14 as Kirigami
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: root
|
||||||
|
property Item target
|
||||||
|
property bool isSelectionEnd: false
|
||||||
|
visible: Kirigami.Settings.tabletMode && target.activeFocus && (isSelectionEnd ? target.selectionStart !== target.selectionEnd : true)
|
||||||
|
active: visible
|
||||||
|
sourceComponent: Kirigami.ShadowedRectangle {
|
||||||
|
id: handle
|
||||||
|
property real selectionStartX: Math.floor(Qt.inputMethod.anchorRectangle.x + (Qt.inputMethod.cursorRectangle.width - width)/2)
|
||||||
|
property real selectionStartY: Math.floor(Qt.inputMethod.anchorRectangle.y + Qt.inputMethod.cursorRectangle.height + pointyBitVerticalOffset)
|
||||||
|
property real selectionEndX: Math.floor(Qt.inputMethod.cursorRectangle.x + (Qt.inputMethod.cursorRectangle.width - width)/2)
|
||||||
|
property real selectionEndY: Math.floor(Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height + pointyBitVerticalOffset)
|
||||||
|
property real pointyBitVerticalOffset: Math.abs(pointyBit.y*2)
|
||||||
|
parent: Overlay.overlay
|
||||||
|
x: isSelectionEnd ? selectionEndX : selectionStartX
|
||||||
|
y: isSelectionEnd ? selectionEndY : selectionStartY
|
||||||
|
|
||||||
|
// HACK: make it appear above most popups that show up in the
|
||||||
|
// overlay in case any of them use TextField or TextArea
|
||||||
|
z: 999
|
||||||
|
|
||||||
|
//opacity: target.activeFocus ? 1 : 0
|
||||||
|
implicitHeight: {
|
||||||
|
let h = Kirigami.Units.gridUnit
|
||||||
|
return h - (h % 2 == 0 ? 1 : 0)
|
||||||
|
}
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
radius: width/2
|
||||||
|
|
||||||
|
color: target.selectionColor
|
||||||
|
|
||||||
|
shadow {
|
||||||
|
color: Qt.rgba(0,0,0,0.2)
|
||||||
|
size: 3
|
||||||
|
yOffset: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: pointyBit
|
||||||
|
x: (parent.width - width)/2
|
||||||
|
y: -height/4 + 0.2 // magic number to get it to line up with the edge of the circle
|
||||||
|
implicitHeight: parent.implicitHeight/2
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
antialiasing: true
|
||||||
|
rotation: 45
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.ShadowedRectangle {
|
||||||
|
id: inner
|
||||||
|
visible: target.selectionStart !== target.selectionEnd && (handle.y < selectionStartY || handle.y < selectionEndY)
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Kirigami.Units.smallBorder
|
||||||
|
color: target.selectedTextColor
|
||||||
|
radius: height/2
|
||||||
|
Rectangle {
|
||||||
|
id: innerPointyBit
|
||||||
|
x: (parent.width - width)/2
|
||||||
|
y: -height/4 + 0.8 // magic number to get it to line up with the edge of the circle
|
||||||
|
implicitHeight: pointyBit.implicitHeight
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
antialiasing: true
|
||||||
|
rotation: 45
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
enabled: handle.visible
|
||||||
|
anchors.fill: parent
|
||||||
|
// preventStealing: true
|
||||||
|
onPositionChanged: {
|
||||||
|
let pos = mapToItem(root.target, mouse.x, mouse.y);
|
||||||
|
pos = root.target.positionAt(pos.x, pos.y - handle.height - handle.pointyBitVerticalOffset);
|
||||||
|
|
||||||
|
if (target.selectionStart !== target.selectionEnd) {
|
||||||
|
if (!isSelectionEnd) {
|
||||||
|
root.target.select(Math.min(pos, root.target.selectionEnd - 1), root.target.selectionEnd);
|
||||||
|
} else {
|
||||||
|
root.target.select(root.target.selectionStart, Math.max(pos, root.target.selectionStart + 1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.target.cursorPosition = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2018 Marco Martin <mart@kde.org>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick 2.1
|
||||||
|
import QtQuick.Layouts 1.2
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import org.kde.kirigami 2.5 as Kirigami
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property Item controlRoot
|
||||||
|
parent: controlRoot ? controlRoot.Window.contentItem : undefined
|
||||||
|
modal: false
|
||||||
|
focus: false
|
||||||
|
closePolicy: Popup.NoAutoClose
|
||||||
|
property bool shouldBeVisible: false
|
||||||
|
|
||||||
|
x: {
|
||||||
|
if (!controlRoot || !controlRoot.Window.contentItem) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Math.min(Math.max(0, controlRoot.mapToItem(root.parent, controlRoot.positionToRectangle(controlRoot.selectionStart).x, 0).x - root.width/2), controlRoot.Window.contentItem.width - root.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
y: {
|
||||||
|
if (!controlRoot || !controlRoot.Window.contentItem) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var desiredY = controlRoot.mapToItem(root.parent, 0, controlRoot.positionToRectangle(controlRoot.selectionStart).y).y - root.height;
|
||||||
|
|
||||||
|
if (desiredY >= 0) {
|
||||||
|
return Math.min(desiredY, controlRoot.Window.contentItem.height - root.height);
|
||||||
|
} else {
|
||||||
|
return Math.min(Math.max(0, controlRoot.mapToItem(root.parent, 0, controlRoot.positionToRectangle(controlRoot.selectionEnd).y + Math.round(Kirigami.Units.gridUnit*1.5)).y), controlRoot.Window.contentItem.height - root.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
visible: controlRoot ? shouldBeVisible && Qt.platform.os !== "android" && Kirigami.Settings.tabletMode && (controlRoot.selectedText.length > 0 || controlRoot.canPaste) : false
|
||||||
|
|
||||||
|
width: contentItem.implicitWidth + leftPadding + rightPadding
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
ToolButton {
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
icon.name: "edit-cut"
|
||||||
|
visible: controlRoot && controlRoot.selectedText.length > 0 && (!controlRoot.hasOwnProperty("echoMode") || controlRoot.echoMode === TextInput.Normal)
|
||||||
|
onClicked: {
|
||||||
|
controlRoot.cut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolButton {
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
icon.name: "edit-copy"
|
||||||
|
visible: controlRoot && controlRoot.selectedText.length > 0 && (!controlRoot.hasOwnProperty("echoMode") || controlRoot.echoMode === TextInput.Normal)
|
||||||
|
onClicked: {
|
||||||
|
controlRoot.copy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToolButton {
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
icon.name: "edit-paste"
|
||||||
|
visible: controlRoot && controlRoot.canPaste
|
||||||
|
onClicked: {
|
||||||
|
controlRoot.paste();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
259
imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml
Normal file
259
imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
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()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,3 +5,7 @@ ReplyPane 1.0 ReplyPane.qml
|
|||||||
AttachmentPane 1.0 AttachmentPane.qml
|
AttachmentPane 1.0 AttachmentPane.qml
|
||||||
CompletionMenu 1.0 CompletionMenu.qml
|
CompletionMenu 1.0 CompletionMenu.qml
|
||||||
EmojiPickerPane 1.0 EmojiPickerPane.qml
|
EmojiPickerPane 1.0 EmojiPickerPane.qml
|
||||||
|
singleton TextFieldContextMenu 1.0 TextFieldContextMenu.qml
|
||||||
|
CursorDelegate 1.0 CursorDelegate.qml
|
||||||
|
CursorHandle 1.0 CursorHandle.qml
|
||||||
|
singleton MobileTextActionsToolBar 1.0 MobileTextActionsToolBar.qml
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Controls 2.15 as QQC2
|
|||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
import org.kde.sonnet 1.0 as Sonnet
|
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
import NeoChat.Settings 1.0
|
import NeoChat.Settings 1.0
|
||||||
@@ -66,11 +65,6 @@ Kirigami.ScrollablePage {
|
|||||||
iconName: "network-connect"
|
iconName: "network-connect"
|
||||||
onTriggered: pageSettingStack.push("qrc:/imports/NeoChat/Page/DevicesPage.qml")
|
onTriggered: pageSettingStack.push("qrc:/imports/NeoChat/Page/DevicesPage.qml")
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
|
||||||
text: i18n("Spell Checking")
|
|
||||||
iconName: "tools-check-spelling"
|
|
||||||
onTriggered: pageSettingStack.push(spellCheckingPage)
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("About NeoChat")
|
text: i18n("About NeoChat")
|
||||||
icon.name: "help-about"
|
icon.name: "help-about"
|
||||||
@@ -91,11 +85,4 @@ Kirigami.ScrollablePage {
|
|||||||
aboutData: Controller.aboutData
|
aboutData: Controller.aboutData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: spellCheckingPage
|
|
||||||
Sonnet.ConfigPage {
|
|
||||||
wideMode: pageSettingStack.wideMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
4
res.qrc
4
res.qrc
@@ -26,6 +26,10 @@
|
|||||||
<file>imports/NeoChat/Component/ChatBox/AttachmentPane.qml</file>
|
<file>imports/NeoChat/Component/ChatBox/AttachmentPane.qml</file>
|
||||||
<file>imports/NeoChat/Component/ChatBox/ReplyPane.qml</file>
|
<file>imports/NeoChat/Component/ChatBox/ReplyPane.qml</file>
|
||||||
<file>imports/NeoChat/Component/ChatBox/CompletionMenu.qml</file>
|
<file>imports/NeoChat/Component/ChatBox/CompletionMenu.qml</file>
|
||||||
|
<file>imports/NeoChat/Component/ChatBox/CursorHandle.qml</file>
|
||||||
|
<file>imports/NeoChat/Component/ChatBox/CursorDelegate.qml</file>
|
||||||
|
<file>imports/NeoChat/Component/ChatBox/MobileTextActionsToolBar.qml</file>
|
||||||
|
<file>imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml</file>
|
||||||
<file>imports/NeoChat/Component/ChatBox/qmldir</file>
|
<file>imports/NeoChat/Component/ChatBox/qmldir</file>
|
||||||
<file>imports/NeoChat/Component/Emoji/EmojiPicker.qml</file>
|
<file>imports/NeoChat/Component/Emoji/EmojiPicker.qml</file>
|
||||||
<file>imports/NeoChat/Component/Emoji/qmldir</file>
|
<file>imports/NeoChat/Component/Emoji/qmldir</file>
|
||||||
|
|||||||
Reference in New Issue
Block a user