diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000000000000000000000000000000000000..1a5279993de34a287779282866750468991ca80d --- /dev/null +++ b/.clang-tidy @@ -0,0 +1 @@ +Checks: '-*,analyzer-cplusplus.NewDeleteLeaks' diff --git a/src/app/accountadapter.cpp b/src/app/accountadapter.cpp index 95cb761120df8a32ddf714980cf97b64c05fd334..e022e70d34890c2796933eb593218659ab50068b 100644 --- a/src/app/accountadapter.cpp +++ b/src/app/accountadapter.cpp @@ -22,7 +22,7 @@ #include "appsettingsmanager.h" #include "qtutils.h" -#include "qmlregister.h" +#include "accountlistmodel.h" #include <QtConcurrent/QtConcurrent> @@ -33,14 +33,7 @@ AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager, : QmlAdapterBase(instance, parent) , settingsManager_(settingsManager) , systemTray_(systemTray) - , accountListModel_(new AccountListModel(instance)) - , deviceItemListModel_(new DeviceItemListModel(instance, parent)) - , moderatorListModel_(new ModeratorListModel(instance, parent)) { - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, accountListModel_.get(), "AccountListModel"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, deviceItemListModel_.get(), "DeviceItemListModel"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, moderatorListModel_.get(), "ModeratorListModel"); - connect(&lrcInstance_->accountModel(), &AccountModel::accountStatusChanged, this, @@ -53,8 +46,13 @@ AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager, connect(systemTray_, &SystemTray::countChanged, - accountListModel_.get(), + qApp->property("AccountListModel").value<AccountListModel*>(), &AccountListModel::updateNotifications); + + // Switch account to the specified index when an account is added. + connect(this, &AccountAdapter::accountAdded, this, [this](const QString&, int index) { + changeAccount(index); + }); } AccountModel* diff --git a/src/app/accountadapter.h b/src/app/accountadapter.h index 0407fc95af3e01296efaeaf428b82d025b383491..8ff685db73dcee5944273417d3ead86e0508cbf5 100644 --- a/src/app/accountadapter.h +++ b/src/app/accountadapter.h @@ -20,21 +20,20 @@ #include "qmladapterbase.h" -#include "accountlistmodel.h" -#include "deviceitemlistmodel.h" -#include "moderatorlistmodel.h" #include "systemtray.h" #include "lrcinstance.h" -#include "utils.h" #include <QSettings> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class AppSettingsManager; class AccountAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON Q_PROPERTY(lrc::api::AccountModel* model READ getModel NOTIFY modelChanged) @@ -45,6 +44,13 @@ Q_SIGNALS: void modelChanged(); public: + static AccountAdapter* create(QQmlEngine*, QJSEngine*) + { + return new AccountAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(), + qApp->property("SystemTray").value<SystemTray*>(), + qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit AccountAdapter(AppSettingsManager* settingsManager, SystemTray* systemTray, LRCInstance* instance, @@ -100,9 +106,5 @@ private: AppSettingsManager* settingsManager_; SystemTray* systemTray_; - - QScopedPointer<AccountListModel> accountListModel_; - QScopedPointer<DeviceItemListModel> deviceItemListModel_; - QScopedPointer<ModeratorListModel> moderatorListModel_; }; Q_DECLARE_METATYPE(AccountAdapter*) diff --git a/src/app/accountlistmodel.cpp b/src/app/accountlistmodel.cpp index d64760a0fddae9130156959ee755f9e0028bbcf7..b4a60e1aacbb011c3877e5844d3b0813309f5107 100644 --- a/src/app/accountlistmodel.cpp +++ b/src/app/accountlistmodel.cpp @@ -20,11 +20,8 @@ #include "accountlistmodel.h" #include "lrcinstance.h" -#include "utils.h" #include "api/account.h" -#include "api/contact.h" -#include "api/conversation.h" #include <QDateTime> diff --git a/src/app/accountlistmodel.h b/src/app/accountlistmodel.h index 67921a523a84477c38343690f30c26be41295361..9ffd94efc40315ce1db9674019d8d1b83ff1e515 100644 --- a/src/app/accountlistmodel.h +++ b/src/app/accountlistmodel.h @@ -51,11 +51,9 @@ public: QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QHash<int, QByteArray> roleNames() const override; - // reset the model when there's new account added Q_INVOKABLE void reset(); - void updateNotifications(); -protected: +private: using Role = AccountList::Role; }; diff --git a/src/app/avadapter.h b/src/app/avadapter.h index 306ce46dd4ef2b4cbab33f2abcccd800456e8cb2..ce5427fb9f7cc5524b5b4be578df58e9f21e9947 100644 --- a/src/app/avadapter.h +++ b/src/app/avadapter.h @@ -20,23 +20,31 @@ #include "qmladapterbase.h" #include "lrcinstance.h" +#include "qtutils.h" +#include "rendererinformationlistmodel.h" #include <QObject> #include <QVariant> #include <QString> -#include <qtutils.h> - -#include "rendererinformationlistmodel.h" +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class AvAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON + QML_PROPERTY(bool, muteCamera) QML_RO_PROPERTY(QStringList, windowsNames) QML_RO_PROPERTY(QList<QVariant>, windowsIds) QML_RO_PROPERTY(QVariant, renderersInfoList) public: + static AvAdapter* create(QQmlEngine*, QJSEngine*) + { + return new AvAdapter(qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit AvAdapter(LRCInstance* instance, QObject* parent = nullptr); ~AvAdapter() = default; diff --git a/src/app/avatarregistry.h b/src/app/avatarregistry.h index 2133f6c36a7850028e48350dd7260644e3f69f65..b224e20f1ed7aaa73ed4acbd2c485c9d23b50daf 100644 --- a/src/app/avatarregistry.h +++ b/src/app/avatarregistry.h @@ -20,13 +20,22 @@ #include <QObject> #include <QMap> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class LRCInstance; class AvatarRegistry : public QObject { Q_OBJECT + QML_SINGLETON + public: + static AvatarRegistry* create(QQmlEngine*, QJSEngine*) + { + return new AvatarRegistry(qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit AvatarRegistry(LRCInstance* instance, QObject* parent = nullptr); ~AvatarRegistry() = default; diff --git a/src/app/calladapter.cpp b/src/app/calladapter.cpp index af49bdef7521a440ae8bfebe310e132a0c1f1ec5..6ac9b3d5519dcf291e9a8a47ce13b72706895d98 100644 --- a/src/app/calladapter.cpp +++ b/src/app/calladapter.cpp @@ -26,8 +26,8 @@ #include "calladapter.h" #include "systemtray.h" -#include "qmlregister.h" #include "appsettingsmanager.h" +#include "pttlistener.h" #include <api/callmodel.h> #include <api/callparticipantsmodel.h> @@ -45,19 +45,15 @@ CallAdapter::CallAdapter(AppSettingsManager* settingsManager, : QmlAdapterBase(instance, parent) , systemTray_(systemTray) , callInformationListModel_(std::make_unique<CallInformationListModel>()) - , listener_(new PTTListener(settingsManager, this)) { - // Expose the Push-to-talk listener to QML as a singleton - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, listener_, "PttListener"); + // Get the PTTListener instance. + listener_ = qApp->property("PTTListener").value<PTTListener*>(); set_callInformationList(QVariant::fromValue(callInformationListModel_.get())); timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation); - overlayModel_.reset(new CallOverlayModel(lrcInstance_, listener_, this)); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, overlayModel_.get(), "CallOverlayModel"); - accountId_ = lrcInstance_->get_currentAccountId(); connectCallModel(accountId_); diff --git a/src/app/calladapter.h b/src/app/calladapter.h index 0b00bc788196ae96bf2da916b3d3cf4f96d36a3b..cefeee01429981c68c8394f9801c85487b3a7a73 100644 --- a/src/app/calladapter.h +++ b/src/app/calladapter.h @@ -23,32 +23,34 @@ #include "lrcinstance.h" #include "qmladapterbase.h" #include "screensaver.h" -#include "calloverlaymodel.h" - -#ifdef HAVE_GLOBAL_PTT -#include "pttlistener.h" -#endif +#include "callInformationListModel.h" #include <QObject> #include <QString> #include <QVariant> #include <QSystemTrayIcon> - -#include "callInformationListModel.h" +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class SystemTray; class AppSettingsManager; +class PTTListener; class CallAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON + QML_PROPERTY(bool, hasCall) QML_RO_PROPERTY(QVariant, callInformationList) public: - QTimer* timer; - enum MuteStates { UNMUTED, LOCAL_MUTED, MODERATOR_MUTED, BOTH_MUTED }; - Q_ENUM(MuteStates) + static CallAdapter* create(QQmlEngine*, QJSEngine*) + { + return new CallAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(), + qApp->property("SystemTray").value<SystemTray*>(), + qApp->property("LRCInstance").value<LRCInstance*>()); + } explicit CallAdapter(AppSettingsManager* settingsManager, SystemTray* systemTray, @@ -56,7 +58,10 @@ public: QObject* parent = nullptr); ~CallAdapter(); -public: + QTimer* timer; + enum MuteStates { UNMUTED, LOCAL_MUTED, MODERATOR_MUTED, BOTH_MUTED }; + Q_ENUM(MuteStates) + Q_INVOKABLE void startTimerInformation(); Q_INVOKABLE void stopTimerInformation(); Q_INVOKABLE void placeAudioOnlyCall(); @@ -131,7 +136,6 @@ private: ScreenSaver screenSaver; SystemTray* systemTray_; - QScopedPointer<CallOverlayModel> overlayModel_; VectorString currentConfSubcalls_; std::unique_ptr<CallInformationListModel> callInformationListModel_; diff --git a/src/app/calloverlaymodel.h b/src/app/calloverlaymodel.h index a93145f9538c96ee2f7a67b4dcb60acdb70bca27..934802bc228e72893be7a14fdc4b54f3e2504e82 100644 --- a/src/app/calloverlaymodel.h +++ b/src/app/calloverlaymodel.h @@ -21,8 +21,6 @@ #include "lrcinstance.h" #include "qtutils.h" -#include "mainapplication.h" - #include "pttlistener.h" #include <QAbstractListModel> diff --git a/src/app/commoncomponents/ChangePttKeyPopup.qml b/src/app/commoncomponents/ChangePttKeyPopup.qml index 9cdd0bad8b988e49780a394f799b08004d287285..f5c17bf4ea129a64a2293e778ac961c0ce8bbe14 100644 --- a/src/app/commoncomponents/ChangePttKeyPopup.qml +++ b/src/app/commoncomponents/ChangePttKeyPopup.qml @@ -37,7 +37,7 @@ BaseModalDialog { button2Role: DialogButtonBox.RejectRole button1.onClicked: { if (!(pressedKey === Qt.Key_unknown)){ - PttListener.setPttKey(pressedKey); + PTTListener.setPttKey(pressedKey); choiceMade(pressedKey); } close(); @@ -102,7 +102,7 @@ BaseModalDialog { id: keyItem Keys.onPressed: (event)=>{ - keyLabel.text = PttListener.keyToString(event.key); + keyLabel.text = PTTListener.keyToString(event.key); pressedKey = event.key; } } diff --git a/src/app/connectioninfolistmodel.cpp b/src/app/connectioninfolistmodel.cpp index 1d904c735d1b399ec5ab20232b5006c0cbefc47c..9ca1503ef6bd0a1c15b528432831b8421afcc7f1 100644 --- a/src/app/connectioninfolistmodel.cpp +++ b/src/app/connectioninfolistmodel.cpp @@ -126,10 +126,6 @@ ConnectionInfoListModel::roleNames() const void ConnectionInfoListModel::update() { - const auto accountId = lrcInstance_->get_currentAccountId(); - if (accountId.isEmpty()) { - return; - } aggregateData(); } @@ -224,4 +220,4 @@ ConnectionInfoListModel::resetData() peerIds_.clear(); peerData_.clear(); endResetModel(); -} \ No newline at end of file +} diff --git a/src/app/connectioninfolistmodel.h b/src/app/connectioninfolistmodel.h index 4b049af1942d30beda994159932a47edf8ab6210..47deb1225d53aff2f525f80240988a9a095c1079 100644 --- a/src/app/connectioninfolistmodel.h +++ b/src/app/connectioninfolistmodel.h @@ -41,8 +41,10 @@ enum Role { Q_ENUM_NS(Role) } // namespace ConnectionInfoList -class ConnectionInfoListModel : public AbstractListModelBase +class ConnectionInfoListModel final : public AbstractListModelBase { + Q_OBJECT + public: explicit ConnectionInfoListModel(LRCInstance* instance, QObject* parent = nullptr); @@ -56,7 +58,6 @@ private: using Role = ConnectionInfoList::Role; VectorMapStringString connectionInfoList_; - QVector<QString> peerIds_; QMap<QString, QMap<QString, QMap<QString, QVariant>>> peerData_; void aggregateData(); diff --git a/src/app/contactadapter.cpp b/src/app/contactadapter.cpp index 91d6995633f41ef8496dc65f99fe1105ed4117e9..b2e6f6b71d35797f013242b8fd17b3a8c5185e97 100644 --- a/src/app/contactadapter.cpp +++ b/src/app/contactadapter.cpp @@ -21,16 +21,10 @@ #include "contactadapter.h" #include "lrcinstance.h" -#include "qmlregister.h" ContactAdapter::ContactAdapter(LRCInstance* instance, QObject* parent) : QmlAdapterBase(instance, parent) - , connectionInfoListModel_(new ConnectionInfoListModel(lrcInstance_, this)) { - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, - connectionInfoListModel_.get(), - "ConnectionInfoListModel"); - selectableProxyModel_.reset(new SelectableProxyModel(this)); if (lrcInstance_) { connect(lrcInstance_, @@ -252,12 +246,6 @@ ContactAdapter::removeContact(const QString& peerUri, bool banContact) accInfo.contactModel->removeContact(peerUri, banContact); } -void -ContactAdapter::updateConnectionInfo() -{ - connectionInfoListModel_->update(); -} - void ContactAdapter::connectSignals() { diff --git a/src/app/contactadapter.h b/src/app/contactadapter.h index 80b67be408ebec05d2b9c5f9fe490c5b1a44b397..b8931f3c238893ea47e736a7645d821aa4e42161 100644 --- a/src/app/contactadapter.h +++ b/src/app/contactadapter.h @@ -20,12 +20,12 @@ #include "qmladapterbase.h" #include "smartlistmodel.h" -#include "conversationlistmodel.h" -#include "connectioninfolistmodel.h" #include <QObject> #include <QSortFilterProxyModel> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class LRCInstance; @@ -80,8 +80,14 @@ private: class ContactAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON public: + static ContactAdapter* create(QQmlEngine*, QJSEngine*) + { + return new ContactAdapter(qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit ContactAdapter(LRCInstance* instance, QObject* parent = nullptr); ~ContactAdapter() = default; @@ -91,7 +97,6 @@ public: Q_INVOKABLE void setSearchFilter(const QString& filter); Q_INVOKABLE void contactSelected(int index); Q_INVOKABLE void removeContact(const QString& peerUri, bool banContact); - Q_INVOKABLE void updateConnectionInfo(); void connectSignals(); @@ -106,7 +111,6 @@ private: SmartListModel::Type listModeltype_; QScopedPointer<SmartListModel> smartListModel_; QScopedPointer<SelectableProxyModel> selectableProxyModel_; - QScopedPointer<ConnectionInfoListModel> connectionInfoListModel_; QStringList defaultModerators_; diff --git a/src/app/conversationlistmodel.cpp b/src/app/conversationlistmodel.cpp index ac80946396aa98d5bd619d4e79474b12ba542fec..b914f73187b7a84dc6b60ff9a4632ca2a73f1f93 100644 --- a/src/app/conversationlistmodel.cpp +++ b/src/app/conversationlistmodel.cpp @@ -55,10 +55,15 @@ ConversationListModel::ConversationListModel(LRCInstance* instance, QObject* par &ConversationListModel::endRemoveRows, Qt::DirectConnection); - connect(model_, &ConversationModel::dataChanged, this, [this](int position) { - const auto index = createIndex(position, 0); - Q_EMIT ConversationListModel::dataChanged(index, index); - }, Qt::QueuedConnection); + connect( + model_, + &ConversationModel::dataChanged, + this, + [this](int position) { + const auto index = createIndex(position, 0); + Q_EMIT ConversationListModel::dataChanged(index, index); + }, + Qt::QueuedConnection); } int diff --git a/src/app/conversationsadapter.cpp b/src/app/conversationsadapter.cpp index 857ed5ee1b250020136f2c7260995c78f39e3648..8e9ecaaa017bfb84076ef7924bd652a13b50a8c2 100644 --- a/src/app/conversationsadapter.cpp +++ b/src/app/conversationsadapter.cpp @@ -24,7 +24,9 @@ #include "qmlregister.h" #include "qtutils.h" +#ifdef Q_OS_LINUX #include "namedirectory.h" +#endif #include <QApplication> #include <QJsonObject> @@ -33,18 +35,21 @@ using namespace lrc::api; ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray, LRCInstance* instance, + ConversationListProxyModel* convProxyModel, + SelectableListProxyModel* searchProxyModel, QObject* parent) : QmlAdapterBase(instance, parent) , systemTray_(systemTray) , convSrcModel_(new ConversationListModel(lrcInstance_)) - , convModel_(new ConversationListProxyModel(convSrcModel_.get())) + , convModel_(convProxyModel) , searchSrcModel_(new SearchResultsListModel(lrcInstance_)) - , searchModel_(new SelectableListProxyModel(searchSrcModel_.get())) + , searchModel_(searchProxyModel) { - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, convModel_.get(), "ConversationListModel"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, searchModel_.get(), "SearchResultsListModel"); + convModel_->bindSourceModel(convSrcModel_.get()); + searchModel_->bindSourceModel(searchSrcModel_.get()); - new SelectableListProxyGroupModel({convModel_.data(), searchModel_.data()}, this); + set_convListProxyModel(QVariant::fromValue(convModel_)); + set_searchListProxyModel(QVariant::fromValue(searchModel_)); // this will trigger when the invite filter tab is selected connect(this, &ConversationsAdapter::filterRequestsChanged, [this]() { @@ -112,32 +117,27 @@ ConversationsAdapter::ConversationsAdapter(SystemTray* systemTray, connect(&lrcInstance_->behaviorController(), &BehaviorController::newUnreadInteraction, this, - &ConversationsAdapter::onNewUnreadInteraction, - Qt::UniqueConnection); + &ConversationsAdapter::onNewUnreadInteraction); connect(&lrcInstance_->behaviorController(), &BehaviorController::newReadInteraction, this, - &ConversationsAdapter::onNewReadInteraction, - Qt::UniqueConnection); + &ConversationsAdapter::onNewReadInteraction); connect(&lrcInstance_->behaviorController(), &BehaviorController::newTrustRequest, this, - &ConversationsAdapter::onNewTrustRequest, - Qt::UniqueConnection); + &ConversationsAdapter::onNewTrustRequest); connect(&lrcInstance_->behaviorController(), &BehaviorController::trustRequestTreated, this, - &ConversationsAdapter::onTrustRequestTreated, - Qt::UniqueConnection); + &ConversationsAdapter::onTrustRequestTreated); connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, this, - &ConversationsAdapter::onCurrentAccountIdChanged, - Qt::UniqueConnection); + &ConversationsAdapter::onCurrentAccountIdChanged); connectConversationModel(); } @@ -168,7 +168,6 @@ ConversationsAdapter::onNewUnreadInteraction(const QString& accountId, if (interaction.authorUri == accountInfo.profileInfo.uri) return; auto from = accountInfo.contactModel->bestNameForContact(interaction.authorUri); - auto to = lrcInstance_->accountModel().bestNameForAccount(accountId); auto body_ = interaction.body; if (interaction.type == interaction::Type::DATA_TRANSFER) { @@ -180,6 +179,7 @@ ConversationsAdapter::onNewUnreadInteraction(const QString& accountId, if (preferences["ignoreNotifications"] == "true") return; #ifdef Q_OS_LINUX + auto to = lrcInstance_->accountModel().bestNameForAccount(accountId); auto contactPhoto = Utils::contactPhoto(lrcInstance_, interaction.authorUri, QSize(50, 50), @@ -599,75 +599,49 @@ void ConversationsAdapter::connectConversationModel() { // Signal connections - auto currentConversationModel = lrcInstance_->getCurrentConversationModel(); - if (currentConversationModel == nullptr) { + auto model = lrcInstance_->getCurrentConversationModel(); + if (!model) { return; } - QObject::connect(currentConversationModel, - &ConversationModel::modelChanged, - this, - &ConversationsAdapter::onModelChanged, - Qt::UniqueConnection); - - QObject::connect(lrcInstance_->getCurrentContactModel(), - &ContactModel::profileUpdated, - this, - &ConversationsAdapter::onProfileUpdated, - Qt::UniqueConnection); - - QObject::connect(currentConversationModel, - &ConversationModel::conversationUpdated, - this, - &ConversationsAdapter::onConversationUpdated, - Qt::UniqueConnection); - QObject::connect(currentConversationModel, - &ConversationModel::conversationRemoved, - this, - &ConversationsAdapter::onConversationRemoved, - Qt::UniqueConnection); - - QObject::connect(currentConversationModel, - &ConversationModel::filterChanged, - this, - &ConversationsAdapter::onFilterChanged, - Qt::UniqueConnection); - - QObject::connect(currentConversationModel, - &ConversationModel::conversationCleared, - this, - &ConversationsAdapter::onConversationCleared, - Qt::UniqueConnection); - - QObject::connect(currentConversationModel, - &ConversationModel::searchStatusChanged, - this, - &ConversationsAdapter::onSearchStatusChanged, - Qt::UniqueConnection); - - QObject::connect(currentConversationModel, - &ConversationModel::searchResultUpdated, - this, - &ConversationsAdapter::onSearchResultUpdated, - Qt::UniqueConnection); - - QObject::connect(currentConversationModel, - &ConversationModel::searchResultEnded, - this, - &ConversationsAdapter::onSearchResultEnded, - Qt::UniqueConnection); - - QObject::connect(currentConversationModel, - &ConversationModel::conversationReady, - this, - &ConversationsAdapter::onConversationReady, - Qt::UniqueConnection); - - QObject::connect(lrcInstance_->getCurrentContactModel(), - &ContactModel::bannedStatusChanged, - this, - &ConversationsAdapter::onBannedStatusChanged, - Qt::UniqueConnection); + auto connectObjectSignal = [this](auto obj, auto signal, auto slot) { + connect(obj, signal, this, slot, Qt::UniqueConnection); + }; + + connectObjectSignal(model, + &ConversationModel::modelChanged, + &ConversationsAdapter::onModelChanged); + connectObjectSignal(model, + &ConversationModel::profileUpdated, + &ConversationsAdapter::onProfileUpdated); + connectObjectSignal(model, + &ConversationModel::conversationUpdated, + &ConversationsAdapter::onConversationUpdated); + connectObjectSignal(model, + &ConversationModel::conversationRemoved, + &ConversationsAdapter::onConversationRemoved); + connectObjectSignal(model, + &ConversationModel::filterChanged, + &ConversationsAdapter::onFilterChanged); + connectObjectSignal(model, + &ConversationModel::conversationCleared, + &ConversationsAdapter::onConversationCleared); + connectObjectSignal(model, + &ConversationModel::searchStatusChanged, + &ConversationsAdapter::onSearchStatusChanged); + connectObjectSignal(model, + &ConversationModel::searchResultUpdated, + &ConversationsAdapter::onSearchResultUpdated); + connectObjectSignal(model, + &ConversationModel::searchResultEnded, + &ConversationsAdapter::onSearchResultEnded); + connectObjectSignal(model, + &ConversationModel::conversationReady, + &ConversationsAdapter::onConversationReady); + + connectObjectSignal(lrcInstance_->getCurrentContactModel(), + &ContactModel::bannedStatusChanged, + &ConversationsAdapter::onBannedStatusChanged); convSrcModel_.reset(new ConversationListModel(lrcInstance_)); convModel_->bindSourceModel(convSrcModel_.get()); diff --git a/src/app/conversationsadapter.h b/src/app/conversationsadapter.h index 59d970d520a11510e0622d5c0bfaded8e7867275..0a1114f3be99cf67460f1038a95ff6cebe13e2d7 100644 --- a/src/app/conversationsadapter.h +++ b/src/app/conversationsadapter.h @@ -26,19 +26,36 @@ #include <QObject> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class SystemTray; class ConversationsAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON + QML_PROPERTY(bool, filterRequests) QML_PROPERTY(int, totalUnreadMessageCount) QML_PROPERTY(int, pendingRequestCount) + QML_RO_PROPERTY(QVariant, convListProxyModel) + QML_RO_PROPERTY(QVariant, searchListProxyModel) public: + static ConversationsAdapter* create(QQmlEngine*, QJSEngine*) + { + return new ConversationsAdapter( + qApp->property("SystemTray").value<SystemTray*>(), + qApp->property("LRCInstance").value<LRCInstance*>(), + qApp->property("ConvListProxyModel").value<ConversationListProxyModel*>(), + qApp->property("ConvSearchListProxyModel").value<SelectableListProxyModel*>()); + } + explicit ConversationsAdapter(SystemTray* systemTray, LRCInstance* instance, + ConversationListProxyModel* convProxyModel, + SelectableListProxyModel* searchProxyModel, QObject* parent = nullptr); ~ConversationsAdapter() = default; @@ -46,9 +63,9 @@ public: void connectConversationModel(); Q_INVOKABLE QString createSwarm(const QString& title, - const QString& description, - const QString& avatar, - const VectorString& participants); + const QString& description, + const QString& avatar, + const VectorString& participants); Q_INVOKABLE void setFilter(const QString& filterString); Q_INVOKABLE void setFilterAndSelect(const QString& filterString); Q_INVOKABLE void ignoreFiltering(const QVariant& hightlighted); @@ -107,9 +124,9 @@ private: SystemTray* systemTray_; QScopedPointer<ConversationListModel> convSrcModel_; - QScopedPointer<ConversationListProxyModel> convModel_; + ConversationListProxyModel* convModel_; QScopedPointer<SearchResultsListModel> searchSrcModel_; - QScopedPointer<SelectableListProxyModel> searchModel_; + SelectableListProxyModel* searchModel_; std::atomic_bool selectFirst_ {false}; }; diff --git a/src/app/currentaccount.h b/src/app/currentaccount.h index c3f97b6bbf9131a283c12de87b4ae3745a42beca..f2f5e7337c7f8684f25e4f26d3c72f5244c8533c 100644 --- a/src/app/currentaccount.h +++ b/src/app/currentaccount.h @@ -24,6 +24,8 @@ #include <QObject> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration #define ACCOUNT_CONFIG_SETTINGS_PROPERTY_BASE(type, prop) \ PROPERTY_GETTER_BASE(type, prop) \ @@ -97,7 +99,7 @@ private: \ class CurrentAccount final : public QObject { Q_OBJECT - // Basic settings + QML_SINGLETON QML_RO_PROPERTY(QString, id) QML_RO_PROPERTY(QString, uri) @@ -194,6 +196,12 @@ class CurrentAccount final : public QObject QML_ACCOUNT_CONFIG_SETTINGS_PROPERTY(QJsonObject, uiCustomization) public: + static CurrentAccount* create(QQmlEngine*, QJSEngine*) + { + return new CurrentAccount(qApp->property("LRCInstance").value<LRCInstance*>(), + qApp->property("AppSettingsManager").value<AppSettingsManager*>()); + } + explicit CurrentAccount(LRCInstance* lrcInstance, AppSettingsManager* settingsManager, QObject* parent = nullptr); diff --git a/src/app/currentaccounttomigrate.cpp b/src/app/currentaccounttomigrate.cpp index 21c01223ca266158a7e8e63d8bb1f15fad3ec7f1..e670d72d5967d5c5359debb00a4664925dcc29e9 100644 --- a/src/app/currentaccounttomigrate.cpp +++ b/src/app/currentaccounttomigrate.cpp @@ -58,8 +58,6 @@ CurrentAccountToMigrate::CurrentAccountToMigrate(LRCInstance* instance, QObject* &CurrentAccountToMigrate::slotAccountRemoved); } -CurrentAccountToMigrate::~CurrentAccountToMigrate() {} - void CurrentAccountToMigrate::removeCurrentAccountToMigrate() { @@ -133,4 +131,4 @@ CurrentAccountToMigrate::slotAccountRemoved(const QString& accountId) Q_EMIT allMigrationsFinished(); else Q_EMIT currentAccountToMigrateRemoved(); -} \ No newline at end of file +} diff --git a/src/app/currentaccounttomigrate.h b/src/app/currentaccounttomigrate.h index 3d05720a5bc234a6b57eddcdc992a94b8998c17a..b1b075c788e6fff83d4159a3f896e6d5b9bc79d6 100644 --- a/src/app/currentaccounttomigrate.h +++ b/src/app/currentaccounttomigrate.h @@ -19,15 +19,19 @@ #pragma once -#include <QObject> - #include "qtutils.h" +#include <QObject> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration + class LRCInstance; class CurrentAccountToMigrate : public QObject { Q_OBJECT + QML_SINGLETON + QML_RO_PROPERTY(int, accountToMigrateListSize) QML_RO_PROPERTY(QString, accountId) QML_RO_PROPERTY(QString, managerUsername) @@ -36,8 +40,13 @@ class CurrentAccountToMigrate : public QObject QML_RO_PROPERTY(QString, alias) public: + static CurrentAccountToMigrate* create(QQmlEngine*, QJSEngine*) + { + return new CurrentAccountToMigrate(qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit CurrentAccountToMigrate(LRCInstance* lrcInstance, QObject* parent = nullptr); - ~CurrentAccountToMigrate(); + ~CurrentAccountToMigrate() = default; Q_INVOKABLE void removeCurrentAccountToMigrate(); diff --git a/src/app/currentcall.cpp b/src/app/currentcall.cpp index 2b5ecae882bbe8d8f4355c6fbbc3977af1e379bf..7ef2af43b124352df5115993e87ce4801ecc0207 100644 --- a/src/app/currentcall.cpp +++ b/src/app/currentcall.cpp @@ -16,7 +16,8 @@ */ #include "currentcall.h" -#include "qmlregister.h" + +#include "callparticipantsmodel.h" #include <api/callparticipantsmodel.h> #include <api/devicemodel.h> @@ -25,8 +26,7 @@ CurrentCall::CurrentCall(LRCInstance* lrcInstance, QObject* parent) : QObject(parent) , lrcInstance_(lrcInstance) { - participantsModel_.reset(new CallParticipantsModel(lrcInstance_, this)); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, participantsModel_.get(), "CallParticipantsModel"); + participantsModel_ = qApp->property("CallParticipantsModel").value<CallParticipantsModel*>(); connect(lrcInstance_, &LRCInstance::currentAccountIdChanged, diff --git a/src/app/currentcall.h b/src/app/currentcall.h index 921e3a918d93e6292713f867ba3f929bec447a07..f9688747244c0dd397f09ca468429fdfebe499ec 100644 --- a/src/app/currentcall.h +++ b/src/app/currentcall.h @@ -19,14 +19,18 @@ #include "lrcinstance.h" #include "qtutils.h" -#include "callparticipantsmodel.h" #include <QObject> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration + +class CallParticipantsModel; class CurrentCall final : public QObject { Q_OBJECT + QML_SINGLETON QML_RO_PROPERTY(QString, id) QML_RO_PROPERTY(QStringList, uris) @@ -55,6 +59,11 @@ class CurrentCall final : public QObject QML_PROPERTY(bool, flipSelf) public: + static CurrentCall* create(QQmlEngine*, QJSEngine*) + { + return new CurrentCall(qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit CurrentCall(LRCInstance* lrcInstance, QObject* parent = nullptr); ~CurrentCall() = default; Q_INVOKABLE QVariantList getConferencesInfos() const; @@ -85,5 +94,5 @@ private Q_SLOTS: private: LRCInstance* lrcInstance_; - QScopedPointer<CallParticipantsModel> participantsModel_; + CallParticipantsModel* participantsModel_; }; diff --git a/src/app/currentconversation.cpp b/src/app/currentconversation.cpp index 1b2354c2f838336c9e828969f3b819e25fd16f7f..8fa2b74cecfd3e5aebeaec0483d3ed49ac7d08bc 100644 --- a/src/app/currentconversation.cpp +++ b/src/app/currentconversation.cpp @@ -24,7 +24,9 @@ CurrentConversation::CurrentConversation(LRCInstance* lrcInstance, QObject* pare : QObject(parent) , lrcInstance_(lrcInstance) { - uris_ = new CurrentConversationMembers(lrcInstance, this); + membersModel_ = new CurrentConversationMembers(lrcInstance, this); + set_members(QVariant::fromValue(membersModel_)); + // whenever the account changes, reconnect the new conversation model // for updates to the conversation and call state/id connect(lrcInstance_, @@ -55,7 +57,7 @@ CurrentConversation::updateData() // If the conversation is empty, clear the id and return. if (convId.isEmpty()) { set_id(); - uris_->setMembers({}, {}, {}); + membersModel_->setMembers({}, {}, {}); return; } @@ -95,7 +97,7 @@ CurrentConversation::updateData() for (const auto& banned : bannedUris) uris.push_back(banned); } - uris_->setMembers(accountId, convId, uris); + membersModel_->setMembers(accountId, convId, uris); set_isSwarm(convInfo.isSwarm()); set_isLegacy(convInfo.isLegacy()); set_isCoreDialog(convInfo.isCoreDialog()); @@ -202,12 +204,6 @@ CurrentConversation::setInfo(const QString& key, const QString& value) accInfo.conversationModel->updateConversationInfos(convId, infos); } -CurrentConversationMembers* -CurrentConversation::uris() const -{ - return uris_; -} - void CurrentConversation::onConversationUpdated(const QString& convId) { @@ -266,16 +262,22 @@ CurrentConversation::updateConversationPreferences(const QString& convId) void CurrentConversation::connectModel() { - uris_->setMembers({}, {}, {}); + membersModel_->setMembers({}, {}, {}); auto convModel = lrcInstance_->getCurrentConversationModel(); if (!convModel) return; - connect(lrcInstance_->getCurrentConversationModel(), - &ConversationModel::conversationUpdated, - this, - &CurrentConversation::onConversationUpdated, - Qt::UniqueConnection); + auto connectObjectSignal = [this](auto obj, auto signal, auto slot) { + connect(obj, signal, this, slot, Qt::UniqueConnection); + }; + + connectObjectSignal(convModel, + &ConversationModel::conversationUpdated, + &CurrentConversation::onConversationUpdated); + connectObjectSignal(convModel, + &ConversationModel::profileUpdated, + &CurrentConversation::updateProfile); + connect(lrcInstance_->getCurrentConversationModel(), &ConversationModel::profileUpdated, this, diff --git a/src/app/currentconversation.h b/src/app/currentconversation.h index b42b9c7f01f70530016a240dc4df10650ccbfdd5..44eb3234f8209927c5bf90cf964188796f2f0d9a 100644 --- a/src/app/currentconversation.h +++ b/src/app/currentconversation.h @@ -23,6 +23,8 @@ #include <QObject> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration // an adapter object to expose a conversation::Info struct // as a group of observable properties @@ -30,6 +32,8 @@ class CurrentConversation final : public QObject { Q_OBJECT + QML_SINGLETON + QML_PROPERTY(QString, id) QML_PROPERTY(QString, title) QML_PROPERTY(QString, description) @@ -56,10 +60,17 @@ class CurrentConversation final : public QObject QML_PROPERTY(QStringList, backendErrors) QML_PROPERTY(QString, lastSelfMessageId) QML_RO_PROPERTY(bool, hasCall) + QML_RO_PROPERTY(QVariant, members) public: + static CurrentConversation* create(QQmlEngine*, QJSEngine*) + { + return new CurrentConversation(qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit CurrentConversation(LRCInstance* lrcInstance, QObject* parent = nullptr); ~CurrentConversation() = default; + Q_INVOKABLE void scrollToMsg(const QString& msgId); Q_INVOKABLE void setPreference(const QString& key, const QString& value); Q_INVOKABLE QString getPreference(const QString& key) const; @@ -88,7 +99,7 @@ Q_SIGNALS: private: LRCInstance* lrcInstance_; - CurrentConversationMembers* uris_; + CurrentConversationMembers* membersModel_; void connectModel(); }; diff --git a/src/app/currentconversationmembers.h b/src/app/currentconversationmembers.h index e0485c23b154333b5c9fb5a26b71908255684873..ea8bb61eb8d7bb01962403a0417beada26e89df6 100644 --- a/src/app/currentconversationmembers.h +++ b/src/app/currentconversationmembers.h @@ -41,7 +41,7 @@ Q_ENUM_NS(Role) class CurrentConversationMembers : public QAbstractListModel { Q_OBJECT - QML_PROPERTY(int, count) + QML_RO_PROPERTY(int, count) public: explicit CurrentConversationMembers(LRCInstance* lrcInstance, QObject* parent = nullptr); @@ -56,4 +56,4 @@ private: QString accountId_; QString convId_; QStringList members_; -}; \ No newline at end of file +}; diff --git a/src/app/imagedownloader.h b/src/app/imagedownloader.h index f5b71741cc460a81f2b72ed62382b1c27d1edeb4..e0cf725f66792197a37433c5fea48e96fdf2484a 100644 --- a/src/app/imagedownloader.h +++ b/src/app/imagedownloader.h @@ -18,16 +18,28 @@ #pragma once #include "networkmanager.h" +#include "connectivitymonitor.h" #include "qtutils.h" +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration + class ImageDownloader : public NetworkManager { Q_OBJECT + QML_SINGLETON QML_PROPERTY(QString, cachePath) public: + static ImageDownloader* create(QQmlEngine*, QJSEngine*) + { + return new ImageDownloader( + qApp->property("ConnectivityMonitor").value<ConnectivityMonitor*>()); + } + explicit ImageDownloader(ConnectivityMonitor* cm, QObject* parent = nullptr); + ~ImageDownloader() = default; // Download an image and call onDownloadImageFinished when done Q_INVOKABLE void downloadImage(const QUrl& url, const QString& localPath); @@ -39,4 +51,4 @@ Q_SIGNALS: private Q_SLOTS: // Saves the image to the localPath and emits the appropriate signal void onDownloadImageFinished(const QByteArray& reply, const QString& localPath); -}; \ No newline at end of file +}; diff --git a/src/app/mainapplication.cpp b/src/app/mainapplication.cpp index 144bfbc62edd4ae68e9c10060c085d496e377a64..330412dccb2110a4ea17bf8738f2d5dc340b5ed6 100644 --- a/src/app/mainapplication.cpp +++ b/src/app/mainapplication.cpp @@ -26,6 +26,8 @@ #include "connectivitymonitor.h" #include "systemtray.h" #include "videoprovider.h" +#include "previewengine.h" +#include "conversationlistmodel.h" #include <QWKQuick/qwkquickglobal.h> @@ -42,7 +44,6 @@ #include <QQuickWindow> #include <QLoggingCategory> -#include <locale.h> #include <thread> #ifdef Q_OS_WIN @@ -188,6 +189,7 @@ MainApplication::init() connectivityMonitor_ = new ConnectivityMonitor(this); settingsManager_ = new AppSettingsManager(this); systemTray_ = new SystemTray(settingsManager_, this); + previewEngine_ = new PreviewEngine(connectivityMonitor_, this); // These should should be QueuedConnection to ensure that the // they are executed after the QML engine has been initialized, @@ -407,6 +409,7 @@ MainApplication::initQmlLayer() systemTray_, settingsManager_, connectivityMonitor_, + previewEngine_, &screenInfo_, this); diff --git a/src/app/mainapplication.h b/src/app/mainapplication.h index 5c039e882f733bc0d5584f0621116fa9ea80c3fb..3bbba00a8758031f10ffadb84023df31cbd79224 100644 --- a/src/app/mainapplication.h +++ b/src/app/mainapplication.h @@ -35,6 +35,7 @@ class ConnectivityMonitor; class AppSettingsManager; class SystemTray; +class PreviewEngine; // Provides information about the screen the app is displayed on class ScreenInfo : public QObject @@ -120,6 +121,7 @@ private: ConnectivityMonitor* connectivityMonitor_; SystemTray* systemTray_; AppSettingsManager* settingsManager_; + PreviewEngine* previewEngine_; ScreenInfo screenInfo_; }; diff --git a/src/app/mainview/components/AddMemberPanel.qml b/src/app/mainview/components/AddMemberPanel.qml index 27826eb6cae02a37580f9da286596f1877971b8e..2874dc2796cc2aa8728d1112c1c46923bb52bc5b 100644 --- a/src/app/mainview/components/AddMemberPanel.qml +++ b/src/app/mainview/components/AddMemberPanel.qml @@ -57,8 +57,8 @@ Rectangle { Layout.leftMargin: 4 Layout.rightMargin: 4 - // Reset the model if visible or the CurrentConversationMembers.count changes (0 or greater) - model: visible && CurrentConversationMembers.count >= 0 ? ContactAdapter.getContactSelectableModel(type) : null + // Reset the model if visible or the current conv member count changes (0 or greater) + model: visible && CurrentConversation.members.count >= 0 ? ContactAdapter.getContactSelectableModel(type) : null delegate: ContactPickerItemDelegate { id: contactPickerItemDelegate diff --git a/src/app/mainview/components/ConversationExtrasPanel.qml b/src/app/mainview/components/ConversationExtrasPanel.qml index e319f4e7eeb517e47dada52154996d79ee6d5d52..5fa4d1be3f60413c1362b539b898e66388ba6110 100644 --- a/src/app/mainview/components/ConversationExtrasPanel.qml +++ b/src/app/mainview/components/ConversationExtrasPanel.qml @@ -65,12 +65,12 @@ StackLayout { } Connections { - target: CurrentConversationMembers + target: CurrentConversation.members function onCountChanged() { // Close the panel if there are 8 or more members in the // conversation AND the "Add Member" panel is currently open. - if (CurrentConversationMembers.count >= 8 && isOpen(ChatView.AddMemberPanel)) { + if (CurrentConversation.members.count >= 8 && isOpen(ChatView.AddMemberPanel)) { closePanel(); } } diff --git a/src/app/mainview/components/ConversationListView.qml b/src/app/mainview/components/ConversationListView.qml index 9a23671c3a79ded0d5f58ea05a4fc8a752ca78ac..d0b1fdcd225cab36ba9bf2beea44d7692e39f130 100644 --- a/src/app/mainview/components/ConversationListView.qml +++ b/src/app/mainview/components/ConversationListView.qml @@ -70,28 +70,6 @@ JamiListView { onCountChanged: positionViewAtBeginning() - add: Transition { - NumberAnimation { - property: "opacity" - from: 0 - to: 1.0 - duration: JamiTheme.smartListTransitionDuration - } - } - - displaced: Transition { - NumberAnimation { - properties: "x,y" - easing.type: Easing.OutCubic - duration: JamiTheme.smartListTransitionDuration - } - NumberAnimation { - property: "opacity" - to: 1.0 - duration: JamiTheme.smartListTransitionDuration * (1 - from) - } - } - Behavior on opacity { NumberAnimation { easing.type: Easing.OutCubic diff --git a/src/app/mainview/components/PluginHandlerPicker.qml b/src/app/mainview/components/PluginHandlerPicker.qml index ce21b7e87d1b378f8730b6623dc9fb6d26d257e3..c0a4e4066b2462b2cb0c84f49be503a1074e36d3 100644 --- a/src/app/mainview/components/PluginHandlerPicker.qml +++ b/src/app/mainview/components/PluginHandlerPicker.qml @@ -57,7 +57,7 @@ Popup { if (isCall) { pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id); } else { - var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]; + var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.members[0]; pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId); } } @@ -69,7 +69,7 @@ Popup { pluginhandlerPickerListView.model = PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id); } else { var accountId = LRCInstance.currentAccountId; - var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]; + var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.members[0]; PluginModel.toggleChatHandler(handlerId, accountId, peerId, !isLoaded); pluginhandlerPickerListView.model = PluginAdapter.getChatHandlerSelectableModel(accountId, peerId); } @@ -124,7 +124,7 @@ Popup { if (isCall) { return PluginAdapter.getMediaHandlerSelectableModel(CurrentCall.id); } else { - var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversationMembers[0]; + var peerId = CurrentConversation.isSwarm ? CurrentConversation.id : CurrentConversation.members[0]; return PluginAdapter.getChatHandlerSelectableModel(LRCInstance.currentAccountId, peerId); } } diff --git a/src/app/mainview/components/SidePanel.qml b/src/app/mainview/components/SidePanel.qml index 0037f7ebd270139dcdc643e6dbca4e3d0aafb8e3..01ce34680ba19ff29d6d408f398729573af113f8 100644 --- a/src/app/mainview/components/SidePanel.qml +++ b/src/app/mainview/components/SidePanel.qml @@ -378,7 +378,7 @@ SidePanelBase { return parent.height; } - model: SearchResultsListModel + model: ConversationsAdapter.searchListProxyModel headerLabel: JamiStrings.searchResults headerVisible: true } @@ -389,7 +389,7 @@ SidePanelBase { Layout.fillWidth: true Layout.fillHeight: true - model: ConversationListModel + model: ConversationsAdapter.convListProxyModel headerLabel: JamiStrings.conversations headerVisible: count && searchResultsListView.visible } @@ -411,7 +411,7 @@ SidePanelBase { Layout.preferredWidth: parent.width Layout.fillHeight: true - model: ConversationListModel + model: ConversationsAdapter.convListProxyModel delegate: SmartListItemDelegate { interactive: false diff --git a/src/app/mainview/components/SwarmDetailsPanel.qml b/src/app/mainview/components/SwarmDetailsPanel.qml index 2f0d5a597427d1b07be97a67286ccab27c9e3cc3..ef439fe160445c3252754e150aacbb6d767f6fdf 100644 --- a/src/app/mainview/components/SwarmDetailsPanel.qml +++ b/src/app/mainview/components/SwarmDetailsPanel.qml @@ -205,7 +205,7 @@ Rectangle { objectName: "members" visible: !CurrentConversation.isCoreDialog labelText: { - var membersNb = CurrentConversationMembers.count; + var membersNb = CurrentConversation.members.count; if (membersNb > 1) return JamiStrings.members.arg(membersNb); return JamiStrings.member; @@ -581,7 +581,7 @@ Rectangle { } } - model: CurrentConversationMembers + model: CurrentConversation.members delegate: ItemDelegate { id: member diff --git a/src/app/messagesadapter.cpp b/src/app/messagesadapter.cpp index 823300e046d731fbbb27b9eccb0c98b88f1a8b52..9bc034e6ccd77bdaf89789e6ba8e335eaae43408 100644 --- a/src/app/messagesadapter.cpp +++ b/src/app/messagesadapter.cpp @@ -26,6 +26,7 @@ #include "appsettingsmanager.h" #include "qtutils.h" #include "messageparser.h" +#include "previewengine.h" #include <api/datatransfermodel.h> diff --git a/src/app/messagesadapter.h b/src/app/messagesadapter.h index e333e66ba763401162662a552409227706126c94..1deb16089e9fdeedc24ed7063c2917a884af41e4 100644 --- a/src/app/messagesadapter.h +++ b/src/app/messagesadapter.h @@ -20,16 +20,18 @@ #include "lrcinstance.h" #include "qmladapterbase.h" + #include "previewengine.h" +#include "messageparser.h" +#include "appsettingsmanager.h" #include <QObject> #include <QString> #include <QTimer> #include <QSortFilterProxyModel> - -class AppSettingsManager; -class MessageParser; +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class FilteredMsgListModel final : public QSortFilterProxyModel { @@ -63,6 +65,8 @@ public: class MessagesAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON + QML_RO_PROPERTY(QVariant, messageListModel) QML_PROPERTY(QString, replyToId) QML_PROPERTY(QString, editId) @@ -71,23 +75,19 @@ class MessagesAdapter final : public QmlAdapterBase QML_PROPERTY(QString, searchbarPrompt) public: + static MessagesAdapter* create(QQmlEngine*, QJSEngine*) + { + return new MessagesAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(), + qApp->property("PreviewEngine").value<PreviewEngine*>(), + qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit MessagesAdapter(AppSettingsManager* settingsManager, PreviewEngine* previewEngine, LRCInstance* instance, QObject* parent = nullptr); ~MessagesAdapter() = default; -Q_SIGNALS: - void newInteraction(const QString& id, int type); - void newMessageBarPlaceholderText(QString& placeholderText); - void newFilePasted(QString filePath); - void newTextPasted(); - void moreMessagesLoaded(qint32 loadingRequestId); - void timestampUpdated(); - void fileCopied(const QString& dest); - void messageParsed(const QString& msgId, const QString& msg); - -protected: Q_INVOKABLE bool isDocument(const interaction::Type& type); Q_INVOKABLE void loadMoreMessages(); Q_INVOKABLE void connectConversationModel(); @@ -149,6 +149,15 @@ protected: inline MessageListModel* getMsgListSourceModel() const; +Q_SIGNALS: + void newInteraction(const QString& id, int type); + void newFilePasted(const QString& filePath); + void newTextPasted(); + void moreMessagesLoaded(qint32 loadingRequestId); + void timestampUpdated(); + void fileCopied(const QString& dest); + void messageParsed(const QString& msgId, const QString& msg); + private Q_SLOTS: void onNewInteraction(const QString& convUid, const QString& interactionId, diff --git a/src/app/pluginadapter.cpp b/src/app/pluginadapter.cpp index bcdd53c55151e3e652cb7242a0d4311c906f06dd..ab0864e0f1bedb8a01ea27996074716be4560d71 100644 --- a/src/app/pluginadapter.cpp +++ b/src/app/pluginadapter.cpp @@ -24,7 +24,8 @@ #include "networkmanager.h" #include "lrcinstance.h" #include "appsettingsmanager.h" -#include "qmlregister.h" + +#include "api/pluginmodel.h" #include <QJsonArray> #include <QJsonDocument> @@ -35,8 +36,7 @@ PluginAdapter::PluginAdapter(LRCInstance* instance, AppSettingsManager* settingsManager, - QObject* parent, - QString baseUrl) + QObject* parent) : QmlAdapterBase(instance, parent) , pluginStoreListModel_(new PluginStoreListModel(instance, this)) , pluginVersionManager_(new PluginVersionManager(instance, settingsManager, this)) @@ -44,8 +44,9 @@ PluginAdapter::PluginAdapter(LRCInstance* instance, , lrcInstance_(instance) , settingsManager_(settingsManager) { - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, pluginStoreListModel_, "PluginStoreListModel"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, pluginListModel_, "PluginListModel") + pluginListModel_ = qApp->property("PluginListModel").value<PluginListModel*>(); + pluginStoreListModel_ = qApp->property("PluginStoreListModel").value<PluginStoreListModel*>(); + updateHandlersListCount(); connect(&lrcInstance_->pluginModel(), &lrc::api::PluginModel::modelUpdated, diff --git a/src/app/pluginadapter.h b/src/app/pluginadapter.h index da93da9537bf9c70ecae03cd7538cba4c925aebf..38e8c62a0f6406cdbb7264ec63482aa10cd92fd0 100644 --- a/src/app/pluginadapter.h +++ b/src/app/pluginadapter.h @@ -19,33 +19,37 @@ #pragma once #include "qmladapterbase.h" -#include "pluginlistmodel.h" #include "pluginhandlerlistmodel.h" -#include "pluginlistpreferencemodel.h" -#include "appsettingsmanager.h" -#include "pluginversionmanager.h" -#include "pluginstorelistmodel.h" -#include "preferenceitemlistmodel.h" #include <QObject> #include <QSortFilterProxyModel> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration -class PluginVersionManager; +class PluginListModel; class PluginStoreListModel; +class PluginVersionManager; class AppSettingsManager; class PluginAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON + QML_PROPERTY(int, callMediaHandlersListCount) QML_PROPERTY(int, chatHandlersListCount) public: + static PluginAdapter* create(QQmlEngine*, QJSEngine*) + { + return new PluginAdapter(qApp->property("LRCInstance").value<LRCInstance*>(), + qApp->property("AppSettingsManager").value<AppSettingsManager*>()); + } + explicit PluginAdapter(LRCInstance* instance, AppSettingsManager* settingsManager, - QObject* parent = nullptr, - QString baseUrl = "https://plugins.jami.net"); + QObject* parent = nullptr); ~PluginAdapter() = default; Q_INVOKABLE void getPluginsFromStore(); @@ -73,9 +77,9 @@ Q_SIGNALS: private: void updateHandlersListCount(); + PluginListModel* pluginListModel_; PluginStoreListModel* pluginStoreListModel_; PluginVersionManager* pluginVersionManager_; - PluginListModel* pluginListModel_; std::unique_ptr<PluginHandlerListModel> pluginHandlerListModel_; diff --git a/src/app/positionmanager.h b/src/app/positionmanager.h index 42c9ca35e9ab1211a9c482570fb1c6c799c4c7e2..88cc821cee28e797dab13cf3f86fdb12375ac440 100644 --- a/src/app/positionmanager.h +++ b/src/app/positionmanager.h @@ -27,16 +27,27 @@ #include <QMutex> #include <QObject> #include <QString> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class PositionManager : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON + // map of elements : map key and isUnpin QML_PROPERTY(QVariantMap, mapStatus) QML_PROPERTY(bool, mapAutoOpening) QML_PROPERTY(int, positionShareConvIdsCount) QML_PROPERTY(int, sharingUrisCount) public: + static PositionManager* create(QQmlEngine*, QJSEngine*) + { + return new PositionManager(qApp->property("AppSettingsManager").value<AppSettingsManager*>(), + qApp->property("SystemTray").value<SystemTray*>(), + qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit PositionManager(AppSettingsManager* settingsManager, SystemTray* systemTray, LRCInstance* instance, diff --git a/src/app/previewengine.cpp b/src/app/previewengine.cpp index 8aa570eb21b6e09163a76719540a9379ed4e29c2..6d3310261a9cfcf4bd7f56df0cb23cf600cae8f5 100644 --- a/src/app/previewengine.cpp +++ b/src/app/previewengine.cpp @@ -17,32 +17,72 @@ #include "previewengine.h" +#include "htmlparser.h" + #include <QRegularExpression> #include <QThread> -const QRegularExpression PreviewEngine::newlineRe("\\r?\\n"); +class PreviewEngine::Parser : public QObject +{ + Q_OBJECT + +public: + explicit Parser(QObject* parent = nullptr); + ~Parser() = default; + + Q_SIGNAL void infoReady(const QString& id, const QVariantMap& info); + +public: + Q_SLOT void processHTML(const QString& id, const QString& link, const QString& data); + +private: + // An instance of HtmlParser used to parse HTML. + HtmlParser* htmlParser_; + + QString getTagContent(const QList<QString>& tags, const QString& value); + QString getTitle(const QList<QString>& metaTags); + QString getDescription(const QList<QString>& metaTags); + QString getImage(const QList<QString>& metaTags); + + static const QRegularExpression newlineRe; +}; + +const QRegularExpression PreviewEngine::Parser::newlineRe("\\r?\\n"); PreviewEngine::PreviewEngine(ConnectivityMonitor* cm, QObject* parent) : NetworkManager(cm, parent) - , htmlParser_(new HtmlParser(this)) + , parser_(new PreviewEngine::Parser) { - // Run this object in a separate thread. - thread_ = new QThread(); - moveToThread(thread_); - thread_->start(); + parserThread_ = new QThread(); + parser_->moveToThread(parserThread_); + + connect(this, &PreviewEngine::parseLink, this, &PreviewEngine::onParseLink); + connect(this, &PreviewEngine::htmlReady, parser_.get(), &Parser::processHTML); + connect(parser_.get(), &Parser::infoReady, this, &PreviewEngine::infoReady); - // Connect on a queued connection to avoid blocking caller thread. - connect(this, &PreviewEngine::parseLink, this, &PreviewEngine::onParseLink, Qt::QueuedConnection); + parserThread_->start(); } PreviewEngine::~PreviewEngine() { - thread_->quit(); - thread_->wait(); + parserThread_->quit(); + parserThread_->wait(); } +void +PreviewEngine::onParseLink(const QString& id, const QString& link) +{ + sendGetRequest(link, + [this, id, link](const QByteArray& html) { Q_EMIT htmlReady(id, link, html); }); +} + +PreviewEngine::Parser::Parser(QObject* parent) + : QObject(parent) + , htmlParser_(new HtmlParser(this)) +{} + QString -PreviewEngine::getTagContent(const QList<QString>& tags, const QString& value) +PreviewEngine::Parser::getTagContent(const QList<QString>& tags, const QString& value) { Q_FOREACH (auto tag, tags) { const QRegularExpression re("(property|name)=\"(og:|twitter:|)" + value @@ -56,7 +96,7 @@ PreviewEngine::getTagContent(const QList<QString>& tags, const QString& value) } QString -PreviewEngine::getTitle(const QList<QString>& metaTags) +PreviewEngine::Parser::getTitle(const QList<QString>& metaTags) { // Try with opengraph/twitter props QString title = getTagContent(metaTags, "title"); @@ -73,7 +113,7 @@ PreviewEngine::getTitle(const QList<QString>& metaTags) } QString -PreviewEngine::getDescription(const QList<QString>& metaTags) +PreviewEngine::Parser::getDescription(const QList<QString>& metaTags) { // Try with og/twitter props QString desc = getTagContent(metaTags, "description"); @@ -84,7 +124,7 @@ PreviewEngine::getDescription(const QList<QString>& metaTags) } QString -PreviewEngine::getImage(const QList<QString>& metaTags) +PreviewEngine::Parser::getImage(const QList<QString>& metaTags) { // Try with og/twitter props QString image = getTagContent(metaTags, "image"); @@ -101,25 +141,25 @@ PreviewEngine::getImage(const QList<QString>& metaTags) } void -PreviewEngine::onParseLink(const QString& messageId, const QString& link) +PreviewEngine::Parser::processHTML(const QString& id, const QString& link, const QString& data) { - sendGetRequest(QUrl(link), [this, messageId, link](const QByteArray& html) { - htmlParser_->parseHtmlString(html); - auto tagsNodes = htmlParser_->getTagsNodes({TidyTag_META}); - auto metaTagNodes = tagsNodes[TidyTag_META]; - QList<QString> metaTags; - Q_FOREACH (auto tag, metaTagNodes) { - metaTags.append(htmlParser_->getNodeText(tag)); - } - QString domain = QUrl(link).host(); - if (domain.isEmpty()) { - domain = link; - } - Q_EMIT infoReady(messageId, - {{"title", getTitle(metaTags)}, - {"description", getDescription(metaTags)}, - {"image", getImage(metaTags)}, - {"url", link}, - {"domain", domain}}); - }); + htmlParser_->parseHtmlString(data); + auto tagsNodes = htmlParser_->getTagsNodes({TidyTag_META}); + auto metaTagNodes = tagsNodes[TidyTag_META]; + QList<QString> metaTags; + Q_FOREACH (auto tag, metaTagNodes) { + metaTags.append(htmlParser_->getNodeText(tag)); + } + QString domain = QUrl(link).host(); + if (domain.isEmpty()) { + domain = link; + } + Q_EMIT infoReady(id, + {{"title", getTitle(metaTags)}, + {"description", getDescription(metaTags)}, + {"image", getImage(metaTags)}, + {"url", link}, + {"domain", domain}}); } + +#include "previewengine.moc" diff --git a/src/app/previewengine.h b/src/app/previewengine.h index 0ba009e192deed0d2195f3fc5ab19dbd04c1ecbb..660a960cf839d9ccae3b301c34456afafe11fc3e 100644 --- a/src/app/previewengine.h +++ b/src/app/previewengine.h @@ -19,8 +19,6 @@ #include "networkmanager.h" -#include "htmlparser.h" - class PreviewEngine final : public NetworkManager { Q_OBJECT @@ -30,21 +28,14 @@ public: ~PreviewEngine(); Q_SIGNALS: - void parseLink(const QString& messageId, const QString& link); - void infoReady(const QString& messageId, const QVariantMap& info); + void parseLink(const QString& id, const QString& link); + void infoReady(const QString& id, const QVariantMap& info); private: - Q_SLOT void onParseLink(const QString& messageId, const QString& link); - - // An instance of HtmlParser used to parse HTML. - HtmlParser* htmlParser_; - - QString getTagContent(const QList<QString>& tags, const QString& value); - QString getTitle(const QList<QString>& metaTags); - QString getDescription(const QList<QString>& metaTags); - QString getImage(const QList<QString>& metaTags); - - static const QRegularExpression newlineRe; + Q_SLOT void onParseLink(const QString& id, const QString& link); + Q_SIGNAL void htmlReady(const QString& id, const QString& link, const QByteArray& data); - QThread* thread_; + class Parser; + QScopedPointer<Parser> parser_; + QThread* parserThread_; }; diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp index a0d1c816e9e4659222c5481d51e4025b2df206f7..96480c2ba072bdf82451efd2cdd213bea0c25a5c 100644 --- a/src/app/qmlregister.cpp +++ b/src/app/qmlregister.cpp @@ -27,7 +27,6 @@ #include "positionmanager.h" #include "tipsmodel.h" #include "connectivitymonitor.h" -#include "previewengine.h" #include "imagedownloader.h" #include "utilsadapter.h" #include "conversationsadapter.h" @@ -36,7 +35,8 @@ #include "currentaccount.h" #include "videodevices.h" #include "currentaccounttomigrate.h" - +#include "pttlistener.h" +#include "calloverlaymodel.h" #include "accountlistmodel.h" #include "mediacodeclistmodel.h" #include "audiodevicemodel.h" @@ -47,7 +47,10 @@ #include "smartlistmodel.h" #include "filestosendlistmodel.h" #include "callInformationListModel.h" -#include "rendererinformationlistmodel.h" +#include "connectioninfolistmodel.h" +#include "callparticipantsmodel.h" +#include "pluginlistmodel.h" +#include "pluginstorelistmodel.h" #include "qrimageprovider.h" #include "avatarimageprovider.h" @@ -75,6 +78,7 @@ // clang-format off // TODO: remove this #define QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(T) \ + QQmlEngine::setObjectOwnership(&T::instance(), QQmlEngine::CppOwnership); \ qmlRegisterSingletonType<T>(NS_MODELS, MODULE_VER_MAJ, MODULE_VER_MIN, #T, \ [](QQmlEngine* e, QJSEngine* se) -> QObject* { \ Q_UNUSED(e); Q_UNUSED(se); \ @@ -97,6 +101,10 @@ MODULE_VER_MAJ, MODULE_VER_MIN, #T, \ "Don't try to add to a qml definition of " #T); +#define REG_QML_SINGLETON qmlRegisterSingletonType +#define REG_MODEL NS_MODELS, MODULE_VER_MAJ, MODULE_VER_MIN +#define CREATE(Obj) [=](QQmlEngine*, QJSEngine*) -> QObject* { return Obj; } + namespace Utils { /*! @@ -108,65 +116,103 @@ registerTypes(QQmlEngine* engine, SystemTray* systemTray, AppSettingsManager* settingsManager, ConnectivityMonitor* connectivityMonitor, + PreviewEngine* previewEngine, ScreenInfo* screenInfo, QObject* app) { - // setup the adapters (their lifetimes are that of MainApplication) - auto avatarRegistry = new AvatarRegistry(lrcInstance, engine); - auto callAdapter = new CallAdapter(settingsManager, systemTray, lrcInstance, engine); - auto previewEngine = new PreviewEngine(connectivityMonitor, engine); - auto imageDownloader = new ImageDownloader(connectivityMonitor, engine); - auto messagesAdapter = new MessagesAdapter(settingsManager, previewEngine, lrcInstance, engine); - auto positionManager = new PositionManager(settingsManager, systemTray, lrcInstance, engine); - auto conversationsAdapter = new ConversationsAdapter(systemTray, lrcInstance, engine); - auto avAdapter = new AvAdapter(lrcInstance, engine); - auto contactAdapter = new ContactAdapter(lrcInstance, engine); - auto accountAdapter = new AccountAdapter(settingsManager, systemTray, lrcInstance, engine); - auto utilsAdapter = new UtilsAdapter(settingsManager, systemTray, lrcInstance, engine); - auto pluginAdapter = new PluginAdapter(lrcInstance, settingsManager, engine); - auto currentCall = new CurrentCall(lrcInstance, engine); - auto currentConversation = new CurrentConversation(lrcInstance, engine); - auto currentAccount = new CurrentAccount(lrcInstance, settingsManager, engine); - auto tipsModel = new TipsModel(settingsManager, engine); - auto videoDevices = new VideoDevices(lrcInstance, engine); - auto currentAccountToMigrate = new CurrentAccountToMigrate(lrcInstance, engine); - auto wizardViewStepModel = new WizardViewStepModel(lrcInstance, accountAdapter, settingsManager, engine); + /* Used in ContactAdapter */ + auto connectionInfoListModel = new ConnectionInfoListModel(lrcInstance, app); + qApp->setProperty("ConnectionInfoListModel", QVariant::fromValue(connectionInfoListModel)); + QQmlEngine::setObjectOwnership(connectionInfoListModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<ConnectionInfoListModel>(REG_MODEL, "ConnectionInfoListModel", CREATE(connectionInfoListModel)); + + /* Used in AccountAdapter */ + auto accountListModel = new AccountListModel(lrcInstance, app); + qApp->setProperty("AccountListModel", QVariant::fromValue(accountListModel)); + QQmlEngine::setObjectOwnership(accountListModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<AccountListModel>(REG_MODEL, "AccountListModel", CREATE(accountListModel)); + + auto deviceItemListModel = new DeviceItemListModel(lrcInstance, app); + qApp->setProperty("DeviceItemListModel", QVariant::fromValue(deviceItemListModel)); + QQmlEngine::setObjectOwnership(deviceItemListModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<DeviceItemListModel>(REG_MODEL, "DeviceItemListModel", CREATE(deviceItemListModel)); + + auto moderatorListModel = new ModeratorListModel(lrcInstance, app); + qApp->setProperty("ModeratorListModel", QVariant::fromValue(moderatorListModel)); + QQmlEngine::setObjectOwnership(moderatorListModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<ModeratorListModel>(REG_MODEL, "ModeratorListModel", CREATE(moderatorListModel)); + + /* Used in CallAdapter */ + auto pttListener = new PTTListener(settingsManager, app); + qApp->setProperty("PTTListener", QVariant::fromValue(pttListener)); + QQmlEngine::setObjectOwnership(pttListener, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<PTTListener>(REG_MODEL, "PTTListener", CREATE(pttListener)); + + auto callOverlayModel = new CallOverlayModel(lrcInstance, pttListener, app); + qApp->setProperty("CallOverlayModel", QVariant::fromValue(callOverlayModel)); + QQmlEngine::setObjectOwnership(callOverlayModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<CallOverlayModel>(REG_MODEL, "CallOverlayModel", CREATE(callOverlayModel)); + + /* Used in CurrentCall */ + auto callParticipantsModel = new CallParticipantsModel(lrcInstance, app); + qApp->setProperty("CallParticipantsModel", QVariant::fromValue(callParticipantsModel)); + QQmlEngine::setObjectOwnership(callParticipantsModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<CallParticipantsModel>(REG_MODEL, "CallParticipantsModel", CREATE(callParticipantsModel)); + + /* Used in ConversationsAdapter */ + auto convListProxyModel = new ConversationListProxyModel(nullptr, app); + qApp->setProperty("ConvListProxyModel", QVariant::fromValue(convListProxyModel)); + auto searchProxyListModel = new SelectableListProxyModel(nullptr, app); + qApp->setProperty("ConvSearchListProxyModel", QVariant::fromValue(searchProxyListModel)); + + // This causes mutually exclusive selection between the two proxy models. + new SelectableListProxyGroupModel({convListProxyModel, searchProxyListModel}, app); + + /* Used in PluginManager */ + auto pluginListModel = new PluginListModel(lrcInstance, app); + qApp->setProperty("PluginListModel", QVariant::fromValue(pluginListModel)); + QQmlEngine::setObjectOwnership(pluginListModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<PluginListModel>(REG_MODEL, "PluginListModel", CREATE(pluginListModel)); + + auto pluginStoreListModel = new PluginStoreListModel(lrcInstance, app); + qApp->setProperty("PluginStoreListModel", QVariant::fromValue(pluginStoreListModel)); + QQmlEngine::setObjectOwnership(pluginStoreListModel, QQmlEngine::CppOwnership); + REG_QML_SINGLETON<PluginStoreListModel>(REG_MODEL, "PluginStoreListModel", CREATE(pluginStoreListModel)); + + // Register app-level objects that are used by QML created objects. + // These MUST be set prior to loading the initial QML file, in order to + // be available to the QML adapter class factory creation methods. + qApp->setProperty("LRCInstance", QVariant::fromValue(lrcInstance)); + qApp->setProperty("SystemTray", QVariant::fromValue(systemTray)); + qApp->setProperty("AppSettingsManager", QVariant::fromValue(settingsManager)); + qApp->setProperty("ConnectivityMonitor", QVariant::fromValue(connectivityMonitor)); + qApp->setProperty("PreviewEngine", QVariant::fromValue(previewEngine)); // qml adapter registration - QML_REGISTERSINGLETONTYPE_POBJECT(NS_HELPERS, avatarRegistry, "AvatarRegistry"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, callAdapter, "CallAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, tipsModel, "TipsModel"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, messagesAdapter, "MessagesAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, positionManager, "PositionManager"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, conversationsAdapter, "ConversationsAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, avAdapter, "AvAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, contactAdapter, "ContactAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, accountAdapter, "AccountAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, utilsAdapter, "UtilsAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, pluginAdapter, "PluginAdapter"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentCall, "CurrentCall"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation, "CurrentConversation"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentConversation->uris(), "CurrentConversationMembers"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccount, "CurrentAccount"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, videoDevices, "VideoDevices"); - QML_REGISTERSINGLETONTYPE_POBJECT(NS_ADAPTERS, currentAccountToMigrate, "CurrentAccountToMigrate") - QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, wizardViewStepModel, "WizardViewStepModel") - QML_REGISTERSINGLETONTYPE_POBJECT(NS_HELPERS, imageDownloader, "ImageDownloader") + QML_REGISTERSINGLETON_TYPE(NS_HELPERS, AvatarRegistry); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, AccountAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CallAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, MessagesAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, ConversationsAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, ContactAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, UtilsAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, PositionManager); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, AvAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, PluginAdapter); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentAccount); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentConversation); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentCall); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, TipsModel); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, VideoDevices); + QML_REGISTERSINGLETON_TYPE(NS_ADAPTERS, CurrentAccountToMigrate); + QML_REGISTERSINGLETON_TYPE(NS_MODELS, WizardViewStepModel); + QML_REGISTERSINGLETON_TYPE(NS_HELPERS, ImageDownloader); // TODO: remove these QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance->avModel()) QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, PluginModel, &lrcInstance->pluginModel()) QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, AppVersionManager, lrcInstance->getAppVersionManager()) - - // Hack for QtCreator autocomplete (part 2) - // https://bugreports.qt.io/browse/QTCREATORBUG-20569 - // Use a dummy object to register the import namespace. - // This occurs when we register from within MainApplication - QML_REGISTERNAMESPACE(NS_MODELS, dummy::staticMetaObject, ""); - QML_REGISTERNAMESPACE(NS_ADAPTERS, dummy::staticMetaObject, ""); - QML_REGISTERNAMESPACE(NS_CONSTANTS, dummy::staticMetaObject, ""); - QML_REGISTERNAMESPACE(NS_HELPERS, dummy::staticMetaObject, ""); - QML_REGISTERNAMESPACE(NS_ENUMS, dummy::staticMetaObject, ""); + QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(NameDirectory); // C++ singleton // QAbstractListModels QML_REGISTERTYPE(NS_MODELS, BannedListModel); @@ -176,10 +222,7 @@ registerTypes(QQmlEngine* engine, QML_REGISTERTYPE(NS_MODELS, PreferenceItemListModel); QML_REGISTERTYPE(NS_MODELS, PluginListPreferenceModel); QML_REGISTERTYPE(NS_MODELS, FilesToSendListModel); - QML_REGISTERTYPE(NS_MODELS, SmartListModel); - QML_REGISTERTYPE(NS_MODELS, MessageListModel); QML_REGISTERTYPE(NS_MODELS, CallInformationListModel); - QML_REGISTERTYPE(NS_MODELS, RendererInformationListModel); // Roles & type enums for models QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList"); @@ -201,10 +244,6 @@ registerTypes(QQmlEngine* engine, QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, lrcInstance, "LRCInstance") QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, settingsManager, "AppSettingsManager") - // C++ singletons - // TODO: remove this - QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(NameDirectory); - // Lrc namespaces, models, and singletons QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::staticMetaObject, "Lrc"); QML_REGISTERNAMESPACE(NS_MODELS, lrc::api::account::staticMetaObject, "Account"); @@ -239,13 +278,7 @@ registerTypes(QQmlEngine* engine, QML_REGISTERUNCREATABLE(NS_ENUMS, VideoFormatFpsModel) engine->addImageProvider(QLatin1String("qrImage"), new QrImageProvider(lrcInstance)); - engine->addImageProvider(QLatin1String("avatarimage"), - new AvatarImageProvider(lrcInstance)); - - engine->setObjectOwnership(&lrcInstance->avModel(), QQmlEngine::CppOwnership); - engine->setObjectOwnership(&lrcInstance->pluginModel(), QQmlEngine::CppOwnership); - engine->setObjectOwnership(lrcInstance->getAppVersionManager(), QQmlEngine::CppOwnership); - engine->setObjectOwnership(&NameDirectory::instance(), QQmlEngine::CppOwnership); + engine->addImageProvider(QLatin1String("avatarimage"), new AvatarImageProvider(lrcInstance)); } // clang-format on } // namespace Utils diff --git a/src/app/qmlregister.h b/src/app/qmlregister.h index 510a010a131038a0ea152f4add3eb7735646fd68..3e0bc38a234242317e1c4517a74ec5f4385c3884 100644 --- a/src/app/qmlregister.h +++ b/src/app/qmlregister.h @@ -46,6 +46,9 @@ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") } // namespace dummy // clang-format off +#define QML_REGISTERSINGLETON_TYPE(NS, T) \ + qmlRegisterSingletonType<T>(NS, MODULE_VER_MAJ, MODULE_VER_MIN, #T, T::create); + #define QML_REGISTERSINGLETONTYPE_POBJECT(NS, I, N) \ QQmlEngine::setObjectOwnership(I, QQmlEngine::CppOwnership); \ { using T = std::remove_reference<decltype(*I)>::type; \ @@ -67,6 +70,7 @@ void registerTypes(QQmlEngine* engine, SystemTray* systemTray, AppSettingsManager* appSettingsManager, ConnectivityMonitor* connectivityMonitor, + PreviewEngine* previewEngine, ScreenInfo* screenInfo, QObject* app); } diff --git a/src/app/settingsview/components/CallSettingsPage.qml b/src/app/settingsview/components/CallSettingsPage.qml index b637c979bab3684504234b0146e35d47a81a922f..431d554f42f066b71fdb7eecad683c358de2bc97 100644 --- a/src/app/settingsview/components/CallSettingsPage.qml +++ b/src/app/settingsview/components/CallSettingsPage.qml @@ -34,7 +34,7 @@ SettingsPageBase { property bool isSIP: CurrentAccount.type === Profile.Type.SIP property int itemWidth: 132 - property string key: PttListener.keyToString(PttListener.getCurrentKey()) + property string key: PTTListener.keyToString(PTTListener.getCurrentKey()) title: JamiStrings.callSettingsTitle function updateAndShowModeratorsSlot() { @@ -437,7 +437,7 @@ SettingsPageBase { onClicked: { var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ChangePttKeyPopup.qml"); dlg.choiceMade.connect(function (chosenKey) { - keyLabel.text = PttListener.keyToString(chosenKey); + keyLabel.text = PTTListener.keyToString(chosenKey); }); } } diff --git a/src/app/settingsview/components/ConnectionMonitoringTable.qml b/src/app/settingsview/components/ConnectionMonitoringTable.qml index 1337a74a184e6a4c5c5c86d038e8e880ec943647..9819cc46bf25d01dca8bed02973f92d9890ac67e 100644 --- a/src/app/settingsview/components/ConnectionMonitoringTable.qml +++ b/src/app/settingsview/components/ConnectionMonitoringTable.qml @@ -14,13 +14,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ + import QtQuick import QtQuick.Controls import QtQuick.Layouts + import net.jami.Adapters 1.1 import net.jami.Constants 1.1 import net.jami.Enums 1.1 import net.jami.Models 1.1 + import "../../commoncomponents" import "../js/logviewwindowcreation.js" as LogViewWindowCreation @@ -109,16 +112,14 @@ ListView { model: ConnectionInfoListModel - Component.onCompleted: { - ContactAdapter.updateConnectionInfo(); - } + Component.onCompleted: ConnectionInfoListModel.update() Timer { interval: 1000 running: root.visible repeat: true onTriggered: { - ContactAdapter.updateConnectionInfo(); + ConnectionInfoListModel.update(); listview.rota = listview.rota + 5; } } diff --git a/src/app/tipsmodel.h b/src/app/tipsmodel.h index 4cac64bca8e548171f2862b66263caa427fcc77c..fa3af4356a019e9c674ba622579ad1aa75511132 100644 --- a/src/app/tipsmodel.h +++ b/src/app/tipsmodel.h @@ -17,12 +17,14 @@ */ #pragma once -#include "lrcinstance.h" #include "appsettingsmanager.h" -#include "qtutils.h" + +#include "typedefs.h" #include <QAbstractListModel> #include <QObject> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration #define TIPS_ROLES \ X(TipId) \ @@ -44,8 +46,14 @@ Q_ENUM_NS(Role) class TipsModel : public QAbstractListModel { Q_OBJECT + QML_SINGLETON public: + static TipsModel* create(QQmlEngine*, QJSEngine*) + { + return new TipsModel(qApp->property("AppSettingsManager").value<AppSettingsManager*>()); + } + TipsModel(AppSettingsManager* sm, QObject* parent = nullptr); int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -58,4 +66,4 @@ public Q_SLOTS: private: VectorMapStringString tips_; AppSettingsManager* settingsManager_; -}; \ No newline at end of file +}; diff --git a/src/app/utilsadapter.h b/src/app/utilsadapter.h index 2f1b6abb7ecae093bb4511afd65ae839996b22e0..5cb5c5a89a7476bcf087d68f66baa1ef257226bc 100644 --- a/src/app/utilsadapter.h +++ b/src/app/utilsadapter.h @@ -30,12 +30,15 @@ #include <QApplication> #include <QObject> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration #if __has_include(<gio/gio.h>) #include <gio/gio.h> #endif #if defined(WIN32) && __has_include(<winrt/Windows.Foundation.h>) +#define _SILENCE_CLANG_COROUTINE_MESSAGE #include <winrt/Windows.Foundation.h> #define WATCHSYSTEMTHEME __has_include(<winrt/Windows.UI.ViewManagement.h>) @@ -67,9 +70,18 @@ bool readAppsUseLightThemeRegistry(bool getValue); class UtilsAdapter final : public QmlAdapterBase { Q_OBJECT + QML_SINGLETON + QML_PROPERTY(QStringList, logList) QML_RO_PROPERTY(bool, isRTL) public: + static UtilsAdapter* create(QQmlEngine*, QJSEngine*) + { + return new UtilsAdapter(qApp->property("AppSettingsManager").value<AppSettingsManager*>(), + qApp->property("SystemTray").value<SystemTray*>(), + qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit UtilsAdapter(AppSettingsManager* settingsManager, SystemTray* systemTray, LRCInstance* instance, diff --git a/src/app/videodevices.cpp b/src/app/videodevices.cpp index 133b11727380aec9ab30522247384f73db9bfd0f..a986c4f4b058e9269eb843f10fcc40a8db58ae6f 100644 --- a/src/app/videodevices.cpp +++ b/src/app/videodevices.cpp @@ -18,7 +18,8 @@ #include "videodevices.h" -// VideoInputDeviceModel +#include "api/devicemodel.h" + VideoInputDeviceModel::VideoInputDeviceModel(LRCInstance* lrcInstance, VideoDevices* videoDeviceInstance) : QAbstractListModel(videoDeviceInstance) @@ -124,7 +125,6 @@ VideoFormatResolutionModel::getCurrentIndex() const return resultList.size() > 0 ? resultList[0].row() : 0; } -// VideoFormatFpsModel VideoFormatFpsModel::VideoFormatFpsModel(LRCInstance* lrcInstance, VideoDevices* videoDeviceInstance) : QAbstractListModel(videoDeviceInstance) , lrcInstance_(lrcInstance) @@ -177,7 +177,6 @@ VideoFormatFpsModel::getCurrentIndex() const return resultList.size() > 0 ? resultList[0].row() : 0; } -// VideoDevices VideoDevices::VideoDevices(LRCInstance* lrcInstance, QObject* parent) : QObject(parent) , lrcInstance_(lrcInstance) diff --git a/src/app/videodevices.h b/src/app/videodevices.h index 5232e927031439a47e200871d946820c5e99fc4f..546381d77ce1ada78950f12cb361a540e2f182ab 100644 --- a/src/app/videodevices.h +++ b/src/app/videodevices.h @@ -21,9 +21,9 @@ #include "lrcinstance.h" #include "qtutils.h" -#include "api/devicemodel.h" - #include <QObject> +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class VideoDevices; @@ -115,6 +115,8 @@ private: class VideoDevices : public QObject { Q_OBJECT + QML_SINGLETON + QML_RO_PROPERTY(int, listSize) QML_RO_PROPERTY(QString, defaultChannel) @@ -130,6 +132,11 @@ class VideoDevices : public QObject QML_RO_PROPERTY(QVariant, sharingFpsSourceModel) public: + static VideoDevices* create(QQmlEngine*, QJSEngine*) + { + return new VideoDevices(qApp->property("LRCInstance").value<LRCInstance*>()); + } + explicit VideoDevices(LRCInstance* lrcInstance, QObject* parent = nullptr); ~VideoDevices() = default; diff --git a/src/app/wizardview/WizardView.qml b/src/app/wizardview/WizardView.qml index 2a1f9b738c0f33fc972699d84ef89d83888ff241..995de99198e10120d89fee940008eca61c4bff41 100644 --- a/src/app/wizardview/WizardView.qml +++ b/src/app/wizardview/WizardView.qml @@ -50,6 +50,31 @@ BaseView { } } + // Handle the end of the wizard account creation process. + Connections { + target: WizardViewStepModel + function onCreateAccountRequested(creationOption) { + switch (creationOption) { + case WizardViewStepModel.AccountCreationOption.CreateJamiAccount: + case WizardViewStepModel.AccountCreationOption.CreateRendezVous: + case WizardViewStepModel.AccountCreationOption.ImportFromBackup: + case WizardViewStepModel.AccountCreationOption.ImportFromDevice: + AccountAdapter.createJamiAccount(WizardViewStepModel.accountCreationInfo); + break; + case WizardViewStepModel.AccountCreationOption.ConnectToAccountManager: + AccountAdapter.createJAMSAccount(WizardViewStepModel.accountCreationInfo); + break; + case WizardViewStepModel.AccountCreationOption.CreateSipAccount: + AccountAdapter.createSIPAccount(WizardViewStepModel.accountCreationInfo); + break; + default: + print("Bad account creation option: " + creationOption); + WizardViewStepModel.closeWizardView(); + break; + } + } + } + Connections { target: WizardViewStepModel diff --git a/src/app/wizardviewstepmodel.cpp b/src/app/wizardviewstepmodel.cpp index fc48c1ad338f636195af28c8d2ed90609109da64..08038f614342391fe95a9bf3bcbd087e970e11ee 100644 --- a/src/app/wizardviewstepmodel.cpp +++ b/src/app/wizardviewstepmodel.cpp @@ -18,26 +18,23 @@ #include "wizardviewstepmodel.h" -#include "accountadapter.h" #include "appsettingsmanager.h" +#include "lrcinstance.h" + +#include "api/accountmodel.h" WizardViewStepModel::WizardViewStepModel(LRCInstance* lrcInstance, - AccountAdapter* accountAdapter, AppSettingsManager* appSettingsManager, QObject* parent) : QObject(parent) , lrcInstance_(lrcInstance) - , accountAdapter_(accountAdapter) , appSettingsManager_(appSettingsManager) { reset(); - - connect(accountAdapter_, - &AccountAdapter::accountAdded, + connect(&lrcInstance_->accountModel(), + &AccountModel::accountAdded, this, - [this](QString accountId, int index) { - accountAdapter_->changeAccount(index); - + [this](const QString& accountId) { auto accountCreationOption = get_accountCreationOption(); if (accountCreationOption == AccountCreationOption::ConnectToAccountManager || accountCreationOption == AccountCreationOption::CreateSipAccount) { @@ -66,30 +63,8 @@ WizardViewStepModel::startAccountCreationFlow(AccountCreationOption accountCreat void WizardViewStepModel::nextStep() { - auto accountCreationOption = get_accountCreationOption(); - if (get_mainStep() == MainSteps::Initial - || accountCreationOption == AccountCreationOption::None) { - return; - } - - switch (accountCreationOption) { - case AccountCreationOption::CreateJamiAccount: - case AccountCreationOption::CreateRendezVous: - case AccountCreationOption::ImportFromBackup: - case AccountCreationOption::ImportFromDevice: { - accountAdapter_->createJamiAccount(get_accountCreationInfo()); - break; - } - case AccountCreationOption::ConnectToAccountManager: { - accountAdapter_->createJAMSAccount(get_accountCreationInfo()); - break; - } - case AccountCreationOption::CreateSipAccount: { - accountAdapter_->createSIPAccount(get_accountCreationInfo()); - break; - } - default: - return; + if (mainStep_ != MainSteps::Initial) { + Q_EMIT createAccountRequested(accountCreationOption_); } } diff --git a/src/app/wizardviewstepmodel.h b/src/app/wizardviewstepmodel.h index c21decead1c550ae41d118c2c9ebb9333d57d474..4928325e78a3dccbd1a6ab73f80aee3cb6bf1d51 100644 --- a/src/app/wizardviewstepmodel.h +++ b/src/app/wizardviewstepmodel.h @@ -18,11 +18,13 @@ #pragma once +#include "qtutils.h" + #include <QObject> #include <QVariant> #include <QMap> - -#include "qtutils.h" +#include <QQmlEngine> // QML registration +#include <QApplication> // QML registration class AccountAdapter; class LRCInstance; @@ -31,6 +33,7 @@ class AppSettingsManager; class WizardViewStepModel : public QObject { Q_OBJECT + QML_SINGLETON Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") public: @@ -54,12 +57,17 @@ public: QML_PROPERTY(MainSteps, mainStep) QML_PROPERTY(AccountCreationOption, accountCreationOption) - QML_PROPERTY(QVariantMap, accountCreationInfo) public: + static WizardViewStepModel* create(QQmlEngine*, QJSEngine*) + { + return new WizardViewStepModel(qApp->property("LRCInstance").value<LRCInstance*>(), + qApp->property("AppSettingsManager") + .value<AppSettingsManager*>()); + } + explicit WizardViewStepModel(LRCInstance* lrcInstance, - AccountAdapter* accountAdapter, AppSettingsManager* appSettingsManager, QObject* parent = nullptr); @@ -70,11 +78,11 @@ public: Q_SIGNALS: void accountIsReady(QString accountId); void closeWizardView(); + void createAccountRequested(AccountCreationOption); private: void reset(); LRCInstance* lrcInstance_; - AccountAdapter* accountAdapter_; AppSettingsManager* appSettingsManager_; }; diff --git a/tests/qml/main.cpp b/tests/qml/main.cpp index a604a5c729277fb55279885777619209d0a17e95..d25e81cebcfca2d46b40bdbfb1359dfbb3472295 100644 --- a/tests/qml/main.cpp +++ b/tests/qml/main.cpp @@ -136,6 +136,7 @@ public Q_SLOTS: systemTray_.get(), settingsManager_.get(), connectivityMonitor_.get(), + previewEngine_.get(), &screenInfo_, this); diff --git a/tests/qml/src/TestWrapper.qml b/tests/qml/src/TestWrapper.qml index 3d22ce6c3bef693dc149ed082872ce8240beba04..d8f4510d4b70aef954429bb585d9c826b5ac92d9 100644 --- a/tests/qml/src/TestWrapper.qml +++ b/tests/qml/src/TestWrapper.qml @@ -17,6 +17,7 @@ import QtQuick import QtQuick.Controls +import QtTest import "../../../src/app/" @@ -24,8 +25,18 @@ import "../../../src/app/" // each UUT from having to manage its own top level app management objects // (currently ViewManager, ViewCoordinator, and ApplicationWindow). Item { - // This will be our UUT. - required default property var uut + id: tw + + width: childrenRect.width + height: childrenRect.height + + // A binding to the windowShown property + Binding { + tw.appWindow: uut.Window.window + when: QTestRootObject.windowShown + } + + Component.onCompleted: viewCoordinator.init(this) // These are our fake app management objects. The caveat is that they // must be maintained in sync with the actual objects in the app for now. @@ -33,7 +44,7 @@ Item { // sync them. property ViewManager viewManager: ViewManager {} property ViewCoordinator viewCoordinator: ViewCoordinator {} - property ApplicationWindow appWindow: ApplicationWindow { + property QtObject appWindow: QtObject { property bool useFrameless: false } } diff --git a/tests/qml/src/tst_CallMessageDelegate.qml b/tests/qml/src/tst_CallMessageDelegate.qml index 4d68c32a0ae7b820206cce5ed14ed42e603be5ee..a7b022b75d5958a1aa80f5a4e21fa2c453142998 100644 --- a/tests/qml/src/tst_CallMessageDelegate.qml +++ b/tests/qml/src/tst_CallMessageDelegate.qml @@ -28,17 +28,20 @@ import "../../../src/app/mainview" import "../../../src/app/mainview/components" import "../../../src/app/commoncomponents" -CallMessageDelegate { - id: uut - type: Interaction.Type.CALL +TestWrapper { + CallMessageDelegate { + id: uut - TestCase { - name: "Check basic visibility for header buttons" - function test_checkBasicVisibility() { - var moreButton = findChild(uut, "more") - var replyButton = findChild(uut, "reply") - compare(moreButton.visible, false) - compare(replyButton.visible, false) + type: Interaction.Type.CALL + + TestCase { + name: "Check basic visibility for option buttons" + function test_checkOptionButtonsVisibility() { + var moreButton = findChild(uut, "more") + var replyButton = findChild(uut, "reply") + compare(moreButton.visible, false) + compare(replyButton.visible, false) + } } } -} \ No newline at end of file +} diff --git a/tests/qml/src/tst_OngoingCallPage.qml b/tests/qml/src/tst_OngoingCallPage.qml index 62f9a3754f9a93a35c77b2bca16cd69382603797..0485d61c4faf3effa367d51c6d57c6c039e4541e 100644 --- a/tests/qml/src/tst_OngoingCallPage.qml +++ b/tests/qml/src/tst_OngoingCallPage.qml @@ -25,67 +25,59 @@ import net.jami.Constants 1.1 import "../../../src/app/" import "../../../src/app/mainview/components" -OngoingCallPage { - id: uut - - width: 800 - height: 600 - - property QtObject appWindow - property ViewManager viewManager: ViewManager {} - property ViewCoordinator viewCoordinator: ViewCoordinator {} - - TestCase { - name: "Check basic visibility of action bar during a call" - when: windowShown // Mouse events can only be handled - // after the window has been shown. - - property var callOverlay - property var mainOverlay - - function initTestCase() { - callOverlay = findChild(uut, "callOverlay") - mainOverlay = findChild(callOverlay, "mainOverlay") - - // The CallActionBar on the OngoingCallPage starts out invisible and - // is made visible whenever the user moves their mouse. - // This is implemented via an event filter in the CallOverlayModel - // class. The event filter is created when the MainOverlay becomes - // visible. In the actual Jami application, this happens when a call - // is started, but we need to toggle the visiblity manually here - // because the MainOverlay is visible at the beginning of the test. - appWindow = uut.Window.window - mainOverlay.visible = false - mainOverlay.visible = true - - // Calling mouseMove() will generate warnings if we don't call init first. - viewCoordinator.init(uut) - } - - function test_checkBasicVisibility() { - var callActionBar = findChild(mainOverlay, "callActionBar") - - // The primary and secondary actions in the CallActionBar are currently being added - // one by one (not using a loop) to CallOverlayModel in the Component.onCompleted - // block of CallActionBar.qml. The two lines below are meant as a sanity check - // that no action has been forgotten. - compare(callActionBar.primaryActions.length, CallOverlayModel.primaryModel().rowCount()) - compare(callActionBar.secondaryActions.length, CallOverlayModel.secondaryModel().rowCount()) - - compare(callActionBar.visible, false) - mouseMove(uut) - - // We need to wait for the fade-in animation of the CallActionBar to be completed - // before we check that it's visible. - var waitTime = JamiTheme.overlayFadeDuration + 100 - // Make sure we have time to check that the CallActioinBar is visible before it fades out: - verify(waitTime + 100 < JamiTheme.overlayFadeDelay) - // Note: The CallActionBar is supposed to stay visible for a few seconds. If the above - // check fails, then this means that either overlayFadeDuration or overlayFadeDelay - // got changed to a value that's way too high/low. - - wait(waitTime) - compare(callActionBar.visible, true) +TestWrapper { + OngoingCallPage { + id: uut + + width: 800 + height: 600 + + TestCase { + name: "Check basic visibility of action bar during a call" + when: windowShown // Mouse events can only be handled + // after the window has been shown. + + property var mainOverlay + + function initTestCase() { + mainOverlay = findChild(uut, "mainOverlay") + + // The CallActionBar on the OngoingCallPage starts out invisible and + // is made visible whenever the user moves their mouse. + // This is implemented via an event filter in the CallOverlayModel + // class. The event filter is created when the MainOverlay becomes + // visible. In the actual Jami application, this happens when a call + // is started, but we need to toggle the visiblity manually here + // because the MainOverlay is visible at the beginning of the test. + mainOverlay.visible = false + mainOverlay.visible = true + } + + function test_checkCallActionBarVisibility() { + var callActionBar = findChild(mainOverlay, "callActionBar") + + // The primary and secondary actions in the CallActionBar are currently being added + // one by one (not using a loop) to CallOverlayModel in the Component.onCompleted + // block of CallActionBar.qml. The two lines below are meant as a sanity check + // that no action has been forgotten. + compare(callActionBar.primaryActions.length, CallOverlayModel.primaryModel().rowCount()) + compare(callActionBar.secondaryActions.length, CallOverlayModel.secondaryModel().rowCount()) + + compare(callActionBar.visible, false) + mouseMove(uut) + + // We need to wait for the fade-in animation of the CallActionBar to be completed + // before we check that it's visible. + var waitTime = JamiTheme.overlayFadeDuration + 100 + // Make sure we have time to check that the CallActioinBar is visible before it fades out: + verify(waitTime + 100 < JamiTheme.overlayFadeDelay) + // Note: The CallActionBar is supposed to stay visible for a few seconds. If the above + // check fails, then this means that either overlayFadeDuration or overlayFadeDelay + // got changed to a value that's way too high/low. + + wait(waitTime) + compare(callActionBar.visible, true) + } } } -} \ No newline at end of file +} diff --git a/tests/qml/src/tst_WelcomePage.qml b/tests/qml/src/tst_WelcomePage.qml index d7cc95883cda98273236db57f0e90c7e0d52ce19..3acefa89f9cc59c8d79556cc5b67d3036e4e9562 100644 --- a/tests/qml/src/tst_WelcomePage.qml +++ b/tests/qml/src/tst_WelcomePage.qml @@ -24,9 +24,12 @@ import "../../../src/app/" import "../../../src/app/mainview/components" TestWrapper { - uut: WelcomePage { + WelcomePage { + id: uut + TestCase { name: "Open 'About Jami' popup" + when: windowShown function test_openAboutPopup() { var aboutJamiButton = findChild(uut, "aboutJami")