Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
431 changes: 431 additions & 0 deletions src/AppearanceManager.cpp

Large diffs are not rendered by default.

131 changes: 131 additions & 0 deletions src/AppearanceManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#pragma once

#include <functional>

#include <QColor>
#include <QObject>
#include <QPalette>
#include <QString>
#include <Qt>

class ApplicationSettings;
class QWidget;

struct AppearanceTokens
{
QColor surfaceEditor;
QColor surfaceShell;
QColor surfaceRaised;
QColor surfaceHover;
QColor borderDefault;
QColor textPrimary;
QColor textEditor;
QColor textSecondary;
QColor textDisabled;
QColor accentPrimary;
QColor accentHover;
QColor focusBorder;
QColor selectionActive;
QColor selectionInactive;
QColor stateError;
QColor stateWarning;
QColor stateSuccess;
QColor stateInformation;
QColor editorCaret;
QColor editorCurrentLine;
QColor editorWhitespace;
QColor editorIndentGuide;
QColor editorIndentGuideActive;
QColor editorBraceMatch;
QColor editorBraceError;
QColor syntaxComment;
QColor syntaxString;
QColor syntaxNumber;
QColor syntaxKeyword;
QColor syntaxControlFlow;
QColor syntaxFunction;
QColor syntaxType;
QColor syntaxVariable;
QColor syntaxConstant;
QColor syntaxTag;
QColor syntaxAttribute;
QColor diffAddedMarker;
QColor diffModifiedMarker;
QColor diffDeletedMarker;
QColor diffAddedFill;
QColor diffDeletedFill;
};

class AppearanceManager : public QObject
{
Q_OBJECT

public:
enum class Mode {
System,
Light,
Dark
};
Q_ENUM(Mode)

enum class EffectiveAppearance {
Light,
Dark
};
Q_ENUM(EffectiveAppearance)

using ColorSchemeProvider = std::function<Qt::ColorScheme()>;

explicit AppearanceManager(ApplicationSettings *settings, QObject *parent = nullptr);
AppearanceManager(ApplicationSettings *settings, ColorSchemeProvider colorSchemeProvider,
QObject *parent = nullptr);

Mode requestedMode() const { return mode; }
EffectiveAppearance effectiveAppearance() const { return effective; }
bool isDark() const { return effective == EffectiveAppearance::Dark; }
const AppearanceTokens &tokens() const { return currentTokens; }

static Mode modeFromString(const QString &value);
static QString modeToString(Mode mode);
static EffectiveAppearance resolve(Mode mode, Qt::ColorScheme systemScheme,
EffectiveAppearance unknownFallback);
static int scintillaColor(const QColor &color);
static unsigned int scintillaElementColor(const QColor &color);

public slots:
void setRequestedMode(Mode requestedMode);
void refreshSystemAppearance();

signals:
void effectiveAppearanceChanged(AppearanceManager::EffectiveAppearance appearance);

protected:
bool eventFilter(QObject *watched, QEvent *event) override;

private:
void onAppearanceSettingChanged(const QString &value);
EffectiveAppearance resolveEffectiveAppearance() const;
EffectiveAppearance paletteFallback() const;
void restoreSystemPalette();
void applyApplicationAppearance();
void applyNativeAppearance();
void applyNativeAppearance(QWidget *window);
void updateEffectiveAppearance(bool forceUpdate = false);

static AppearanceTokens darkTokens();
static AppearanceTokens lightTokens();
static AppearanceTokens systemTokens(const QPalette &palette,
EffectiveAppearance effectiveAppearance,
bool highContrast);
static QPalette explicitPalette(const AppearanceTokens &tokens);

ApplicationSettings *settings;
ColorSchemeProvider colorSchemeProvider;
Mode mode;
EffectiveAppearance effective;
AppearanceTokens currentTokens;
QPalette systemPaletteSnapshot;
bool hasSystemPaletteSnapshot = false;
bool applyingPalette = false;
bool updatingAppearance = false;
};
152 changes: 152 additions & 0 deletions src/AppearanceTrace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#pragma once

#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QElapsedTimer>
#include <QFile>
#include <QFileInfo>
#include <QMutex>
#include <QMutexLocker>
#include <QStringList>

#include <utility>

namespace AppearanceTrace {

struct State
{
QMutex mutex;
quint64 nextCycle = 0;
quint64 activeCycle = 0;
QStringList records;
};

inline State &state()
{
static State value;
return value;
}

inline QString outputPath()
{
return qEnvironmentVariable("NOTEPADSHARP_APPEARANCE_TRACE");
}

inline bool enabled()
{
return !qEnvironmentVariableIsEmpty("NOTEPADSHARP_APPEARANCE_TRACE");
}

inline void initialize()
{
if (!enabled())
return;

const QString path = outputPath();
QFileInfo(path).dir().mkpath(QStringLiteral("."));
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
file.write(QStringLiteral("%1 pid=%2 trace-enabled version=%3\n")
.arg(QDateTime::currentDateTime().toString(Qt::ISODateWithMs))
.arg(QCoreApplication::applicationPid())
.arg(QCoreApplication::applicationVersion()).toUtf8());
file.flush();
}
}

inline quint64 beginCycle(const QString &trigger, const QString &details)
{
if (!enabled())
return 0;

State &trace = state();
QMutexLocker locker(&trace.mutex);
trace.activeCycle = ++trace.nextCycle;
trace.records = {
QStringLiteral("%1 pid=%2 cycle=%3 begin trigger=%4 %5")
.arg(QDateTime::currentDateTime().toString(Qt::ISODateWithMs))
.arg(QCoreApplication::applicationPid())
.arg(trace.activeCycle)
.arg(trigger, details)
};
return trace.activeCycle;
}

inline quint64 currentCycle()
{
State &trace = state();
QMutexLocker locker(&trace.mutex);
return trace.activeCycle;
}

inline void record(quint64 cycle, const QString &component, qint64 elapsedMs,
const QString &details = {})
{
if (cycle == 0)
return;

State &trace = state();
QMutexLocker locker(&trace.mutex);
if (trace.activeCycle != cycle)
return;

trace.records.append(QStringLiteral("cycle=%1 component=%2 elapsed-ms=%3 %4")
.arg(cycle).arg(component).arg(elapsedMs).arg(details));
}

inline void endCycle(quint64 cycle, qint64 elapsedMs, const QString &details)
{
if (cycle == 0)
return;

QStringList records;
{
State &trace = state();
QMutexLocker locker(&trace.mutex);
if (trace.activeCycle != cycle)
return;

trace.records.append(QStringLiteral("cycle=%1 end elapsed-ms=%2 %3")
.arg(cycle).arg(elapsedMs).arg(details));
records = trace.records;
trace.records.clear();
trace.activeCycle = 0;
}

const QString path = outputPath();
QFileInfo(path).dir().mkpath(QStringLiteral("."));
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
file.write(records.join(QLatin1Char('\n')).toUtf8());
file.write("\n");
file.flush();
}
}

class Scope
{
public:
Scope(QString component, QString details = {})
: cycle(enabled() ? currentCycle() : 0)
, component(std::move(component))
, details(std::move(details))
{
if (cycle != 0)
timer.start();
}

~Scope()
{
if (cycle != 0)
record(cycle, component, timer.elapsed(), details);
}

private:
quint64 cycle;
QString component;
QString details;
QElapsedTimer timer;
};

} // namespace AppearanceTrace
7 changes: 7 additions & 0 deletions src/ApplicationSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ ApplicationSettings::ApplicationSettings(QObject *parent)
{
}

ApplicationSettings::ApplicationSettings(const QString &fileName, QSettings::Format format, QObject *parent)
: QSettings{fileName, format, parent}
{
}

CREATE_SETTING(Gui, ShowMenuBar, showMenuBar, bool, true)
CREATE_SETTING(Gui, ShowToolBar, showToolBar, bool, true)
CREATE_SETTING(Gui, ShowTabBar, showTabBar, bool, true)
Expand All @@ -60,6 +65,8 @@ CREATE_SETTING(App, DefaultDirectory, defaultDirectory, QString, QString())

CREATE_SETTING(App, Translation, translation, QString, QStringLiteral(""))

CREATE_SETTING(Gui, Appearance, appearance, QString, QStringLiteral("light"))

CREATE_SETTING(Editor, ShowWhitespace, showWhitespace, bool, false);
CREATE_SETTING(Editor, ShowEndOfLine, showEndOfLine, bool, false);
CREATE_SETTING(Editor, ShowWrapSymbol, showWrapSymbol, bool, false);
Expand Down
3 changes: 3 additions & 0 deletions src/ApplicationSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class ApplicationSettings : public QSettings

public:
explicit ApplicationSettings(QObject *parent = nullptr);
ApplicationSettings(const QString &fileName, QSettings::Format format, QObject *parent = nullptr);

enum DefaultDirectoryBehaviorEnum {
FollowCurrentDocument,
Expand Down Expand Up @@ -108,6 +109,8 @@ class ApplicationSettings : public QSettings

DEFINE_SETTING(Translation, translation, QString)

DEFINE_SETTING(Appearance, appearance, QString)

DEFINE_SETTING(ShowWhitespace, showWhitespace, bool);
DEFINE_SETTING(ShowEndOfLine, showEndOfLine, bool);
DEFINE_SETTING(ShowWrapSymbol, showWrapSymbol, bool)
Expand Down
8 changes: 8 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ qt_add_executable(NotepadSharp
WIN32
MACOSX_BUNDLE
ActionUtils.h
AppearanceManager.h
ApplicationSettings.h
ByteArrayUtils.h
ColorPickerDelegate.h
Expand All @@ -17,6 +18,7 @@ qt_add_executable(NotepadSharp
DefaultDirectoryManager.h
DockedEditor.h
DockedEditorTitleBar.h
EditorAppearance.h

EditorManager.h
EditorPrintPreviewRenderer.h
Expand Down Expand Up @@ -55,9 +57,11 @@ qt_add_executable(NotepadSharp
SpinBoxDelegate.h
TranslationManager.h
UndoAction.h
WindowsAppearance.h
ZoomEventWatcher.h
resources.qrc
scripts.qrc
AppearanceManager.cpp
ApplicationSettings.cpp
ColorPickerDelegate.cpp
ComboBoxDelegate.cpp
Expand All @@ -70,6 +74,8 @@ qt_add_executable(NotepadSharp
DebugManager.cpp
DefaultDirectoryManager.cpp
DockedEditor.cpp
EditorDecorationAppearance.cpp
EditorAppearance.cpp

EditorManager.cpp
EditorPrintPreviewRenderer.cpp
Expand Down Expand Up @@ -105,6 +111,7 @@ qt_add_executable(NotepadSharp
SpinBoxDelegate.cpp
TranslationManager.cpp
UndoAction.cpp
WindowsAppearance.cpp
ZoomEventWatcher.cpp
decorators/ApplicationDecorator.cpp
decorators/ApplicationDecorator.h
Expand Down Expand Up @@ -230,6 +237,7 @@ if(WIN32)
@ONLY
)
target_sources(NotepadSharp PRIVATE "${CMAKE_BINARY_DIR}/version.rc")
target_link_libraries(NotepadSharp PRIVATE dwmapi)
configure_file(
"${CMAKE_SOURCE_DIR}/icon/NotepadSharp.ico"
"${CMAKE_BINARY_DIR}/NotepadSharp.ico"
Expand Down
Loading
Loading