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 "DesktopAppListItem.hpp"
|
||||||
|
#include "IconUtil.hpp"
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QIcon>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
DesktopAppListItem::DesktopAppListItem(const dmenu::DesktopEntry &entry)
|
DesktopAppListItem::DesktopAppListItem(const dmenu::DesktopEntry &entry)
|
||||||
@ -24,7 +22,7 @@ QString DesktopAppListItem::description() const
|
|||||||
|
|
||||||
QUrl DesktopAppListItem::iconUrl() const
|
QUrl DesktopAppListItem::iconUrl() const
|
||||||
{
|
{
|
||||||
return resolveIconUrl(m_entry.icon_path);
|
return IconUtil::resolveAppIcon(m_entry.icon_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DesktopAppListItem::execute()
|
void DesktopAppListItem::execute()
|
||||||
@ -44,54 +42,3 @@ QString DesktopAppListItem::itemType() const
|
|||||||
return "app";
|
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;
|
QString itemType() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl resolveIconUrl(const std::string &iconPath) const;
|
|
||||||
|
|
||||||
dmenu::DesktopEntry m_entry;
|
dmenu::DesktopEntry m_entry;
|
||||||
};
|
};
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "PluginInterface.hpp"
|
#include "PluginInterface.hpp"
|
||||||
#include "GenericListItem.hpp"
|
#include "GenericListItem.hpp"
|
||||||
|
#include "IconUtil.hpp"
|
||||||
#include "files.hpp"
|
#include "files.hpp"
|
||||||
#include "fuzzy.hpp"
|
#include "fuzzy.hpp"
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
@ -241,90 +242,12 @@ namespace plugins
|
|||||||
// Create description showing parent directory
|
// Create description showing parent directory
|
||||||
QString description = parentDir.isEmpty() ? fullPath : QString("%1/.../%2").arg(parentDir, fileName);
|
QString description = parentDir.isEmpty() ? fullPath : QString("%1/.../%2").arg(parentDir, fileName);
|
||||||
|
|
||||||
// Simple icon based on file extension
|
// Get icon using shared utility
|
||||||
QUrl icon = getFileIcon(filePath);
|
QUrl icon = IconUtil::getFileIcon(filePath);
|
||||||
|
|
||||||
return ListItems::createFile(fileName, fullPath, icon);
|
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
|
// Member variables
|
||||||
std::vector<std::string> m_searchDirectories;
|
std::vector<std::string> m_searchDirectories;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user