[office/crow-translate] /: Use KIconThemes for icon loading with theme fallback
Mauritius Clemens <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit db0775f7a38fb92e732639ed4bf61b4ac2870b5e by Mauritius Clemens.
Committed on 16/08/2026 at 21:39.
Pushed by pillowtrucker into branch 'master'.
Use KIconThemes for icon loading with theme fallback
Some icon names used by crow-translate (e.g. the Clear button's
"edit-clear-all") are breeze-era names that older themes such as Oxygen
do not provide, leaving the affected widgets without an icon (bug 509329).
Link KIconThemes (WITH_KICONTHEMES, default ON) and:
- call KIconTheme::initTheme() before constructing the application object;
it installs KIconEngine so that ALL QIcon::fromTheme() lookups (including
the ones uic generates from .ui files) resolve through the full KDE theme
chain: the configured theme, its Inherits chain and the fallback theme
- route the app/tray icon loads through a small IconUtils helper using
KIconLoader, falling back to QIcon::fromTheme when KIconThemes is not
available or a lookup comes up empty (canReturnNull, no placeholder)
- load the window icon after the application object exists, as
KIconLoader::global() requires a live QGuiApplication
CCBUG: 509329
M +1 -0 .kde-ci.yml
M +9 -0 CMakeLists.txt
A +54 -0 src/iconutils.h [License: GPL(v3.0+)]
M +20 -1 src/main.cpp
M +4 -3 src/trayicon.cpp
https://invent.kde.org/office/crow-translate/-/commit/db0775f7a38fb92e732639ed4bf61b4ac2870b5e
diff --git a/.kde-ci.yml b/.kde-ci.yml
index 7d3d9ae5..2df79048 100644
--- a/.kde-ci.yml
+++ b/.kde-ci.yml
@@ -5,6 +5,7 @@ Dependencies:
- 'on': ['@all']
'require':
'frameworks/extra-cmake-modules': '@stable-kf6'
+ 'frameworks/kiconthemes': '@stable-kf6'
- 'on': ['Linux', 'FreeBSD']
'require':
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6f262b0c..d0e5f0b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -70,6 +70,7 @@ else()
option(WITH_PORTABLE_MODE "Enable portable functionality" OFF)
endif()
option(WITH_KWAYLAND "Use KWayland for better Wayland integration" ON)
+option(WITH_KICONTHEMES "Use KIconThemes for proper icon theme fallback" ON)
option(WITH_PIPER_TTS "Enable Piper neural TTS provider (requires onnxruntime and espeak-ng)" ON)
option(WITH_TTS "Enable text-to-speech support (Mozhi and Qt providers, plus Piper if WITH_PIPER_TTS)" ON)
@@ -96,6 +97,9 @@ add_subdirectory(src/3rdparty/qhotkey)
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Network LinguistTools Concurrent StateMachine TextToSpeech)
find_package(Tesseract REQUIRED)
+if(WITH_KICONTHEMES)
+ find_package(KF6 REQUIRED COMPONENTS IconThemes)
+endif()
if(UNIX)
find_package(Qt6 REQUIRED COMPONENTS DBus)
if(NOT APPLE)
@@ -444,6 +448,11 @@ target_link_libraries(${PROJECT_NAME}-lib PUBLIC
target_link_libraries(${PROJECT_NAME}-lib PUBLIC Qt6::TextToSpeech)
+if(WITH_KICONTHEMES)
+ target_link_libraries(${PROJECT_NAME}-lib PUBLIC KF6::IconThemes)
+ target_compile_definitions(${PROJECT_NAME}-lib PUBLIC WITH_KICONTHEMES)
+endif()
+
# Link Piper dependencies if enabled
if(WITH_PIPER_TTS)
if(NOT ONNXRuntime_USE_STATIC)
diff --git a/src/iconutils.h b/src/iconutils.h
new file mode 100644
index 00000000..2d8199c4
--- /dev/null
+++ b/src/iconutils.h
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: 2025 Mauritius Clemens <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <QIcon>
+#include <QPixmap>
+#include <QString>
+
+#ifdef WITH_KICONTHEMES
+#include <KIconLoader>
+#endif
+
+namespace IconUtils
+{
+/*
+ * Icon loading helpers with theme fallback.
+ *
+ * Some icon names crow-translate uses (e.g. "edit-clear-all") are breeze
+ * names that older themes like Oxygen do not provide (bug 509329). When
+ * KIconThemes is available we resolve through KIconLoader, which walks the
+ * full KDE theme chain (configured theme -> its Inherits -> fallback theme
+ * -> the themes compiled into the binary via qrc). KIconLoader requires a
+ * living QGuiApplication, so only call these after the app object exists.
+ *
+ * canReturnNull=true makes KIconLoader return a null pixmap instead of the
+ * "unknown" placeholder when nothing is found, so the QIcon::fromTheme
+ * fallback can engage.
+ */
+inline QIcon load(const QString &name)
+{
+#ifdef WITH_KICONTHEMES
+ const QPixmap pixmap = KIconLoader::global()->loadIcon(name, KIconLoader::Panel, 22, KIconLoader::DefaultState, QStringList(), nullptr, true);
+ if (!pixmap.isNull()) {
+ return QIcon(pixmap);
+ }
+#endif
+ return QIcon::fromTheme(name);
+}
+
+inline QIcon loadDesktop(const QString &name)
+{
+#ifdef WITH_KICONTHEMES
+ const QPixmap pixmap = KIconLoader::global()->loadIcon(name, KIconLoader::Desktop, 512, KIconLoader::DefaultState, QStringList(), nullptr, true);
+ if (!pixmap.isNull()) {
+ return QIcon(pixmap);
+ }
+#endif
+ return QIcon::fromTheme(name);
+}
+} // namespace IconUtils
diff --git a/src/main.cpp b/src/main.cpp
index 0102c7aa..ac0719b8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -7,12 +7,17 @@
#include "cli.h"
#include "cmake.h"
+#include "iconutils.h"
#include "instancepingerdialog.h"
#include "language.h"
#include "mainwindow.h"
#include "singleapplication.h"
#include "settings/appsettings.h"
+#ifdef WITH_KICONTHEMES
+#include <KIconTheme>
+#endif
+
#ifdef Q_OS_UNIX
#include "ocr/ocr.h"
@@ -44,7 +49,16 @@ int launchGui(int argc, char *argv[])
Q_INIT_RESOURCE(engines);
Q_INIT_RESOURCE(icon_theme);
- QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral(APPLICATION_ID)));
+#ifdef WITH_KICONTHEMES
+ // Set up KDE icon theming before the application object exists (required;
+ // it installs a startup hook). This makes QIcon::fromTheme() calls go
+ // through KIconEngine, which resolves icons through the full KDE theme
+ // chain (configured theme -> its Inherits -> fallback), so names that only
+ // exist in newer themes (e.g. "edit-clear-all" under Oxygen, bug 509329)
+ // still resolve. On the KDE platform theme it is a no-op.
+ KIconTheme::initTheme();
+#endif
+
#if defined(Q_OS_WIN)
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
@@ -56,6 +70,11 @@ int launchGui(int argc, char *argv[])
const SingleApplication app(argc, argv, true);
+ // KIconLoader::global() requires a live QGuiApplication; set the window
+ // icon only after the application object exists (loading it earlier
+ // crashes in the color-scheme lookup on a null application).
+ QGuiApplication::setWindowIcon(IconUtils::loadDesktop(QStringLiteral(APPLICATION_ID)));
+
AppSettings settings;
settings.loadCustomLanguageRegistry(); // Load persisted custom languages
Language::setCustomLanguageRegistryChangedCallback(&AppSettings::onCustomLanguageRegistryChanged);
diff --git a/src/trayicon.cpp b/src/trayicon.cpp
index af85748c..01de5612 100644
--- a/src/trayicon.cpp
+++ b/src/trayicon.cpp
@@ -7,6 +7,7 @@
#include "trayicon.h"
+#include "iconutils.h"
#include "mainwindow.h"
#include <QAction>
@@ -22,9 +23,9 @@
TrayIcon::TrayIcon(MainWindow *parent)
: QSystemTrayIcon(parent)
, m_trayMenu(new QMenu(parent))
- , m_showMainWindowAction(m_trayMenu->addAction(QIcon::fromTheme(QStringLiteral(APPLICATION_ID "-tray")), tr("Show window"), parent, &MainWindow::open))
- , m_openSettingsAction(m_trayMenu->addAction(QIcon::fromTheme(QStringLiteral("preferences-other")), tr("Settings"), parent, &MainWindow::openSettings))
- , m_quitAction(m_trayMenu->addAction(QIcon::fromTheme(QStringLiteral("application-exit")), tr("Quit"), parent, &MainWindow::quit))
+ , m_showMainWindowAction(m_trayMenu->addAction(IconUtils::load(QStringLiteral(APPLICATION_ID "-tray")), tr("Show window"), parent, &MainWindow::open))
+ , m_openSettingsAction(m_trayMenu->addAction(IconUtils::load(QStringLiteral("preferences-other")), tr("Settings"), parent, &MainWindow::openSettings))
+ , m_quitAction(m_trayMenu->addAction(IconUtils::load(QStringLiteral("application-exit")), tr("Quit"), parent, &MainWindow::quit))
{
m_reportBugAction = m_trayMenu->addAction(tr("Report Bug…"));
connect(m_reportBugAction, &QAction::triggered, this, []() {