From 8e4edd7ab80e425e1cf0c3f4b367f6725efa1638 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Wed, 17 Jul 2024 10:13:19 +0300 Subject: [PATCH] Move search to plugins --- CMakeLists.txt | 1 + lib/CMakeLists.txt | 1 + lib/search/CMakeLists.txt | 26 +++ lib/search/glaciersearchplugin.h | 45 ++++ lib/search/plugins/CMakeLists.txt | 1 + lib/search/plugins/application/CMakeLists.txt | 19 ++ .../application/applicationsearchplugin.cpp | 64 ++++++ .../application/applicationsearchplugin.h | 39 ++++ lib/search/searchpluginmanager.cpp | 79 +++++++ lib/search/searchpluginmanager.h | 48 ++++ src/CMakeLists.txt | 19 +- src/glacier_global.h | 29 +++ src/main.cpp | 6 +- .../controlcenterbuttonsmodel.cpp | 0 src/{ => models}/controlcenterbuttonsmodel.h | 0 src/{ => models}/glacierwindowmodel.cpp | 0 src/{ => models}/glacierwindowmodel.h | 0 src/models/searchmodel.cpp | 87 +++++++ src/models/searchmodel.h | 54 +++++ src/qml/AppLauncher.qml | 2 +- src/qml/applauncher/SearchListView.qml | 217 ++++-------------- 21 files changed, 552 insertions(+), 185 deletions(-) create mode 100644 lib/CMakeLists.txt create mode 100644 lib/search/CMakeLists.txt create mode 100644 lib/search/glaciersearchplugin.h create mode 100644 lib/search/plugins/CMakeLists.txt create mode 100644 lib/search/plugins/application/CMakeLists.txt create mode 100644 lib/search/plugins/application/applicationsearchplugin.cpp create mode 100644 lib/search/plugins/application/applicationsearchplugin.h create mode 100644 lib/search/searchpluginmanager.cpp create mode 100644 lib/search/searchpluginmanager.h create mode 100644 src/glacier_global.h rename src/{ => models}/controlcenterbuttonsmodel.cpp (100%) rename src/{ => models}/controlcenterbuttonsmodel.h (100%) rename src/{ => models}/glacierwindowmodel.cpp (100%) rename src/{ => models}/glacierwindowmodel.h (100%) create mode 100644 src/models/searchmodel.cpp create mode 100644 src/models/searchmodel.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d208dd..28d179b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ find_package(KF6BluezQt REQUIRED) add_definitions(-DBLUEZQT_VERSION_MAJOR=${KF6BluezQt_VERSION_MAJOR}) add_definitions(-DBLUEZQT_VERSION_MINOR=${KF6BluezQt_VERSION_MINOR}) +add_subdirectory(lib) add_subdirectory(src) add_subdirectory(settings-plugins) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..45e548e --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(search) diff --git a/lib/search/CMakeLists.txt b/lib/search/CMakeLists.txt new file mode 100644 index 0000000..b884b5a --- /dev/null +++ b/lib/search/CMakeLists.txt @@ -0,0 +1,26 @@ +set(PROJECT glacierhomesearch) + +set(SRC + searchpluginmanager.h + searchpluginmanager.cpp) + +set(PUBLIC_HEADERS + glaciersearchplugin.h) + +include_directories(${CMAKE_SOURCE_DIR}/src) + +add_library(${PROJECT} SHARED ${SRC} ${HEADERS} ${PUBLIC_HEADERS}) +add_library(GlacierHome::Search ALIAS ${PROJECT}) + +target_link_libraries(${PROJECT} + Qt6::Core) + +set_target_properties(${PROJECT} PROPERTIES VERSION 0.1 SOVERSION 0) +add_definitions( -DINSTALL_LIBDIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") +add_subdirectory(plugins) + +install(TARGETS ${PROJECT} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/) + +install(FILES ${PUBLIC_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glacier-home) diff --git a/lib/search/glaciersearchplugin.h b/lib/search/glaciersearchplugin.h new file mode 100644 index 0000000..bae31e1 --- /dev/null +++ b/lib/search/glaciersearchplugin.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GLACIERSEARCHPLUGIN_H +#define GLACIERSEARCHPLUGIN_H + +#include +#include +#include +#include + +class GLACIER_EXPORT GlacierSearchPlugin : public QObject { + Q_OBJECT +public: + struct SearchResult { + QString iconTitle; + QString iconSource; + QString category; + QString extraCaption; + QMap action; + }; + + virtual void search(QString searchString) = 0; +signals: + void searchResultReady(QList results); +}; +Q_DECLARE_INTERFACE(GlacierSearchPlugin, "GlacierHome.SearchPlugin") + +#endif // GLACIERSEARCHPLUGIN_H diff --git a/lib/search/plugins/CMakeLists.txt b/lib/search/plugins/CMakeLists.txt new file mode 100644 index 0000000..7983944 --- /dev/null +++ b/lib/search/plugins/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(application) diff --git a/lib/search/plugins/application/CMakeLists.txt b/lib/search/plugins/application/CMakeLists.txt new file mode 100644 index 0000000..af78e66 --- /dev/null +++ b/lib/search/plugins/application/CMakeLists.txt @@ -0,0 +1,19 @@ +SET(PLUGINNAME application) + +set(SRC ${PLUGINNAME}searchplugin.cpp) +SET(HEADERS ${PLUGINNAME}searchplugin.h) + +include_directories(${CMAKE_SOURCE_DIR}/lib) +set(CMAKE_AUTOMOC ON) +add_definitions(-DQT_PLUGIN) + +add_library(${PLUGINNAME} MODULE ${SRC} ${HEADERS}) + +target_link_libraries(${PLUGINNAME} PUBLIC + Qt6::Core + Qt6::DBus + GlacierHome::Search + PkgConfig::LIPSTICK) + +install(TARGETS ${PLUGINNAME} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/glacier-home/plugins/search) diff --git a/lib/search/plugins/application/applicationsearchplugin.cpp b/lib/search/plugins/application/applicationsearchplugin.cpp new file mode 100644 index 0000000..f379e09 --- /dev/null +++ b/lib/search/plugins/application/applicationsearchplugin.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "applicationsearchplugin.h" + +ApplicationSearchPlugin::ApplicationSearchPlugin(QObject* parent) + : m_launchModel(new LauncherModel) +{ +} + +void ApplicationSearchPlugin::search(QString searchString) +{ + m_searchResults.clear(); + + for (int i = 0; i < m_launchModel.itemCount(); i++) { + QObject* item = m_launchModel.get(i); + if (item->property("title").toString().toLower().indexOf(searchString) != -1 + && !item->property("isBlacklisted").toBool()) { + SearchResult result; + result.iconTitle = item->property("title").toString(); + + QString iconSource = item->property("iconId").toString(); + if (iconSource.isEmpty()) { + iconSource = "/usr/share/glacier-home/qml/theme/default-icon.png"; + } else { + if (iconSource.startsWith("/")) { + iconSource = "file://" + iconSource; + } else if (!iconSource.startsWith("file:///")) { + iconSource = "image://theme/" + iconSource; + } + } + result.iconSource = iconSource; + + result.category = tr("Application"); + result.extraCaption = tr("installed on your device"); + QMap action; + action.insert("type", "exec"); + action.insert("app_id", i); + result.action = action; + + m_searchResults.push_back(result); + } + } + + if (!m_searchResults.isEmpty()) { + emit searchResultReady(m_searchResults); + } +} diff --git a/lib/search/plugins/application/applicationsearchplugin.h b/lib/search/plugins/application/applicationsearchplugin.h new file mode 100644 index 0000000..dec604e --- /dev/null +++ b/lib/search/plugins/application/applicationsearchplugin.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef APPLICATIONSEARCHPLUGIN_H +#define APPLICATIONSEARCHPLUGIN_H + +#include +#include + +class ApplicationSearchPlugin : public GlacierSearchPlugin { + Q_OBJECT + Q_INTERFACES(GlacierSearchPlugin) + Q_PLUGIN_METADATA(IID "GlacierHome.SearchPlugin") +public: + explicit ApplicationSearchPlugin(QObject* parent = nullptr); + void search(QString searchString); + +private: + LauncherModel m_launchModel; + QList m_searchResults; +}; + +#endif // APPLICATIONSEARCHPLUGIN_H diff --git a/lib/search/searchpluginmanager.cpp b/lib/search/searchpluginmanager.cpp new file mode 100644 index 0000000..c02efbf --- /dev/null +++ b/lib/search/searchpluginmanager.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "searchpluginmanager.h" +#include +#include +#include + +#ifndef INSTALL_LIBDIR +#error INTALLINSTALL_LIBDIR is not set! +#endif + +SearchPluginManager::SearchPluginManager(QObject* parent) + : QObject { parent } +{ + QTimer::singleShot(0, this, SLOT(loadSearchPlugins())); +} + +SearchPluginManager::~SearchPluginManager() +{ + foreach (const GlacierSearchPlugin* plugin, m_pluginList) { + disconnect(plugin, &GlacierSearchPlugin::searchResultReady, this, &SearchPluginManager::searchResultPluginHandler); + delete plugin; + } +} + +void SearchPluginManager::search(QString searchString) +{ + m_searchResults.clear(); + + foreach (GlacierSearchPlugin* plugin, m_pluginList) { + plugin->search(searchString); + } +} + +void SearchPluginManager::loadSearchPlugins() +{ + QDir pluginsDir(QString::fromUtf8(INSTALL_LIBDIR) + "/glacier-home/plugins/search"); + QList pluginsLibList = pluginsDir.entryList(QDir::Files); + + for (const QString& file : qAsConst(pluginsLibList)) { + QPluginLoader pluginLoader(pluginsDir.path() + "/" + file); + + QObject* plugin = pluginLoader.instance(); + if (plugin) { + GlacierSearchPlugin* searchPlugin = qobject_cast(plugin); + if (searchPlugin != nullptr) { + m_pluginList.push_back(searchPlugin); + connect(searchPlugin, &GlacierSearchPlugin::searchResultReady, this, &SearchPluginManager::searchResultReady); + } else { + qWarning() << "CANT CAST PLIUGIN FROM" << pluginsDir.path() + "/" + file; + } + } else { + delete plugin; + } + } +} + +void SearchPluginManager::searchResultPluginHandler(QList results) +{ + m_searchResults.append(results); + emit searchResultReady(m_searchResults); +} diff --git a/lib/search/searchpluginmanager.h b/lib/search/searchpluginmanager.h new file mode 100644 index 0000000..914192c --- /dev/null +++ b/lib/search/searchpluginmanager.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SEARCHPLUGINMANAGER_H +#define SEARCHPLUGINMANAGER_H + +#include "glaciersearchplugin.h" +#include +#include +#include + +class SearchPluginManager : public QObject { + Q_OBJECT +public: + explicit SearchPluginManager(QObject* parent = nullptr); + virtual ~SearchPluginManager(); + + void search(QString searchString); + +signals: + void searchResultReady(QList results); + +private slots: + void loadSearchPlugins(); + void searchResultPluginHandler(QList results); + +private: + QList m_pluginList; + QList m_searchResults; +}; + +#endif // SEARCHPLUGINMANAGER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bfc3de3..ccfcd27 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,18 +2,20 @@ file(GLOB_RECURSE QML_JS_FILES *.qml *.js) set(SRC main.cpp - controlcenterbuttonsmodel.cpp - controlcenterbuttonsmodel.h fileutils.cpp fileutils.h - glacierwindowmodel.cpp - glacierwindowmodel.h mceconnect.cpp mceconnect.h logging.h logging.cpp - ${QML_JS_FILES} - ) + glacier_global.h + models/glacierwindowmodel.cpp + models/glacierwindowmodel.h + models/controlcenterbuttonsmodel.cpp + models/controlcenterbuttonsmodel.h + models/searchmodel.h + models/searchmodel.cpp + ${QML_JS_FILES}) #add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/geoagent.h ${CMAKE_CURRENT_SOURCE_DIR}/geoagent.cpp # DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.GeoClue2.Agent.xml @@ -35,13 +37,16 @@ if(USE_GEOCLUE2) geoagent.h) endif() -add_executable(lipstick ${SRC} ${GEOCLUE_SRC}) +add_executable(lipstick ${SRC} ${GEOCLUE_SRC} ${PUBLIC_HEADERS}) + +include_directories(${CMAKE_SOURCE_DIR}/lib) target_link_libraries(lipstick PUBLIC Qt6::Gui Qt6::Qml Qt6::Quick Qt6::DBus + GlacierHome::Search PkgConfig::LIPSTICK PkgConfig::MLITE6 PkgConfig::NEMODEVICELOCK diff --git a/src/glacier_global.h b/src/glacier_global.h new file mode 100644 index 0000000..bfc51d0 --- /dev/null +++ b/src/glacier_global.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GLACIER_GLOBAL_H +#define GLACIER_GLOBAL_H + +#if defined(GLACIER_LIBRARY) +#define GLACIER_EXPORT Q_DECL_EXPORT +#else +#define GLACIER_EXPORT Q_DECL_IMPORT +#endif + +#endif // GLACIER_GLOBAL_H diff --git a/src/main.cpp b/src/main.cpp index 541c179..478548b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,14 +34,15 @@ #include #include -#include "controlcenterbuttonsmodel.h" +#include "models/controlcenterbuttonsmodel.h" +#include "models/glacierwindowmodel.h" +#include "models/searchmodel.h" #ifdef USE_GEOCLUE2 #include "geoclueagent.h" #endif #include "fileutils.h" -#include "glacierwindowmodel.h" #include "logging.h" #include "mceconnect.h" @@ -99,6 +100,7 @@ int main(int argc, char** argv) qmlRegisterType("org.nemomobile.glacier", 1, 0, "GlacierWindowModel"); qmlRegisterType("org.nemomobile.glacier", 1, 0, "GlacierMceConnect"); qmlRegisterType("org.nemomobile.glacier", 1, 0, "ControlCenterButtonsModel"); + qmlRegisterType("org.nemomobile.glacier", 1, 0, "GlacierSearchModel"); #ifdef USE_GEOCLUE2 app.engine()->rootContext()->setContextProperty("usegeoclue2", true); qmlRegisterType("org.nemomobile.glacier", 1, 0, "GlacierGeoAgent"); diff --git a/src/controlcenterbuttonsmodel.cpp b/src/models/controlcenterbuttonsmodel.cpp similarity index 100% rename from src/controlcenterbuttonsmodel.cpp rename to src/models/controlcenterbuttonsmodel.cpp diff --git a/src/controlcenterbuttonsmodel.h b/src/models/controlcenterbuttonsmodel.h similarity index 100% rename from src/controlcenterbuttonsmodel.h rename to src/models/controlcenterbuttonsmodel.h diff --git a/src/glacierwindowmodel.cpp b/src/models/glacierwindowmodel.cpp similarity index 100% rename from src/glacierwindowmodel.cpp rename to src/models/glacierwindowmodel.cpp diff --git a/src/glacierwindowmodel.h b/src/models/glacierwindowmodel.h similarity index 100% rename from src/glacierwindowmodel.h rename to src/models/glacierwindowmodel.h diff --git a/src/models/searchmodel.cpp b/src/models/searchmodel.cpp new file mode 100644 index 0000000..597d55f --- /dev/null +++ b/src/models/searchmodel.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "searchmodel.h" + +SearchModel::SearchModel(QObject* parent) + : QAbstractListModel { parent } + , m_manager(new SearchPluginManager(this)) +{ + m_hash.insert(Qt::UserRole, QByteArray("iconTitle")); + m_hash.insert(Qt::UserRole + 1, QByteArray("iconSource")); + m_hash.insert(Qt::UserRole + 2, QByteArray("category")); + m_hash.insert(Qt::UserRole + 3, QByteArray("extraCaption")); + m_hash.insert(Qt::UserRole + 4, QByteArray("action")); + + connect(m_manager, &SearchPluginManager::searchResultReady, this, &SearchModel::searchResultHandler); +} + +int SearchModel::rowCount(const QModelIndex& parent) const +{ + return m_searchResults.count(); +} + +QVariant SearchModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + if (index.row() >= m_searchResults.size()) { + return QVariant(); + } + + GlacierSearchPlugin::SearchResult result = m_searchResults.at(index.row()); + if (role == Qt::UserRole) { + return result.iconTitle; + } + if (role == Qt::UserRole + 1) { + return result.iconSource; + } + if (role == Qt::UserRole + 2) { + return result.category; + } + if (role == Qt::UserRole + 3) { + return result.extraCaption; + } + if (role == Qt::UserRole + 4) { + return result.action; + } + + return QVariant(); +} + +void SearchModel::search(QString searchString) +{ + m_manager->search(searchString); +} + +void SearchModel::searchResultHandler(QList results) +{ + beginResetModel(); + m_searchResults.clear(); + m_searchResults = results; + endResetModel(); + emit countChanged(); +} + +int SearchModel::count() const +{ + return m_searchResults.count(); +} diff --git a/src/models/searchmodel.h b/src/models/searchmodel.h new file mode 100644 index 0000000..0c66ac9 --- /dev/null +++ b/src/models/searchmodel.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Chupligin Sergey + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SEARCHMODEL_H +#define SEARCHMODEL_H + +#include +#include + +#include + +class SearchModel : public QAbstractListModel { + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged FINAL) + +public: + explicit SearchModel(QObject* parent = nullptr); + + int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role) const; + QHash roleNames() const { return m_hash; } + + Q_INVOKABLE void search(QString searchString); + int count() const; + +signals: + void countChanged(); + +private slots: + void searchResultHandler(QList results); + +private: + QHash m_hash; + SearchPluginManager* m_manager; + QList m_searchResults; +}; + +#endif // SEARCHMODEL_H diff --git a/src/qml/AppLauncher.qml b/src/qml/AppLauncher.qml index 137a39f..14961e1 100644 --- a/src/qml/AppLauncher.qml +++ b/src/qml/AppLauncher.qml @@ -41,7 +41,7 @@ Flickable{ width: parent.width height: desktop.height property var switcher: null - property string searchString + property alias searchString: searchListView.searchString ConfigurationValue { id: alwaysShowSearch diff --git a/src/qml/applauncher/SearchListView.qml b/src/qml/applauncher/SearchListView.qml index d483331..03ef0c6 100644 --- a/src/qml/applauncher/SearchListView.qml +++ b/src/qml/applauncher/SearchListView.qml @@ -31,16 +31,28 @@ ****************************************************************************************/ import QtQuick import Nemo.Controls +import Nemo.DBus import org.nemomobile.lipstick +import org.nemomobile.glacier Item { id:searchList height: calculateHeight() anchors.bottomMargin:Theme.itemSpacingHuge property alias searchField: searchField + property alias searchString: searchField.text property int oldHeight + GlacierSearchModel{ + id: searchModel + } + + DBusInterface{ + id: callingIface + bus: DBus.SessionBus + } + function cleanup(){ searchField.focus = false appLauncher.searchString = "" @@ -57,7 +69,7 @@ Item { { if(searchList.visible){ if(searchField.text.length > 0){ - return parent.height + return parent.height } return searchRow.height+Theme.itemSpacingMedium } @@ -117,14 +129,10 @@ Item { width:parent.width - searchIcon.width - Theme.itemSpacingMedium placeholderText: qsTr("Search") - Binding { - target: appLauncher - property: "searchString" - value: searchField.text.toLowerCase().trim() - } onTextChanged: { - if(text.lenght>0) { + if(searchField.text.length > 0) { searchField.forceActiveFocus() + searchModel.search(searchField.text) } } @@ -170,36 +178,26 @@ Item { } ListView { - id:listView + id: searchResultListView clip: true width: parent.width height:contentHeight anchors{ top: searchRow.bottom - topMargin: listModel.count > 0 ? Theme.itemSpacingSmall : 0 + topMargin: searchModel.count > 0 ? Theme.itemSpacingSmall : 0 } - visible: searchString.length>0 + visible: searchModel.count > 0 section.property: 'category' section.delegate: Component{ id: sectionHeading Rectangle { - width: listView.width + width: searchResultListView.width height: Theme.itemHeightMedium color: "transparent" Text { id: sectionText - text: { - switch (section) { - case 'Application': - return qsTr("Application") - case 'Contact': - return qsTr("Contact") - default: - return qsTr("Content") - } - } - + text: section font.capitalization: Font.AllUppercase font.pixelSize: Theme.fontSizeSmall color: Theme.textColor @@ -214,7 +212,7 @@ Item { id: line height: 1 color: Theme.textColor - width: listView.width-sectionText.width-Theme.itemHeightExtraSmall + width: searchResultListView.width-sectionText.width-Theme.itemHeightExtraSmall anchors{ left: sectionText.right leftMargin: Theme.itemSpacingSmall @@ -230,130 +228,15 @@ Item { Connections { target: appLauncher - function onSearchStringChanged() { listView.update() } + function onSearchStringChanged() { searchResultListView.update() } } - model: ListModel { - id: listModel - } - - //Orginal function ** Copyright (C) 2013 Jolla Ltd. ** Contact: Joona Petrell **BSD - //Function has been modified - function update() { - if(searchString.length<1) { - listModel.clear() - } else { - var iconTitle - var category - var extraCaption - var iconId - var found - var i - - var titles = [] - var contacts = [] - for (i = 0; i < searchLauncherModel.itemCount; ++i) { - if (searchLauncherModel.get(i).type === LauncherModel.Folder) { - for(var j = 0; j< searchLauncherModel.get(i).itemCount; ++j ) { - titles.push({ - 'iconTitle':searchLauncherModel.get(i).get(j).title, - 'iconSource':searchLauncherModel.get(i).get(j).iconId, - 'id':i, - 'folderId':j, - 'category':qsTr("Application"), - 'extraCaption': qsTr("installed on your device") - }) - } - } else { - titles.push({ - 'iconTitle':searchLauncherModel.get(i).title, - 'iconSource':searchLauncherModel.get(i).iconId, - 'id':i, - 'folderId':-1, - 'category':qsTr("Application"), - 'extraCaption': qsTr("installed on your device") - }) - } - } - for (i = 0; i < peopleModel.count; ++i) { - if(peopleModel.get(i).firstName && peopleModel.get(i).lastName) { - contacts.push({ - 'title':(peopleModel.get(i).firstName + " " + peopleModel.get(i).lastName), - 'iconSource':peopleModel.get(i).avatarUrl.toString(), - 'extraCaption':peopleModel.get(i).phoneNumbers, - 'category':qsTr("Contact") - }) - } - } - var filteredTitles = titles.filter(function (icon) { - return icon.iconTitle.toLowerCase().indexOf(searchString) !== -1 - }) - // helper objects that can be quickly accessed - var filteredTitleObject = new Object - for (i = 0; i < filteredTitles.length; ++i) { - filteredTitleObject[filteredTitles[i].iconTitle] = true - } - var existingTitleObject = new Object - for (i = 0; i < count; ++i) { - iconTitle = listModel.get(i).title - existingTitleObject[iconTitle] = true - } - - // remove items no longer in filtered set - i = 0 - while (i < count) { - iconTitle = listModel.get(i).title - found = filteredTitleObject.hasOwnProperty(iconTitle) - if (!found) { - listModel.remove(i) - } else { - i++ - } - } - // add new items - for (i = 0; i < filteredTitles.length; ++i) { - iconTitle = filteredTitles[i].iconTitle - iconId = filteredTitles[i].iconSource - var id = filteredTitles[i].id - var folderId = filteredTitles[i].folderId - category = filteredTitles[i].category - found = existingTitleObject.hasOwnProperty(iconTitle) - if (!found) { - // for simplicity, just adding to end instead of corresponding position in original list - listModel.append({'title':iconTitle, 'iconSource':iconId, 'id':id, 'folderId':folderId, 'category':category, 'extraCaption': ""}) - } - } - for (i = 0; i < contacts.length; ++i) { - iconTitle = contacts[i].title - iconId = contacts[i].iconSource - extraCaption = contacts[i].extraCaption[0] - category = contacts[i].category - listModel.append({'title':iconTitle, 'iconSource':iconId, 'extraCaption':extraCaption, 'category':category}) - } - } - } + model: searchModel delegate: Item { - width: listView.width + width: searchResultListView.width height:Theme.itemHeightExtraLarge*1.2 - property string iconCaption: model.title - property string iconSource: { - if(model.iconSource) { - if (model.iconSource.indexOf("file:///") == 0) { - return model.iconSource - } else { - if( model.iconSource.indexOf("/") == 0) { - return "file://" + model.iconSource - } else { - return "image://theme/" + model.iconSource - } - } - } else { - return "/usr/share/glacier-home/qml/theme/default-icon.png" - } - } - Rectangle { anchors.fill: parent color: "#11ffffff" @@ -380,16 +263,8 @@ Item { } width: height height: parent.height - Theme.itemSpacingHuge - enabled: { - if(searchLauncherModel.get(model.id).type === LauncherModel.Application) { - if(searchLauncherModel.get(model.id).isLaunching) - return switcher.switchModel.getWindowIdForTitle(model.title) == 0 - } else if (searchLauncherModel.get(model.id).type === LauncherModel.Folder && model.folderId > -1) { - if (searchLauncherModel.get(model.id).get(model.folderId).isLaunching) - return switcher.switchModel.getWindowIdForTitle(model.title) == 0 - } - return false - } + enabled: false + Connections { target: Lipstick.compositor function onWindowAdded(window) { @@ -411,7 +286,7 @@ Item { height: labelWrapper.childrenRect.height Label { id:mainLabel - text:iconCaption + text:iconTitle anchors { left: parent.left right: parent.right @@ -437,30 +312,22 @@ Item { MouseArea { id:mouse anchors.fill: parent - onClicked: { - switch (category ) { - case "Application": - var winId - if (searchLauncherModel.get(model.id).type !== LauncherModel.Folder) { - winId = switcher.switchModel.getWindowIdForTitle(model.title) - if (winId == 0 && !searchLauncherModel.get(model.id).isLaunching) - searchLauncherModel.get(model.id).launchApplication() - else - Lipstick.compositor.windowToFront(winId) - } else if (searchLauncherModel.get(model.id).type === LauncherModel.Folder && model.folderId > -1) { - winId = switcher.switchModel.getWindowIdForTitle(model.title) - if (winId == 0 && !searchLauncherModel.get(model.id).get(model.folderId).isLaunching) - searchLauncherModel.get(model.id).get(model.folderId).launchApplication() - else - Lipstick.compositor.windowToFront(winId) - } + onClicked: if(action.type == "exec") { + var winId = switcher.switchModel.getWindowIdForTitle(iconTitle) + if (winId == 0 && !launcherModel.get(action.app_id).isLaunching) { + launcherModel.get(action.app_id).launchApplication() + } else { + Lipstick.compositor.windowToFront(winId) + } + } else if (action.type == "dbus") { + callingIface.service = action.dbus_service + callingIface.path = action.dbus_path + callingIface.iface = action.dbus_iface - break - case "Contact": - console.log("Call to person. Or open contextmenu where sms and call") - break - } - } + callingIface.call(action.dbus_call, action.dbus_params) + } else { + console.warn("Wron action type:"+action.type) + } } } }