421 lines
16 KiB
QML
421 lines
16 KiB
QML
// SPDX-FileCopyrightText: 2025 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 QtCore
|
|
import QtQuick
|
|
import QtQuick.Controls as QQC2
|
|
import QtQuick.Layouts
|
|
|
|
import org.kde.kirigami as Kirigami
|
|
|
|
import org.kde.neochat.libneochat as LibNeoChat
|
|
import org.kde.neochat.messagecontent as MessageContent
|
|
|
|
pragma ComponentBehavior: Bound
|
|
|
|
RowLayout {
|
|
id: root
|
|
|
|
/**
|
|
* @brief The current room that user is viewing.
|
|
*/
|
|
required property LibNeoChat.NeoChatRoom room
|
|
|
|
required property MessageContent.ChatBarMessageContentModel contentModel
|
|
|
|
required property real maxAvailableWidth
|
|
|
|
readonly property real uncompressedImplicitWidth: boldButton.implicitWidth +
|
|
italicButton.implicitWidth +
|
|
extraTextFormatRow.implicitWidth +
|
|
listRow.implicitWidth +
|
|
styleButton.implicitWidth +
|
|
emojiButton.implicitWidth +
|
|
linkButton.implicitWidth +
|
|
root.spacing * 7 +
|
|
Kirigami.Units.gridUnit
|
|
|
|
readonly property real listCompressedImplicitWidth: boldButton.implicitWidth +
|
|
italicButton.implicitWidth +
|
|
extraTextFormatRow.implicitWidth +
|
|
compressedListButton.implicitWidth +
|
|
styleButton.uncompressedWidth +
|
|
emojiButton.implicitWidth +
|
|
linkButton.implicitWidth +
|
|
root.spacing * 7 +
|
|
Kirigami.Units.gridUnit
|
|
|
|
readonly property real extraTextCompressedImplicitWidth: boldButton.implicitWidth +
|
|
italicButton.implicitWidth +
|
|
compressedExtraTextFormatButton.implicitWidth +
|
|
compressedListButton.implicitWidth +
|
|
styleButton.uncompressedWidth +
|
|
emojiButton.implicitWidth +
|
|
linkButton.implicitWidth +
|
|
root.spacing * 7 +
|
|
Kirigami.Units.gridUnit
|
|
|
|
readonly property ChatButtonHelper chatButtonHelper: ChatButtonHelper {
|
|
textItem: root.contentModel.focusedTextItem
|
|
inQuote: root.contentModel.focusType == LibNeoChat.MessageComponentType.Quote
|
|
hasAttachment: root.contentModel.hasAttachment
|
|
}
|
|
|
|
signal clicked
|
|
|
|
QQC2.ToolButton {
|
|
id: boldButton
|
|
Shortcut {
|
|
sequence: "Ctrl+B"
|
|
onActivated: boldButton.clicked()
|
|
}
|
|
icon.name: "format-text-bold"
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
text: i18nc("@action:button", "Bold")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: root.chatButtonHelper.bold
|
|
onClicked: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.Bold);
|
|
root.clicked()
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
QQC2.ToolButton {
|
|
id: italicButton
|
|
Shortcut {
|
|
sequence: "Ctrl+I"
|
|
onActivated: italicButton.clicked()
|
|
}
|
|
icon.name: "format-text-italic"
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
text: i18nc("@action:button", "Italic")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: root.chatButtonHelper.italic
|
|
onClicked: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.Italic);
|
|
root.clicked()
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
RowLayout {
|
|
id: extraTextFormatRow
|
|
visible: root.maxAvailableWidth > root.listCompressedImplicitWidth
|
|
QQC2.ToolButton {
|
|
id: underlineButton
|
|
Shortcut {
|
|
sequence: "Ctrl+U"
|
|
onActivated: underlineButton.clicked()
|
|
}
|
|
icon.name: "format-text-underline"
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
text: i18nc("@action:button", "Underline")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: root.chatButtonHelper.underline
|
|
onClicked: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.Underline);
|
|
root.clicked();
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
QQC2.ToolButton {
|
|
icon.name: "format-text-strikethrough"
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
text: i18nc("@action:button", "Strikethrough")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: root.chatButtonHelper.strikethrough
|
|
onClicked: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.Strikethrough);
|
|
root.clicked()
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
}
|
|
QQC2.ToolButton {
|
|
id: compressedExtraTextFormatButton
|
|
visible: root.maxAvailableWidth < root.listCompressedImplicitWidth
|
|
icon.name: "dialog-text-and-font"
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
text: i18nc("@action:button", "Format Text")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
onClicked: {
|
|
let dialog = compressedTextFormatMenu.createObject(compressedExtraTextFormatButton) as QQC2.Menu
|
|
dialog.onClosed.connect(() => {
|
|
compressedExtraTextFormatButton.checked = false;
|
|
});
|
|
dialog.open();
|
|
compressedExtraTextFormatButton.checked = true;
|
|
}
|
|
|
|
Component {
|
|
id: compressedTextFormatMenu
|
|
QQC2.Menu {
|
|
y: -implicitHeight
|
|
|
|
QQC2.MenuItem {
|
|
icon.name: "format-text-underline"
|
|
text: i18nc("@action:button", "Underline")
|
|
checkable: true
|
|
checked: root.chatButtonHelper.underline
|
|
onTriggered: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.Underline);
|
|
root.clicked();
|
|
}
|
|
}
|
|
QQC2.MenuItem {
|
|
icon.name: "format-text-strikethrough"
|
|
text: i18nc("@action:button", "Strikethrough")
|
|
checkable: true
|
|
checked: root.chatButtonHelper.strikethrough
|
|
onTriggered: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.Strikethrough);
|
|
root.clicked();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
StyleButton {
|
|
id: styleButton
|
|
Layout.minimumWidth: compressed ? -1 : Kirigami.Units.gridUnit * 10 + Kirigami.Units.largeSpacing * 2
|
|
|
|
icon.name: "typewriter"
|
|
text: i18nc("@action:button", "Text Style")
|
|
style: root.chatButtonHelper.currentStyle
|
|
inQuote: root.contentModel.focusType == LibNeoChat.MessageComponentType.Quote
|
|
compressed: root.maxAvailableWidth < root.extraTextCompressedImplicitWidth
|
|
enabled: root.chatButtonHelper.styleFormatEnabled
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: styleMenu.visible
|
|
onClicked: {
|
|
if (styleMenu.visible) {
|
|
styleMenu.close();
|
|
return;
|
|
}
|
|
open = true;
|
|
styleMenu.open();
|
|
}
|
|
|
|
StylePicker {
|
|
id: styleMenu
|
|
width: styleButton.compressed ? implicitWidth : styleButton.width
|
|
chatContentModel: root.contentModel
|
|
chatButtonHelper: root.chatButtonHelper
|
|
inQuote: root.contentModel.focusType == LibNeoChat.MessageComponentType.Quote
|
|
|
|
onClosed: {
|
|
root.clicked()
|
|
styleButton.open = false;
|
|
}
|
|
}
|
|
}
|
|
QQC2.ToolButton {
|
|
id: emojiButton
|
|
visible: !Kirigami.Settings.isMobile
|
|
icon.name: "smiley"
|
|
text: i18n("Emojis & Stickers")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
|
|
onClicked: {
|
|
let dialog = (emojiDialog.createObject(root) as EmojiDialog).open();
|
|
}
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
QQC2.ToolTip.text: text
|
|
}
|
|
QQC2.ToolButton {
|
|
id: linkButton
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
icon.name: "insert-link-symbolic"
|
|
text: root.chatButtonHelper.currentLinkUrl.length > 0 ? i18nc("@action:button", "Edit link") : i18nc("@action:button", "Insert link")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
onClicked: {
|
|
let dialog = linkDialog.createObject(QQC2.Overlay.overlay, {
|
|
linkText: root.chatButtonHelper.currentLinkText,
|
|
linkUrl: root.chatButtonHelper.currentLinkUrl
|
|
})
|
|
dialog.onAccepted.connect(() => {
|
|
root.chatButtonHelper.updateLink(dialog.linkUrl, dialog.linkText)
|
|
root.clicked();
|
|
});
|
|
dialog.open();
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
RowLayout {
|
|
id: listRow
|
|
visible: root.maxAvailableWidth > root.uncompressedImplicitWidth
|
|
QQC2.ToolButton {
|
|
icon.name: "format-list-unordered"
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
text: i18nc("@action:button", "Unordered List")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: root.chatButtonHelper.unorderedList
|
|
onClicked: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.UnorderedList);
|
|
root.clicked();
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
QQC2.ToolButton {
|
|
icon.name: "format-list-ordered"
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
text: i18nc("@action:button", "Ordered List")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: root.chatButtonHelper.orderedList
|
|
onClicked: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.OrderedList);
|
|
root.clicked();
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
QQC2.ToolButton {
|
|
id: indentAction
|
|
icon.name: "format-indent-more"
|
|
enabled: root.chatButtonHelper.richFormatEnabled && root.chatButtonHelper.canIndentListMore
|
|
text: i18nc("@action:button", "Increase List Level")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
onClicked: {
|
|
root.chatButtonHelper.indentListMore();
|
|
root.clicked();
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
QQC2.ToolButton {
|
|
id: dedentAction
|
|
icon.name: "format-indent-less"
|
|
enabled: root.chatButtonHelper.richFormatEnabled && root.chatButtonHelper.canIndentListLess
|
|
text: i18nc("@action:button", "Decrease List Level")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
onClicked: {
|
|
root.chatButtonHelper.indentListLess();
|
|
root.clicked();
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
}
|
|
QQC2.ToolButton {
|
|
id: compressedListButton
|
|
enabled: root.chatButtonHelper.richFormatEnabled
|
|
visible: root.maxAvailableWidth < root.uncompressedImplicitWidth
|
|
icon.name: "format-list-unordered"
|
|
text: i18nc("@action:button", "List Style")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: compressedListMenu.visible
|
|
onClicked: {
|
|
compressedListMenu.open()
|
|
}
|
|
|
|
QQC2.Menu {
|
|
id: compressedListMenu
|
|
y: -implicitHeight
|
|
|
|
QQC2.MenuItem {
|
|
icon.name: "format-list-unordered"
|
|
text: i18nc("@action:button", "Unordered List")
|
|
onTriggered: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.UnorderedList);
|
|
root.clicked();
|
|
}
|
|
}
|
|
QQC2.MenuItem {
|
|
icon.name: "format-list-ordered"
|
|
text: i18nc("@action:button", "Ordered List")
|
|
onTriggered: {
|
|
root.chatButtonHelper.setFormat(LibNeoChat.RichFormat.OrderedList);
|
|
root.clicked();
|
|
}
|
|
}
|
|
QQC2.MenuItem {
|
|
icon.name: "format-indent-more"
|
|
text: i18nc("@action:button", "Increase List Level")
|
|
enabled: root.chatButtonHelper.canIndentListMore
|
|
onTriggered: {
|
|
root.chatButtonHelper.indentListMore();
|
|
root.clicked();
|
|
}
|
|
}
|
|
QQC2.MenuItem {
|
|
icon.name: "format-indent-less"
|
|
text: i18nc("@action:button", "Decrease List Level")
|
|
enabled: root.chatButtonHelper.canIndentListLess
|
|
onTriggered: {
|
|
root.chatButtonHelper.indentListLess();
|
|
root.clicked();
|
|
}
|
|
}
|
|
}
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
|
|
Component {
|
|
id: linkDialog
|
|
LinkDialog {}
|
|
}
|
|
|
|
Component {
|
|
id: emojiDialog
|
|
EmojiDialog {
|
|
x: root.width - width
|
|
y: -implicitHeight
|
|
|
|
modal: false
|
|
includeCustom: true
|
|
closeOnChosen: false
|
|
|
|
currentRoom: root.room
|
|
|
|
onChosen: emoji => {
|
|
root.chatButtonHelper.insertText(emoji);
|
|
close();
|
|
}
|
|
onClosed: if (emojiButton.checked) {
|
|
emojiButton.checked = false;
|
|
}
|
|
}
|
|
}
|
|
}
|