feat: add missing KF6 framework recipes

This commit is contained in:
2026-05-07 07:53:26 +01:00
parent d8d498f831
commit a69f479b52
2374 changed files with 2610246 additions and 0 deletions
@@ -0,0 +1,4 @@
if(TARGET Qt6::Widgets)
add_executable(codeeditor codeeditor.cpp main.cpp)
target_link_libraries(codeeditor Qt6::Widgets KF6SyntaxHighlighting)
endif()
@@ -0,0 +1,349 @@
/*
SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: MIT
*/
#include "codeeditor.h"
#include <KSyntaxHighlighting/Definition>
#include <KSyntaxHighlighting/FoldingRegion>
#include <KSyntaxHighlighting/SyntaxHighlighter>
#include <KSyntaxHighlighting/Theme>
#include <QActionGroup>
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QFontDatabase>
#include <QMenu>
#include <QPainter>
#include <QPalette>
class CodeEditorSidebar : public QWidget
{
Q_OBJECT
public:
explicit CodeEditorSidebar(CodeEditor *editor);
QSize sizeHint() const override;
protected:
void paintEvent(QPaintEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
CodeEditor *m_codeEditor;
};
CodeEditorSidebar::CodeEditorSidebar(CodeEditor *editor)
: QWidget(editor)
, m_codeEditor(editor)
{
}
QSize CodeEditorSidebar::sizeHint() const
{
return QSize(m_codeEditor->sidebarWidth(), 0);
}
void CodeEditorSidebar::paintEvent(QPaintEvent *event)
{
m_codeEditor->sidebarPaintEvent(event);
}
void CodeEditorSidebar::mouseReleaseEvent(QMouseEvent *event)
{
if (event->pos().x() >= width() - m_codeEditor->fontMetrics().lineSpacing()) {
auto block = m_codeEditor->blockAtPosition(event->pos().y());
if (!block.isValid() || !m_codeEditor->isFoldable(block)) {
return;
}
m_codeEditor->toggleFold(block);
}
QWidget::mouseReleaseEvent(event);
}
CodeEditor::CodeEditor(QWidget *parent)
: QPlainTextEdit(parent)
, m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(document()))
, m_sideBar(new CodeEditorSidebar(this))
{
setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
setTheme((palette().color(QPalette::Base).lightness() < 128) ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme)
: m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme));
connect(this, &QPlainTextEdit::blockCountChanged, this, &CodeEditor::updateSidebarGeometry);
connect(this, &QPlainTextEdit::updateRequest, this, &CodeEditor::updateSidebarArea);
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
updateSidebarGeometry();
highlightCurrentLine();
}
CodeEditor::~CodeEditor()
{
}
void CodeEditor::openFile(const QString &fileName)
{
QFile f(fileName);
if (!f.open(QFile::ReadOnly)) {
qWarning() << "Failed to open" << fileName << ":" << f.errorString();
return;
}
clear();
const auto def = m_repository.definitionForFileName(fileName);
m_highlighter->setDefinition(def);
setWindowTitle(fileName);
setPlainText(QString::fromUtf8(f.readAll()));
}
void CodeEditor::contextMenuEvent(QContextMenuEvent *event)
{
auto menu = createStandardContextMenu(event->pos());
menu->addSeparator();
auto openAction = menu->addAction(QStringLiteral("Open File..."));
connect(openAction, &QAction::triggered, this, [this]() {
const auto fileName = QFileDialog::getOpenFileName(this, QStringLiteral("Open File"));
if (!fileName.isEmpty()) {
openFile(fileName);
}
});
// syntax selection
auto hlActionGroup = new QActionGroup(menu);
hlActionGroup->setExclusive(true);
auto hlGroupMenu = menu->addMenu(QStringLiteral("Syntax"));
QMenu *hlSubMenu = hlGroupMenu;
QString currentGroup;
for (const auto &def : m_repository.definitions()) {
if (def.isHidden()) {
continue;
}
if (currentGroup != def.section()) {
currentGroup = def.section();
hlSubMenu = hlGroupMenu->addMenu(def.translatedSection());
}
Q_ASSERT(hlSubMenu);
auto action = hlSubMenu->addAction(def.translatedName());
action->setCheckable(true);
action->setData(def.name());
hlActionGroup->addAction(action);
if (def.name() == m_highlighter->definition().name()) {
action->setChecked(true);
}
}
connect(hlActionGroup, &QActionGroup::triggered, this, [this](QAction *action) {
const auto defName = action->data().toString();
const auto def = m_repository.definitionForName(defName);
m_highlighter->setDefinition(def);
});
// theme selection
auto themeGroup = new QActionGroup(menu);
themeGroup->setExclusive(true);
auto themeMenu = menu->addMenu(QStringLiteral("Theme"));
for (const auto &theme : m_repository.themes()) {
auto action = themeMenu->addAction(theme.translatedName());
action->setCheckable(true);
action->setData(theme.name());
themeGroup->addAction(action);
if (theme.name() == m_highlighter->theme().name()) {
action->setChecked(true);
}
}
connect(themeGroup, &QActionGroup::triggered, this, [this](QAction *action) {
const auto themeName = action->data().toString();
const auto theme = m_repository.theme(themeName);
setTheme(theme);
});
menu->exec(event->globalPos());
delete menu;
}
void CodeEditor::resizeEvent(QResizeEvent *event)
{
QPlainTextEdit::resizeEvent(event);
updateSidebarGeometry();
}
void CodeEditor::setTheme(const KSyntaxHighlighting::Theme &theme)
{
auto pal = qApp->palette();
if (theme.isValid()) {
pal.setColor(QPalette::Base, theme.editorColor(KSyntaxHighlighting::Theme::BackgroundColor));
pal.setColor(QPalette::Highlight, theme.editorColor(KSyntaxHighlighting::Theme::TextSelection));
}
setPalette(pal);
m_highlighter->setTheme(theme);
m_highlighter->rehighlight();
highlightCurrentLine();
}
int CodeEditor::sidebarWidth() const
{
int digits = 1;
auto count = blockCount();
while (count >= 10) {
++digits;
count /= 10;
}
return 4 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits + fontMetrics().lineSpacing();
}
void CodeEditor::sidebarPaintEvent(QPaintEvent *event)
{
QPainter painter(m_sideBar);
painter.fillRect(event->rect(), m_highlighter->theme().editorColor(KSyntaxHighlighting::Theme::IconBorder));
auto block = firstVisibleBlock();
auto blockNumber = block.blockNumber();
int top = blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + blockBoundingRect(block).height();
const int currentBlockNumber = textCursor().blockNumber();
const auto foldingMarkerSize = fontMetrics().lineSpacing();
while (block.isValid() && top <= event->rect().bottom()) {
if (block.isVisible() && bottom >= event->rect().top()) {
const auto number = QString::number(blockNumber + 1);
painter.setPen(m_highlighter->theme().editorColor((blockNumber == currentBlockNumber) ? KSyntaxHighlighting::Theme::CurrentLineNumber
: KSyntaxHighlighting::Theme::LineNumbers));
painter.drawText(0, top, m_sideBar->width() - 2 - foldingMarkerSize, fontMetrics().height(), Qt::AlignRight, number);
}
// folding marker
if (block.isVisible() && isFoldable(block)) {
QPolygonF polygon;
if (isFolded(block)) {
polygon << QPointF(foldingMarkerSize * 0.4, foldingMarkerSize * 0.25);
polygon << QPointF(foldingMarkerSize * 0.4, foldingMarkerSize * 0.75);
polygon << QPointF(foldingMarkerSize * 0.8, foldingMarkerSize * 0.5);
} else {
polygon << QPointF(foldingMarkerSize * 0.25, foldingMarkerSize * 0.4);
polygon << QPointF(foldingMarkerSize * 0.75, foldingMarkerSize * 0.4);
polygon << QPointF(foldingMarkerSize * 0.5, foldingMarkerSize * 0.8);
}
painter.save();
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(m_highlighter->theme().editorColor(KSyntaxHighlighting::Theme::CodeFolding)));
painter.translate(m_sideBar->width() - foldingMarkerSize, top);
painter.drawPolygon(polygon);
painter.restore();
}
block = block.next();
top = bottom;
bottom = top + blockBoundingRect(block).height();
++blockNumber;
}
}
void CodeEditor::updateSidebarGeometry()
{
setViewportMargins(sidebarWidth(), 0, 0, 0);
const auto r = contentsRect();
m_sideBar->setGeometry(QRect(r.left(), r.top(), sidebarWidth(), r.height()));
}
void CodeEditor::updateSidebarArea(const QRect &rect, int dy)
{
if (dy) {
m_sideBar->scroll(0, dy);
} else {
m_sideBar->update(0, rect.y(), m_sideBar->width(), rect.height());
}
}
void CodeEditor::highlightCurrentLine()
{
QTextEdit::ExtraSelection selection;
selection.format.setBackground(QColor(m_highlighter->theme().editorColor(KSyntaxHighlighting::Theme::CurrentLine)));
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
QList<QTextEdit::ExtraSelection> extraSelections;
extraSelections.append(selection);
setExtraSelections(extraSelections);
}
QTextBlock CodeEditor::blockAtPosition(int y) const
{
auto block = firstVisibleBlock();
if (!block.isValid()) {
return QTextBlock();
}
int top = blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + blockBoundingRect(block).height();
do {
if (top <= y && y <= bottom) {
return block;
}
block = block.next();
top = bottom;
bottom = top + blockBoundingRect(block).height();
} while (block.isValid());
return QTextBlock();
}
bool CodeEditor::isFoldable(const QTextBlock &block) const
{
return m_highlighter->startsFoldingRegion(block);
}
bool CodeEditor::isFolded(const QTextBlock &block) const
{
if (!block.isValid()) {
return false;
}
const auto nextBlock = block.next();
if (!nextBlock.isValid()) {
return false;
}
return !nextBlock.isVisible();
}
void CodeEditor::toggleFold(const QTextBlock &startBlock)
{
// we also want to fold the last line of the region, therefore the ".next()"
const auto endBlock = m_highlighter->findFoldingRegionEnd(startBlock).next();
if (isFolded(startBlock)) {
// unfold
auto block = startBlock.next();
while (block.isValid() && !block.isVisible()) {
block.setVisible(true);
block.setLineCount(block.layout()->lineCount());
block = block.next();
}
} else {
// fold
auto block = startBlock.next();
while (block.isValid() && block != endBlock) {
block.setVisible(false);
block.setLineCount(0);
block = block.next();
}
}
// redraw document
document()->markContentsDirty(startBlock.position(), endBlock.position() - startBlock.position() + 1);
// update scrollbars
Q_EMIT document()->documentLayout()->documentSizeChanged(document()->documentLayout()->documentSize());
}
#include "codeeditor.moc"
#include "moc_codeeditor.cpp"
@@ -0,0 +1,53 @@
/*
SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef CODEEDITOR_H
#define CODEEDITOR_H
#include <KSyntaxHighlighting/Repository>
#include <QPlainTextEdit>
namespace KSyntaxHighlighting
{
class SyntaxHighlighter;
}
class CodeEditorSidebar;
class CodeEditor : public QPlainTextEdit
{
Q_OBJECT
public:
explicit CodeEditor(QWidget *parent = nullptr);
~CodeEditor() override;
void openFile(const QString &fileName);
protected:
void contextMenuEvent(QContextMenuEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private:
friend class CodeEditorSidebar;
void setTheme(const KSyntaxHighlighting::Theme &theme);
int sidebarWidth() const;
void sidebarPaintEvent(QPaintEvent *event);
void updateSidebarGeometry();
void updateSidebarArea(const QRect &rect, int dy);
void highlightCurrentLine();
QTextBlock blockAtPosition(int y) const;
bool isFoldable(const QTextBlock &block) const;
bool isFolded(const QTextBlock &block) const;
void toggleFold(const QTextBlock &block);
KSyntaxHighlighting::Repository m_repository;
KSyntaxHighlighting::SyntaxHighlighter *m_highlighter;
CodeEditorSidebar *m_sideBar;
};
#endif // CODEEDITOR_H
@@ -0,0 +1,28 @@
/*
SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: MIT
*/
#include "codeeditor.h"
#include <QApplication>
#include <QCommandLineParser>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QCommandLineParser parser;
parser.addHelpOption();
parser.addPositionalArgument(QStringLiteral("source"), QStringLiteral("The source file to highlight."));
parser.process(app);
CodeEditor edit;
edit.resize(1024, 1024);
edit.show();
if (parser.positionalArguments().size() == 1) {
edit.openFile(parser.positionalArguments().at(0));
}
return app.exec();
}