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
4 changes: 2 additions & 2 deletions src/AppearanceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,8 @@ AppearanceTokens AppearanceManager::darkTokens()
QColor(QStringLiteral("#FFA657")), QColor(QStringLiteral("#79C0FF")),
QColor(QStringLiteral("#7EE787")), QColor(QStringLiteral("#9CDCFE")),
QColor(QStringLiteral("#72C892")), QColor(QStringLiteral("#0078D4")),
QColor(QStringLiteral("#F28772")), QColor(52, 125, 57, 38),
QColor(201, 60, 55, 38)
QColor(QStringLiteral("#F28772")), QColor(52, 125, 57, 89),
QColor(201, 60, 55, 89)
};
}

Expand Down
12 changes: 12 additions & 0 deletions src/DockedEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ DockedEditor::DockedEditor(QWidget *parent) : QObject(parent)
});
}

QString DockedEditor::tabTitleStyleSheet(const QColor &active, const QColor &inactive)
{
auto cssColor = [](const QColor &color) {
return QStringLiteral("rgba(%1, %2, %3, %4)")
.arg(color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha());
};
return QStringLiteral(
"ads--CDockWidgetTab[activeTab=\"true\"] ads--CElidingLabel { color: %1; }\n"
"ads--CDockWidgetTab[activeTab=\"false\"] ads--CElidingLabel { color: %2; }\n")
.arg(cssColor(active), cssColor(inactive));
}


ScintillaNext *DockedEditor::getCurrentEditor() const
{
Expand Down
4 changes: 4 additions & 0 deletions src/DockedEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
#ifndef DOCKEDEDITOR_H
#define DOCKEDEDITOR_H

#include <QColor>
#include <QObject>
#include <QString>

#include "DockManager.h"
#include "ScintillaNext.h"
Expand All @@ -37,6 +39,8 @@ class DockedEditor : public QObject
public:
explicit DockedEditor(QWidget *parent);

static QString tabTitleStyleSheet(const QColor &active, const QColor &inactive);

ScintillaNext *getCurrentEditor() const;
ads::CDockAreaWidget *currentDockArea() const;

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

#include <QColor>
#include <QIcon>
#include <QPainter>
#include <QPixmap>
#include <QString>

namespace ThemedIcon {

inline QPixmap tintedPixmap(const QIcon &source, int size, const QColor &color)
{
QPixmap pixmap = source.pixmap(size, size, QIcon::Normal, QIcon::Off);
QPainter painter(&pixmap);
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.fillRect(pixmap.rect(), color);
return pixmap;
}

inline QIcon monochrome(const QString &resource, const QColor &normal,
const QColor &disabled)
{
const QIcon source(resource);
QIcon result;
for (const int size : {16, 20, 24, 32}) {
result.addPixmap(tintedPixmap(source, size, normal), QIcon::Normal);
result.addPixmap(tintedPixmap(source, size, disabled), QIcon::Disabled);
}
return result;
}

} // namespace ThemedIcon
2 changes: 1 addition & 1 deletion src/compare/CompareToolBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ void CompareToolBar::applyAppearance()
"QLabel#compareModifiedDot { background: %9; border-radius: 3px; }")
.arg(cssColor(tokens.surfaceShell), cssColor(tokens.borderDefault),
cssColor(tokens.textPrimary), cssColor(tokens.surfaceHover),
cssColor(tokens.surfaceRaised), cssColor(tokens.textSecondary),
cssColor(tokens.surfaceRaised), cssColor(tokens.textPrimary),
cssColor(tokens.diffAddedMarker), cssColor(tokens.diffDeletedMarker),
cssColor(tokens.diffModifiedMarker)));

Expand Down
14 changes: 13 additions & 1 deletion src/dialogs/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
#include "FadingIndicator.h"

#include "ActionUtils.h"
#include "ThemedIcon.h"


MainWindow::MainWindow(NotepadSharpApplication *app) :
Expand Down Expand Up @@ -130,8 +131,11 @@ MainWindow::MainWindow(NotepadSharpApplication *app) :
QMenu *compareMenu = new QMenu(tr("&Compare"), this);
ui->menuBar->insertMenu(ui->menuSettings->menuAction(), compareMenu);

const AppearanceTokens &appearanceTokens = app->getAppearanceManager()->tokens();
comparePreviousAction = new QAction(
QIcon(QStringLiteral(":/icons/git-compare-arrows.svg")),
ThemedIcon::monochrome(QStringLiteral(":/icons/git-compare-arrows.svg"),
appearanceTokens.textPrimary,
appearanceTokens.textDisabled),
tr("Compare with Previous Tab"),
this);
comparePreviousAction->setObjectName(QStringLiteral("actionComparePrevious"));
Expand Down Expand Up @@ -2014,6 +2018,14 @@ void MainWindow::applyStyleSheet()
sheet = f.readAll();
f.close();

const AppearanceTokens &tokens = app->getAppearanceManager()->tokens();
if (comparePreviousAction) {
comparePreviousAction->setIcon(ThemedIcon::monochrome(
QStringLiteral(":/icons/git-compare-arrows.svg"),
tokens.textPrimary, tokens.textDisabled));
}
sheet += DockedEditor::tabTitleStyleSheet(tokens.textPrimary, tokens.textSecondary);

// If there is a "custom.css" file where the ini is located, load it as a style sheet addition
QString directoryPath = QFileInfo(app->getSettings()->fileName()).absolutePath();
QString fullPath = QDir(directoryPath).filePath("custom.css");
Expand Down
4 changes: 0 additions & 4 deletions src/stylesheets/npp.css
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,6 @@ ads--CDockAreaTitleBar
padding-bottom: 0px;
}

ads--CDockWidgetTab[activeTab="false"] QLabel {
color: palette(placeholder-text);
}

#tabCloseButton
{
background: transparent;
Expand Down
47 changes: 47 additions & 0 deletions tests/AppearanceManagerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,39 @@
#include <QTemporaryDir>
#include <QEvent>
#include <QFile>
#include <QtMath>
#include <QtTest>

namespace {

QColor composite(const QColor &foreground, const QColor &background)
{
const double alpha = foreground.alphaF();
return QColor::fromRgbF(
foreground.redF() * alpha + background.redF() * (1.0 - alpha),
foreground.greenF() * alpha + background.greenF() * (1.0 - alpha),
foreground.blueF() * alpha + background.blueF() * (1.0 - alpha));
}

double luminance(const QColor &color)
{
auto linear = [](double channel) {
return channel <= 0.04045 ? channel / 12.92 : qPow((channel + 0.055) / 1.055, 2.4);
};
return 0.2126 * linear(color.redF())
+ 0.7152 * linear(color.greenF())
+ 0.0722 * linear(color.blueF());
}

double contrast(const QColor &first, const QColor &second)
{
const double lighter = qMax(luminance(first), luminance(second));
const double darker = qMin(luminance(first), luminance(second));
return (lighter + 0.05) / (darker + 0.05);
}

} // namespace

#include "AppearanceManager.h"
#include "ApplicationSettings.h"

Expand All @@ -23,6 +54,7 @@ private slots:
void ignoresWidgetPaletteEventsWhenApplicationPaletteIsUnchanged();
void refreshesOnceWhenApplicationPaletteChanges();
void writesOptInPerformanceTrace();
void darkCompareLineFillsRemainVisible();
};

void AppearanceManagerTests::parsesOnlyCanonicalWireValues()
Expand Down Expand Up @@ -230,6 +262,21 @@ void AppearanceManagerTests::writesOptInPerformanceTrace()
QVERIFY(trace.contains(QStringLiteral(" end elapsed-ms=")));
}

void AppearanceManagerTests::darkCompareLineFillsRemainVisible()
{
QTemporaryDir directory;
ApplicationSettings settings(directory.filePath(QStringLiteral("settings.ini")), QSettings::IniFormat);
AppearanceManager manager(&settings, [] { return Qt::ColorScheme::Dark; });
manager.setRequestedMode(AppearanceManager::Mode::Dark);

const AppearanceTokens &tokens = manager.tokens();
const QColor added = composite(tokens.diffAddedFill, tokens.surfaceEditor);
const QColor deleted = composite(tokens.diffDeletedFill, tokens.surfaceEditor);

QVERIFY(contrast(added, tokens.surfaceEditor) >= 1.35);
QVERIFY(contrast(deleted, tokens.surfaceEditor) >= 1.35);
}

QTEST_MAIN(AppearanceManagerTests)

#include "AppearanceManagerTests.moc"
95 changes: 94 additions & 1 deletion tests/CompareSessionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#include "CompareToolBar.h"
#include "DockedEditor.h"
#include "ScintillaNext.h"
#include "ThemedIcon.h"

#include "DockWidgetTab.h"
#include "ElidingLabel.h"

#include <QAction>
#include <QElapsedTimer>
Expand All @@ -24,6 +28,8 @@
#include <QTemporaryDir>
#include <QTest>

#include <algorithm>

using Compare::ChangeKind;
using Compare::DiffHunk;
using Compare::Navigator;
Expand All @@ -34,6 +40,45 @@ class CompareSessionTests : public QObject
Q_OBJECT

private slots:
void darkStyleKeepsInactiveTabTitlesReadable()
{
QFile styleSheet(QStringLiteral(":/stylesheets/npp.css"));
QVERIFY(styleSheet.open(QIODevice::ReadOnly | QIODevice::Text));
const QByteArray css = styleSheet.readAll();
QWidget host;
QPalette darkPalette = host.palette();
darkPalette.setColor(QPalette::Window, QColor(QStringLiteral("#191A1B")));
darkPalette.setColor(QPalette::WindowText, QColor(QStringLiteral("#BFBFBF")));
darkPalette.setColor(QPalette::Base, QColor(QStringLiteral("#121314")));
darkPalette.setColor(QPalette::Text, QColor(QStringLiteral("#BBBEBF")));
darkPalette.setColor(QPalette::PlaceholderText, QColor(QStringLiteral("#555555")));
host.setPalette(darkPalette);
host.resize(900, 600);

DockedEditor dockedEditor(&host);
dockedEditor.addEditor(new ScintillaNext(QStringLiteral("first.cpp")));
dockedEditor.addEditor(new ScintillaNext(QStringLiteral("second.cpp")));
host.setStyleSheet(QString::fromUtf8(css)
+ DockedEditor::tabTitleStyleSheet(
QColor(QStringLiteral("#BFBFBF")), QColor(QStringLiteral("#8C8C8C"))));
host.show();
QCoreApplication::processEvents();

const QList<ads::CDockWidgetTab *> tabs = host.findChildren<ads::CDockWidgetTab *>();
QCOMPARE(tabs.size(), 2);
auto inactive = std::find_if(tabs.cbegin(), tabs.cend(), [](const auto *tab) {
return !tab->isActiveTab();
});
QVERIFY(inactive != tabs.cend());

const QList<ads::CElidingLabel *> labels =
(*inactive)->findChildren<ads::CElidingLabel *>();
QCOMPARE(labels.size(), 1);
labels.first()->ensurePolished();
QCOMPARE(labels.first()->palette().color(QPalette::WindowText),
QColor(QStringLiteral("#8C8C8C")));
}

void navigationWrapsInBothDirections()
{
Navigator navigator;
Expand Down Expand Up @@ -73,11 +118,59 @@ private slots:
dockedEditor.splitToRightOf(secondEditor, thirdEditor);
QVERIFY(dockedEditor.previousEditor(thirdEditor) == nullptr);
QCOMPARE(dockedEditor.previousEditor(secondEditor), firstEditor);

const QIcon compareIcon(QStringLiteral(":/icons/git-compare-arrows.svg"));
QVERIFY(!compareIcon.isNull());
}

void newlyAddedActiveTabCanCompareWithPrevious()
{
QWidget host;
host.resize(900, 600);
DockedEditor dockedEditor(&host);
auto *firstEditor = new ScintillaNext(QStringLiteral("New 1"));
auto *secondEditor = new ScintillaNext(QStringLiteral("New 2"));

dockedEditor.addEditor(firstEditor);
dockedEditor.addEditor(secondEditor);
host.show();
QCoreApplication::processEvents();

QCOMPARE(dockedEditor.getCurrentEditor(), secondEditor);
QCOMPARE(dockedEditor.previousEditor(dockedEditor.getCurrentEditor()), firstEditor);
}

void darkCompareActionIconUsesThemeColors()
{
const QColor normal(QStringLiteral("#BFBFBF"));
const QColor disabled(QStringLiteral("#555555"));
const QIcon icon = ThemedIcon::monochrome(
QStringLiteral(":/icons/git-compare-arrows.svg"), normal, disabled);

QVERIFY(!icon.isNull());
auto firstOpaquePixel = [](const QPixmap &pixmap) {
const QImage image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
for (int y = 0; y < image.height(); ++y) {
for (int x = 0; x < image.width(); ++x) {
const QColor pixel = image.pixelColor(x, y);
if (pixel.alpha() > 0)
return pixel;
}
}
return QColor();
};

const QColor normalPixel = firstOpaquePixel(icon.pixmap(24, 24, QIcon::Normal));
const QColor disabledPixel = firstOpaquePixel(icon.pixmap(24, 24, QIcon::Disabled));
QVERIFY(normalPixel.isValid());
QVERIFY(disabledPixel.isValid());
QCOMPARE(normalPixel.red(), normal.red());
QCOMPARE(normalPixel.green(), normal.green());
QCOMPARE(normalPixel.blue(), normal.blue());
QVERIFY(qAbs(disabledPixel.red() - disabled.red()) <= 8);
QVERIFY(qAbs(disabledPixel.green() - disabled.green()) <= 8);
QVERIFY(qAbs(disabledPixel.blue() - disabled.blue()) <= 8);
}

void mapsInsertedRowsToTheirNearestBoundary()
{
const QVector<DiffHunk> hunks = {
Expand Down