MessageSource Line Numbers
Create a model for getting line numbers from a QQuickTextDocument and then add them to the MessageSource page
This commit is contained in:
@@ -148,6 +148,8 @@ add_library(neochat STATIC
|
|||||||
models/itinerarymodel.h
|
models/itinerarymodel.h
|
||||||
proxycontroller.cpp
|
proxycontroller.cpp
|
||||||
proxycontroller.h
|
proxycontroller.h
|
||||||
|
models/linemodel.cpp
|
||||||
|
models/linemodel.h
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||||
|
|||||||
64
src/models/linemodel.cpp
Normal file
64
src/models/linemodel.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 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 "linemodel.h"
|
||||||
|
|
||||||
|
LineModel::LineModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickTextDocument *LineModel::document() const
|
||||||
|
{
|
||||||
|
return m_document;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineModel::setDocument(QQuickTextDocument *document)
|
||||||
|
{
|
||||||
|
if (document == m_document) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_document = document;
|
||||||
|
Q_EMIT documentChanged();
|
||||||
|
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant LineModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &row = index.row();
|
||||||
|
if (row < 0 || row > rowCount()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == LineHeightRole) {
|
||||||
|
auto textDoc = m_document->textDocument();
|
||||||
|
return int(textDoc->documentLayout()->blockBoundingRect(textDoc->findBlockByNumber(row)).height());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int LineModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent);
|
||||||
|
if (m_document == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_document->textDocument()->blockCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> LineModel::roleNames() const
|
||||||
|
{
|
||||||
|
return {{LineHeightRole, "docLineHeight"}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineModel::resetModel()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
80
src/models/linemodel.h
Normal file
80
src/models/linemodel.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 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 <QAbstractListModel>
|
||||||
|
#include <QAbstractTextDocumentLayout>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQuickTextDocument>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class LineModel
|
||||||
|
*
|
||||||
|
* A model to provide line info for a QQuickTextDocument.
|
||||||
|
*
|
||||||
|
* @sa QQuickTextDocument
|
||||||
|
*/
|
||||||
|
class LineModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The QQuickTextDocument that is being handled.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Defines the model roles.
|
||||||
|
*/
|
||||||
|
enum Roles {
|
||||||
|
LineHeightRole = Qt::UserRole + 1, /**< The delegate type of the message. */
|
||||||
|
};
|
||||||
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
|
explicit LineModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
[[nodiscard]] QQuickTextDocument *document() const;
|
||||||
|
void setDocument(QQuickTextDocument *document);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the given role value at the given index.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::data
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of rows in the model.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::rowCount
|
||||||
|
*/
|
||||||
|
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a mapping from Role enum values to role names.
|
||||||
|
*
|
||||||
|
* @sa Roles, QAbstractItemModel::roleNames()
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the model.
|
||||||
|
*
|
||||||
|
* This needs to be called when the QQuickTextDocument container changes width
|
||||||
|
* or height as this may change line heights due to wrapping.
|
||||||
|
*
|
||||||
|
* @sa QQuickTextDocument
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void resetModel();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void documentChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<QQuickTextDocument> m_document = nullptr;
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.syntaxhighlighting
|
import org.kde.syntaxhighlighting
|
||||||
@@ -11,6 +12,7 @@ import org.kde.syntaxhighlighting
|
|||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
Kirigami.Page {
|
Kirigami.Page {
|
||||||
|
id: root
|
||||||
property string sourceText
|
property string sourceText
|
||||||
|
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
@@ -21,6 +23,7 @@ Kirigami.Page {
|
|||||||
title: i18n("Event Source")
|
title: i18n("Event Source")
|
||||||
|
|
||||||
QQC2.ScrollView {
|
QQC2.ScrollView {
|
||||||
|
id: scrollView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
|
||||||
@@ -29,24 +32,71 @@ Kirigami.Page {
|
|||||||
|
|
||||||
QQC2.TextArea {
|
QQC2.TextArea {
|
||||||
id: sourceTextArea
|
id: sourceTextArea
|
||||||
text: sourceText
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
leftPadding: lineNumberColumn.width + lineNumberColumn.anchors.leftMargin + Kirigami.Units.smallSpacing * 2
|
||||||
|
|
||||||
|
text: root.sourceText
|
||||||
readOnly: true
|
readOnly: true
|
||||||
textFormat: TextEdit.PlainText
|
textFormat: TextEdit.PlainText
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
background: Rectangle {
|
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
|
||||||
Kirigami.Theme.inherit: false
|
|
||||||
color: Kirigami.Theme.backgroundColor
|
|
||||||
}
|
|
||||||
|
|
||||||
// opt-out of whatever spell checker a styled TextArea might come with
|
// opt-out of whatever spell checker a styled TextArea might come with
|
||||||
Kirigami.SpellCheck.enabled: false
|
Kirigami.SpellCheck.enabled: false
|
||||||
|
|
||||||
|
onWidthChanged: lineModel.resetModel()
|
||||||
|
onHeightChanged: lineModel.resetModel()
|
||||||
|
|
||||||
SyntaxHighlighter {
|
SyntaxHighlighter {
|
||||||
textEdit: sourceTextArea
|
textEdit: sourceTextArea
|
||||||
definition: "JSON"
|
definition: "JSON"
|
||||||
repository: Repository
|
repository: Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: lineNumberColumn
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: sourceTextArea.top
|
||||||
|
topMargin: sourceTextArea.topPadding
|
||||||
|
left: sourceTextArea.left
|
||||||
|
leftMargin: Kirigami.Units.smallSpacing
|
||||||
|
}
|
||||||
|
spacing: 0
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
model: LineModel {
|
||||||
|
id: lineModel
|
||||||
|
document: sourceTextArea.textDocument
|
||||||
|
}
|
||||||
|
delegate: QQC2.Label {
|
||||||
|
id: label
|
||||||
|
required property int index
|
||||||
|
required property int docLineHeight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: docLineHeight
|
||||||
|
topPadding: 1
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
text: index + 1
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
|
Kirigami.Theme.inherit: false
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.Separator {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: lineNumberColumn.width + lineNumberColumn.anchors.leftMargin + Kirigami.Units.smallSpacing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user