Add rooms runner

This allows to search for and open rooms in KRunner
This commit is contained in:
Nicolas Fella
2022-04-01 10:56:19 +00:00
committed by Tobias Fella
parent 9a5f2e4938
commit 1cc8d915bc
7 changed files with 300 additions and 1 deletions

View File

@@ -63,7 +63,7 @@ if(NOT ANDROID)
endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
target_sources(neochat PRIVATE ../res_desktop.qrc)
target_sources(neochat PRIVATE ../res_desktop.qrc runner.cpp)
else()
target_sources(neochat PRIVATE ../res_android.qrc)
endif()
@@ -139,3 +139,8 @@ if (TARGET KF5::KIOWidgets)
endif()
install(TARGETS neochat ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
endif()

View File

@@ -78,6 +78,11 @@
#include "colorschemer.h"
#endif
#ifdef QT_DBUS_LIB
#include "runner.h"
#include <QDBusConnection>
#endif
using namespace Quotient;
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
@@ -257,6 +262,11 @@ int main(int argc, char *argv[])
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
}
#ifdef QT_DBUS_LIB
Runner runner;
QDBusConnection::sessionBus().registerObject("/RoomRunner", &runner, QDBusConnection::ExportScriptableContents);
#endif
#ifdef HAVE_KDBUSADDONS
KDBusService service(KDBusService::Unique);
service.connect(&service,

View File

@@ -0,0 +1,13 @@
# SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
[Desktop Entry]
Name=NeoChat
Comment=Find rooms in NeoChat
X-KDE-ServiceTypes=Plasma/Runner
Type=Service
Icon=org.kde.neochat
X-Plasma-API=DBus
X-Plasma-DBusRunner-Service=org.kde.neochat
X-Plasma-DBusRunner-Path=/RoomRunner
X-Plasma-Request-Actions-Once=true
X-Plasma-Runner-Min-Letter-Count=3

View File

@@ -399,6 +399,12 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
if (role == SubtitleTextRole) {
return room->subtitleText();
}
if (role == AvatarImageRole) {
return room->avatar(128);
}
if (role == IdRole) {
return room->id();
}
return QVariant();
}

View File

@@ -50,6 +50,8 @@ public:
CurrentRoomRole,
CategoryVisibleRole,
SubtitleTextRole,
AvatarImageRole,
IdRole,
};
Q_ENUM(EventRoles)

94
src/runner.cpp Normal file
View File

@@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QDBusMetaType>
#include "controller.h"
#include "neochatroom.h"
#include "roomlistmodel.h"
#include "roommanager.h"
#include "runner.h"
RemoteImage Runner::serializeImage(const QImage &image)
{
QImage convertedImage = image.convertToFormat(QImage::Format_RGBA8888);
RemoteImage remoteImage{
convertedImage.width(),
convertedImage.height(),
convertedImage.bytesPerLine(),
true, // hasAlpha
8, // bitsPerSample
4, // channels
QByteArray(reinterpret_cast<const char *>(convertedImage.constBits()), convertedImage.sizeInBytes()),
};
return remoteImage;
}
Runner::Runner()
: QObject()
{
qDBusRegisterMetaType<RemoteMatch>();
qDBusRegisterMetaType<RemoteMatches>();
qDBusRegisterMetaType<RemoteAction>();
qDBusRegisterMetaType<RemoteActions>();
qDBusRegisterMetaType<RemoteImage>();
m_model.setSourceModel(&m_sourceModel);
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &Runner::activeConnectionChanged);
}
void Runner::activeConnectionChanged()
{
m_sourceModel.setConnection(Controller::instance().activeConnection());
}
RemoteActions Runner::Actions()
{
return {};
}
RemoteMatches Runner::Match(const QString &searchTerm)
{
m_model.setFilterText(searchTerm);
RemoteMatches matches;
for (int i = 0; i < m_model.rowCount(); ++i) {
RemoteMatch match;
const QString name = m_model.data(m_model.index(i, 0), RoomListModel::DisplayNameRole).toString();
match.iconName = QStringLiteral("org.kde.neochat");
match.id = m_model.data(m_model.index(i, 0), RoomListModel::IdRole).toString();
match.text = name;
match.relevance = 1;
const RemoteImage remoteImage = serializeImage(m_model.data(m_model.index(i, 0), RoomListModel::AvatarImageRole).value<QImage>());
match.properties.insert(QStringLiteral("icon-data"), QVariant::fromValue(remoteImage));
match.properties.insert(QStringLiteral("subtext"), m_model.data(m_model.index(i, 0), RoomListModel::TopicRole).toString());
if (name.compare(searchTerm, Qt::CaseInsensitive) == 0) {
match.type = ExactMatch;
} else {
match.type = CompletionMatch;
}
matches << match;
}
return matches;
}
void Runner::Run(const QString &id, const QString &actionId)
{
Q_UNUSED(actionId);
NeoChatRoom *room = qobject_cast<NeoChatRoom *>(Controller::instance().activeConnection()->room(id));
if (!room) {
return;
}
RoomManager::instance().enterRoom(room);
Q_EMIT Controller::instance().showWindow();
}

169
src/runner.h Normal file
View File

@@ -0,0 +1,169 @@
// SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QDBusContext>
#include <QObject>
#include <QDBusArgument>
#include <QList>
#include <QString>
#include <QVariantMap>
#include "roomlistmodel.h"
#include "sortfilterroomlistmodel.h"
// Copied from KRunner/QueryMatch
enum MatchType {
NoMatch = 0, /**< Null match */
CompletionMatch = 10, /**< Possible completion for the data of the query */
PossibleMatch = 30, /**< Something that may match the query */
InformationalMatch = 50, /**< A purely informational, non-runnable match,
such as the answer to a question or calculation.
The data of the match will be converted to a string
and set in the search field */
HelperMatch = 70, /**< A match that represents an action not directly related
to activating the given search term, such as a search
in an external tool or a command learning trigger. Helper
matches tend to be generic to the query and should not
be autoactivated just because the user hits "Enter"
while typing. They must be explicitly selected to
be activated, but unlike InformationalMatch cause
an action to be triggered. */
ExactMatch = 100, /**< An exact match to the query */
};
struct RemoteMatch {
// sssuda{sv}
QString id;
QString text;
QString iconName;
MatchType type = MatchType::NoMatch;
qreal relevance = 0;
QVariantMap properties;
};
typedef QList<RemoteMatch> RemoteMatches;
struct RemoteAction {
QString id;
QString text;
QString iconName;
};
typedef QList<RemoteAction> RemoteActions;
struct RemoteImage {
// iiibiiay (matching notification spec image-data attribute)
int width;
int height;
int rowStride;
bool hasAlpha;
int bitsPerSample;
int channels;
QByteArray data;
};
inline QDBusArgument &operator<<(QDBusArgument &argument, const RemoteMatch &match)
{
argument.beginStructure();
argument << match.id;
argument << match.text;
argument << match.iconName;
argument << match.type;
argument << match.relevance;
argument << match.properties;
argument.endStructure();
return argument;
}
inline const QDBusArgument &operator>>(const QDBusArgument &argument, RemoteMatch &match)
{
argument.beginStructure();
argument >> match.id;
argument >> match.text;
argument >> match.iconName;
uint type;
argument >> type;
match.type = static_cast<MatchType>(type);
argument >> match.relevance;
argument >> match.properties;
argument.endStructure();
return argument;
}
inline QDBusArgument &operator<<(QDBusArgument &argument, const RemoteAction &action)
{
argument.beginStructure();
argument << action.id;
argument << action.text;
argument << action.iconName;
argument.endStructure();
return argument;
}
inline const QDBusArgument &operator>>(const QDBusArgument &argument, RemoteAction &action)
{
argument.beginStructure();
argument >> action.id;
argument >> action.text;
argument >> action.iconName;
argument.endStructure();
return argument;
}
inline QDBusArgument &operator<<(QDBusArgument &argument, const RemoteImage &image)
{
argument.beginStructure();
argument << image.width;
argument << image.height;
argument << image.rowStride;
argument << image.hasAlpha;
argument << image.bitsPerSample;
argument << image.channels;
argument << image.data;
argument.endStructure();
return argument;
}
inline const QDBusArgument &operator>>(const QDBusArgument &argument, RemoteImage &image)
{
argument.beginStructure();
argument >> image.width;
argument >> image.height;
argument >> image.rowStride;
argument >> image.hasAlpha;
argument >> image.bitsPerSample;
argument >> image.channels;
argument >> image.data;
argument.endStructure();
return argument;
}
Q_DECLARE_METATYPE(RemoteMatch)
Q_DECLARE_METATYPE(RemoteMatches)
Q_DECLARE_METATYPE(RemoteAction)
Q_DECLARE_METATYPE(RemoteActions)
Q_DECLARE_METATYPE(RemoteImage)
class Runner : public QObject, protected QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.krunner1")
public:
Runner();
Q_SCRIPTABLE RemoteActions Actions();
Q_SCRIPTABLE RemoteMatches Match(const QString &searchTerm);
Q_SCRIPTABLE void Run(const QString &id, const QString &actionId);
private:
RemoteImage serializeImage(const QImage &image);
void activeConnectionChanged();
SortFilterRoomListModel m_model;
RoomListModel m_sourceModel;
};