diff --git a/appsettings.kcfg b/appsettings.kcfg
index bf03ead..7183f39 100644
--- a/appsettings.kcfg
+++ b/appsettings.kcfg
@@ -13,5 +13,8 @@
QFontDatabase::systemFont(QFontDatabase::FixedFont)
+
+ false
+
diff --git a/generalsettings.ui b/generalsettings.ui
index 594093c..dc93f55 100644
--- a/generalsettings.ui
+++ b/generalsettings.ui
@@ -44,6 +44,20 @@
-
+ -
+
+
+ eDITOR dARK tHEME
+
+
+
+ -
+
+
+ Enabled
+
+
+
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 4b63902..4889d67 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -109,7 +109,9 @@ void MainWindow::setupActions()
lexerNoneAction->setText("None (Normal Text)");
actionCollection()->addAction(u"lexer_none"_s, lexerNoneAction);
connect(lexerNoneAction, &QAction::triggered, this, [this](){
- // m_editor->setLexer(nullptr);
+ if (SciEdit *editor = m_tabWidget->currentEditor()) {
+ editor->setLexer(nullptr);
+ }
});
for (const QChar & group : LexerGroupActionMenu::groups()) {
@@ -167,6 +169,17 @@ void MainWindow::setupActions()
// m_editor->setIndentationGuides(!m_editor->indentationGuides());
});
+ QAction *toggleEditorDarkThemeAction = new QAction(this);
+ toggleEditorDarkThemeAction->setText("Editor Dark Theme");
+ toggleEditorDarkThemeAction->setCheckable(true);
+ toggleEditorDarkThemeAction->setChecked(AppSettings::editorDarkTheme());
+ actionCollection()->addAction(u"toggle_editor_dark_theme"_s, toggleEditorDarkThemeAction);
+ connect(toggleEditorDarkThemeAction, &QAction::toggled, this, [this](bool checked){
+ AppSettings::setEditorDarkTheme(checked);
+ AppSettings::self()->save();
+ applySettingsToAllEditors();
+ });
+
// Load themes
KColorSchemeManager *manager = KColorSchemeManager::instance();
auto *colorSelectionMenu = KColorSchemeMenu::createMenu(manager, this);
@@ -185,8 +198,12 @@ void MainWindow::showSettings()
dialog->setFaceType(KPageDialog::FlatList);
dialog->addPage(new SettingsPage(dialog), "Appearance", "preferences-desktop-theme-global");
- connect(dialog, &KConfigDialog::settingsChanged, this, [](const QString &dialogName){
- qDebug() << dialogName << "changed";
+ connect(dialog, &KConfigDialog::settingsChanged, this, [this](const QString &dialogName){
+ Q_UNUSED(dialogName)
+ applySettingsToAllEditors();
+ if (QAction *action = actionCollection()->action(QStringLiteral("toggle_editor_dark_theme"))) {
+ action->setChecked(AppSettings::editorDarkTheme());
+ }
});
dialog->show();
@@ -248,12 +265,30 @@ void MainWindow::onCurrentEditorChanged(SciEdit *editor)
// 连接当前编辑器的光标位置变化信号
if (editor) {
+ applySettingsToEditor(editor);
connect(editor, &SciEdit::cursorPosChanged, this, [this](int line, int column) {
m_cursorPosStatusLabel->setText(QString("Ln: %1 Col: %2").arg(line).arg(column));
});
}
}
+void MainWindow::applySettingsToEditor(SciEdit *editor)
+{
+ if (!editor) {
+ return;
+ }
+ editor->setTabWidth(AppSettings::tabWidth());
+ editor->setEditorFont(AppSettings::editorFont());
+ editor->applyTheme(AppSettings::editorDarkTheme());
+}
+
+void MainWindow::applySettingsToAllEditors()
+{
+ for (int i = 0; i < m_tabWidget->count(); ++i) {
+ applySettingsToEditor(m_tabWidget->editorAt(i));
+ }
+}
+
void MainWindow::onDocumentModified(int docIndex, bool modified)
{
Q_UNUSED(docIndex)
diff --git a/mainwindow.h b/mainwindow.h
index 6ab9796..08e1a87 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -31,6 +31,8 @@ private:
void setupActions();
void showSettings();
void applyLexer(const QString & lexer);
+ void applySettingsToEditor(SciEdit *editor);
+ void applySettingsToAllEditors();
void updateWindowTitle();
void updateStatusBar();
void updateActions();
diff --git a/pineapple-notepadui.rc b/pineapple-notepadui.rc
index 401588a..105d443 100644
--- a/pineapple-notepadui.rc
+++ b/pineapple-notepadui.rc
@@ -26,6 +26,7 @@ Template: https://github.com/KDE/kxmlgui/blob/master/src/ui_standards.rc
-->
+
diff --git a/sciedit.cpp b/sciedit.cpp
index c48f62f..2d44f78 100644
--- a/sciedit.cpp
+++ b/sciedit.cpp
@@ -1,22 +1,34 @@
#include "sciedit.h"
+#include
+
+#include
+#include
+
+static sptr_t toBgr(const QColor &color) {
+ return (color.blue() << 16) | (color.green() << 8) | color.red();
+}
+
SciEdit::SciEdit(QWidget * parent)
: ScintillaEdit(parent)
{
connect(this, &ScintillaEditBase::updateUi, this, [this](){
int curPos = currentPos();
emit cursorPosChanged(lineFromPosition(curPos) + 1, column(curPos));
+ updateLineNumberMarginWidth();
});
// 连接文本变化信号
connect(this, QOverload::of(&ScintillaEditBase::modified),
this, &SciEdit::textChanged);
+
+ setLineNumbersEnabled(true);
}
void SciEdit::setStyleFont(const QFont &font, int style)
{
styleSetFont(style, font.family().toUtf8().constData());
- styleSetSizeFractional(0, long(font.pointSizeF() * SC_FONT_SIZE_MULTIPLIER));
+ styleSetSizeFractional(style, long(font.pointSizeF() * SC_FONT_SIZE_MULTIPLIER));
}
void SciEdit::setFolding(FoldType foldType, int margin)
@@ -48,14 +60,64 @@ void SciEdit::setFolding(FoldType foldType, int margin)
void SciEdit::setLexer(Scintilla::ILexer5 *lexer)
{
setILexer((sptr_t)lexer);
+ m_lexerName = lexer ? QString::fromUtf8(lexer->GetName()) : QString();
+ if (lexer == nullptr) {
+ clearDocumentStyle();
+ }
+ applyLexerStyles();
+ colourise(0, -1);
+}
- qDebug() << lexer->GetName() << "lexer-------------";
+void SciEdit::setLineNumbersEnabled(bool enabled)
+{
+ m_lineNumbersEnabled = enabled;
+ setMarginTypeN(0, SC_MARGIN_NUMBER);
+ updateLineNumberMarginWidth();
+}
+
+void SciEdit::setEditorFont(const QFont &font)
+{
+ m_editorFont = font;
+ applyTheme(m_isDarkTheme);
+}
+
+void SciEdit::applyTheme(bool dark)
+{
+ m_isDarkTheme = dark;
+
+ const QColor bg = dark ? QColor(0x1e, 0x1e, 0x1e) : QColor(0xff, 0xff, 0xff);
+ const QColor fg = dark ? QColor(0xd4, 0xd4, 0xd4) : QColor(0x00, 0x00, 0x00);
+ const QColor caret = dark ? QColor(0xff, 0xff, 0xff) : QColor(0x00, 0x00, 0x00);
+ const QColor selectionBg = dark ? QColor(0x26, 0x4f, 0x78) : QColor(0xcc, 0xe8, 0xff);
+ const QColor caretLineBg = dark ? QColor(0x2a, 0x2a, 0x2a) : QColor(0xf5, 0xf5, 0xf5);
+ const QColor lineNumberFg = dark ? QColor(0x85, 0x85, 0x85) : QColor(0x80, 0x80, 0x80);
+ const QColor lineNumberBg = dark ? QColor(0x25, 0x25, 0x26) : QColor(0xf0, 0xf0, 0xf0);
+
+ styleResetDefault();
+ if (!m_editorFont.family().isEmpty()) {
+ setStyleFont(m_editorFont, STYLE_DEFAULT);
+ }
+ styleSetFore(STYLE_DEFAULT, toBgr(fg));
+ styleSetBack(STYLE_DEFAULT, toBgr(bg));
+ styleClearAll();
+
+ setCaretFore(toBgr(caret));
+ setSelFore(true, toBgr(fg));
+ setSelBack(true, toBgr(selectionBg));
+ setCaretLineVisible(true);
+ setCaretLineBack(toBgr(caretLineBg));
+
+ styleSetFore(STYLE_LINENUMBER, toBgr(lineNumberFg));
+ styleSetBack(STYLE_LINENUMBER, toBgr(lineNumberBg));
+ setMarginBackN(0, toBgr(lineNumberBg));
+
+ updateLineNumberMarginWidth();
+ applyLexerStyles();
}
void SciEdit::sendColor(unsigned int iMessage, uptr_t wParam, const QColor &color) const
{
- sptr_t lParam = (color.blue() << 16) | (color.green() << 8) | color.red();
- send(iMessage, wParam, lParam);
+ send(iMessage, wParam, toBgr(color));
}
void SciEdit::setMarkerDefine(int markerNumber, int markerSymbol)
@@ -67,3 +129,115 @@ void SciEdit::setMarkerDefine(int markerNumber, int markerSymbol)
sendColor(SCI_MARKERSETBACK, markerNumber, QColor(128, 128, 128));
}
}
+
+void SciEdit::updateLineNumberMarginWidth()
+{
+ if (!m_lineNumbersEnabled) {
+ setMarginWidthN(0, 0);
+ return;
+ }
+
+ const int lines = qMax(1, lineCount());
+ const int digits = QString::number(lines).size();
+ if (digits == m_lastLineNumberDigits && marginWidthN(0) > 0) {
+ return;
+ }
+ m_lastLineNumberDigits = digits;
+
+ const QString sample = QString(digits, QLatin1Char('9'));
+ const QByteArray sampleBytes = sample.toUtf8();
+ const sptr_t width = textWidth(STYLE_LINENUMBER, sampleBytes.constData()) + 10;
+ setMarginWidthN(0, width);
+}
+
+void SciEdit::applyLexerStyles()
+{
+ if (m_lexerName.isEmpty()) {
+ return;
+ }
+
+ const bool dark = m_isDarkTheme;
+ const QColor comment = dark ? QColor(0x6a, 0x99, 0x55) : QColor(0x00, 0x80, 0x00);
+ const QColor docComment = dark ? QColor(0x60, 0x80, 0x4f) : QColor(0x00, 0x80, 0x00);
+ const QColor keyword = dark ? QColor(0x56, 0x9c, 0xd6) : QColor(0x00, 0x00, 0xff);
+ const QColor type = dark ? QColor(0x4e, 0xc9, 0xb0) : QColor(0x2b, 0x91, 0xaf);
+ const QColor number = dark ? QColor(0xb5, 0xce, 0xa8) : QColor(0x09, 0x86, 0x58);
+ const QColor string = dark ? QColor(0xce, 0x91, 0x78) : QColor(0xa3, 0x15, 0x15);
+ const QColor preprocessor = dark ? QColor(0xc5, 0x86, 0xc0) : QColor(0xaf, 0x00, 0xdb);
+ const QColor tag = dark ? QColor(0x56, 0x9c, 0xd6) : QColor(0x80, 0x00, 0x00);
+ const QColor attr = dark ? QColor(0x9c, 0xdc, 0xfe) : QColor(0xff, 0x00, 0x00);
+
+ auto setFore = [&](int style, const QColor &c) {
+ styleSetFore(style, toBgr(c));
+ };
+ auto setBold = [&](int style, bool b) {
+ styleSetBold(style, b);
+ };
+
+ if (m_lexerName == QStringLiteral("cpp")) {
+ setFore(SCE_C_COMMENT, comment);
+ setFore(SCE_C_COMMENTLINE, comment);
+ setFore(SCE_C_COMMENTDOC, docComment);
+ setFore(SCE_C_COMMENTLINEDOC, docComment);
+ setFore(SCE_C_COMMENTDOCKEYWORD, docComment);
+ setFore(SCE_C_COMMENTDOCKEYWORDERROR, docComment);
+ setFore(SCE_C_NUMBER, number);
+ setFore(SCE_C_STRING, string);
+ setFore(SCE_C_CHARACTER, string);
+ setFore(SCE_C_PREPROCESSOR, preprocessor);
+ setFore(SCE_C_WORD, keyword);
+ setBold(SCE_C_WORD, true);
+ setFore(SCE_C_WORD2, type);
+ setBold(SCE_C_WORD2, true);
+ } else if (m_lexerName == QStringLiteral("python")) {
+ setFore(SCE_P_COMMENTLINE, comment);
+ setFore(SCE_P_NUMBER, number);
+ setFore(SCE_P_STRING, string);
+ setFore(SCE_P_CHARACTER, string);
+ setFore(SCE_P_WORD, keyword);
+ setBold(SCE_P_WORD, true);
+ setFore(SCE_P_WORD2, type);
+ setBold(SCE_P_WORD2, true);
+ setFore(SCE_P_DECORATOR, preprocessor);
+ setFore(SCE_P_CLASSNAME, type);
+ setFore(SCE_P_DEFNAME, type);
+ } else if (m_lexerName == QStringLiteral("hypertext")) {
+ setFore(SCE_H_COMMENT, comment);
+ setFore(SCE_H_TAG, tag);
+ setFore(SCE_H_TAGUNKNOWN, tag);
+ setFore(SCE_H_ATTRIBUTE, attr);
+ setFore(SCE_H_ATTRIBUTEUNKNOWN, attr);
+ setFore(SCE_H_NUMBER, number);
+ setFore(SCE_H_DOUBLESTRING, string);
+ setFore(SCE_H_SINGLESTRING, string);
+ setFore(SCE_H_ENTITY, preprocessor);
+ } else if (m_lexerName == QStringLiteral("xml")) {
+ setFore(SCE_H_COMMENT, comment);
+ setFore(SCE_H_TAG, tag);
+ setFore(SCE_H_TAGUNKNOWN, tag);
+ setFore(SCE_H_ATTRIBUTE, attr);
+ setFore(SCE_H_ATTRIBUTEUNKNOWN, attr);
+ setFore(SCE_H_NUMBER, number);
+ setFore(SCE_H_DOUBLESTRING, string);
+ setFore(SCE_H_SINGLESTRING, string);
+ setFore(SCE_H_ENTITY, preprocessor);
+ } else if (m_lexerName == QStringLiteral("json")) {
+ setFore(SCE_JSON_NUMBER, number);
+ setFore(SCE_JSON_STRING, string);
+ setFore(SCE_JSON_PROPERTYNAME, attr);
+ setFore(SCE_JSON_OPERATOR, preprocessor);
+ setFore(SCE_JSON_LINECOMMENT, comment);
+ setFore(SCE_JSON_BLOCKCOMMENT, comment);
+ } else if (m_lexerName == QStringLiteral("css")) {
+ setFore(SCE_CSS_COMMENT, comment);
+ setFore(SCE_CSS_CLASS, attr);
+ setFore(SCE_CSS_ID, attr);
+ setFore(SCE_CSS_TAG, tag);
+ setFore(SCE_CSS_ATTRIBUTE, attr);
+ setFore(SCE_CSS_DOUBLESTRING, string);
+ setFore(SCE_CSS_SINGLESTRING, string);
+ setFore(SCE_CSS_VALUE, string);
+ setFore(SCE_CSS_IDENTIFIER, keyword);
+ setBold(SCE_CSS_IDENTIFIER, true);
+ }
+}
diff --git a/sciedit.h b/sciedit.h
index 456738f..caa25ec 100644
--- a/sciedit.h
+++ b/sciedit.h
@@ -3,6 +3,9 @@
#include
#include
+#include
+#include
+
class SciEdit : public ScintillaEdit
{
Q_OBJECT
@@ -18,6 +21,9 @@ public:
void setStyleFont(const QFont & font, int style = STYLE_DEFAULT);
void setFolding(FoldType foldType, int margin = 2);
void setLexer(Scintilla::ILexer5 * lexer);
+ void setLineNumbersEnabled(bool enabled);
+ void setEditorFont(const QFont &font);
+ void applyTheme(bool dark);
void sendColor(unsigned int iMessage, uptr_t wParam, const QColor &col) const;
signals:
@@ -26,4 +32,12 @@ signals:
private:
void setMarkerDefine(int markerNumber, int markerSymbol);
+ void updateLineNumberMarginWidth();
+ void applyLexerStyles();
+
+ bool m_lineNumbersEnabled = false;
+ int m_lastLineNumberDigits = 0;
+ bool m_isDarkTheme = false;
+ QFont m_editorFont;
+ QString m_lexerName;
};
diff --git a/tabwidget.cpp b/tabwidget.cpp
index 1a36f50..c0353d2 100644
--- a/tabwidget.cpp
+++ b/tabwidget.cpp
@@ -1,11 +1,41 @@
#include "tabwidget.h"
#include "sciedit.h"
#include "documentmanager.h"
+#include "appsettings.h"
#include
#include
#include
+#include
+#include
+
+static QString lexerNameForDocumentLanguage(const QString &language)
+{
+ if (language == QStringLiteral("C++") ||
+ language == QStringLiteral("C") ||
+ language == QStringLiteral("C/C++ Header")) {
+ return QStringLiteral("cpp");
+ }
+ if (language == QStringLiteral("Python")) {
+ return QStringLiteral("python");
+ }
+ if (language == QStringLiteral("HTML")) {
+ return QStringLiteral("hypertext");
+ }
+ if (language == QStringLiteral("XML")) {
+ return QStringLiteral("xml");
+ }
+ if (language == QStringLiteral("JSON")) {
+ return QStringLiteral("json");
+ }
+ if (language == QStringLiteral("CSS")) {
+ return QStringLiteral("css");
+ }
+
+ return QString();
+}
+
TabWidget::TabWidget(DocumentManager *documentManager, QWidget *parent)
: QTabWidget(parent)
, m_documentManager(documentManager)
@@ -73,6 +103,17 @@ int TabWidget::openDocument(const QString &filePath)
// 设置编辑器内容
QString content = m_documentManager->getDocumentContent(docId);
editor->setText(content.toUtf8().constData());
+ editor->setTabWidth(AppSettings::tabWidth());
+ editor->setEditorFont(AppSettings::editorFont());
+ editor->applyTheme(AppSettings::editorDarkTheme());
+
+ const QString lexerName = lexerNameForDocumentLanguage(m_documentManager->getDocumentLanguage(docId));
+ if (lexerName.isEmpty()) {
+ editor->setLexer(nullptr);
+ } else {
+ Scintilla::ILexer5 *lexer = CreateLexer(lexerName.toStdString().c_str());
+ editor->setLexer(lexer);
+ }
// 重新连接信号
connectEditorSignals(editor);
@@ -136,7 +177,17 @@ bool TabWidget::saveCurrentDocumentAs()
QByteArray content = editor->getText(editor->textLength());
m_documentManager->setDocumentContent(docId, QString::fromUtf8(content));
- return m_documentManager->saveDocumentAs(docId, fileName);
+ const bool saved = m_documentManager->saveDocumentAs(docId, fileName);
+ if (saved) {
+ const QString lexerName = lexerNameForDocumentLanguage(m_documentManager->getDocumentLanguage(docId));
+ if (lexerName.isEmpty()) {
+ editor->setLexer(nullptr);
+ } else {
+ Scintilla::ILexer5 *lexer = CreateLexer(lexerName.toStdString().c_str());
+ editor->setLexer(lexer);
+ }
+ }
+ return saved;
}
void TabWidget::closeCurrentTab()
@@ -349,6 +400,9 @@ void TabWidget::onEditorTextChanged()
SciEdit *TabWidget::createEditor()
{
SciEdit *editor = new SciEdit(this);
+ editor->setTabWidth(AppSettings::tabWidth());
+ editor->setEditorFont(AppSettings::editorFont());
+ editor->applyTheme(AppSettings::editorDarkTheme());
return editor;
}
@@ -379,4 +433,4 @@ void TabWidget::disconnectEditorSignals(SciEdit *editor)
if (editor) {
disconnect(editor, &SciEdit::textChanged, this, &TabWidget::onEditorTextChanged);
}
-}
\ No newline at end of file
+}