Compare commits
1 Commits
v25.08.2
...
work/carl/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29cc585b06 |
@@ -82,7 +82,7 @@ set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
|||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
)
|
)
|
||||||
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels SyntaxHighlighting)
|
||||||
set_package_properties(KF${QT_MAJOR_VERSION} PROPERTIES
|
set_package_properties(KF${QT_MAJOR_VERSION} PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ add_library(neochat STATIC
|
|||||||
mediasizehelper.h
|
mediasizehelper.h
|
||||||
eventhandler.cpp
|
eventhandler.cpp
|
||||||
enums/delegatetype.h
|
enums/delegatetype.h
|
||||||
|
messageformatter.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_qt_declare_logging_category(neochat
|
ecm_qt_declare_logging_category(neochat
|
||||||
@@ -199,7 +200,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
||||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient${QUOTIENT_SUFFIX} cmark::cmark QCoro::Core)
|
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 Qt::Xml KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels KF${QT_MAJOR_VERSION}::SyntaxHighlighting Quotient${QUOTIENT_SUFFIX} cmark::cmark QCoro::Core)
|
||||||
|
|
||||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,8 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "messageformatter.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
||||||
@@ -228,6 +230,12 @@ int main(int argc, char *argv[])
|
|||||||
Login *login = new Login();
|
Login *login = new Login();
|
||||||
UrlHelper urlHelper;
|
UrlHelper urlHelper;
|
||||||
|
|
||||||
|
MessageFormatter formatter;
|
||||||
|
// formatter.formatInternal("<p>hrrejoire</p>\n<pre><code class=\"language-js\">var i = 0; i++; function\n</code></pre>\n<p>rekore</p>\n", new
|
||||||
|
// QTextDocument);
|
||||||
|
|
||||||
|
// return 0;
|
||||||
|
|
||||||
#ifdef HAVE_COLORSCHEME
|
#ifdef HAVE_COLORSCHEME
|
||||||
ColorSchemer colorScheme;
|
ColorSchemer colorScheme;
|
||||||
qmlRegisterSingletonInstance<ColorSchemer>("org.kde.neochat", 1, 0, "ColorSchemer", &colorScheme);
|
qmlRegisterSingletonInstance<ColorSchemer>("org.kde.neochat", 1, 0, "ColorSchemer", &colorScheme);
|
||||||
@@ -236,6 +244,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
qmlRegisterSingletonInstance<MessageFormatter>("org.kde.neochat", 1, 0, "MessageFormatter", &formatter);
|
||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance());
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance());
|
||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "NotificationsManager", &NotificationsManager::instance());
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "NotificationsManager", &NotificationsManager::instance());
|
||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard);
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard);
|
||||||
|
|||||||
133
src/messageformatter.cpp
Normal file
133
src/messageformatter.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
|
||||||
|
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "messageformatter.h"
|
||||||
|
|
||||||
|
#include <QDomDocument>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QPalette>
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQmlProperty>
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include <QTextDocumentFragment>
|
||||||
|
|
||||||
|
#include <KSyntaxHighlighting/definition.h>
|
||||||
|
#include <KSyntaxHighlighting/repository.h>
|
||||||
|
#include <KSyntaxHighlighting/syntaxhighlighter.h>
|
||||||
|
#include <KSyntaxHighlighting/theme.h>
|
||||||
|
|
||||||
|
QTextDocumentFragment copyTextLayoutFrom(QTextDocument *document)
|
||||||
|
{
|
||||||
|
QTextCursor sourceCursor(document);
|
||||||
|
sourceCursor.select(QTextCursor::Document);
|
||||||
|
|
||||||
|
QTextDocument helper;
|
||||||
|
|
||||||
|
// copy the content fragment from the source document into our helper document
|
||||||
|
QTextCursor curs(&helper);
|
||||||
|
curs.insertFragment(sourceCursor.selection());
|
||||||
|
curs.select(QTextCursor::Document);
|
||||||
|
|
||||||
|
// not sure why, but fonts get lost. since this is for codeblocks, we can
|
||||||
|
// just force the mono font. anyone copying this code would probably want
|
||||||
|
// to fix the problem proper if it's not also for codeblocks.
|
||||||
|
const auto fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||||
|
|
||||||
|
const int docStart = sourceCursor.selectionStart();
|
||||||
|
const int docEnd = helper.characterCount() - 1;
|
||||||
|
|
||||||
|
// since the copied fragment above lost the qsyntaxhighlighter stuff,
|
||||||
|
// we gotta go through the qtextlayout and apply those styles to the
|
||||||
|
// document
|
||||||
|
const auto end = document->findBlock(sourceCursor.selectionEnd()).next();
|
||||||
|
for (auto current = document->findBlock(docStart); current.isValid() && current != end; current = current.next()) {
|
||||||
|
const auto layout = current.layout();
|
||||||
|
|
||||||
|
// iterate through the formats, applying them to our document
|
||||||
|
for (const auto &span : layout->formats()) {
|
||||||
|
const int start = current.position() + span.start - docStart;
|
||||||
|
const int end = start + span.length;
|
||||||
|
|
||||||
|
curs.setPosition(qMax(start, 0));
|
||||||
|
curs.setPosition(qMin(end, docEnd), QTextCursor::KeepAnchor);
|
||||||
|
|
||||||
|
auto fmt = span.format;
|
||||||
|
fmt.setFont(fixedFont);
|
||||||
|
|
||||||
|
curs.setCharFormat(fmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QTextDocumentFragment(&helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextDocumentFragment highlight(const QString &code, const QString &language)
|
||||||
|
{
|
||||||
|
using namespace KSyntaxHighlighting;
|
||||||
|
|
||||||
|
static Repository repo;
|
||||||
|
|
||||||
|
auto theme = repo.themeForPalette(QGuiApplication::palette());
|
||||||
|
auto definition = repo.definitionForFileName(QLatin1String("file.") + language);
|
||||||
|
|
||||||
|
QTextDocument doku(code);
|
||||||
|
|
||||||
|
QScopedPointer<SyntaxHighlighter> highlighter(new SyntaxHighlighter(&doku));
|
||||||
|
highlighter->setTheme(theme);
|
||||||
|
highlighter->setDefinition(definition);
|
||||||
|
|
||||||
|
return copyTextLayoutFrom(&doku);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool extractCodeBlock(QTextCursor cursor, QDomElement element)
|
||||||
|
{
|
||||||
|
const auto codeNode = element.firstChild();
|
||||||
|
if (!codeNode.isNull()) {
|
||||||
|
const auto code = codeNode.toElement();
|
||||||
|
if (!code.isNull() && code.tagName() == QLatin1String("code")) {
|
||||||
|
QString lang;
|
||||||
|
auto langClass = code.attribute(QLatin1String("class"), QLatin1String("none"));
|
||||||
|
if (langClass != QLatin1String("none") && langClass.startsWith(QLatin1String("language-"))) {
|
||||||
|
lang = langClass.remove(0, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lang.isNull()) {
|
||||||
|
cursor.insertFragment(highlight(code.text(), lang));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageFormatter::formatInternal(const QString &messageBody, QTextDocument *document)
|
||||||
|
{
|
||||||
|
QTextCursor curs(document);
|
||||||
|
QDomDocument doc(QLatin1String("htmlement"));
|
||||||
|
doc.setContent(QStringLiteral("<div>%1</div>").arg(messageBody));
|
||||||
|
QDomElement docElem = doc.documentElement();
|
||||||
|
QDomNode n = docElem.firstChild();
|
||||||
|
while (!n.isNull()) {
|
||||||
|
QDomElement e = n.toElement();
|
||||||
|
if (!e.isNull()) {
|
||||||
|
if (e.tagName() != QLatin1String("pre") || !extractCodeBlock(curs, e)) {
|
||||||
|
QString outText;
|
||||||
|
QTextStream out(&outText);
|
||||||
|
e.save(out, 0);
|
||||||
|
curs.insertHtml(outText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = n.nextSibling();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EMIT document->contentsChanged();
|
||||||
|
return document->toHtml();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageFormatter::format(const QString &messageBody, QQuickTextDocument *doc, QQuickItem *item)
|
||||||
|
{
|
||||||
|
QColor linkColor = QQmlProperty(item, QLatin1String("Kirigami.Theme.linkColor"), qmlContext(item)).read().value<QColor>();
|
||||||
|
|
||||||
|
return formatInternal(messageBody, doc->textDocument());
|
||||||
|
}
|
||||||
13
src/messageformatter.h
Normal file
13
src/messageformatter.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QQuickTextDocument>
|
||||||
|
|
||||||
|
class MessageFormatter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE QString format(const QString &messageBody, QQuickTextDocument *doc, QQuickItem *item);
|
||||||
|
Q_INVOKABLE QString formatInternal(const QString &messageBody, QTextDocument *doc);
|
||||||
|
};
|
||||||
@@ -44,6 +44,7 @@ TextEdit {
|
|||||||
property bool spoilerRevealed: !hasSpoiler.test(textMessage)
|
property bool spoilerRevealed: !hasSpoiler.test(textMessage)
|
||||||
|
|
||||||
ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage))
|
ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage))
|
||||||
|
onTextMessageChanged: text = MessageFormatter.format(textMessage, contentLabel.textDocument, contentLabel)
|
||||||
|
|
||||||
persistentSelection: true
|
persistentSelection: true
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ TextEdit {
|
|||||||
Controller.forceRefreshTextDocument(root.textDocument, root)
|
Controller.forceRefreshTextDocument(root.textDocument, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
text: "<style>
|
text: "<style>
|
||||||
table {
|
table {
|
||||||
width:100%;
|
width:100%;
|
||||||
@@ -84,7 +86,11 @@ a{
|
|||||||
background: " + Kirigami.Theme.textColor + ";
|
background: " + Kirigami.Theme.textColor + ";
|
||||||
}
|
}
|
||||||
" : "") + "
|
" : "") + "
|
||||||
|
<<<<<<< HEAD:src/qml/Component/Timeline/RichLabel.qml
|
||||||
</style>" + textMessage
|
</style>" + textMessage
|
||||||
|
=======
|
||||||
|
</style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + textMessage + (isEdited ? (" <span style=\"color: " + Kirigami.Theme.disabledTextColor + "\">" + "<span style='font-size: " + Kirigami.Theme.defaultFont.pixelSize +"px'>" + i18n(" (edited)") + "</span>") : "")
|
||||||
|
*/
|
||||||
|
|
||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||||
|
|||||||
Reference in New Issue
Block a user