App icons

This commit is contained in:
Javier Feliz 2025-08-28 13:27:51 -04:00
parent fc2335f138
commit 3a66939627
4 changed files with 123 additions and 50 deletions

View File

@ -2,6 +2,12 @@
#include <QDebug> #include <QDebug>
#include <QProcess> #include <QProcess>
#include <QRegularExpression> #include <QRegularExpression>
#include <QIcon>
#include <QPixmap>
#include <QUrl>
#include <QFile>
#include <QStandardPaths>
#include <QDir>
AppListModel::AppListModel(QObject *parent) AppListModel::AppListModel(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
@ -30,6 +36,8 @@ QVariant AppListModel::data(const QModelIndex &index, int role) const
return QString::fromStdString(app.exec); return QString::fromStdString(app.exec);
case IdRole: case IdRole:
return QString::fromStdString(app.id); return QString::fromStdString(app.id);
case IconRole:
return getIconUrl(app);
default: default:
return QVariant(); return QVariant();
} }
@ -41,6 +49,7 @@ QHash<int, QByteArray> AppListModel::roleNames() const
roles[NameRole] = "name"; roles[NameRole] = "name";
roles[ExecRole] = "exec"; roles[ExecRole] = "exec";
roles[IdRole] = "id"; roles[IdRole] = "id";
roles[IconRole] = "icon";
return roles; return roles;
} }
@ -105,4 +114,58 @@ void AppListModel::updateFilteredApps()
m_filteredIndexes.push_back(static_cast<int>(i)); m_filteredIndexes.push_back(static_cast<int>(i));
} }
} }
}
QUrl AppListModel::getIconUrl(const dmenu::DesktopEntry &app) const
{
QString iconName = QString::fromStdString(app.icon_path);
if (iconName.isEmpty())
return QUrl();
// If it's already a full path, use it directly
if (iconName.startsWith('/')) {
if (QFile::exists(iconName)) {
return QUrl::fromLocalFile(iconName);
}
return QUrl();
}
// Use Qt's proper icon theme search which follows XDG spec
QIcon icon = QIcon::fromTheme(iconName);
if (icon.isNull()) {
return QUrl();
}
// Qt doesn't expose the resolved file path directly, so let's use QStandardPaths
// to search in the proper system directories
QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
// Common icon subdirectories and sizes (prioritized)
QStringList iconSubDirs = {
"icons/hicolor/scalable/apps",
"icons/hicolor/48x48/apps",
"icons/hicolor/64x64/apps",
"icons/hicolor/32x32/apps",
"icons/hicolor/128x128/apps",
"icons/Adwaita/scalable/apps",
"icons/Adwaita/48x48/apps",
"pixmaps"
};
QStringList extensions = {"", ".png", ".svg", ".xpm"};
for (const QString &dataDir : dataDirs) {
for (const QString &iconSubDir : iconSubDirs) {
QString basePath = dataDir + "/" + iconSubDir + "/";
for (const QString &ext : extensions) {
QString fullPath = basePath + iconName + ext;
if (QFile::exists(fullPath)) {
return QUrl::fromLocalFile(fullPath);
}
}
}
}
return QUrl();
} }

View File

@ -16,7 +16,8 @@ public:
enum AppRoles { enum AppRoles {
NameRole = Qt::UserRole + 1, NameRole = Qt::UserRole + 1,
ExecRole, ExecRole,
IdRole IdRole,
IconRole
}; };
explicit AppListModel(QObject *parent = nullptr); explicit AppListModel(QObject *parent = nullptr);
@ -36,6 +37,7 @@ signals:
private: private:
void updateFilteredApps(); void updateFilteredApps();
QUrl getIconUrl(const dmenu::DesktopEntry &app) const;
dmenu::DEVec m_apps; dmenu::DEVec m_apps;
std::vector<int> m_filteredIndexes; std::vector<int> m_filteredIndexes;

View File

@ -30,48 +30,48 @@ std::vector<std::string> split(std::string s, char delimiter)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
dmenu::DEVec apps = dmenu::get_dmenu_app_data(); // dmenu::DEVec apps = dmenu::get_dmenu_app_data();
for (auto &app : *apps.get()) // for (auto &app : *apps.get())
{
std::cout << app.iconPath() << std::endl;
// std::cout << std::format("---\nName: {}\nID: {}\nIcon: {}\nExec: {}\nDisp: {}\n---\n", app.id, app.name, app.iconPath(), app.exec, app.display ? "yes" : "no");
}
// QGuiApplication app(argc, argv);
// QCoreApplication::setApplicationName("waycast");
// // Enable system theme support
// app.setDesktopSettingsAware(true);
// QQmlApplicationEngine engine;
// // Register the AppListModel type with QML
// qmlRegisterType<AppListModel>("WayCast", 1, 0, "AppListModel");
// // Set up layer shell before creating any windows
// QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []()
// { QCoreApplication::exit(-1); });
// engine.loadFromModule("WayCast", "Main");
// // Get the root objects and configure layer shell
// auto rootObjects = engine.rootObjects();
// if (!rootObjects.isEmpty())
// { // {
// QWindow *window = qobject_cast<QWindow *>(rootObjects.first()); // std::cout << app.iconPath() << std::endl;
// if (window) // // std::cout << std::format("---\nName: {}\nID: {}\nIcon: {}\nExec: {}\nDisp: {}\n---\n", app.id, app.name, app.iconPath(), app.exec, app.display ? "yes" : "no");
// {
// LayerShellQt::Window *layerWindow = LayerShellQt::Window::get(window);
// if (layerWindow)
// {
// layerWindow->setLayer(LayerShellQt::Window::LayerTop);
// layerWindow->setAnchors({});
// layerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand);
// // Now show the window after layer shell is configured
// window->show();
// }
// }
// } // }
// return app.exec();
QGuiApplication app(argc, argv);
QCoreApplication::setApplicationName("waycast");
// Enable system theme support
app.setDesktopSettingsAware(true);
QQmlApplicationEngine engine;
// Register the AppListModel type with QML
qmlRegisterType<AppListModel>("WayCast", 1, 0, "AppListModel");
// Set up layer shell before creating any windows
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []()
{ QCoreApplication::exit(-1); });
engine.loadFromModule("WayCast", "Main");
// Get the root objects and configure layer shell
auto rootObjects = engine.rootObjects();
if (!rootObjects.isEmpty())
{
QWindow *window = qobject_cast<QWindow *>(rootObjects.first());
if (window)
{
LayerShellQt::Window *layerWindow = LayerShellQt::Window::get(window);
if (layerWindow)
{
layerWindow->setLayer(LayerShellQt::Window::LayerTop);
layerWindow->setAnchors({});
layerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand);
// Now show the window after layer shell is configured
window->show();
}
}
}
return app.exec();
} }

View File

@ -89,16 +89,24 @@ ApplicationWindow {
anchors.margins: 10 anchors.margins: 10
spacing: 10 spacing: 10
Rectangle { Image {
width: 24 width: 24
height: 24 height: 24
color: palette.button source: model.icon
radius: 4 fillMode: Image.PreserveAspectFit
Text { // Fallback if icon fails to load
anchors.centerIn: parent Rectangle {
text: "📱" anchors.fill: parent
font.pixelSize: 16 color: palette.button
radius: 4
visible: parent.status === Image.Error || parent.status === Image.Null
Text {
anchors.centerIn: parent
text: "📱"
font.pixelSize: 16
}
} }
} }