Add webshortcut search
This commit is contained in:
@@ -68,6 +68,7 @@ if(ANDROID)
|
|||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
find_package(Qt5 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
find_package(Qt5 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||||
|
find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS REQUIRED KIO)
|
||||||
find_package(KF5QQC2DesktopStyle ${KF5_MIN_VERSION} REQUIRED)
|
find_package(KF5QQC2DesktopStyle ${KF5_MIN_VERSION} REQUIRED)
|
||||||
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
||||||
TYPE RUNTIME
|
TYPE RUNTIME
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Loader {
|
|||||||
property string eventType: ""
|
property string eventType: ""
|
||||||
property string formattedBody: ""
|
property string formattedBody: ""
|
||||||
required property string source
|
required property string source
|
||||||
|
property string selectedText: ""
|
||||||
|
|
||||||
property list<Kirigami.Action> actions: [
|
property list<Kirigami.Action> actions: [
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
@@ -65,89 +66,33 @@ Loader {
|
|||||||
onClicked: loadRoot.item.close();
|
onClicked: loadRoot.item.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
QQC2.Menu {
|
||||||
|
id: webshortcutmenu
|
||||||
|
title: i18n("Search for '%1'", webshortcutmodel.trunkatedSearchText)
|
||||||
|
property bool isVisible: selectedText && selectedText.length > 0 && webshortcutmodel.enabled
|
||||||
|
Component.onCompleted: webshortcutmenu.parent.visible = isVisible
|
||||||
|
onIsVisibleChanged: webshortcutmenu.parent.visible = isVisible
|
||||||
|
Instantiator {
|
||||||
|
model: WebShortcutModel {
|
||||||
|
id: webshortcutmodel
|
||||||
|
selectedText: loadRoot.selectedText
|
||||||
|
onOpenUrl: RoomManager.visitNonMatrix(url)
|
||||||
|
}
|
||||||
|
delegate: QQC2.MenuItem {
|
||||||
|
text: model.display
|
||||||
|
icon.name: model.decoration
|
||||||
|
onTriggered: webshortcutmodel.trigger(model.edit)
|
||||||
|
}
|
||||||
|
onObjectAdded: webshortcutmenu.insertItem(0, object)
|
||||||
|
}
|
||||||
|
QQC2.MenuSeparator {}
|
||||||
|
QQC2.MenuItem {
|
||||||
|
text: i18n("Configure Web Shortcuts...")
|
||||||
|
icon.name: "configure"
|
||||||
|
onTriggered: webshortcutmodel.configureWebShortcuts()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
Kirigami.OverlaySheet {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
parent: applicationWindow().overlay
|
|
||||||
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
|
|
||||||
header: Kirigami.Heading {
|
|
||||||
text: i18nc("@title:menu Message detail dialog", "Message detail")
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 0
|
|
||||||
RowLayout {
|
|
||||||
id: headerLayout
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.margins: Kirigami.Units.largeSpacing
|
|
||||||
spacing: Kirigami.Units.largeSpacing
|
|
||||||
Kirigami.Avatar {
|
|
||||||
id: avatar
|
|
||||||
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
|
|
||||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
|
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
name: author.displayName
|
|
||||||
color: author.color
|
|
||||||
}
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Kirigami.Heading {
|
|
||||||
level: 3
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: author.displayName
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
QQC2.Label {
|
|
||||||
text: message
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 24
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
|
|
||||||
onLinkActivated: RoomManager.openResource(link);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Kirigami.Separator {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
RowLayout {
|
|
||||||
spacing: 0
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
|
|
||||||
Repeater {
|
|
||||||
model: ["👍", "👎️", "😄", "🎉", "🚀", "👀"]
|
|
||||||
delegate: QQC2.ItemDelegate {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
contentItem: QQC2.Label {
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.family: "emoji"
|
|
||||||
text: modelData
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
currentRoom.toggleReaction(eventId, modelData)
|
|
||||||
loadRoot.item.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Kirigami.Separator {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
Component {
|
Component {
|
||||||
id: mobileMenu
|
id: mobileMenu
|
||||||
|
|||||||
@@ -372,11 +372,11 @@ Kirigami.ScrollablePage {
|
|||||||
Layout.bottomMargin: Kirigami.Units.largeSpacing * 2
|
Layout.bottomMargin: Kirigami.Units.largeSpacing * 2
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onTapped: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody)
|
onTapped: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody, parent.selectedText)
|
||||||
}
|
}
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
onLongPressed: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody)
|
onLongPressed: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody, parent.selectedText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,11 +399,11 @@ Kirigami.ScrollablePage {
|
|||||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
onTapped: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody)
|
onTapped: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody, parent.selectedText)
|
||||||
}
|
}
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
onLongPressed: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody)
|
onLongPressed: openMessageContext(author, model.message, eventId, toolTip, eventType, model.formattedBody, parent.selectedText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -881,8 +881,9 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Open context menu for normal message
|
/// Open context menu for normal message
|
||||||
function openMessageContext(author, message, eventId, source, eventType, formattedBody) {
|
function openMessageContext(author, message, eventId, source, eventType, formattedBody, selectedText) {
|
||||||
const contextMenu = messageDelegateContextMenu.createObject(page, {
|
const contextMenu = messageDelegateContextMenu.createObject(page, {
|
||||||
|
selectedText: selectedText,
|
||||||
author: author,
|
author: author,
|
||||||
message: message,
|
message: message,
|
||||||
eventId: eventId,
|
eventId: eventId,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ add_executable(neochat
|
|||||||
stickerevent.cpp
|
stickerevent.cpp
|
||||||
chatboxhelper.cpp
|
chatboxhelper.cpp
|
||||||
commandmodel.cpp
|
commandmodel.cpp
|
||||||
|
webshortcutmodel.cpp
|
||||||
../res.qrc
|
../res.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ if(ANDROID)
|
|||||||
"network-connect"
|
"network-connect"
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(neochat PRIVATE Qt5::Widgets)
|
target_link_libraries(neochat PRIVATE Qt5::Widgets KF5::KIOWidgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TARGET KF5::DBusAddons)
|
if(TARGET KF5::DBusAddons)
|
||||||
@@ -99,4 +100,8 @@ if(TARGET KF5::DBusAddons)
|
|||||||
target_compile_definitions(neochat PRIVATE -DHAVE_KDBUSADDONS)
|
target_compile_definitions(neochat PRIVATE -DHAVE_KDBUSADDONS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (TARGET KF5::KIOWidgets)
|
||||||
|
target_compile_definitions(neochat PRIVATE -DHAVE_KIO)
|
||||||
|
endif()
|
||||||
|
|
||||||
install(TARGETS neochat ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
|
install(TARGETS neochat ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
#include "sortfilterroomlistmodel.h"
|
#include "sortfilterroomlistmodel.h"
|
||||||
#include "userdirectorylistmodel.h"
|
#include "userdirectorylistmodel.h"
|
||||||
#include "userlistmodel.h"
|
#include "userlistmodel.h"
|
||||||
|
#include "webshortcutmodel.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
@@ -147,6 +148,7 @@ int main(int argc, char *argv[])
|
|||||||
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
|
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
|
||||||
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
|
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
|
||||||
qmlRegisterType<RoomListModel>("org.kde.neochat", 1, 0, "RoomListModel");
|
qmlRegisterType<RoomListModel>("org.kde.neochat", 1, 0, "RoomListModel");
|
||||||
|
qmlRegisterType<KWebShortcutModel>("org.kde.neochat", 1, 0, "WebShortcutModel");
|
||||||
qmlRegisterType<UserListModel>("org.kde.neochat", 1, 0, "UserListModel");
|
qmlRegisterType<UserListModel>("org.kde.neochat", 1, 0, "UserListModel");
|
||||||
qmlRegisterType<MessageEventModel>("org.kde.neochat", 1, 0, "MessageEventModel");
|
qmlRegisterType<MessageEventModel>("org.kde.neochat", 1, 0, "MessageEventModel");
|
||||||
qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
|
qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
|
||||||
|
|||||||
124
src/webshortcutmodel.cpp
Normal file
124
src/webshortcutmodel.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2010 Eike Hein <hein@kde.org>
|
||||||
|
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "webshortcutmodel.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_KIO
|
||||||
|
#include <KUriFilter>
|
||||||
|
#include <KIO/CommandLauncherJob>
|
||||||
|
#endif
|
||||||
|
#include <KStringHandler>
|
||||||
|
|
||||||
|
struct KWebShortcutModelPrivate
|
||||||
|
{
|
||||||
|
QString selectedText;
|
||||||
|
KUriFilterData filterData;
|
||||||
|
QStringList searchProviders;
|
||||||
|
};
|
||||||
|
|
||||||
|
KWebShortcutModel::KWebShortcutModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
, d(new KWebShortcutModelPrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
KWebShortcutModel::~KWebShortcutModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString KWebShortcutModel::selectedText() const
|
||||||
|
{
|
||||||
|
return d->selectedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString KWebShortcutModel::trunkatedSearchText() const
|
||||||
|
{
|
||||||
|
return KStringHandler::rsqueeze(d->selectedText, 21);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KWebShortcutModel::enabled() const
|
||||||
|
{
|
||||||
|
#ifdef HAVE_KIO
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void KWebShortcutModel::setSelectedText(const QString &selectedText)
|
||||||
|
{
|
||||||
|
if (d->selectedText == selectedText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef HAVE_KIO
|
||||||
|
beginResetModel();
|
||||||
|
d->selectedText = selectedText;
|
||||||
|
|
||||||
|
if (selectedText.isEmpty()) {
|
||||||
|
endResetModel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString searchText = selectedText;
|
||||||
|
searchText = searchText.replace('\n', ' ').replace('\r', ' ').simplified();
|
||||||
|
if (searchText.isEmpty()) {
|
||||||
|
endResetModel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d->filterData.setData(searchText);
|
||||||
|
d->filterData.setSearchFilteringOptions(KUriFilterData::RetrievePreferredSearchProvidersOnly);
|
||||||
|
|
||||||
|
if (KUriFilter::self()->filterSearchUri(d->filterData, KUriFilter::NormalTextFilter)) {
|
||||||
|
d->searchProviders = d->filterData.preferredSearchProviders();
|
||||||
|
}
|
||||||
|
endResetModel();
|
||||||
|
#else
|
||||||
|
d->selectedText = selectedText;
|
||||||
|
#endif
|
||||||
|
Q_EMIT selectedTextChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
int KWebShortcutModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
#ifdef HAVE_KIO
|
||||||
|
if (d->selectedText.count() > 0) {
|
||||||
|
return d->searchProviders.count();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant KWebShortcutModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_KIO
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
return d->searchProviders[index.row()];
|
||||||
|
case Qt::DecorationRole:
|
||||||
|
return d->filterData.iconNameForPreferredSearchProvider(d->searchProviders[index.row()]);
|
||||||
|
case Qt::EditRole:
|
||||||
|
return d->filterData.queryForPreferredSearchProvider(d->searchProviders[index.row()]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void KWebShortcutModel::trigger(const QString &data)
|
||||||
|
{
|
||||||
|
KUriFilterData filterData(data);
|
||||||
|
if (KUriFilter::self()->filterSearchUri(filterData, KUriFilter::WebShortcutFilter)) {
|
||||||
|
Q_EMIT openUrl(filterData.uri().url());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KWebShortcutModel::configureWebShortcuts()
|
||||||
|
{
|
||||||
|
auto job = new KIO::CommandLauncherJob("kcmshell5", QStringList() << "webshortcuts", this);
|
||||||
|
job->exec();
|
||||||
|
}
|
||||||
67
src/webshortcutmodel.h
Normal file
67
src/webshortcutmodel.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class KWebShortcutModelPrivate;
|
||||||
|
|
||||||
|
//! List model of web shortcuts for a a specified selectedText.
|
||||||
|
//!
|
||||||
|
//! This can be used as following in your QML code
|
||||||
|
//!
|
||||||
|
//! ```qml
|
||||||
|
//! QQC2.Menu {
|
||||||
|
//! id: webshortcutmenu
|
||||||
|
//! title: i18n("Search for '%1'", webshortcutmodel.trunkatedSearchText)
|
||||||
|
//! property bool isVisible: selectedText && selectedText.length > 0 && webshortcutmodel.enabled
|
||||||
|
//! Component.onCompleted: webshortcutmenu.parent.visible = isVisible
|
||||||
|
//! onIsVisibleChanged: webshortcutmenu.parent.visible = isVisible
|
||||||
|
//! Instantiator {
|
||||||
|
//! model: WebShortcutModel {
|
||||||
|
//! id: webshortcutmodel
|
||||||
|
//! selectedText: loadRoot.selectedText
|
||||||
|
//! onOpenUrl: Qt.openUrlExternally(url)
|
||||||
|
//! }
|
||||||
|
//! delegate: QQC2.MenuItem {
|
||||||
|
//! text: model.display
|
||||||
|
//! icon.name: model.decoration
|
||||||
|
//! onTriggered: webshortcutmodel.trigger(model.edit)
|
||||||
|
//! }
|
||||||
|
//! onObjectAdded: webshortcutmenu.insertItem(0, object)
|
||||||
|
//! }
|
||||||
|
//! QQC2.MenuSeparator {}
|
||||||
|
//! QQC2.MenuItem {
|
||||||
|
//! text: i18n("Configure Web Shortcuts...")
|
||||||
|
//! icon.name: "configure"
|
||||||
|
//! onTriggered: webshortcutmodel.configureWebShortcuts()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
class KWebShortcutModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString selectedText READ selectedText WRITE setSelectedText NOTIFY selectedTextChanged)
|
||||||
|
Q_PROPERTY(QString trunkatedSearchText READ trunkatedSearchText NOTIFY selectedTextChanged)
|
||||||
|
Q_PROPERTY(bool enabled READ enabled CONSTANT)
|
||||||
|
public:
|
||||||
|
explicit KWebShortcutModel(QObject *parent = nullptr);
|
||||||
|
~KWebShortcutModel();
|
||||||
|
|
||||||
|
QString selectedText() const;
|
||||||
|
void setSelectedText(const QString &selectedText);
|
||||||
|
QString trunkatedSearchText() const;
|
||||||
|
bool enabled() const;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
Q_INVOKABLE void trigger(const QString &data);
|
||||||
|
Q_INVOKABLE void configureWebShortcuts();
|
||||||
|
Q_SIGNALS:
|
||||||
|
void selectedTextChanged();
|
||||||
|
void openUrl(const QUrl &url);
|
||||||
|
private:
|
||||||
|
std::unique_ptr<KWebShortcutModelPrivate> d;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user