diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 121bc7c4..6f5ace30 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,10 +77,10 @@ jobs: with: arch: x64 - - name: Install Qt 6.8 + - name: Install Qt 6.8.1 uses: jurplel/install-qt-action@v4 with: - version: "6.8.0" + version: "6.8.1" host: windows target: desktop arch: win64_msvc2022_64 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 823b1341..037c4998 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -135,10 +135,10 @@ jobs: - name: Install NSIS run: choco install nsis --yes --no-progress - - name: Install Qt 6.8 + - name: Install Qt 6.8.1 uses: jurplel/install-qt-action@v4 with: - version: "6.8.0" + version: "6.8.1" host: windows target: desktop arch: win64_msvc2022_64 diff --git a/BUILDING_WINDOWS.md b/BUILDING_WINDOWS.md index 74dbf39d..4cc64ce0 100644 --- a/BUILDING_WINDOWS.md +++ b/BUILDING_WINDOWS.md @@ -41,7 +41,7 @@ files, in addition to the space requested by each installer. | --- | --- | | [Git for Windows](https://git-scm.com/downloads/win) | Use the normal installer and allow Git to be used from the command line. | | [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio) or Visual Studio Community | Select **Desktop development with C++**, including **MSVC x64/x86 build tools**, a **Windows SDK**, and **C++ CMake tools for Windows**. The full editor is optional. | -| [Qt Online Installer](https://www.qt.io/development/download-open-source) | Choose the **MSVC 2022 64-bit** desktop component. The tested version is **Qt 6.11.1**. Do not choose MinGW or ARM64. | +| [Qt Online Installer](https://www.qt.io/development/download-open-source) | Choose the **MSVC 2022 64-bit** desktop component. **Qt 6.8.1 or newer is required**; 6.8.0 has a Windows font-matching bug. The tested version is **Qt 6.11.1**. Do not choose MinGW or ARM64. | | [LilyPond for Windows](https://lilypond.org/download.html) | Extract the **entire** Windows x86_64 ZIP. The tested version is **2.26.0**. Keep its `bin`, `lib`, and `share` folders together. | The Visual Studio CMake component supplies CMake and Ninja. This project needs diff --git a/LASSIE/CMakeLists.txt b/LASSIE/CMakeLists.txt index 9ab8c689..e4b053a8 100644 --- a/LASSIE/CMakeLists.txt +++ b/LASSIE/CMakeLists.txt @@ -4,6 +4,11 @@ project(LASSIE) message(STATUS "== LASSIE ==") find_package(Qt6 REQUIRED COMPONENTS Widgets Core Xml Network) +if(WIN32 AND Qt6_VERSION VERSION_LESS "6.8.1") + message(FATAL_ERROR + "Windows builds require Qt 6.8.1 or newer. Qt 6.8.0 can substitute " + "the wrong font family for the Windows system font.") +endif() set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) @@ -104,6 +109,20 @@ target_include_directories(LASSIE PRIVATE ${CMAKE_SOURCE_DIR}/LASS/src ) +if(NOT APPLE) + qt_add_resources(LASSIE "app_icons" + PREFIX "/icons" + BASE "${CMAKE_SOURCE_DIR}/packaging/linux" + FILES "${CMAKE_SOURCE_DIR}/packaging/linux/LASSIE.png" + ) +endif() + +if(WIN32) + configure_file("${CMAKE_SOURCE_DIR}/packaging/windows/LASSIE.rc.in" + "${CMAKE_CURRENT_BINARY_DIR}/LASSIE.rc" @ONLY) + target_sources(LASSIE PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/LASSIE.rc") +endif() + set(LASSIE_ICON_MACOS "${CMAKE_SOURCE_DIR}/packaging/macos/LASSIE.icns") if(APPLE AND EXISTS "${LASSIE_ICON_MACOS}") target_sources(LASSIE PRIVATE "${LASSIE_ICON_MACOS}") @@ -131,6 +150,13 @@ install(TARGETS LASSIE RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime ) +if(UNIX AND NOT APPLE) + install(FILES "${CMAKE_SOURCE_DIR}/packaging/linux/LASSIE.desktop" + DESTINATION "${CMAKE_INSTALL_DATADIR}/applications" COMPONENT Runtime) + install(FILES "${CMAKE_SOURCE_DIR}/packaging/linux/LASSIE.png" + DESTINATION "${CMAKE_INSTALL_DATADIR}/icons/hicolor/256x256/apps" COMPONENT Runtime) +endif() + if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(LASSIE) endif() @@ -181,6 +207,21 @@ endif() if(BUILD_TESTING) target_compile_definitions(LASSIE PRIVATE DISSCO_ENABLE_UI_LAYOUT_TESTS) + if(NOT APPLE) + add_test(NAME LASSIE.ApplicationIdentity COMMAND LASSIE) + set_tests_properties(LASSIE.ApplicationIdentity PROPERTIES + ENVIRONMENT "QT_QPA_PLATFORM=offscreen;QT_FORCE_STDERR_LOGGING=1;DISSCO_TEST_APPLICATION_IDENTITY=1" + TIMEOUT 10 + ) + endif() + if(WIN32) + add_test(NAME LASSIE.WindowsSystemFont COMMAND LASSIE) + set_tests_properties(LASSIE.WindowsSystemFont PROPERTIES + ENVIRONMENT "QT_QPA_PLATFORM=windows;QT_FORCE_STDERR_LOGGING=1;DISSCO_TEST_WINDOWS_FONT=1" + SKIP_RETURN_CODE 77 + TIMEOUT 10 + ) + endif() add_test(NAME LASSIE.EnvelopeLegendLayout COMMAND LASSIE ) diff --git a/LASSIE/src/main.cpp b/LASSIE/src/main.cpp index 18c5966b..5ba5ce2e 100644 --- a/LASSIE/src/main.cpp +++ b/LASSIE/src/main.cpp @@ -2,10 +2,19 @@ #include #include +#include #ifdef DISSCO_ENABLE_UI_LAYOUT_TESTS #include +#include +#include #include #include +#ifdef Q_OS_WIN +#ifndef NOGDI +#define NOGDI +#endif +#include +#endif #include "windows/EnvelopeLibraryWindow.hpp" #endif #include "widgets/ComboBoxWheelGuard.hpp" @@ -24,10 +33,56 @@ int main(int argc, char *argv[]) QCoreApplication::setApplicationName("LASSIE"); QApplication a(argc, argv); +#ifndef Q_OS_MACOS + a.setWindowIcon(QIcon(QStringLiteral(":/icons/LASSIE.png"))); +#endif +#ifdef Q_OS_LINUX + a.setDesktopFileName(QStringLiteral("LASSIE")); +#endif ComboBoxWheelGuard comboBoxWheelGuard(a); TextOverflowDisplayPolicy textOverflowDisplayPolicy(a); #ifdef DISSCO_ENABLE_UI_LAYOUT_TESTS +#ifdef Q_OS_WIN + if (qEnvironmentVariableIsSet("DISSCO_TEST_WINDOWS_FONT")) { + const QFont font = a.font(); + if (!QFontDatabase::families().contains(font.family(), Qt::CaseInsensitive)) { + qWarning() << "Skipping font check: system font uses an unlisted alias" + << font.family(); + return 77; + } + const QString resolvedFamily = QFontInfo(font).family(); + if (font.family().compare(resolvedFamily, Qt::CaseInsensitive) != 0) { + qCritical() << "Installed Windows system font was substituted:" + << "requested" << font.family() << "resolved" << resolvedFamily; + return 1; + } + return 0; + } +#endif + + if (qEnvironmentVariableIsSet("DISSCO_TEST_APPLICATION_IDENTITY")) { + EnvelopeLibraryWindow window; + if (a.windowIcon().pixmap(32, 32).isNull() + || window.windowIcon().pixmap(32, 32).isNull()) { + qCritical() << "Application and top-level windows must have a usable icon"; + return 1; + } +#ifdef Q_OS_WIN + if (!FindResourceW(nullptr, MAKEINTRESOURCEW(1), RT_GROUP_ICON)) { + qCritical() << "Executable is missing its Windows application icon resource"; + return 1; + } +#endif +#ifdef Q_OS_LINUX + if (a.desktopFileName() != QStringLiteral("LASSIE")) { + qCritical() << "Desktop identity must match LASSIE.desktop"; + return 1; + } +#endif + return 0; + } + if (qEnvironmentVariableIsSet("DISSCO_TEST_ENVELOPE_LAYOUT")) { EnvelopeLibraryWindow window; window.ensurePolished(); diff --git a/packaging/windows/LASSIE.rc.in b/packaging/windows/LASSIE.rc.in new file mode 100644 index 00000000..7a0d6292 --- /dev/null +++ b/packaging/windows/LASSIE.rc.in @@ -0,0 +1 @@ +1 ICON "@CMAKE_SOURCE_DIR@/packaging/windows/LASSIE.ico"