From dada3e300b915a1fa3b7fb9de5027f0fcb01a6e7 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Fri, 20 Aug 2021 20:31:25 +0000 Subject: [PATCH] Revert "Spellchecking with new Sonnet declarative API" This reverts commit 17bbc60f6f89d4da9234fda5c86b756c749fbc13 --- imports/NeoChat/Component/ChatBox/ChatBar.qml | 91 +++++- .../Component/ChatBox/CursorDelegate.qml | 65 +++++ .../Component/ChatBox/CursorHandle.qml | 98 +++++++ .../ChatBox/MobileTextActionsToolBar.qml | 77 ++++++ .../ChatBox/TextFieldContextMenu.qml | 259 ++++++++++++++++++ imports/NeoChat/Component/ChatBox/qmldir | 4 + imports/NeoChat/Page/SettingsPage.qml | 13 - res.qrc | 4 + 8 files changed, 595 insertions(+), 16 deletions(-) create mode 100644 imports/NeoChat/Component/ChatBox/CursorDelegate.qml create mode 100644 imports/NeoChat/Component/ChatBox/CursorHandle.qml create mode 100644 imports/NeoChat/Component/ChatBox/MobileTextActionsToolBar.qml create mode 100644 imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml diff --git a/imports/NeoChat/Component/ChatBox/ChatBar.qml b/imports/NeoChat/Component/ChatBox/ChatBar.qml index 1392d46ba..add0dc6e5 100644 --- a/imports/NeoChat/Component/ChatBox/ChatBar.qml +++ b/imports/NeoChat/Component/ChatBox/ChatBar.qml @@ -9,7 +9,8 @@ import QtQuick.Templates 2.15 as T import Qt.labs.platform 1.1 as Platform 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 ToolBar { @@ -68,7 +69,7 @@ ToolBar { font: inputField.font } - TextArea { + T.TextArea { id: inputField focus: true /* Some QQC2 styles will have their own predefined backgrounds for TextAreas. @@ -100,9 +101,16 @@ ToolBar { wrapMode: Text.Wrap readOnly: currentRoom.usesEncryption + palette: Kirigami.Theme.palette Kirigami.Theme.colorSet: Kirigami.Theme.View 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 selectionColor: Kirigami.Theme.highlightColor @@ -115,6 +123,65 @@ ToolBar { 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 { id: documentHandler document: inputField.textDocument @@ -124,6 +191,18 @@ ToolBar { 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 { id: timeoutTimer repeat: false @@ -164,6 +243,9 @@ ToolBar { } Keys.onPressed: { + // trigger if context menu button is pressed + TextFieldContextMenu.targetKeyPressed(event, inputField) + if (event.key === Qt.Key_PageDown) { switchRoomDown(); } else if (event.key === Qt.Key_PageUp) { @@ -247,7 +329,10 @@ ToolBar { chatBar.complete(); } + onPressed: MobileTextActionsToolBar.shouldBeVisible = true; + onTextChanged: { + MobileTextActionsToolBar.shouldBeVisible = false; timeoutTimer.restart() repeatTimer.start() currentRoom.cachedInput = text diff --git a/imports/NeoChat/Component/ChatBox/CursorDelegate.qml b/imports/NeoChat/Component/ChatBox/CursorDelegate.qml new file mode 100644 index 000000000..851daa052 --- /dev/null +++ b/imports/NeoChat/Component/ChatBox/CursorDelegate.qml @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2018 Marco Martin + * SPDX-FileCopyrightText: 2021 Noah Davis + * 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() + } + } +} + + diff --git a/imports/NeoChat/Component/ChatBox/CursorHandle.qml b/imports/NeoChat/Component/ChatBox/CursorHandle.qml new file mode 100644 index 000000000..a401f79af --- /dev/null +++ b/imports/NeoChat/Component/ChatBox/CursorHandle.qml @@ -0,0 +1,98 @@ +/* SPDX-FileCopyrightText: 2018 Marco Martin + * SPDX-FileCopyrightText: 2021 Noah Davis + * 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; + } + } + } + } +} diff --git a/imports/NeoChat/Component/ChatBox/MobileTextActionsToolBar.qml b/imports/NeoChat/Component/ChatBox/MobileTextActionsToolBar.qml new file mode 100644 index 000000000..a159ca8eb --- /dev/null +++ b/imports/NeoChat/Component/ChatBox/MobileTextActionsToolBar.qml @@ -0,0 +1,77 @@ +/* + SPDX-FileCopyrightText: 2018 Marco Martin + + 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(); + } + } + } +} + diff --git a/imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml b/imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml new file mode 100644 index 000000000..2c70f1e4f --- /dev/null +++ b/imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml @@ -0,0 +1,259 @@ +/* + SPDX-FileCopyrightText: 2020 Devin Lin + SPDX-FileCopyrightText: 2021 Carl Schwan + + 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()}; + } + } +} diff --git a/imports/NeoChat/Component/ChatBox/qmldir b/imports/NeoChat/Component/ChatBox/qmldir index 28b7a8a94..8f9b11f86 100644 --- a/imports/NeoChat/Component/ChatBox/qmldir +++ b/imports/NeoChat/Component/ChatBox/qmldir @@ -5,3 +5,7 @@ ReplyPane 1.0 ReplyPane.qml AttachmentPane 1.0 AttachmentPane.qml CompletionMenu 1.0 CompletionMenu.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 diff --git a/imports/NeoChat/Page/SettingsPage.qml b/imports/NeoChat/Page/SettingsPage.qml index 48fa1fdf7..be097e101 100644 --- a/imports/NeoChat/Page/SettingsPage.qml +++ b/imports/NeoChat/Page/SettingsPage.qml @@ -7,7 +7,6 @@ import QtQuick.Controls 2.15 as QQC2 import QtQuick.Layouts 1.15 import org.kde.kirigami 2.15 as Kirigami -import org.kde.sonnet 1.0 as Sonnet import org.kde.neochat 1.0 import NeoChat.Settings 1.0 @@ -66,11 +65,6 @@ Kirigami.ScrollablePage { iconName: "network-connect" 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 { text: i18n("About NeoChat") icon.name: "help-about" @@ -91,11 +85,4 @@ Kirigami.ScrollablePage { aboutData: Controller.aboutData } } - - Component { - id: spellCheckingPage - Sonnet.ConfigPage { - wideMode: pageSettingStack.wideMode - } - } } diff --git a/res.qrc b/res.qrc index 0ea107c60..4d4c16c2a 100644 --- a/res.qrc +++ b/res.qrc @@ -26,6 +26,10 @@ imports/NeoChat/Component/ChatBox/AttachmentPane.qml imports/NeoChat/Component/ChatBox/ReplyPane.qml imports/NeoChat/Component/ChatBox/CompletionMenu.qml + imports/NeoChat/Component/ChatBox/CursorHandle.qml + imports/NeoChat/Component/ChatBox/CursorDelegate.qml + imports/NeoChat/Component/ChatBox/MobileTextActionsToolBar.qml + imports/NeoChat/Component/ChatBox/TextFieldContextMenu.qml imports/NeoChat/Component/ChatBox/qmldir imports/NeoChat/Component/Emoji/EmojiPicker.qml imports/NeoChat/Component/Emoji/qmldir