Compare commits

...

2 Commits

Author SHA1 Message Date
f60defc695 Delete unused 2025-08-28 17:47:17 -04:00
ec4bffde8b Fire shit coming up 2025-08-28 17:29:25 -04:00
9 changed files with 479 additions and 132 deletions

View File

@ -1,3 +1,5 @@
#pragma once
#include <string>
#include <filesystem>
#include <vector>
@ -21,6 +23,7 @@ namespace fuzzy
class FuzzyFinder
{
void addDesktopApps(); // Deprecated - use PluginManager instead
public:
// Find method for string candidates
std::vector<FuzzyMatch> find(const std::vector<std::string> &candidates,

View File

@ -29,7 +29,8 @@ QVariant AppListModel::data(const QModelIndex &index, int role) const
const ListItemPtr &item = m_items[itemIndex];
switch (role) {
switch (role)
{
case NameRole:
return item->name();
case DescriptionRole:
@ -57,11 +58,11 @@ void AppListModel::loadItems()
{
beginResetModel();
m_items.clear();
// Get items from all registered plugins
auto& pluginManager = plugins::PluginManager::instance();
auto &pluginManager = plugins::PluginManager::instance();
m_items = pluginManager.getAllItems();
updateFilteredItems();
endResetModel();
}
@ -70,7 +71,7 @@ void AppListModel::executeItem(int index)
{
if (index < 0 || index >= static_cast<int>(m_filteredIndexes.size()))
return;
int itemIndex = m_filteredIndexes[index];
if (itemIndex >= static_cast<int>(m_items.size()))
return;
@ -90,26 +91,21 @@ void AppListModel::setSearchText(const QString &searchText)
m_searchText = searchText;
emit searchTextChanged();
beginResetModel();
// Use plugin manager for search
auto& pluginManager = plugins::PluginManager::instance();
auto &pluginManager = plugins::PluginManager::instance();
m_items = pluginManager.search(searchText);
updateFilteredItems();
endResetModel();
}
void AppListModel::addDesktopApps()
{
// This method is deprecated - plugins now handle data loading
// Kept for backward compatibility but does nothing
endResetModel();
}
void AppListModel::addItems(const std::vector<ListItemPtr> &items)
{
for (const auto &item : items) {
for (const auto &item : items)
{
m_items.push_back(item);
}
}
@ -117,9 +113,10 @@ void AppListModel::addItems(const std::vector<ListItemPtr> &items)
void AppListModel::updateFilteredItems()
{
m_filteredIndexes.clear();
// Since PluginManager now handles filtering, just show all items
for (size_t i = 0; i < m_items.size(); ++i) {
for (size_t i = 0; i < m_items.size(); ++i)
{
m_filteredIndexes.push_back(static_cast<int>(i));
}
}

View File

@ -11,7 +11,8 @@ class AppListModel : public QAbstractListModel
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
public:
enum ItemRoles {
enum ItemRoles
{
NameRole = Qt::UserRole + 1,
DescriptionRole,
IconRole,
@ -26,9 +27,8 @@ public:
Q_INVOKABLE void loadItems();
Q_INVOKABLE void executeItem(int index);
// Add items from different sources
void addDesktopApps(); // Deprecated - use PluginManager instead
void addItems(const std::vector<ListItemPtr> &items);
QString searchText() const;

View File

@ -58,8 +58,9 @@ namespace ListItems {
return std::make_shared<GenericListItem>(
name, exec, icon, "app",
[exec]() {
// Basic app execution - plugins can override for complex logic
QProcess::startDetached("/bin/sh", QStringList() << "-c" << exec);
// Use nohup and redirect output to /dev/null for proper detachment
QString detachedCommand = QString("nohup %1 >/dev/null 2>&1 &").arg(exec);
QProcess::startDetached("/bin/sh", QStringList() << "-c" << detachedCommand);
}
);
}
@ -72,7 +73,9 @@ namespace ListItems {
return std::make_shared<GenericListItem>(
filename, path, icon, "file",
[path]() {
QProcess::startDetached("xdg-open", QStringList() << path);
// Use nohup and redirect output to /dev/null for proper detachment
QString detachedCommand = QString("nohup xdg-open \"%1\" >/dev/null 2>&1 &").arg(path);
QProcess::startDetached("/bin/sh", QStringList() << "-c" << detachedCommand);
}
);
}

View File

@ -14,6 +14,7 @@
#include "ui/AppListModel.hpp"
#include "plugins/PluginManager.hpp"
#include "plugins/DesktopAppPlugin.hpp"
#include "plugins/FileSearchPlugin.hpp"
#include <cstdlib>
#include <string>
#include <iostream>
@ -24,65 +25,66 @@
int main(int argc, char *argv[])
{
fuzzy::FuzzyFinder fuzzy;
std::string home = std::getenv("HOME");
std::cout << "Home Directory: " << home << '\n';
auto projects = files::findAllFiles(home.append("/Documents/wallpapers"), 1);
auto pathMatches = fuzzy.find(projects, "sun", 10);
std::cout << "Path fuzzy search for 'anime':\n";
for (const auto &match : pathMatches)
{
std::cout << " " << match.text << " (score: " << match.score << ")\n";
}
std::cout << "\nAll files found:\n";
for (const auto &p : projects)
{
std::cout << " " << p.string() << std::endl;
}
// fuzzy::FuzzyFinder fuzzy;
// std::string home = std::getenv("HOME");
// std::cout << "Home Directory: " << home << '\n';
// auto projects = files::findAllFiles(home.append("/Documents/wallpapers"), 1);
// auto pathMatches = fuzzy.find(projects, "sun", 10);
// std::cout << "Path fuzzy search for 'anime':\n";
// for (const auto &match : pathMatches)
// {
// std::cout << " " << match.text << " (score: " << match.score << ")\n";
// }
// std::cout << "\nAll files found:\n";
// for (const auto &p : projects)
// {
// std::cout << " " << p.string() << std::endl;
// }
// ACTUAL APP CODE
// QGuiApplication app(argc, argv);
// QCoreApplication::setApplicationName("waycast");
QGuiApplication app(argc, argv);
QCoreApplication::setApplicationName("waycast");
// // Initialize plugin system
// auto& pluginManager = plugins::PluginManager::instance();
// pluginManager.registerPlugin(std::make_unique<plugins::DesktopAppPlugin>());
// Initialize plugin system
auto &pluginManager = plugins::PluginManager::instance();
pluginManager.registerPlugin(std::make_unique<plugins::DesktopAppPlugin>());
pluginManager.registerPlugin(std::make_unique<plugins::FileSearchPlugin>());
// // Enable system theme support
// app.setDesktopSettingsAware(true);
// Enable system theme support
app.setDesktopSettingsAware(true);
// // Ensure we follow system color scheme
// app.setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);
// Ensure we follow system color scheme
app.setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);
// QQmlApplicationEngine engine;
QQmlApplicationEngine engine;
// // Register the AppListModel type with QML
// qmlRegisterType<AppListModel>("WayCast", 1, 0, "AppListModel");
// 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); });
// Set up layer shell before creating any windows
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []()
{ QCoreApplication::exit(-1); });
// engine.loadFromModule("WayCast", "Main");
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);
// 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();
// Now show the window after layer shell is configured
window->show();
}
}
}
return app.exec();
}

View File

@ -1,7 +1,7 @@
#pragma once
#include "../../lib/plugins/PluginInterface.hpp"
#include "../../lib/dmenu.hpp"
#include "PluginInterface.hpp"
#include "dmenu.hpp"
#include "DesktopAppPlugin/DesktopAppListItem.hpp"
#include <algorithm>

View File

@ -1,9 +1,9 @@
#pragma once
#include "../../../lib/ui/ListItem.hpp"
#include "ListItem.hpp"
#undef signals
#include "../../../lib/dmenu.hpp"
#include "dmenu.hpp"
#define signals public
class DesktopAppListItem : public ListItem

View File

@ -1,54 +0,0 @@
#pragma once
#include "../../lib/plugins/PluginInterface.hpp"
#include "../../lib/ui/GenericListItem.hpp"
namespace plugins
{
// Example of how simple it is to create a new plugin
class ExamplePlugin : public SearchPlugin
{
public:
std::vector<ListItemPtr> search(const QString& query) override
{
std::vector<ListItemPtr> results;
// Example: Create some sample items that match any query
if (query.contains("test", Qt::CaseInsensitive)) {
// Using the convenient factory function
results.push_back(ListItems::createItem(
"Test Item 1",
"This is a test item",
"example",
[]() { /* custom action */ }
));
// Or create directly with GenericListItem
results.push_back(std::make_shared<GenericListItem>(
"Test Item 2",
"Another test item",
QUrl(), // no icon
"example",
[]() {
// Custom execute action
qDebug() << "Test item executed!";
}
));
}
return results;
}
std::vector<ListItemPtr> getAllItems() override
{
// Return some default items
return {
ListItems::createItem("Example Item", "Always visible", "example")
};
}
QString pluginName() const override { return "Example Plugin"; }
QString pluginDescription() const override { return "Demonstrates easy plugin creation"; }
int priority() const override { return 10; } // Low priority
};
}

View File

@ -0,0 +1,396 @@
#pragma once
#include "PluginInterface.hpp"
#include "GenericListItem.hpp"
#include "files.hpp"
#include "fuzzy.hpp"
#include <QUrl>
#include <QProcess>
#include <QDir>
#include <QStandardPaths>
#include <QIcon>
#include <QFile>
#include <filesystem>
#include <unordered_set>
#include <algorithm>
namespace plugins
{
class FileSearchPlugin : public SearchPlugin
{
public:
// Constructor with configurable search and ignore directories
explicit FileSearchPlugin(
const std::vector<std::string> &searchDirectories = getDefaultSearchDirs(),
int maxDepth = 3,
size_t maxFiles = 1000)
: m_searchDirectories(searchDirectories), m_maxDepth(maxDepth), m_maxFiles(maxFiles)
{
// Initialize ignore directory names set
m_ignoreDirNames = {
"node_modules", "vendor", ".git", ".svn", ".hg",
"build", "dist", "target", ".cache", "__pycache__",
".pytest_cache", ".mypy_cache", "coverage", ".coverage",
".tox", "venv", ".venv", "env", ".env"
};
// Pre-load all files from search directories
loadFiles();
}
std::vector<ListItemPtr> search(const QString &query) override
{
std::vector<ListItemPtr> results;
if (query.length() < 2)
{
return results; // Require at least 2 characters to avoid huge result sets
}
// Convert file paths to strings for fuzzy matching
std::vector<std::string> filePaths;
filePaths.reserve(m_allFiles.size());
for (const auto &file : m_allFiles)
{
filePaths.push_back(file.filename().string()); // Match against filename only
}
// Use fuzzy finder to get matches
auto fuzzyMatches = m_fuzzyFinder.find(filePaths, query.toStdString(), 50);
// Convert fuzzy matches back to ListItems
results.reserve(fuzzyMatches.size());
for (const auto &match : fuzzyMatches)
{
// Find the original file path that corresponds to this match
auto it = std::find_if(m_allFiles.begin(), m_allFiles.end(),
[&match](const std::filesystem::path &path)
{
return path.filename().string() == match.text;
});
if (it != m_allFiles.end())
{
auto item = createFileListItem(*it, match.score);
results.push_back(item);
}
}
return results;
}
std::vector<ListItemPtr> getAllItems() override
{
// Don't return all files by default (too many)
// Could return recent files or empty list
return {};
}
QString pluginName() const override
{
return "File Search";
}
QString pluginDescription() const override
{
return QString("Searches files in specified directories (currently monitoring %1 files)")
.arg(m_allFiles.size());
}
int priority() const override
{
return 25; // Lower priority than applications, higher than examples
}
// Configuration methods
void addSearchDirectory(const std::string &directory)
{
m_searchDirectories.push_back(directory);
loadFiles(); // Reload files
}
void addIgnoreDirName(const std::string &dirName)
{
m_ignoreDirNames.insert(dirName);
loadFiles(); // Reload files
}
size_t getFileCount() const { return m_allFiles.size(); }
private:
// Default search directories
static std::vector<std::string> getDefaultSearchDirs()
{
std::vector<std::string> dirs;
// Add common user directories
QString home = QDir::homePath();
dirs.push_back((home + "/Documents").toStdString());
dirs.push_back((home + "/Desktop").toStdString());
dirs.push_back((home + "/Downloads").toStdString());
// Add XDG user directories if they exist
auto xdgDirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation);
for (const QString &dir : xdgDirs)
{
if (QDir(dir).exists())
{
dirs.push_back(dir.toStdString());
}
}
return dirs;
}
void loadFiles()
{
m_allFiles.clear();
for (const auto &searchDir : m_searchDirectories)
{
if (!std::filesystem::exists(searchDir))
{
continue;
}
try
{
// Get all files from this directory
auto files = files::findAllFiles(searchDir, m_maxDepth);
for (const auto &file : files)
{
// Check if this file should be ignored
if (shouldIgnoreFile(file))
{
continue;
}
m_allFiles.push_back(file);
// Limit total files to prevent memory issues
if (m_allFiles.size() >= m_maxFiles)
{
return;
}
}
}
catch (const std::filesystem::filesystem_error &e)
{
// Silently skip directories we can't access
continue;
}
}
}
bool shouldIgnoreFile(const std::filesystem::path &file) const
{
try
{
// Check if file is in any ignored directory (walk up the path)
auto current = file.parent_path();
while (!current.empty() && current != current.root_path())
{
std::string dirName = current.filename().string();
// Ignore any directory that starts with .
if (!dirName.empty() && dirName[0] == '.')
{
return true;
}
// Ignore directories by name
if (m_ignoreDirNames.contains(dirName))
{
return true;
}
current = current.parent_path();
}
// Ignore hidden files (starting with .)
std::string fileName = file.filename().string();
if (!fileName.empty() && fileName[0] == '.')
{
return true;
}
// Ignore common temporary file extensions
auto extension = file.extension().string();
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
if (extension == ".tmp" || extension == ".temp" ||
extension == ".log" || extension == ".cache")
{
return true;
}
return false;
}
catch (const std::filesystem::filesystem_error &)
{
return true; // Ignore files we can't access
}
}
ListItemPtr createFileListItem(const std::filesystem::path &filePath, int fuzzyScore)
{
QString fileName = QString::fromStdString(filePath.filename().string());
QString fullPath = QString::fromStdString(filePath.string());
QString parentDir = QString::fromStdString(filePath.parent_path().filename().string());
// 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);
return ListItems::createFile(fileName, fullPath, icon);
}
QUrl getFileIcon(const std::filesystem::path &filePath) const
{
QString ext = QString::fromStdString(filePath.extension().string()).toLower();
QString iconName;
// Use freedesktop.org standard icon names that respect user themes
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";
}
// Use Qt's icon theme system to find the actual icon file
return resolveThemeIcon(iconName);
}
QUrl resolveThemeIcon(const QString& iconName) const
{
// First try Qt's theme system
QIcon icon = QIcon::fromTheme(iconName);
if (!icon.isNull()) {
// Try to find the actual file path by searching standard locations
QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
// Icon subdirectories in order of preference
QStringList iconSubDirs = {
"icons/hicolor/scalable/mimetypes",
"icons/hicolor/48x48/mimetypes",
"icons/hicolor/32x32/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 to a generic file icon if nothing found
QIcon fallbackIcon = QIcon::fromTheme("text-x-generic");
if (!fallbackIcon.isNull()) {
// Try to resolve the fallback icon the same way
QStringList dataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
for (const QString& dataDir : dataDirs) {
QString path = dataDir + "/icons/hicolor/48x48/mimetypes/text-x-generic.png";
if (QFile::exists(path)) {
return QUrl::fromLocalFile(path);
}
}
}
// Ultimate fallback - return empty URL and let QML handle with default
return QUrl();
}
// Member variables
std::vector<std::string> m_searchDirectories;
std::unordered_set<std::string> m_ignoreDirNames;
int m_maxDepth;
size_t m_maxFiles;
std::vector<std::filesystem::path> m_allFiles;
fuzzy::FuzzyFinder m_fuzzyFinder;
};
}