File icons work
This commit is contained in:
parent
6f6b49bd21
commit
e5b9c85355
175
lib/IconUtil.hpp
Normal file
175
lib/IconUtil.hpp
Normal file
@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QIcon>
|
||||
#include <QStandardPaths>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
#include <filesystem>
|
||||
|
||||
namespace IconUtil
|
||||
{
|
||||
/**
|
||||
* Resolves an application icon using Qt's theme system and fallback searching.
|
||||
* Handles both absolute paths and theme icon names.
|
||||
*
|
||||
* @param iconPath The icon path from a desktop entry or theme name
|
||||
* @return QUrl pointing to the resolved icon file, or empty URL if not found
|
||||
*/
|
||||
QUrl resolveAppIcon(const std::string &iconPath);
|
||||
|
||||
/**
|
||||
* Gets an appropriate icon for a file based on its MIME type.
|
||||
* Uses Qt's QMimeDatabase to determine the proper icon name, then resolves to actual file path.
|
||||
*
|
||||
* @param filePath The filesystem path to the file
|
||||
* @return QUrl pointing to the resolved icon file, or empty URL if not found
|
||||
*/
|
||||
QUrl getFileIcon(const std::filesystem::path &filePath);
|
||||
|
||||
/**
|
||||
* Resolves a theme icon name to an actual file path.
|
||||
*
|
||||
* @param iconName The freedesktop.org standard icon name
|
||||
* @return QUrl pointing to the resolved icon file, or empty URL if not found
|
||||
*/
|
||||
QUrl resolveThemeIconToPath(const QString& iconName);
|
||||
}
|
||||
|
||||
// Implementation
|
||||
namespace IconUtil
|
||||
{
|
||||
inline QUrl resolveAppIcon(const std::string &iconPath)
|
||||
{
|
||||
QString iconName = QString::fromStdString(iconPath);
|
||||
|
||||
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);
|
||||
bool themeFound = !icon.isNull();
|
||||
|
||||
// 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" // This should search /path/to/share/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();
|
||||
}
|
||||
|
||||
inline QUrl getFileIcon(const std::filesystem::path &filePath)
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QString fileName = QString::fromStdString(filePath.string());
|
||||
|
||||
// Get MIME type for the file
|
||||
QMimeType mime = db.mimeTypeForFile(fileName);
|
||||
|
||||
// Get the icon name from the MIME type
|
||||
QString iconName = mime.iconName();
|
||||
|
||||
// If no specific icon, try the generic icon name
|
||||
if (iconName.isEmpty()) {
|
||||
iconName = mime.genericIconName();
|
||||
}
|
||||
|
||||
// Ultimate fallback
|
||||
if (iconName.isEmpty()) {
|
||||
iconName = "text-x-generic";
|
||||
}
|
||||
|
||||
// Now resolve the icon name to an actual file path using the same logic as desktop apps
|
||||
return resolveThemeIconToPath(iconName);
|
||||
}
|
||||
|
||||
inline QUrl resolveThemeIconToPath(const QString& iconName)
|
||||
{
|
||||
if (iconName.isEmpty())
|
||||
return QUrl();
|
||||
|
||||
// Use Qt's icon theme system first
|
||||
QIcon icon = QIcon::fromTheme(iconName);
|
||||
if (icon.isNull()) {
|
||||
// Try with fallback
|
||||
icon = QIcon::fromTheme("text-x-generic");
|
||||
}
|
||||
|
||||
// Search in standard icon directories to find the actual file
|
||||
QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
|
||||
|
||||
// Icon subdirectories for MIME type icons (prioritized)
|
||||
QStringList iconSubDirs = {
|
||||
"icons/hicolor/scalable/mimetypes",
|
||||
"icons/hicolor/48x48/mimetypes",
|
||||
"icons/hicolor/32x32/mimetypes",
|
||||
"icons/hicolor/64x64/mimetypes",
|
||||
"icons/hicolor/24x24/mimetypes",
|
||||
"icons/hicolor/16x16/mimetypes",
|
||||
"icons/Adwaita/scalable/mimetypes",
|
||||
"icons/Adwaita/48x48/mimetypes",
|
||||
"icons/Adwaita/32x32/mimetypes",
|
||||
"icons/breeze/mimetypes/22", // KDE Plasma
|
||||
"icons/breeze-dark/mimetypes/22",
|
||||
"icons/Papirus/48x48/mimetypes", // Popular icon theme
|
||||
"icons/elementary/mimetypes/48", // Elementary OS
|
||||
};
|
||||
|
||||
QStringList extensions = {".svg", ".png", ".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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try to resolve text-x-generic if we couldn't find the specific icon
|
||||
if (iconName != "text-x-generic") {
|
||||
return resolveThemeIconToPath("text-x-generic");
|
||||
}
|
||||
|
||||
return QUrl();
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
#include "DesktopAppListItem.hpp"
|
||||
#include "IconUtil.hpp"
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QIcon>
|
||||
#include <QStandardPaths>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
DesktopAppListItem::DesktopAppListItem(const dmenu::DesktopEntry &entry)
|
||||
@ -24,7 +22,7 @@ QString DesktopAppListItem::description() const
|
||||
|
||||
QUrl DesktopAppListItem::iconUrl() const
|
||||
{
|
||||
return resolveIconUrl(m_entry.icon_path);
|
||||
return IconUtil::resolveAppIcon(m_entry.icon_path);
|
||||
}
|
||||
|
||||
void DesktopAppListItem::execute()
|
||||
@ -44,54 +42,3 @@ QString DesktopAppListItem::itemType() const
|
||||
return "app";
|
||||
}
|
||||
|
||||
QUrl DesktopAppListItem::resolveIconUrl(const std::string &iconPath) const
|
||||
{
|
||||
QString iconName = QString::fromStdString(iconPath);
|
||||
|
||||
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);
|
||||
bool themeFound = !icon.isNull();
|
||||
|
||||
// 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" // This should search /path/to/share/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();
|
||||
}
|
@ -19,7 +19,5 @@ public:
|
||||
QString itemType() const override;
|
||||
|
||||
private:
|
||||
QUrl resolveIconUrl(const std::string &iconPath) const;
|
||||
|
||||
dmenu::DesktopEntry m_entry;
|
||||
};
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "PluginInterface.hpp"
|
||||
#include "GenericListItem.hpp"
|
||||
#include "IconUtil.hpp"
|
||||
#include "files.hpp"
|
||||
#include "fuzzy.hpp"
|
||||
#include <QUrl>
|
||||
@ -241,90 +242,12 @@ namespace plugins
|
||||
// Create description showing parent directory
|
||||
QString description = parentDir.isEmpty() ? fullPath : QString("%1/.../%2").arg(parentDir, fileName);
|
||||
|
||||
// Simple icon based on file extension
|
||||
QUrl icon = getFileIcon(filePath);
|
||||
// Get icon using shared utility
|
||||
QUrl icon = IconUtil::getFileIcon(filePath);
|
||||
|
||||
return ListItems::createFile(fileName, fullPath, icon);
|
||||
}
|
||||
|
||||
QUrl getFileIcon(const std::filesystem::path &filePath) const
|
||||
{
|
||||
QString ext = QString::fromStdString(filePath.extension().string()).toLower();
|
||||
QString iconName;
|
||||
|
||||
// Map file extensions to freedesktop.org standard icon names
|
||||
if (ext == ".txt" || ext == ".md" || ext == ".rst" || ext == ".readme")
|
||||
{
|
||||
iconName = "text-x-generic";
|
||||
}
|
||||
else if (ext == ".pdf")
|
||||
{
|
||||
iconName = "application-pdf";
|
||||
}
|
||||
else if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" ||
|
||||
ext == ".bmp" || ext == ".svg" || ext == ".webp" || ext == ".tiff")
|
||||
{
|
||||
iconName = "image-x-generic";
|
||||
}
|
||||
else if (ext == ".mp3" || ext == ".wav" || ext == ".ogg" || ext == ".flac" ||
|
||||
ext == ".m4a" || ext == ".aac" || ext == ".wma")
|
||||
{
|
||||
iconName = "audio-x-generic";
|
||||
}
|
||||
else if (ext == ".mp4" || ext == ".avi" || ext == ".mkv" || ext == ".webm" ||
|
||||
ext == ".mov" || ext == ".wmv" || ext == ".flv" || ext == ".m4v")
|
||||
{
|
||||
iconName = "video-x-generic";
|
||||
}
|
||||
else if (ext == ".zip" || ext == ".tar" || ext == ".gz" || ext == ".bz2" ||
|
||||
ext == ".xz" || ext == ".7z" || ext == ".rar")
|
||||
{
|
||||
iconName = "package-x-generic";
|
||||
}
|
||||
else if (ext == ".cpp" || ext == ".hpp" || ext == ".c" || ext == ".h")
|
||||
{
|
||||
iconName = "text-x-c++src";
|
||||
}
|
||||
else if (ext == ".py")
|
||||
{
|
||||
iconName = "text-x-python";
|
||||
}
|
||||
else if (ext == ".js" || ext == ".ts" || ext == ".json")
|
||||
{
|
||||
iconName = "text-x-javascript";
|
||||
}
|
||||
else if (ext == ".html" || ext == ".htm" || ext == ".css")
|
||||
{
|
||||
iconName = "text-html";
|
||||
}
|
||||
else if (ext == ".xml" || ext == ".xsl" || ext == ".xsd")
|
||||
{
|
||||
iconName = "text-xml";
|
||||
}
|
||||
else if (ext == ".sh" || ext == ".bash" || ext == ".zsh")
|
||||
{
|
||||
iconName = "text-x-script";
|
||||
}
|
||||
else if (ext == ".doc" || ext == ".docx" || ext == ".odt")
|
||||
{
|
||||
iconName = "application-vnd.oasis.opendocument.text";
|
||||
}
|
||||
else if (ext == ".xls" || ext == ".xlsx" || ext == ".ods")
|
||||
{
|
||||
iconName = "application-vnd.oasis.opendocument.spreadsheet";
|
||||
}
|
||||
else if (ext == ".ppt" || ext == ".pptx" || ext == ".odp")
|
||||
{
|
||||
iconName = "application-vnd.oasis.opendocument.presentation";
|
||||
}
|
||||
else
|
||||
{
|
||||
iconName = "text-x-generic"; // Default fallback
|
||||
}
|
||||
|
||||
// Return the theme icon URL that QML can resolve automatically
|
||||
return QUrl(QString("image://theme/%1").arg(iconName));
|
||||
}
|
||||
|
||||
// Member variables
|
||||
std::vector<std::string> m_searchDirectories;
|
||||
|
Loading…
x
Reference in New Issue
Block a user