Add Quick Formatting Menu in the Chatbar
Add a menu to quickly apply text effects when selecting text in the chatbar. 
This commit is contained in:
@@ -139,7 +139,37 @@ QQC2.Control {
|
||||
currentRoom.chatBoxText = text
|
||||
}
|
||||
onCursorRectangleChanged: chatBarScrollView.ensureVisible(cursorRectangle)
|
||||
onSelectedTextChanged: {
|
||||
if (selectedText.length > 0) {
|
||||
quickFormatBar.selectionStart = selectionStart
|
||||
quickFormatBar.selectionEnd = selectionEnd
|
||||
quickFormatBar.open()
|
||||
}
|
||||
}
|
||||
|
||||
QuickFormatBar {
|
||||
id: quickFormatBar
|
||||
|
||||
x: textField.cursorRectangle.x
|
||||
y: textField.cursorRectangle.y - height
|
||||
|
||||
onFormattingSelected: chatBar.formatText(format, selectionStart, selectionEnd)
|
||||
}
|
||||
|
||||
Keys.onDeletePressed: {
|
||||
if (selectedText.length > 0) {
|
||||
remove(selectionStart, selectionEnd)
|
||||
} else {
|
||||
remove(cursorPosition, cursorPosition + 1)
|
||||
}
|
||||
if (textField.text == selectedText || textField.text.length <= 1) {
|
||||
currentRoom.sendTypingNotification(false)
|
||||
repeatTimer.stop()
|
||||
}
|
||||
if (quickFormatBar.visible) {
|
||||
quickFormatBar.close()
|
||||
}
|
||||
}
|
||||
Keys.onEnterPressed: {
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.complete()
|
||||
@@ -180,9 +210,14 @@ QQC2.Control {
|
||||
completionMenu.decrementIndex()
|
||||
} else if (event.key === Qt.Key_Down && completionMenu.visible) {
|
||||
completionMenu.incrementIndex()
|
||||
} else if (event.key === Qt.Key_Backspace && textField.text.length <= 1) {
|
||||
currentRoom.sendTypingNotification(false)
|
||||
repeatTimer.stop()
|
||||
} else if (event.key === Qt.Key_Backspace) {
|
||||
if (textField.text == selectedText || textField.text.length <= 1) {
|
||||
currentRoom.sendTypingNotification(false)
|
||||
repeatTimer.stop()
|
||||
}
|
||||
if (quickFormatBar.visible && selectedText.length > 0) {
|
||||
quickFormatBar.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onShortcutOverride: {
|
||||
@@ -393,4 +428,47 @@ QQC2.Control {
|
||||
currentRoom.chatBoxReplyId = "";
|
||||
messageSent()
|
||||
}
|
||||
|
||||
function formatText(format, selectionStart, selectionEnd) {
|
||||
let index = textField.cursorPosition;
|
||||
|
||||
/*
|
||||
* There cannot be white space at the beginning or end of the string for the
|
||||
* formatting to work so move the sectionStart and sectionEnd markers past any whitespace.
|
||||
*/
|
||||
let innerText = textField.text.substr(selectionStart, selectionEnd - selectionStart);
|
||||
if (innerText.charAt(innerText.length - 1) === " ") {
|
||||
let trimmedRightString = innerText.replace(/\s*$/,"");
|
||||
let trimDifference = innerText.length - trimmedRightString.length;
|
||||
selectionEnd -= trimDifference;
|
||||
}
|
||||
if (innerText.charAt(0) === " ") {
|
||||
let trimmedLeftString = innerText.replace(/^\s*/,"");
|
||||
let trimDifference = innerText.length - trimmedLeftString.length;
|
||||
selectionStart = selectionStart + trimDifference;
|
||||
}
|
||||
|
||||
let startText = textField.text.substr(0, selectionStart);
|
||||
// Needs updating with the new selectionStart and selectionEnd with white space trimmed.
|
||||
innerText = textField.text.substr(selectionStart, selectionEnd - selectionStart);
|
||||
let endText = textField.text.substr(selectionEnd);
|
||||
|
||||
textField.text = "";
|
||||
textField.text = startText + format.start + innerText + format.end + format.extra + endText;
|
||||
|
||||
/*
|
||||
* Put the cursor where it was when the popup was opened accounting for the
|
||||
* new markup.
|
||||
*
|
||||
* The exception is for a hyperlink where it is placed ready to start typing
|
||||
* the url.
|
||||
*/
|
||||
if (format.extra !== "") {
|
||||
textField.cursorPosition = selectionEnd + format.start.length + format.end.length;
|
||||
} else if (index == selectionStart) {
|
||||
textField.cursorPosition = index;
|
||||
} else {
|
||||
textField.cursorPosition = index + format.start.length + format.end.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
136
src/qml/Component/ChatBox/QuickFormatBar.qml
Normal file
136
src/qml/Component/ChatBox/QuickFormatBar.qml
Normal file
@@ -0,0 +1,136 @@
|
||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
QQC2.Popup {
|
||||
id: root
|
||||
|
||||
property var selectionStart
|
||||
property var selectionEnd
|
||||
|
||||
signal formattingSelected(var format, int selectionStart, int selectionEnd)
|
||||
|
||||
padding: 1
|
||||
|
||||
contentItem: Flow {
|
||||
QQC2.ToolButton {
|
||||
icon.name: "format-text-bold"
|
||||
text: i18n("Bold")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
const format = {
|
||||
start: "**",
|
||||
end: "**",
|
||||
extra: "",
|
||||
}
|
||||
formattingSelected(format, selectionStart, selectionEnd)
|
||||
root.close()
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: "format-text-italic"
|
||||
text: i18n("Italic")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
const format = {
|
||||
start: "*",
|
||||
end: "*",
|
||||
extra: "",
|
||||
}
|
||||
formattingSelected(format, selectionStart, selectionEnd)
|
||||
root.close()
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: "format-text-strikethrough"
|
||||
text: i18n("Strikethrough")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
const format = {
|
||||
start: "<del>",
|
||||
end: "</del>",
|
||||
extra: "",
|
||||
}
|
||||
formattingSelected(format, selectionStart, selectionEnd)
|
||||
root.close()
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: "format-text-code"
|
||||
text: i18n("Code block")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
const format = {
|
||||
start: "`",
|
||||
end: "`",
|
||||
extra: "",
|
||||
}
|
||||
formattingSelected(format, selectionStart, selectionEnd)
|
||||
root.close()
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: "format-text-blockquote"
|
||||
text: i18n("Quote")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
const format = {
|
||||
start: selectionStart == 0 ? ">" : "\n>",
|
||||
end: "\n\n",
|
||||
extra: "",
|
||||
}
|
||||
formattingSelected(format, selectionStart, selectionEnd)
|
||||
root.close()
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: "link"
|
||||
text: i18n("Insert link")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
const format = {
|
||||
start: "[",
|
||||
end: "](",
|
||||
extra: ")",
|
||||
}
|
||||
formattingSelected(format, selectionStart, selectionEnd)
|
||||
root.close()
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
<file alias="ReplyPane.qml">qml/Component/ChatBox/ReplyPane.qml</file>
|
||||
<file alias="CompletionMenu.qml">qml/Component/ChatBox/CompletionMenu.qml</file>
|
||||
<file alias="PieProgressBar.qml">qml/Component/ChatBox/PieProgressBar.qml</file>
|
||||
<file alias="QuickFormatBar.qml">qml/Component/ChatBox/QuickFormatBar.qml</file>
|
||||
<file alias="EmojiPicker.qml">qml/Component/Emoji/EmojiPicker.qml</file>
|
||||
<file alias="ReplyComponent.qml">qml/Component/Timeline/ReplyComponent.qml</file>
|
||||
<file alias="StateDelegate.qml">qml/Component/Timeline/StateDelegate.qml</file>
|
||||
|
||||
Reference in New Issue
Block a user