From 8a149b6c4f1a0a4957ce6e4f1e10e7daa9021237 Mon Sep 17 00:00:00 2001 From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> Date: Mon, 8 Apr 2024 18:15:06 -0400 Subject: [PATCH] callview: use dynamic loading for call views This commit replaces a StackLayout with a Loader allowing us to load initial and ongoing call views dynamically based on the current conversation's call state. This may fix several issues related to conversation loading including a possible uncaught binding loop based on observing CurrentConversation.id changes. - small header clean up Change-Id: Idfc723d8b39f19aafb026c19f26590910b5c26cd --- src/app/calloverlaymodel.cpp | 46 +++++++++---------- src/app/calloverlaymodel.h | 3 +- src/app/commoncomponents/LocalVideo.qml | 2 + src/app/mainview/ConversationView.qml | 28 ++++++----- src/app/mainview/components/CallStackView.qml | 37 +++++++++------ src/app/mainview/components/MainOverlay.qml | 14 ++---- src/app/videodevices.cpp | 5 +- src/libclient/avmodel.cpp | 22 ++++----- src/libclient/lrc.cpp | 2 - 9 files changed, 78 insertions(+), 81 deletions(-) diff --git a/src/app/calloverlaymodel.cpp b/src/app/calloverlaymodel.cpp index 5e6555216..8ace2b795 100644 --- a/src/app/calloverlaymodel.cpp +++ b/src/app/calloverlaymodel.cpp @@ -362,36 +362,32 @@ CallOverlayModel::clearControls() } void -CallOverlayModel::registerFilter(QObject* object, QQuickItem* item) +CallOverlayModel::setEventFilterActive(QObject* object, QQuickItem* item, bool isActive) { QQuickWindow* window = qobject_cast<QQuickWindow*>(object); if (!window || !item) { - C_WARN << "Attempting to register an invalid object or item" << object << item; + C_WARN << "Attempting to" << (isActive ? "register" : "unregister") + << "an invalid object or item" << window << item; return; } - if (watchedItems_.contains(item)) { - C_DBG << "Item already registered" << item; - } - watchedItems_.push_back(item); - if (watchedItems_.size() == 1) { - window->installEventFilter(this); - } -} - -void -CallOverlayModel::unregisterFilter(QObject* object, QQuickItem* item) -{ - QQuickWindow* window = qobject_cast<QQuickWindow*>(object); - if (!window || !item) { - C_WARN << "Attempting to unregister an invalid object or item" << object << item; - return; - } - if (!watchedItems_.contains(item)) { - C_DBG << "Item not registered" << item; - } - watchedItems_.removeOne(item); - if (watchedItems_.size() == 0) { - window->removeEventFilter(this); + if (isActive) { + if (watchedItems_.contains(item)) { + C_DBG << "Item already registered" << item; + } else { + watchedItems_.push_back(item); + if (watchedItems_.size() == 1) { + window->installEventFilter(this); + } + } + } else { + if (!watchedItems_.contains(item)) { + C_DBG << "Item not registered" << item; + } else { + watchedItems_.removeOne(item); + if (watchedItems_.size() == 0) { + window->removeEventFilter(this); + } + } } } diff --git a/src/app/calloverlaymodel.h b/src/app/calloverlaymodel.h index c940896ad..231293569 100644 --- a/src/app/calloverlaymodel.h +++ b/src/app/calloverlaymodel.h @@ -137,8 +137,7 @@ public: Q_INVOKABLE QVariant overflowHiddenModel(); Q_INVOKABLE QVariant pendingConferenceesModel(); - Q_INVOKABLE void registerFilter(QObject* object, QQuickItem* item); - Q_INVOKABLE void unregisterFilter(QObject* object, QQuickItem* item); + Q_INVOKABLE void setEventFilterActive(QObject* object, QQuickItem* item, bool isActive); bool eventFilter(QObject* object, QEvent* event) override; Q_SIGNALS: diff --git a/src/app/commoncomponents/LocalVideo.qml b/src/app/commoncomponents/LocalVideo.qml index c746f6d93..cf5346470 100644 --- a/src/app/commoncomponents/LocalVideo.qml +++ b/src/app/commoncomponents/LocalVideo.qml @@ -28,6 +28,8 @@ VideoView { crop: true visible: isRendering && visibilityCondition + Component.onDestruction: VideoDevices.stopDevice(rendererId); + function startWithId(id, force = false) { if (id !== undefined && id.length === 0) { stop(); diff --git a/src/app/mainview/ConversationView.qml b/src/app/mainview/ConversationView.qml index 9b8b7f0da..299602464 100644 --- a/src/app/mainview/ConversationView.qml +++ b/src/app/mainview/ConversationView.qml @@ -48,10 +48,10 @@ ListSelectionView { leftPaneItem: viewCoordinator.getView("SidePanel", true) rightPaneItem: StackLayout { + id: conversationStackLayout objectName: "ConversationLayout" - currentIndex: !CurrentConversation.hasCall ? 0 : 1 - onCurrentIndexChanged: chatView.parent = currentIndex === 1 ? callStackView.chatViewContainer : chatViewContainer + currentIndex: CurrentConversation.hasCall ? 1 : 0 anchors.fill: parent @@ -64,24 +64,28 @@ ListSelectionView { ChatView { id: chatView anchors.fill: parent - inCallView: parent == callStackView.chatViewContainer + + // Parent the chat view to the call stack view when in call. + parent: callStackView.chatViewContainer ? callStackView.chatViewContainer : chatViewContainer + inCallView: parent === callStackView.chatViewContainer readonly property string currentConvId: CurrentConversation.id onCurrentConvIdChanged: { - if (!CurrentConversation.hasCall) { - Qt.callLater(focusChatView); - } else { - dismiss(); - callStackView.contentView.forceActiveFocus(); - } + Qt.callLater(function() { + if (CurrentConversation.hasCall) { + callStackView.contentView.forceActiveFocus(); + } else { + focusChatView(); + } + }); } onDismiss: { - if (!inCallView) { - viewNode.dismiss(); - } else { + if (inCallView) { callStackView.chatViewContainer.visible = false; callStackView.contentView.forceActiveFocus(); + } else { + viewNode.dismiss(); } } diff --git a/src/app/mainview/components/CallStackView.qml b/src/app/mainview/components/CallStackView.qml index 88c651243..bd5d23dd8 100644 --- a/src/app/mainview/components/CallStackView.qml +++ b/src/app/mainview/components/CallStackView.qml @@ -25,7 +25,11 @@ import "../../commoncomponents" Item { id: root - property alias chatViewContainer: ongoingCallPage.chatViewContainer + property var chatViewContainer: { + if (callStackMainView.item instanceof OngoingCallPage) + return callStackMainView.item.chatViewContainer; + return undefined; + } property alias contentView: callStackMainView property var sipKeys: ["1", "2", "3", "A", "4", "5", "6", "B", "7", "8", "9", "C", "*", "0", "#", "D"] @@ -61,44 +65,49 @@ Item { // TODO: this should all be done by listening to // parent visibility change or parent `Component.onDestruction` function needToCloseInCallConversationAndPotentialWindow() { - ongoingCallPage.closeInCallConversation(); - ongoingCallPage.closeContextMenuAndRelatedWindows(); + if (callStackMainView.item instanceof OngoingCallPage) { + callStackMainView.item.closeInCallConversation(); + callStackMainView.item.closeContextMenuAndRelatedWindows(); + } } function toggleFullScreen() { if (!layoutManager.isCallFullscreen) { - layoutManager.pushFullScreenItem(callStackMainView.currentItem, callStackMainView, null, null); + layoutManager.pushFullScreenItem(callStackMainView.item, callStackMainView, null, null); } else { - layoutManager.removeFullScreenItem(callStackMainView.currentItem); + layoutManager.removeFullScreenItem(callStackMainView.item); } } - StackLayout { + Loader { id: callStackMainView anchors.fill: parent - property Item currentItem: itemAt(currentIndex) - - currentIndex: { + sourceComponent: { switch (CurrentCall.status) { case Call.Status.IN_PROGRESS: case Call.Status.CONNECTED: case Call.Status.PAUSED: - return 1; + return ongoingCallPageComponent; case Call.Status.SEARCHING: case Call.Status.CONNECTING: case Call.Status.INCOMING_RINGING: case Call.Status.OUTGOING_RINGING: + return initialCallPageComponent; default: - return 0; + return null; } } - InitialCallPage { + Component { + id: initialCallPageComponent + InitialCallPage {} } - OngoingCallPage { - id: ongoingCallPage + + Component { + id: ongoingCallPageComponent + OngoingCallPage {} } } } diff --git a/src/app/mainview/components/MainOverlay.qml b/src/app/mainview/components/MainOverlay.qml index 9bc7863ba..4a48d745c 100644 --- a/src/app/mainview/components/MainOverlay.qml +++ b/src/app/mainview/components/MainOverlay.qml @@ -61,17 +61,9 @@ Item { opacity: 0 - // (un)subscribe to an app-wide mouse move event trap filtered - // for the overlay's geometry - function setupFilter() { - if (visible) { - CallOverlayModel.registerFilter(appWindow, this); - } else { - CallOverlayModel.unregisterFilter(appWindow, this); - } - } - Component.onCompleted: setupFilter() - onVisibleChanged: setupFilter() + Component.onCompleted: CallOverlayModel.setEventFilterActive(appWindow, this, true) + Component.onDestruction: CallOverlayModel.setEventFilterActive(appWindow, this, false) + onVisibleChanged: CallOverlayModel.setEventFilterActive(appWindow, this, visible) Connections { target: CallOverlayModel diff --git a/src/app/videodevices.cpp b/src/app/videodevices.cpp index 013d502f7..f3003e43f 100644 --- a/src/app/videodevices.cpp +++ b/src/app/videodevices.cpp @@ -18,7 +18,7 @@ #include "videodevices.h" -#include "api/devicemodel.h" +#include "global.h" VideoInputDeviceModel::VideoInputDeviceModel(LRCInstance* lrcInstance, VideoDevices* videoDeviceInstance) @@ -260,11 +260,10 @@ VideoDevices::stopDevice(const QString& id) return; } - qInfo() << "Stopping device" << id; if (lrcInstance_->avModel().stopPreview(id)) { deviceOpen_ = false; } else { - qWarning() << "Failed to stop device" << id; + C_DBG << "Failed to stop device" << id; } } diff --git a/src/libclient/avmodel.cpp b/src/libclient/avmodel.cpp index 3712bfcb1..56285fce8 100644 --- a/src/libclient/avmodel.cpp +++ b/src/libclient/avmodel.cpp @@ -25,6 +25,8 @@ #include "directrenderer.h" #else #include "shmrenderer.h" +#include <csignal> +#include <thread> #endif #include "callbackshandler.h" #include "dbus/callmanager.h" @@ -42,11 +44,7 @@ #include <algorithm> // std::sort #include <chrono> -#include <csignal> #include <iomanip> // for std::put_time -#include <fstream> -#include <mutex> -#include <thread> #include <string> #include <sstream> @@ -328,7 +326,7 @@ void AVModel::setDeviceSettings(video::Settings& settings) { MapStringString newSettings; - auto rate = QString::number(settings.rate, 'f', 7); + auto rate = QString::number(static_cast<double>(settings.rate), 'f', 7); rate = rate.left(rate.length() - 1); newSettings["channel"] = settings.channel; newSettings["name"] = settings.name; @@ -357,7 +355,7 @@ AVModel::getDeviceIdFromName(const QString& deviceName) const return settings.name == deviceName; }); if (iter == devices.end()) { - qWarning() << "Couldn't find device: " << deviceName; + LC_WARN << "Couldn't find device: " << deviceName; return {}; } return *iter; @@ -491,7 +489,7 @@ void AVModel::stopLocalRecorder(const QString& path) const { if (path.isEmpty()) { - qWarning("stopLocalRecorder: can't stop non existing recording"); + LC_WARN << "stopLocalRecorder: can't stop non existing recording"; return; } @@ -664,7 +662,7 @@ AVModel::getListWindows() const }); if (xcb_connection_has_error(c.get())) { - qDebug() << "xcb connection has error"; + LC_DBG << "xcb connection has error"; return ret; } @@ -690,7 +688,7 @@ AVModel::getListWindows() const propertyPtr replyPropList(xcb_get_property_reply(c.get(), propCookieList, &e), [](auto* ptr) { free(ptr); }); if (e) { - qDebug() << "Error: " << e->error_code; + LC_DBG << "Error: " << e->error_code; free(e); } if (replyPropList.get()) { @@ -710,7 +708,7 @@ AVModel::getListWindows() const free(ptr); }}; if (e) { - qDebug() << "Error: " << e->error_code; + LC_DBG << "Error: " << e->error_code; free(e); } if (replyProp.get()) { @@ -987,7 +985,7 @@ AVModelPimpl::getDevice(int type) const if (deviceIdx < devices.size()) result = devices.at(deviceIdx); } catch (std::bad_alloc& ba) { - qWarning() << "bad_alloc caught: " << ba.what(); + LC_WARN << "bad_alloc caught: " << ba.what(); return ""; } return result; @@ -1060,7 +1058,7 @@ AVModelPimpl::removeRenderer(const QString& id) QWriteLocker lk(&renderersMutex_); auto it = renderers_.find(id); if (it == renderers_.end()) { - qWarning() << "Cannot remove renderer. " << id << "not found"; + LC_DBG << "Cannot remove renderer. " << id << " not found"; return {}; } auto removed = std::move(it->second); diff --git a/src/libclient/lrc.cpp b/src/libclient/lrc.cpp index e486e5cbf..46fc8c227 100644 --- a/src/libclient/lrc.cpp +++ b/src/libclient/lrc.cpp @@ -19,8 +19,6 @@ ***************************************************************************/ #include "api/lrc.h" -#include <locale> - #if !defined(_MSC_VER) #include <unistd.h> #endif -- GitLab