diff --git a/README.md b/README.md index 5e18ccfb..62797409 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * Option to ignore certain file extensions when navigating through a folder. * Show image loading errors inside viewport instead of as modal dialog. * Improved performance during rapid image navigation when holding down the previous/next file shortcut keys, and configurable speed no longer linked to key repeat rate. -* Windows: Dark mode supported in Windows 11 by default. Windows 10 users get a "non-native theme" option which supports dark mode. +* Windows: New style which supports dark mode. * macOS: Option to reuse existing window when launching with image. * Configurable window positioning behavior after matching image size. * More accurate zoom-to-fit plus customizable overscan setting. @@ -27,7 +27,7 @@ ![screenshot](docs/screenshot.png) ## Supported platforms * Windows 10+ (x64 or ARM64 binaries). You may need to install the [Visual C++ runtime](https://aka.ms/vs/17/release/vc_redist.x64.exe) if you don't have it already. -* macOS 11+ (Universal binary). +* macOS 12+ (Universal binary). * Legacy Windows (7+, x86), Legacy macOS (10.13+, x64), and Linux AppImage binaries are built via GitHub Actions, but not well tested nor published as releases. ## About releases There's nothing particularly special about the builds uploaded under "Releases". Every once in a while, typically after enough noteworthy changes, I simply download the binaries from a GitHub Actions run and upload them as a "Release". I do this to make them more easily accessible since the Actions artifacts are only available to users logged into GitHub, and to preserve them since the Actions artifacts expire after 90 days. But in general, I avoid committing unfinished/untested work to the `master` branch. So if you see a commit you're interested in there, no need to wait for a release; simply download the artifact from Actions. diff --git a/dist/scripts/download-plugins.ps1 b/dist/scripts/download-plugins.ps1 index b09e41f8..dfc83cad 100755 --- a/dist/scripts/download-plugins.ps1 +++ b/dist/scripts/download-plugins.ps1 @@ -72,7 +72,11 @@ if ($pluginNames -contains 'KImageFormats') { CopyFrameworkDlls "heif.dll" @("libde265.dll") CopyFrameworkDlls "raw.dll" @("lcms2.dll", "zlib1.dll") CopyFrameworkDlls "jxl.dll" @("brotlicommon.dll", "brotlidec.dll", "brotlienc.dll", "hwy.dll", "jxl_cms.dll", "jxl_threads.dll", "lcms2.dll") - CopyFrameworkDlls "OpenEXR-3_2.dll" @("deflate.dll", "Iex-3_2.dll", "IlmThread-3_2.dll", "Imath-3_1.dll", "OpenEXRCore-3_2.dll") + if ($kfMajorVer -ge 6) { + CopyFrameworkDlls "OpenEXR-3_3.dll" @("deflate.dll", "Iex-3_3.dll", "IlmThread-3_3.dll", "Imath-3_1.dll", "OpenEXRCore-3_3.dll") + } else { + CopyFrameworkDlls "OpenEXR-3_2.dll" @("deflate.dll", "Iex-3_2.dll", "IlmThread-3_2.dll", "Imath-3_1.dll", "OpenEXRCore-3_2.dll") + } } elseif ($IsMacOS) { cp KImageFormats/KImageFormats/output/kimg_*.* "$out_imf/" cp KImageFormats/KImageFormats/output/libKF?Archive.?.dylib "$out_frm/" diff --git a/dist/scripts/windeployqt.ps1 b/dist/scripts/windeployqt.ps1 index 7979a4de..e5f21f5e 100755 --- a/dist/scripts/windeployqt.ps1 +++ b/dist/scripts/windeployqt.ps1 @@ -45,6 +45,12 @@ if ($env:buildArch -eq 'Arm64') { windeployqt --no-compiler-runtime bin\qView.exe } +if ($qtVersion -ge [version]'6.8.1') { + # Copy font so windows11 style can work on Windows 10 + New-Item -ItemType Directory -Path "bin\fonts" -Force + Copy-Item -Path "dist\win\fonts\Segoe Fluent Icons.ttf" -Destination "bin\fonts" +} + if ($NightlyVersion -eq '') { # Call innomake if we are not building a nightly version (no version passed) & "dist/scripts/innomake.ps1" diff --git a/dist/win/fonts/Segoe Fluent Icons.ttf b/dist/win/fonts/Segoe Fluent Icons.ttf new file mode 100644 index 00000000..8f05a4bb Binary files /dev/null and b/dist/win/fonts/Segoe Fluent Icons.ttf differ diff --git a/src/main.cpp b/src/main.cpp index c4c82a83..2b2a400b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "qvwin32functions.h" #include +#include int main(int argc, char *argv[]) { @@ -16,30 +17,44 @@ int main(int argc, char *argv[]) SettingsManager::migrateOldSettings(); + QString defaultStyleName; + QStringList fontsToInstall; + #ifdef Q_OS_WIN - bool useNonNativeTheme = QSettings().value("options/nonnativetheme").toBool(); -#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) - useNonNativeTheme = false; -#elif QT_VERSION >= QT_VERSION_CHECK(6, 7, 3) - if (useNonNativeTheme && QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) - useNonNativeTheme = false; // Disable if we can use windows11 style instead -#endif - QString styleName = useNonNativeTheme ? "fusion" : QString(); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && QT_VERSION <= QT_VERSION_CHECK(6, 7, 2) - if (styleName.isEmpty()) - styleName = "windowsvista"; // windows11 style was buggy for a while after it was introduced + defaultStyleName = "windowsvista"; // windows11 style was buggy for a while after it was introduced +#elif QT_VERSION >= QT_VERSION_CHECK(6, 8, 1) + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows11) + { + // Qt's windows11 style can work on Windows 10, but it isn't enabled by default because the required + // font isn't included with Windows 10. If we can locate and install the font, use windows11 style. + const QString windows11StyleFontPath = QDir(QApplication::applicationDirPath()).filePath("fonts/Segoe Fluent Icons.ttf"); + if (QFile::exists(windows11StyleFontPath)) + { + defaultStyleName = "windows11"; + fontsToInstall.append(windows11StyleFontPath); + } + } #endif - // The docs recommend calling QApplication's static setStyle before its constructor, one reason - // being that this allows styles to be set via command line arguments. Unfortunately it seems - // that after running windeployqt, some styles such as windowsvista and windows11 aren't yet - // loaded/available until the constructor runs. So we'll use this environment variable instead, - // which Qt uses as a fallback override mechanism if a style wasn't specified via command line. - if (!styleName.isEmpty()) - qputenv("QT_STYLE_OVERRIDE", styleName.toLocal8Bit()); #endif + if (!defaultStyleName.isEmpty() && qEnvironmentVariableIsEmpty("QT_STYLE_OVERRIDE")) + { + // The docs recommend calling QApplication's static setStyle before its constructor, one reason + // being that this allows styles to be set via command line arguments. Unfortunately it seems + // that after running windeployqt, some styles such as windowsvista and windows11 aren't yet + // loaded/available until the constructor runs. So we'll use this environment variable instead, + // which Qt uses as a fallback override mechanism if a style wasn't specified via command line. + qputenv("QT_STYLE_OVERRIDE", defaultStyleName.toLocal8Bit()); + } + QVApplication app(argc, argv); + for (const QString &font : fontsToInstall) + { + QFontDatabase::addApplicationFont(font); + } + QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); diff --git a/src/qvoptionsdialog.cpp b/src/qvoptionsdialog.cpp index 26b6b914..dc9e757c 100644 --- a/src/qvoptionsdialog.cpp +++ b/src/qvoptionsdialog.cpp @@ -26,7 +26,6 @@ QVOptionsDialog::QVOptionsDialog(QWidget *parent) : connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &QVOptionsDialog::buttonBoxClicked); connect(ui->shortcutsTable, &QTableWidget::cellDoubleClicked, this, &QVOptionsDialog::shortcutCellDoubleClicked); connect(ui->bgColorCheckbox, &QCheckBox::stateChanged, this, &QVOptionsDialog::bgColorCheckboxStateChanged); - connect(ui->nonNativeThemeCheckbox, &QCheckBox::stateChanged, this, [this](int state) { restartNotifyForCheckbox("nonnativetheme", state); }); connect(ui->submenuIconsCheckbox, &QCheckBox::stateChanged, this, [this](int state) { restartNotifyForCheckbox("submenuicons", state); }); connect(ui->slideshowKeepsWindowOnTopCheckbox, &QCheckBox::stateChanged, this, [this](int state) { restartNotifyForCheckbox("slideshowkeepswindowontop", state); }); connect(ui->smoothScalingLimitCheckbox, &QCheckBox::stateChanged, this, &QVOptionsDialog::smoothScalingLimitCheckboxStateChanged); @@ -64,16 +63,6 @@ QVOptionsDialog::QVOptionsDialog(QWidget *parent) : setWindowTitle(tr("Preferences")); // Platform specific settings -#ifdef Q_OS_WIN -#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) - ui->nonNativeThemeCheckbox->hide(); -#elif QT_VERSION >= QT_VERSION_CHECK(6, 7, 3) - if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) - ui->nonNativeThemeCheckbox->hide(); -#endif -#else - ui->nonNativeThemeCheckbox->hide(); -#endif #ifdef Q_OS_MACOS ui->menubarCheckbox->hide(); #else @@ -188,8 +177,6 @@ void QVOptionsDialog::syncSettings(bool defaults, bool makeConnections) syncSpinBox(ui->minWindowResizeSpinBox, "minwindowresizedpercentage", defaults, makeConnections); // maxwindowresizedperecentage syncSpinBox(ui->maxWindowResizeSpinBox, "maxwindowresizedpercentage", defaults, makeConnections); - // nonnativetheme - syncCheckbox(ui->nonNativeThemeCheckbox, "nonnativetheme", defaults, makeConnections); // titlebaralwaysdark syncCheckbox(ui->darkTitlebarCheckbox, "titlebaralwaysdark", defaults, makeConnections); // quitonlastwindow @@ -617,13 +604,10 @@ void QVOptionsDialog::customizePalette() palette.setColor(QPalette::Highlight, Qv::getPerceivedBrightness(textColor) > 0.5 ? QColor(0, 65, 127) : QColor(75, 166, 255)); ui->categoryList->setPalette(palette); } -#if defined(Q_OS_WIN) && QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) +#if defined(Q_OS_WIN) && QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && QT_VERSION < QT_VERSION_CHECK(6, 8, 1) if (currentStyle.compare("windows11", Qt::CaseInsensitive) == 0) { - // This is to work around styling issues with radio buttons and checkboxes. Not sure if it's because of - // the type of container they're in (stacked widget + scroll area) but they seem to inherit incorrect - // colors. Setting the widgets' palettes to the application's palette isn't enough because Qt keeps - // track of which colors were explicitly set, so we need to set the specific colors we need. + // Workaround for QTBUG-130828 const auto specifyWindowAndAccentColors = [&](QWidget *widget) { QPalette palette = widget->palette(); palette.setColor(QPalette::Active, QPalette::Window, appPalette.color(QPalette::Active, QPalette::Window)); diff --git a/src/qvoptionsdialog.ui b/src/qvoptionsdialog.ui index 6401d662..3e65e3da 100644 --- a/src/qvoptionsdialog.ui +++ b/src/qvoptionsdialog.ui @@ -298,20 +298,13 @@ - - - Non-native theme (supports dark mode) - - - - Show menubar - + Choose whether or not to display the titlebar text while in fullscreen @@ -321,7 +314,7 @@ - + Show icons in "Open Recent" and "Open With" submenus @@ -334,21 +327,21 @@ - + Keep window on top during slideshow - + Reuse window when launching with image - + Choose whether or not the titlebar should always be dark regardless of your chosen macOS appearance @@ -361,14 +354,14 @@ - + &Quit on last window closed - + Persist session across app restarts diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp index b87cde19..c6b38804 100644 --- a/src/settingsmanager.cpp +++ b/src/settingsmanager.cpp @@ -196,7 +196,6 @@ void SettingsManager::initializeSettingsLibrary() settingsLibrary.insert("aftermatchingsizemode", {static_cast(Qv::AfterMatchingSize::CenterOnPrevious), {}}); settingsLibrary.insert("minwindowresizedpercentage", {20, {}}); settingsLibrary.insert("maxwindowresizedpercentage", {70, {}}); - settingsLibrary.insert("nonnativetheme", {false, {}}); settingsLibrary.insert("titlebaralwaysdark", {false, {}}); settingsLibrary.insert("quitonlastwindow", {false, {}}); settingsLibrary.insert("menubarenabled", {false, {}});