Improve the style picker
This commit is contained in:
@@ -16,6 +16,7 @@ ecm_add_qml_module(Chatbar GENERATE_PLUGIN_SOURCE
|
|||||||
EmojiPicker.qml
|
EmojiPicker.qml
|
||||||
EmojiDialog.qml
|
EmojiDialog.qml
|
||||||
EmojiTonesPicker.qml
|
EmojiTonesPicker.qml
|
||||||
|
StylePicker.qml
|
||||||
ImageEditorPage.qml
|
ImageEditorPage.qml
|
||||||
VoiceMessageDialog.qml
|
VoiceMessageDialog.qml
|
||||||
ImageDialog.qml
|
ImageDialog.qml
|
||||||
@@ -23,4 +24,15 @@ ecm_add_qml_module(Chatbar GENERATE_PLUGIN_SOURCE
|
|||||||
LocationChooser.qml
|
LocationChooser.qml
|
||||||
NewPollDialog.qml
|
NewPollDialog.qml
|
||||||
TableDialog.qml
|
TableDialog.qml
|
||||||
|
SOURCES
|
||||||
|
styledelegatehelper.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(Chatbar PRIVATE ${CMAKE_BINARY_DIR})
|
||||||
|
target_link_libraries(Chatbar PRIVATE
|
||||||
|
Qt::Core
|
||||||
|
Qt::Quick
|
||||||
|
Qt::QuickControls2
|
||||||
|
KF6::Kirigami
|
||||||
|
LibNeoChat
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ QQC2.ToolBar {
|
|||||||
readonly property real uncompressedImplicitWidth: textFormatRow.implicitWidth +
|
readonly property real uncompressedImplicitWidth: textFormatRow.implicitWidth +
|
||||||
listRow.implicitWidth +
|
listRow.implicitWidth +
|
||||||
styleButton.implicitWidth +
|
styleButton.implicitWidth +
|
||||||
codeButton.implicitWidth +
|
|
||||||
emojiButton.implicitWidth +
|
emojiButton.implicitWidth +
|
||||||
linkButton.implicitWidth +
|
linkButton.implicitWidth +
|
||||||
sendRow.implicitWidth +
|
sendRow.implicitWidth +
|
||||||
@@ -48,7 +47,6 @@ QQC2.ToolBar {
|
|||||||
readonly property real listCompressedImplicitWidth: textFormatRow.implicitWidth +
|
readonly property real listCompressedImplicitWidth: textFormatRow.implicitWidth +
|
||||||
compressedListButton.implicitWidth +
|
compressedListButton.implicitWidth +
|
||||||
styleButton.implicitWidth +
|
styleButton.implicitWidth +
|
||||||
codeButton.implicitWidth +
|
|
||||||
emojiButton.implicitWidth +
|
emojiButton.implicitWidth +
|
||||||
linkButton.implicitWidth +
|
linkButton.implicitWidth +
|
||||||
sendRow.implicitWidth +
|
sendRow.implicitWidth +
|
||||||
@@ -59,7 +57,6 @@ QQC2.ToolBar {
|
|||||||
readonly property real textFormatCompressedImplicitWidth: compressedTextFormatButton.implicitWidth +
|
readonly property real textFormatCompressedImplicitWidth: compressedTextFormatButton.implicitWidth +
|
||||||
compressedListButton.implicitWidth +
|
compressedListButton.implicitWidth +
|
||||||
styleButton.implicitWidth +
|
styleButton.implicitWidth +
|
||||||
codeButton.implicitWidth +
|
|
||||||
emojiButton.implicitWidth +
|
emojiButton.implicitWidth +
|
||||||
linkButton.implicitWidth +
|
linkButton.implicitWidth +
|
||||||
sendRow.implicitWidth +
|
sendRow.implicitWidth +
|
||||||
@@ -348,86 +345,24 @@ QQC2.ToolBar {
|
|||||||
checkable: true
|
checkable: true
|
||||||
checked: styleMenu.visible
|
checked: styleMenu.visible
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
if (styleMenu.visible) {
|
||||||
|
styleMenu.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
styleMenu.open()
|
styleMenu.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Menu {
|
StylePicker {
|
||||||
id: styleMenu
|
id: styleMenu
|
||||||
y: -implicitHeight
|
chatContentModel: root.contentModel
|
||||||
|
|
||||||
QQC2.MenuItem {
|
onClosed: root.clicked()
|
||||||
text: i18nc("@item:inmenu no heading", "Paragraph")
|
|
||||||
onTriggered: root.contentModel.insertComponentAtCursor(LibNeoChat.MessageComponentType.Text);
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@item:inmenu heading level 1 (largest)", "Heading 1")
|
|
||||||
onTriggered: {
|
|
||||||
root.focusedDocumentHandler.style = LibNeoChat.ChatDocumentHandler.Heading1;
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@item:inmenu heading level 2", "Heading 2")
|
|
||||||
onTriggered: {
|
|
||||||
root.focusedDocumentHandler.style = LibNeoChat.ChatDocumentHandler.Heading2;
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@item:inmenu heading level 3", "Heading 3")
|
|
||||||
onTriggered: {
|
|
||||||
root.focusedDocumentHandler.style = LibNeoChat.ChatDocumentHandler.Heading3;
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@item:inmenu heading level 4", "Heading 4")
|
|
||||||
onTriggered: {
|
|
||||||
root.focusedDocumentHandler.style = LibNeoChat.ChatDocumentHandler.Heading4;
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@item:inmenu heading level 5", "Heading 5")
|
|
||||||
onTriggered: {
|
|
||||||
root.focusedDocumentHandler.style = LibNeoChat.ChatDocumentHandler.Heading5;
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@item:inmenu heading level 6 (smallest)", "Heading 6")
|
|
||||||
onTriggered: {
|
|
||||||
root.focusedDocumentHandler.style = LibNeoChat.ChatDocumentHandler.Heading6;
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@item:inmenu", "Quote")
|
|
||||||
onTriggered: {
|
|
||||||
root.contentModel.insertComponentAtCursor(LibNeoChat.MessageComponentType.Quote);
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ToolTip.text: text
|
QQC2.ToolTip.text: text
|
||||||
QQC2.ToolTip.visible: hovered
|
QQC2.ToolTip.visible: hovered
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
}
|
}
|
||||||
QQC2.ToolButton {
|
|
||||||
id: codeButton
|
|
||||||
icon.name: "format-text-code"
|
|
||||||
text: i18n("Code")
|
|
||||||
display: QQC2.AbstractButton.IconOnly
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.contentModel.insertComponentAtCursor(LibNeoChat.MessageComponentType.Code);
|
|
||||||
root.clicked();
|
|
||||||
}
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
}
|
|
||||||
Kirigami.Separator {
|
Kirigami.Separator {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.margins: 0
|
Layout.margins: 0
|
||||||
|
|||||||
121
src/chatbar/StylePicker.qml
Normal file
121
src/chatbar/StylePicker.qml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
QQC2.Popup {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property MessageContent.ChatBarMessageContentModel chatContentModel
|
||||||
|
readonly property LibNeoChat.ChatDocumentHandler focusedDocumentHandler: chatContentModel.focusedDocumentHandler
|
||||||
|
|
||||||
|
y: -implicitHeight
|
||||||
|
|
||||||
|
contentItem: ColumnLayout {
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: 9
|
||||||
|
|
||||||
|
delegate: QQC2.TextArea {
|
||||||
|
id: styleDelegate
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: Kirigami.Units.gridUnit * 7
|
||||||
|
Layout.minimumHeight: Kirigami.Units.gridUnit * 2
|
||||||
|
leftPadding: lineRow.visible ? lineRow.width + lineRow.anchors.leftMargin + Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
||||||
|
enabled: root.chatContentModel.focusType !== LibNeoChat.MessageComponentType.Code || styleDelegate.index === LibNeoChat.TextStyle.Paragraph || styleDelegate.index === LibNeoChat.TextStyle.Quote
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: false
|
||||||
|
|
||||||
|
onPressed: (event) => {
|
||||||
|
if (styleDelegate.index === LibNeoChat.TextStyle.Paragraph ||
|
||||||
|
styleDelegate.index === LibNeoChat.TextStyle.Code ||
|
||||||
|
styleDelegate.index === LibNeoChat.TextStyle.Quote
|
||||||
|
) {
|
||||||
|
root.chatContentModel.insertStyleAtCursor(styleDelegate.index);
|
||||||
|
} else {
|
||||||
|
root.focusedDocumentHandler.style = styleDelegate.index;
|
||||||
|
}
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: lineRow
|
||||||
|
anchors {
|
||||||
|
top: styleDelegate.top
|
||||||
|
bottom: styleDelegate.bottom
|
||||||
|
left: styleDelegate.left
|
||||||
|
leftMargin: Kirigami.Units.smallSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: styleDelegate.index === LibNeoChat.TextStyle.Code
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: "1"
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
|
||||||
|
font.family: "monospace"
|
||||||
|
}
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyleDelegateHelper {
|
||||||
|
textItem: styleDelegate
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
Kirigami.Theme.colorSet: styleDelegate.index === LibNeoChat.TextStyle.Quote ? Kirigami.Theme.Window : Kirigami.Theme.View
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
border {
|
||||||
|
width: 1
|
||||||
|
color: styleDelegate.hovered || (root.focusedDocumentHandler?.style ?? false) === styleDelegate.index ?
|
||||||
|
Kirigami.Theme.highlightColor :
|
||||||
|
Kirigami.ColorUtils.linearInterpolation(
|
||||||
|
Kirigami.Theme.backgroundColor,
|
||||||
|
Kirigami.Theme.textColor,
|
||||||
|
Kirigami.Theme.frameContrast
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Kirigami.ShadowedRectangle {
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
|
||||||
|
border {
|
||||||
|
width: 1
|
||||||
|
color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, Kirigami.Theme.frameContrast)
|
||||||
|
}
|
||||||
|
|
||||||
|
shadow {
|
||||||
|
size: Kirigami.Units.gridUnit
|
||||||
|
yOffset: 0
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/chatbar/styledelegatehelper.cpp
Normal file
88
src/chatbar/styledelegatehelper.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#include "styledelegatehelper.h"
|
||||||
|
|
||||||
|
#include <QQuickTextDocument>
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
|
#include "enums/textstyle.h"
|
||||||
|
|
||||||
|
StyleDelegateHelper::StyleDelegateHelper(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem *StyleDelegateHelper::textItem() const
|
||||||
|
{
|
||||||
|
return m_textItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StyleDelegateHelper::setTextItem(QQuickItem *textItem)
|
||||||
|
{
|
||||||
|
if (textItem == m_textItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_textItem = textItem;
|
||||||
|
|
||||||
|
if (m_textItem) {
|
||||||
|
if (document()) {
|
||||||
|
formatDocument();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT textItemChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextDocument *StyleDelegateHelper::document() const
|
||||||
|
{
|
||||||
|
if (!m_textItem) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto quickDocument = qvariant_cast<QQuickTextDocument *>(m_textItem->property("textDocument"));
|
||||||
|
return quickDocument ? quickDocument->textDocument() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StyleDelegateHelper::formatDocument()
|
||||||
|
{
|
||||||
|
if (!document()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cursor = QTextCursor(document());
|
||||||
|
if (cursor.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
cursor.select(QTextCursor::Document);
|
||||||
|
cursor.removeSelectedText();
|
||||||
|
const auto style = static_cast<TextStyle::Style>(m_textItem->property("index").toInt());
|
||||||
|
const auto string = TextStyle::styleString(style);
|
||||||
|
|
||||||
|
const int headingLevel = style <= 6 ? style : 0;
|
||||||
|
// Apparently, 5 is maximum for FontSizeAdjustment; otherwise level=1 and
|
||||||
|
// level=2 look the same
|
||||||
|
const int sizeAdjustment = headingLevel > 0 ? 5 - headingLevel : 0;
|
||||||
|
|
||||||
|
QTextBlockFormat blkfmt;
|
||||||
|
blkfmt.setHeadingLevel(headingLevel);
|
||||||
|
cursor.mergeBlockFormat(blkfmt);
|
||||||
|
|
||||||
|
QTextCharFormat chrfmt;
|
||||||
|
chrfmt.setFontWeight(headingLevel > 0 ? QFont::Bold : QFont::Normal);
|
||||||
|
chrfmt.setProperty(QTextFormat::FontSizeAdjustment, sizeAdjustment / 2);
|
||||||
|
if (style == TextStyle::Code) {
|
||||||
|
chrfmt.setFontFamilies({u"monospace"_s});
|
||||||
|
} else if (style == TextStyle::Quote) {
|
||||||
|
chrfmt.setFontItalic(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.mergeBlockCharFormat(chrfmt);
|
||||||
|
cursor.insertText(string);
|
||||||
|
cursor.endEditBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_styledelegatehelper.cpp"
|
||||||
36
src/chatbar/styledelegatehelper.h
Normal file
36
src/chatbar/styledelegatehelper.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
class QTextDocument;
|
||||||
|
|
||||||
|
class StyleDelegateHelper : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The QML text Item the ChatDocumentHandler is handling.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QQuickItem *textItem READ textItem WRITE setTextItem NOTIFY textItemChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit StyleDelegateHelper(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QQuickItem *textItem() const;
|
||||||
|
void setTextItem(QQuickItem *textItem);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void textItemChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<QQuickItem> m_textItem;
|
||||||
|
QTextDocument *document() const;
|
||||||
|
|
||||||
|
void formatDocument();
|
||||||
|
};
|
||||||
@@ -33,6 +33,7 @@ target_sources(LibNeoChat PRIVATE
|
|||||||
enums/pushrule.h
|
enums/pushrule.h
|
||||||
enums/roomsortparameter.cpp
|
enums/roomsortparameter.cpp
|
||||||
enums/roomsortorder.h
|
enums/roomsortorder.h
|
||||||
|
enums/textstyle.h
|
||||||
enums/timelinemarkreadcondition.h
|
enums/timelinemarkreadcondition.h
|
||||||
events/imagepackevent.cpp
|
events/imagepackevent.cpp
|
||||||
events/pollevent.cpp
|
events/pollevent.cpp
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "chatbartype.h"
|
#include "chatbartype.h"
|
||||||
#include "chatdocumenthandler_logging.h"
|
#include "chatdocumenthandler_logging.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
|
#include "textstyle.h"
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
@@ -1027,12 +1028,12 @@ int ChatDocumentHandler::currentListStyle() const
|
|||||||
return -textCursor().currentList()->format().style();
|
return -textCursor().currentList()->format().style();
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatDocumentHandler::Style ChatDocumentHandler::style() const
|
TextStyle::Style ChatDocumentHandler::style() const
|
||||||
{
|
{
|
||||||
return static_cast<Style>(textCursor().blockFormat().headingLevel());
|
return static_cast<TextStyle::Style>(textCursor().blockFormat().headingLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatDocumentHandler::setStyle(ChatDocumentHandler::Style style)
|
void ChatDocumentHandler::setStyle(TextStyle::Style style)
|
||||||
{
|
{
|
||||||
const int headingLevel = style <= 6 ? style : 0;
|
const int headingLevel = style <= 6 ? style : 0;
|
||||||
// Apparently, 5 is maximum for FontSizeAdjustment; otherwise level=1 and
|
// Apparently, 5 is maximum for FontSizeAdjustment; otherwise level=1 and
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
#include "enums/chatbartype.h"
|
#include "enums/chatbartype.h"
|
||||||
|
#include "enums/textstyle.h"
|
||||||
#include "models/completionmodel.h"
|
#include "models/completionmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "nestedlisthelper_p.h"
|
#include "nestedlisthelper_p.h"
|
||||||
@@ -110,7 +111,7 @@ class ChatDocumentHandler : public QObject
|
|||||||
Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY formatChanged)
|
Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY formatChanged)
|
||||||
Q_PROPERTY(bool strikethrough READ strikethrough WRITE setStrikethrough NOTIFY formatChanged)
|
Q_PROPERTY(bool strikethrough READ strikethrough WRITE setStrikethrough NOTIFY formatChanged)
|
||||||
|
|
||||||
Q_PROPERTY(ChatDocumentHandler::Style style READ style WRITE setStyle NOTIFY styleChanged)
|
Q_PROPERTY(TextStyle::Style style READ style WRITE setStyle NOTIFY styleChanged)
|
||||||
|
|
||||||
// Q_PROPERTY(bool canIndentList READ canIndentList NOTIFY cursorPositionChanged)
|
// Q_PROPERTY(bool canIndentList READ canIndentList NOTIFY cursorPositionChanged)
|
||||||
// Q_PROPERTY(bool canDedentList READ canDedentList NOTIFY cursorPositionChanged)
|
// Q_PROPERTY(bool canDedentList READ canDedentList NOTIFY cursorPositionChanged)
|
||||||
@@ -132,25 +133,6 @@ public:
|
|||||||
End,
|
End,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Enum to define available styles.
|
|
||||||
*
|
|
||||||
* @note The Paragraph and Heading values are intentially fixed to match heading
|
|
||||||
* level values returned by QTextBlockFormat::headingLevel().
|
|
||||||
*
|
|
||||||
* @sa QTextBlockFormat::headingLevel()
|
|
||||||
*/
|
|
||||||
enum Style {
|
|
||||||
Paragraph = 0,
|
|
||||||
Heading1 = 1,
|
|
||||||
Heading2 = 2,
|
|
||||||
Heading3 = 3,
|
|
||||||
Heading4 = 4,
|
|
||||||
Heading5 = 5,
|
|
||||||
Heading6 = 6,
|
|
||||||
};
|
|
||||||
Q_ENUM(Style);
|
|
||||||
|
|
||||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
ChatBarType::Type type() const;
|
ChatBarType::Type type() const;
|
||||||
@@ -218,8 +200,8 @@ public:
|
|||||||
bool canDedentList() const;
|
bool canDedentList() const;
|
||||||
int currentListStyle() const;
|
int currentListStyle() const;
|
||||||
|
|
||||||
Style style() const;
|
TextStyle::Style style() const;
|
||||||
void setStyle(Style style);
|
void setStyle(TextStyle::Style style);
|
||||||
|
|
||||||
// bool list() const;
|
// bool list() const;
|
||||||
// void setList(bool list);
|
// void setList(bool list);
|
||||||
|
|||||||
74
src/libneochat/enums/textstyle.h
Normal file
74
src/libneochat/enums/textstyle.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class TextStyle
|
||||||
|
*
|
||||||
|
* A class with the Style enum for available text styles.
|
||||||
|
*/
|
||||||
|
class TextStyle : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_UNCREATABLE("")
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Enum to define available styles.
|
||||||
|
*
|
||||||
|
* @note The Paragraph and Heading values are intentially fixed to match heading
|
||||||
|
* level values returned by QTextBlockFormat::headingLevel().
|
||||||
|
*
|
||||||
|
* @sa QTextBlockFormat::headingLevel()
|
||||||
|
*/
|
||||||
|
enum Style {
|
||||||
|
Paragraph = 0,
|
||||||
|
Heading1 = 1,
|
||||||
|
Heading2 = 2,
|
||||||
|
Heading3 = 3,
|
||||||
|
Heading4 = 4,
|
||||||
|
Heading5 = 5,
|
||||||
|
Heading6 = 6,
|
||||||
|
Code = 7,
|
||||||
|
Quote = 8,
|
||||||
|
};
|
||||||
|
Q_ENUM(Style);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translate the Kind enum value to a human readable string.
|
||||||
|
*
|
||||||
|
* @sa Kind
|
||||||
|
*/
|
||||||
|
static QString styleString(Style style)
|
||||||
|
{
|
||||||
|
switch (style) {
|
||||||
|
case Style::Paragraph:
|
||||||
|
return u"Paragraph"_s;
|
||||||
|
case Style::Heading1:
|
||||||
|
return u"Heading 1"_s;
|
||||||
|
case Style::Heading2:
|
||||||
|
return u"Heading 2"_s;
|
||||||
|
case Style::Heading3:
|
||||||
|
return u"Heading 3"_s;
|
||||||
|
case Style::Heading4:
|
||||||
|
return u"Heading 4"_s;
|
||||||
|
case Style::Heading5:
|
||||||
|
return u"Heading 5"_s;
|
||||||
|
case Style::Heading6:
|
||||||
|
return u"Heading 6"_s;
|
||||||
|
case Style::Code:
|
||||||
|
return u"Code"_s;
|
||||||
|
case Style::Quote:
|
||||||
|
return u"\"Quote\""_s;
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "chatdocumenthandler.h"
|
#include "chatdocumenthandler.h"
|
||||||
#include "enums/chatbartype.h"
|
#include "enums/chatbartype.h"
|
||||||
#include "enums/messagecomponenttype.h"
|
#include "enums/messagecomponenttype.h"
|
||||||
|
#include "enums/textstyle.h"
|
||||||
#include "messagecontentmodel.h"
|
#include "messagecontentmodel.h"
|
||||||
|
|
||||||
ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
||||||
@@ -277,11 +278,28 @@ ChatBarMessageContentModel::insertComponent(int row, MessageComponentType::Type
|
|||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatBarMessageContentModel::insertStyleAtCursor(TextStyle::Style style)
|
||||||
|
{
|
||||||
|
switch (style) {
|
||||||
|
case TextStyle::Paragraph:
|
||||||
|
insertComponentAtCursor(MessageComponentType::Text);
|
||||||
|
return;
|
||||||
|
case TextStyle::Code:
|
||||||
|
insertComponentAtCursor(MessageComponentType::Code);
|
||||||
|
return;
|
||||||
|
case TextStyle::Quote:
|
||||||
|
insertComponentAtCursor(MessageComponentType::Quote);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ChatBarMessageContentModel::insertComponentAtCursor(MessageComponentType::Type type)
|
void ChatBarMessageContentModel::insertComponentAtCursor(MessageComponentType::Type type)
|
||||||
{
|
{
|
||||||
if (m_components[m_currentFocusComponent.row()].type == type) {
|
if (m_components[m_currentFocusComponent.row()].type == type) {
|
||||||
if (type == MessageComponentType::Text && focusedDocumentHandler()) {
|
if (type == MessageComponentType::Text && focusedDocumentHandler()) {
|
||||||
focusedDocumentHandler()->setStyle(ChatDocumentHandler::Paragraph);
|
focusedDocumentHandler()->setStyle(TextStyle::Paragraph);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "chatdocumenthandler.h"
|
#include "chatdocumenthandler.h"
|
||||||
#include "enums/messagecomponenttype.h"
|
#include "enums/messagecomponenttype.h"
|
||||||
|
#include "enums/textstyle.h"
|
||||||
#include "messagecomponent.h"
|
#include "messagecomponent.h"
|
||||||
#include "models/messagecontentmodel.h"
|
#include "models/messagecontentmodel.h"
|
||||||
|
|
||||||
@@ -55,6 +56,8 @@ public:
|
|||||||
Q_INVOKABLE void refocusCurrentComponent() const;
|
Q_INVOKABLE void refocusCurrentComponent() const;
|
||||||
ChatDocumentHandler *focusedDocumentHandler() const;
|
ChatDocumentHandler *focusedDocumentHandler() const;
|
||||||
|
|
||||||
|
Q_INVOKABLE void insertStyleAtCursor(TextStyle::Style style);
|
||||||
|
|
||||||
Q_INVOKABLE void insertComponentAtCursor(MessageComponentType::Type type);
|
Q_INVOKABLE void insertComponentAtCursor(MessageComponentType::Type type);
|
||||||
|
|
||||||
Q_INVOKABLE void addAttachment(const QUrl &path);
|
Q_INVOKABLE void addAttachment(const QUrl &path);
|
||||||
|
|||||||
Reference in New Issue
Block a user