Files
Nebula-OS/core/applications/ApplicationService.cpp
T
andrew b0c95b9e78 Add desktop app service and VM dev session tooling
Introduces a new `core/applications` QML/C++ module (`Nebula.Applications`) to discover `.desktop` entries, expose filterable application models, and launch installed apps safely from the shell. The desktop launcher now uses real installed apps with themed icon loading and fallback glyphs, adds output-aware surface sizing via `OutputTracker`, and wires in image/icon providers. It also adds VMware-focused Sway/shell wrapper scripts, updates Sway config/session env defaults, and refreshes README docs to document the new development flow and Desktop 0.1 behavior.
2026-08-26 17:19:30 +12:00

110 lines
3.0 KiB
C++

#include "ApplicationService.hpp"
#include "DesktopEntry.hpp"
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QSet>
#include <QVector>
#include <utility>
ApplicationService::ApplicationService(QObject *parent)
: QObject(parent)
, m_model(new ApplicationModel(this))
, m_filtered(new ApplicationFilterModel(this))
{
m_filtered->setSourceModel(m_model);
m_filtered->setSortRole(ApplicationModel::NameRole);
m_filtered->sort(0, Qt::AscendingOrder);
connect(m_model, &ApplicationModel::countChanged, this, &ApplicationService::countChanged);
connect(m_filtered, &ApplicationFilterModel::filterChanged, this, &ApplicationService::filterChanged);
refresh();
}
ApplicationService *ApplicationService::create(QQmlEngine *engine, QJSEngine *)
{
auto *service = new ApplicationService(engine);
QQmlEngine::setObjectOwnership(service, QQmlEngine::CppOwnership);
return service;
}
ApplicationModel *ApplicationService::model() const
{
return m_model;
}
ApplicationFilterModel *ApplicationService::filteredModel() const
{
return m_filtered;
}
QString ApplicationService::filter() const
{
return m_filtered->filter();
}
void ApplicationService::setFilter(const QString &filter)
{
m_filtered->setFilter(filter);
}
int ApplicationService::count() const
{
return m_model->count();
}
bool ApplicationService::launch(const QString &desktopId) const
{
const DesktopEntry *entry = m_model->entryById(desktopId);
if (!entry) {
qWarning("nebula: unknown application %s", qPrintable(desktopId));
return false;
}
return entry->launch();
}
void ApplicationService::refresh()
{
QVector<DesktopEntry> entries;
QSet<QString> seenIds;
const QStringList directories = applicationDirectories();
qInfo("nebula: scanning %d application director%s",
directories.size(),
directories.size() == 1 ? "y" : "ies");
for (const QString &directory : directories) {
QDir dir(directory);
if (!dir.exists())
continue;
QDirIterator iterator(directory, {QStringLiteral("*.desktop")}, QDir::Files, QDirIterator::Subdirectories);
while (iterator.hasNext()) {
const QString filePath = iterator.next();
QString relative = QDir(directory).relativeFilePath(filePath);
relative.replace(QLatin1Char('/'), QLatin1Char('-'));
const QString desktopId = relative;
if (seenIds.contains(desktopId))
continue;
seenIds.insert(desktopId);
DesktopEntry entry = parseDesktopEntryFile(filePath, desktopId);
if (entry.name.isEmpty())
entry.name = QFileInfo(filePath).completeBaseName();
if (!entry.isLauncherVisible())
continue;
entries.append(std::move(entry));
}
}
qInfo("nebula: discovered %d launcher application%s",
entries.size(),
entries.size() == 1 ? "" : "s");
m_model->setEntries(std::move(entries));
}