#include #include #include #include #include #include #include #include #include #include #include #include #include #include "olliedbusclient.h" #include "thememanager.h" #include "chatblockmodel.h" static QIcon makeBrainIcon() { // Use the theme icon if available (from hicolor scalable/apps/ollie.svg) if (QIcon::hasThemeIcon("ollie")) return QIcon::fromTheme("ollie"); // Fallback: render FA6 brain glyph from installed font QFont fa("Font Awesome 6 Free Solid"); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (!QFontDatabase::hasFamily("Font Awesome 6 Free Solid")) { #else if (!QFontDatabase().families().contains("Font Awesome 6 Free Solid")) { #endif fa = QFont("Font Awesome 6 Free"); fa.setWeight(QFont::Black); } QIcon icon; for (int size : {16, 22, 32, 48, 64, 128}) { QPixmap pix(size, size); pix.fill(Qt::transparent); QPainter p(&pix); fa.setPixelSize(size - 2); p.setFont(fa); p.setPen(QColor(0xe9, 0x1e, 0x63)); p.drawText(pix.rect(), Qt::AlignCenter, QString(QChar(0xf5dc))); p.end(); icon.addPixmap(pix); } return icon; } int main(int argc, char *argv[]) { // HiDPI support #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif QApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication app(argc, argv); app.setApplicationName("ollie-gui"); app.setDesktopFileName("org.ollie.gui"); app.setOrganizationName("ollie"); app.setWindowIcon(makeBrainIcon()); // Don't force org.kde.desktop style — it ignores explicit palette overrides. // Let the platform theme provide the style, or fall back to Basic which // properly respects the QML palette {} block. if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) QQuickStyle::setStyle("Basic"); // Theme manager — reads KDE color schemes, applies palette auto *theme = new ThemeManager(&app); QQmlApplicationEngine engine; // Register OllieDBusClient as a singleton auto *dbus = new OllieDBusClient(&app); auto *chatModel = new ChatBlockModel(&app); engine.rootContext()->setContextProperty("dbus", dbus); engine.rootContext()->setContextProperty("theme", theme); engine.rootContext()->setContextProperty("chatModel", chatModel); engine.rootContext()->setContextProperty("systemTrayAvailable", QSystemTrayIcon::isSystemTrayAvailable()); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QUrl url(QStringLiteral("qrc:/gui/main.qml")); #else const QUrl url(QStringLiteral("qrc:/main.qml")); #endif QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); // --- System tray icon (shown when close-to-tray is enabled) --- auto *trayIcon = new QSystemTrayIcon(&app); trayIcon->setIcon(app.windowIcon()); trayIcon->setToolTip("Ollie"); auto *trayMenu = new QMenu(); auto *showAction = trayMenu->addAction("Show"); trayMenu->addSeparator(); auto *quitAction = trayMenu->addAction("Quit"); trayIcon->setContextMenu(trayMenu); // Toggle window visibility on tray icon activation QObject::connect(trayIcon, &QSystemTrayIcon::activated, [&engine](QSystemTrayIcon::ActivationReason reason) { if (reason != QSystemTrayIcon::Trigger) return; auto roots = engine.rootObjects(); if (roots.isEmpty()) return; auto *window = qobject_cast(roots.first()); if (!window) return; if (window->isVisible()) { window->hide(); } else { window->show(); window->raise(); window->requestActivate(); } }); QObject::connect(showAction, &QAction::triggered, [&engine]() { auto roots = engine.rootObjects(); if (roots.isEmpty()) return; auto *window = qobject_cast(roots.first()); if (!window) return; window->show(); window->raise(); window->requestActivate(); }); QObject::connect(quitAction, &QAction::triggered, &app, &QApplication::quit); // Show/hide tray icon based on setting auto updateTrayVisibility = [trayIcon, theme]() { trayIcon->setVisible(theme->closeToTray()); }; updateTrayVisibility(); QObject::connect(theme, &ThemeManager::closeToTrayChanged, updateTrayVisibility); return app.exec(); }