parent
f3d086024b
commit
2832de18d7
4 changed files with 325 additions and 20 deletions
|
|
@ -2,13 +2,15 @@ pragma Singleton
|
|||
|
||||
import qs.config
|
||||
import qs.utils
|
||||
import Caelestia
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Searcher {
|
||||
id: root
|
||||
|
||||
function launch(entry: DesktopEntry): void {
|
||||
appDb.incrementFrequency(entry.id);
|
||||
|
||||
if (entry.runInTerminal)
|
||||
Quickshell.execDetached({
|
||||
command: ["app2unit", "--", ...Config.general.apps.terminal, `${Quickshell.shellDir}/assets/wrap_term_launch.sh`, ...entry.command],
|
||||
|
|
@ -50,10 +52,10 @@ Searcher {
|
|||
weights = [1];
|
||||
|
||||
if (!search.startsWith(`${prefix}t `))
|
||||
return query(search).map(e => e.modelData);
|
||||
return query(search).map(e => e.entry);
|
||||
}
|
||||
|
||||
const results = query(search.slice(prefix.length + 2)).map(e => e.modelData);
|
||||
const results = query(search.slice(prefix.length + 2)).map(e => e.entry);
|
||||
if (search.startsWith(`${prefix}t `))
|
||||
return results.filter(a => a.runInTerminal);
|
||||
return results;
|
||||
|
|
@ -63,24 +65,31 @@ Searcher {
|
|||
return keys.map(k => item[k]).join(" ");
|
||||
}
|
||||
|
||||
list: variants.instances
|
||||
list: appDb.apps
|
||||
useFuzzy: Config.launcher.useFuzzy.apps
|
||||
|
||||
Variants {
|
||||
id: variants
|
||||
AppDb {
|
||||
id: appDb
|
||||
|
||||
model: [...DesktopEntries.applications.values].filter(a => !Config.launcher.hiddenApps.includes(a.id)).sort((a, b) => a.name.localeCompare(b.name))
|
||||
path: `${Paths.state}/apps.sqlite`
|
||||
entries: DesktopEntries.applications.values.filter(a => !Config.launcher.hiddenApps.includes(a.id))
|
||||
}
|
||||
|
||||
QtObject {
|
||||
required property DesktopEntry modelData
|
||||
readonly property string id: modelData.id
|
||||
readonly property string name: modelData.name
|
||||
readonly property string desc: modelData.comment
|
||||
readonly property string execString: modelData.execString
|
||||
readonly property string wmClass: modelData.startupClass
|
||||
readonly property string genericName: modelData.genericName
|
||||
readonly property string categories: modelData.categories.join(" ")
|
||||
readonly property string keywords: modelData.keywords.join(" ")
|
||||
}
|
||||
}
|
||||
// Variants {
|
||||
// id: variants
|
||||
|
||||
// model: [...DesktopEntries.applications.values].filter(a => !Config.launcher.hiddenApps.includes(a.id)).sort((a, b) => a.name.localeCompare(b.name))
|
||||
|
||||
// QtObject {
|
||||
// required property DesktopEntry modelData
|
||||
// readonly property string id: modelData.id
|
||||
// readonly property string name: modelData.name
|
||||
// readonly property string desc: modelData.comment
|
||||
// readonly property string execString: modelData.execString
|
||||
// readonly property string wmClass: modelData.startupClass
|
||||
// readonly property string genericName: modelData.genericName
|
||||
// readonly property string categories: modelData.categories.join(" ")
|
||||
// readonly property string keywords: modelData.keywords.join(" ")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui Concurrent)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui Concurrent Sql)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(Qalculate IMPORTED_TARGET libqalculate REQUIRED)
|
||||
pkg_check_modules(Pipewire IMPORTED_TARGET libpipewire-0.3 REQUIRED)
|
||||
|
|
@ -21,6 +21,7 @@ qt_add_qml_module(caelestia
|
|||
audiocollector.hpp audiocollector.cpp
|
||||
audioprovider.hpp audioprovider.cpp
|
||||
cavaprovider.hpp cavaprovider.cpp
|
||||
appdb.hpp appdb.cpp
|
||||
)
|
||||
|
||||
qt_query_qml_module(caelestia
|
||||
|
|
@ -45,6 +46,7 @@ target_link_libraries(caelestia PRIVATE
|
|||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::Concurrent
|
||||
Qt::Sql
|
||||
PkgConfig::Qalculate
|
||||
PkgConfig::Pipewire
|
||||
PkgConfig::Aubio
|
||||
|
|
|
|||
201
plugin/src/Caelestia/appdb.cpp
Normal file
201
plugin/src/Caelestia/appdb.cpp
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
#include "appdb.hpp"
|
||||
|
||||
#include <qsqldatabase.h>
|
||||
#include <qsqlquery.h>
|
||||
#include <quuid.h>
|
||||
|
||||
namespace caelestia {
|
||||
|
||||
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_entry(entry)
|
||||
, m_frequency(frequency) {}
|
||||
|
||||
QObject* AppEntry::entry() const {
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
quint32 AppEntry::frequency() const {
|
||||
return m_frequency;
|
||||
}
|
||||
|
||||
void AppEntry::setFrequency(unsigned int frequency) {
|
||||
if (m_frequency != frequency) {
|
||||
m_frequency = frequency;
|
||||
emit frequencyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AppEntry::incrementFrequency() {
|
||||
m_frequency++;
|
||||
emit frequencyChanged();
|
||||
}
|
||||
|
||||
QString AppEntry::id() const {
|
||||
return m_entry->property("id").toString();
|
||||
}
|
||||
|
||||
QString AppEntry::name() const {
|
||||
return m_entry->property("name").toString();
|
||||
}
|
||||
|
||||
QString AppEntry::desc() const {
|
||||
return m_entry->property("comment").toString();
|
||||
}
|
||||
|
||||
QString AppEntry::execString() const {
|
||||
return m_entry->property("execString").toString();
|
||||
}
|
||||
|
||||
QString AppEntry::wmClass() const {
|
||||
return m_entry->property("startupClass").toString();
|
||||
}
|
||||
|
||||
QString AppEntry::genericName() const {
|
||||
return m_entry->property("genericName").toString();
|
||||
}
|
||||
|
||||
QString AppEntry::categories() const {
|
||||
return m_entry->property("categories").toStringList().join(" ");
|
||||
}
|
||||
|
||||
QString AppEntry::keywords() const {
|
||||
return m_entry->property("keywords").toStringList().join(" ");
|
||||
}
|
||||
|
||||
AppDb::AppDb(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_uuid(QUuid::createUuid().toString()) {
|
||||
auto db = QSqlDatabase::addDatabase("QSQLITE", m_uuid);
|
||||
db.setDatabaseName(":memory:");
|
||||
db.open();
|
||||
|
||||
QSqlQuery query(db);
|
||||
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)");
|
||||
}
|
||||
|
||||
QString AppDb::uuid() const {
|
||||
return m_uuid;
|
||||
}
|
||||
|
||||
QString AppDb::path() const {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void AppDb::setPath(const QString& path) {
|
||||
if (m_path == path) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_path = path;
|
||||
emit pathChanged();
|
||||
|
||||
auto db = QSqlDatabase::database(m_uuid, false);
|
||||
db.close();
|
||||
db.setDatabaseName(path);
|
||||
|
||||
updateAppFrequencies();
|
||||
}
|
||||
|
||||
QList<QObject*> AppDb::entries() const {
|
||||
return m_entries;
|
||||
}
|
||||
|
||||
void AppDb::setEntries(const QList<QObject*>& entries) {
|
||||
if (m_entries == entries) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_entries = entries;
|
||||
emit entriesChanged();
|
||||
|
||||
updateApps();
|
||||
}
|
||||
|
||||
QList<AppEntry*> AppDb::apps() const {
|
||||
auto apps = m_apps.values();
|
||||
std::sort(apps.begin(), apps.end(), [](AppEntry* a, AppEntry* b) {
|
||||
if (a->frequency() != b->frequency()) {
|
||||
return a->frequency() > b->frequency();
|
||||
}
|
||||
return a->name().localeAwareCompare(b->name()) < 0;
|
||||
});
|
||||
return apps;
|
||||
}
|
||||
|
||||
void AppDb::incrementFrequency(const QString& id) {
|
||||
auto db = QSqlDatabase::database(m_uuid);
|
||||
QSqlQuery query(db);
|
||||
|
||||
query.prepare("INSERT INTO frequencies (id, frequency) "
|
||||
"VALUES (:id, 1) "
|
||||
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
|
||||
query.bindValue(":id", id);
|
||||
query.exec();
|
||||
|
||||
for (auto app : m_apps) {
|
||||
if (app->id() == id) {
|
||||
const auto before = apps();
|
||||
|
||||
app->incrementFrequency();
|
||||
|
||||
if (before != apps()) {
|
||||
emit appsChanged();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "AppDb::incrementFrequency: could not find app with id" << id;
|
||||
}
|
||||
|
||||
quint32 AppDb::getFrequency(const QString& id) const {
|
||||
auto db = QSqlDatabase::database(m_uuid);
|
||||
QSqlQuery query(db);
|
||||
|
||||
query.prepare("SELECT frequency FROM frequencies WHERE id = :id");
|
||||
query.bindValue(":id", id);
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
return query.value(0).toUInt();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AppDb::updateAppFrequencies() {
|
||||
for (auto app : m_apps) {
|
||||
app->setFrequency(getFrequency(app->id()));
|
||||
}
|
||||
}
|
||||
|
||||
void AppDb::updateApps() {
|
||||
bool dirty = false;
|
||||
|
||||
for (auto entry : m_entries) {
|
||||
const auto id = entry->property("id").toString();
|
||||
if (!m_apps.contains(id)) {
|
||||
dirty = true;
|
||||
m_apps.insert(id, new AppEntry(entry, getFrequency(id), this));
|
||||
}
|
||||
}
|
||||
|
||||
QSet<QString> newIds;
|
||||
for (auto entry : m_entries) {
|
||||
newIds.insert(entry->property("id").toString());
|
||||
}
|
||||
|
||||
for (auto id : m_apps.keys()) {
|
||||
if (!newIds.contains(id)) {
|
||||
dirty = true;
|
||||
delete m_apps.take(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
emit appsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace caelestia
|
||||
93
plugin/src/Caelestia/appdb.hpp
Normal file
93
plugin/src/Caelestia/appdb.hpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include <qhash.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace caelestia {
|
||||
|
||||
class AppEntry : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("AppEntry instances can only be retrieved from an AppDb")
|
||||
|
||||
// The actual DesktopEntry, but we don't have access to the type so it's a QObject
|
||||
Q_PROPERTY(QObject* entry READ entry CONSTANT)
|
||||
|
||||
Q_PROPERTY(quint32 frequency READ frequency NOTIFY frequencyChanged)
|
||||
Q_PROPERTY(QString id READ id CONSTANT)
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(QString desc READ desc CONSTANT)
|
||||
Q_PROPERTY(QString execString READ execString CONSTANT)
|
||||
Q_PROPERTY(QString wmClass READ wmClass CONSTANT)
|
||||
Q_PROPERTY(QString genericName READ genericName CONSTANT)
|
||||
Q_PROPERTY(QString categories READ categories CONSTANT)
|
||||
Q_PROPERTY(QString keywords READ keywords CONSTANT)
|
||||
|
||||
public:
|
||||
explicit AppEntry(QObject* entry, quint32 frequency, QObject* parent = nullptr);
|
||||
|
||||
[[nodiscard]] QObject* entry() const;
|
||||
|
||||
[[nodiscard]] quint32 frequency() const;
|
||||
void setFrequency(quint32 frequency);
|
||||
void incrementFrequency();
|
||||
|
||||
[[nodiscard]] QString id() const;
|
||||
[[nodiscard]] QString name() const;
|
||||
[[nodiscard]] QString desc() const;
|
||||
[[nodiscard]] QString execString() const;
|
||||
[[nodiscard]] QString wmClass() const;
|
||||
[[nodiscard]] QString genericName() const;
|
||||
[[nodiscard]] QString categories() const;
|
||||
[[nodiscard]] QString keywords() const;
|
||||
|
||||
signals:
|
||||
void frequencyChanged();
|
||||
|
||||
private:
|
||||
QObject* m_entry;
|
||||
quint32 m_frequency;
|
||||
};
|
||||
|
||||
class AppDb : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(QString uuid READ uuid CONSTANT)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
|
||||
Q_PROPERTY(QList<QObject*> entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
|
||||
Q_PROPERTY(QList<AppEntry*> apps READ apps NOTIFY appsChanged)
|
||||
|
||||
public:
|
||||
explicit AppDb(QObject* parent = nullptr);
|
||||
|
||||
[[nodiscard]] QString uuid() const;
|
||||
|
||||
[[nodiscard]] QString path() const;
|
||||
void setPath(const QString& path);
|
||||
|
||||
[[nodiscard]] QList<QObject*> entries() const;
|
||||
void setEntries(const QList<QObject*>& entries);
|
||||
|
||||
[[nodiscard]] QList<AppEntry*> apps() const;
|
||||
|
||||
Q_INVOKABLE void incrementFrequency(const QString& id);
|
||||
|
||||
signals:
|
||||
void pathChanged();
|
||||
void entriesChanged();
|
||||
void appsChanged();
|
||||
|
||||
private:
|
||||
const QString m_uuid;
|
||||
QString m_path;
|
||||
QList<QObject*> m_entries;
|
||||
QHash<QString, AppEntry*> m_apps;
|
||||
|
||||
quint32 getFrequency(const QString& id) const;
|
||||
void updateAppFrequencies();
|
||||
void updateApps();
|
||||
};
|
||||
|
||||
} // namespace caelestia
|
||||
Loading…
Add table
Reference in a new issue