210 lines
6.6 KiB
C++
210 lines
6.6 KiB
C++
#include <QApplication>
|
|
#include <QQmlApplicationEngine>
|
|
#include <QQmlContext>
|
|
#include <QQuickStyle>
|
|
#include <QIcon>
|
|
#include <QPixmap>
|
|
#include <QPainter>
|
|
#include <QFont>
|
|
#include <QFontDatabase>
|
|
#include <QSystemTrayIcon>
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
#include <QWindow>
|
|
#include <QDBusConnection>
|
|
#include <QDBusInterface>
|
|
|
|
#include "olliedbusclient.h"
|
|
#include "thememanager.h"
|
|
#include "chatblockmodel.h"
|
|
|
|
class GuiActivator : public QObject
|
|
{
|
|
Q_OBJECT
|
|
Q_CLASSINFO("D-Bus Interface", "org.ollie.Gui")
|
|
|
|
public:
|
|
explicit GuiActivator(QQmlApplicationEngine *engine, QObject *parent = nullptr)
|
|
: QObject(parent)
|
|
, m_engine(engine)
|
|
{
|
|
}
|
|
|
|
public slots:
|
|
void Activate()
|
|
{
|
|
const auto roots = m_engine->rootObjects();
|
|
if (roots.isEmpty())
|
|
return;
|
|
auto *window = qobject_cast<QWindow *>(roots.first());
|
|
if (!window)
|
|
return;
|
|
window->show();
|
|
window->raise();
|
|
window->requestActivate();
|
|
}
|
|
|
|
void SelectSession(const QString &id)
|
|
{
|
|
auto *dbus = qobject_cast<OllieDBusClient *>(
|
|
m_engine->rootContext()->contextProperty("dbus").value<QObject *>());
|
|
if (dbus)
|
|
dbus->setActiveSessionId(id);
|
|
Activate();
|
|
}
|
|
|
|
private:
|
|
QQmlApplicationEngine *m_engine;
|
|
};
|
|
|
|
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;
|
|
|
|
// Only one GUI may run per desktop session. A second invocation asks the
|
|
// existing process to show and activate its window, then exits.
|
|
constexpr auto guiService = "org.ollie.Gui";
|
|
constexpr auto guiPath = "/org/ollie/Gui";
|
|
GuiActivator activator(&engine, &app);
|
|
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
|
sessionBus.registerObject(guiPath, &activator, QDBusConnection::ExportAllSlots);
|
|
if (!sessionBus.registerService(guiService)) {
|
|
QDBusInterface runningGui(guiService, guiPath, guiService, sessionBus);
|
|
runningGui.call(QDBus::NoBlock, QStringLiteral("Activate"));
|
|
return 0;
|
|
}
|
|
|
|
// Register types for QML instantiation
|
|
qmlRegisterType<ChatBlockModel>("org.ollie.gui", 1, 0, "ChatBlockModel");
|
|
|
|
// 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<QWindow *>(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<QWindow *>(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();
|
|
}
|
|
|
|
#include "main.moc"
|