1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-04-21 21:52:03 +02:00

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
This commit is contained in:
Andreas Traczyk 2024-04-08 18:15:06 -04:00 committed by Adrien Béraud
parent 6105f4f7ce
commit 8a149b6c4f
9 changed files with 78 additions and 81 deletions

View file

@ -362,36 +362,32 @@ CallOverlayModel::clearControls()
} }
void void
CallOverlayModel::registerFilter(QObject* object, QQuickItem* item) CallOverlayModel::setEventFilterActive(QObject* object, QQuickItem* item, bool isActive)
{ {
QQuickWindow* window = qobject_cast<QQuickWindow*>(object); QQuickWindow* window = qobject_cast<QQuickWindow*>(object);
if (!window || !item) { 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; return;
} }
if (watchedItems_.contains(item)) { if (isActive) {
C_DBG << "Item already registered" << item; if (watchedItems_.contains(item)) {
} C_DBG << "Item already registered" << item;
watchedItems_.push_back(item); } else {
if (watchedItems_.size() == 1) { watchedItems_.push_back(item);
window->installEventFilter(this); if (watchedItems_.size() == 1) {
} window->installEventFilter(this);
} }
}
void } else {
CallOverlayModel::unregisterFilter(QObject* object, QQuickItem* item) if (!watchedItems_.contains(item)) {
{ C_DBG << "Item not registered" << item;
QQuickWindow* window = qobject_cast<QQuickWindow*>(object); } else {
if (!window || !item) { watchedItems_.removeOne(item);
C_WARN << "Attempting to unregister an invalid object or item" << object << item; if (watchedItems_.size() == 0) {
return; window->removeEventFilter(this);
} }
if (!watchedItems_.contains(item)) { }
C_DBG << "Item not registered" << item;
}
watchedItems_.removeOne(item);
if (watchedItems_.size() == 0) {
window->removeEventFilter(this);
} }
} }

View file

@ -137,8 +137,7 @@ public:
Q_INVOKABLE QVariant overflowHiddenModel(); Q_INVOKABLE QVariant overflowHiddenModel();
Q_INVOKABLE QVariant pendingConferenceesModel(); Q_INVOKABLE QVariant pendingConferenceesModel();
Q_INVOKABLE void registerFilter(QObject* object, QQuickItem* item); Q_INVOKABLE void setEventFilterActive(QObject* object, QQuickItem* item, bool isActive);
Q_INVOKABLE void unregisterFilter(QObject* object, QQuickItem* item);
bool eventFilter(QObject* object, QEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override;
Q_SIGNALS: Q_SIGNALS:

View file

@ -28,6 +28,8 @@ VideoView {
crop: true crop: true
visible: isRendering && visibilityCondition visible: isRendering && visibilityCondition
Component.onDestruction: VideoDevices.stopDevice(rendererId);
function startWithId(id, force = false) { function startWithId(id, force = false) {
if (id !== undefined && id.length === 0) { if (id !== undefined && id.length === 0) {
stop(); stop();

View file

@ -48,10 +48,10 @@ ListSelectionView {
leftPaneItem: viewCoordinator.getView("SidePanel", true) leftPaneItem: viewCoordinator.getView("SidePanel", true)
rightPaneItem: StackLayout { rightPaneItem: StackLayout {
id: conversationStackLayout
objectName: "ConversationLayout" objectName: "ConversationLayout"
currentIndex: !CurrentConversation.hasCall ? 0 : 1 currentIndex: CurrentConversation.hasCall ? 1 : 0
onCurrentIndexChanged: chatView.parent = currentIndex === 1 ? callStackView.chatViewContainer : chatViewContainer
anchors.fill: parent anchors.fill: parent
@ -64,24 +64,28 @@ ListSelectionView {
ChatView { ChatView {
id: chatView id: chatView
anchors.fill: parent 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 readonly property string currentConvId: CurrentConversation.id
onCurrentConvIdChanged: { onCurrentConvIdChanged: {
if (!CurrentConversation.hasCall) { Qt.callLater(function() {
Qt.callLater(focusChatView); if (CurrentConversation.hasCall) {
} else { callStackView.contentView.forceActiveFocus();
dismiss(); } else {
callStackView.contentView.forceActiveFocus(); focusChatView();
} }
});
} }
onDismiss: { onDismiss: {
if (!inCallView) { if (inCallView) {
viewNode.dismiss();
} else {
callStackView.chatViewContainer.visible = false; callStackView.chatViewContainer.visible = false;
callStackView.contentView.forceActiveFocus(); callStackView.contentView.forceActiveFocus();
} else {
viewNode.dismiss();
} }
} }

View file

@ -25,7 +25,11 @@ import "../../commoncomponents"
Item { Item {
id: root 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 alias contentView: callStackMainView
property var sipKeys: ["1", "2", "3", "A", "4", "5", "6", "B", "7", "8", "9", "C", "*", "0", "#", "D"] 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 // TODO: this should all be done by listening to
// parent visibility change or parent `Component.onDestruction` // parent visibility change or parent `Component.onDestruction`
function needToCloseInCallConversationAndPotentialWindow() { function needToCloseInCallConversationAndPotentialWindow() {
ongoingCallPage.closeInCallConversation(); if (callStackMainView.item instanceof OngoingCallPage) {
ongoingCallPage.closeContextMenuAndRelatedWindows(); callStackMainView.item.closeInCallConversation();
callStackMainView.item.closeContextMenuAndRelatedWindows();
}
} }
function toggleFullScreen() { function toggleFullScreen() {
if (!layoutManager.isCallFullscreen) { if (!layoutManager.isCallFullscreen) {
layoutManager.pushFullScreenItem(callStackMainView.currentItem, callStackMainView, null, null); layoutManager.pushFullScreenItem(callStackMainView.item, callStackMainView, null, null);
} else { } else {
layoutManager.removeFullScreenItem(callStackMainView.currentItem); layoutManager.removeFullScreenItem(callStackMainView.item);
} }
} }
StackLayout { Loader {
id: callStackMainView id: callStackMainView
anchors.fill: parent anchors.fill: parent
property Item currentItem: itemAt(currentIndex) sourceComponent: {
currentIndex: {
switch (CurrentCall.status) { switch (CurrentCall.status) {
case Call.Status.IN_PROGRESS: case Call.Status.IN_PROGRESS:
case Call.Status.CONNECTED: case Call.Status.CONNECTED:
case Call.Status.PAUSED: case Call.Status.PAUSED:
return 1; return ongoingCallPageComponent;
case Call.Status.SEARCHING: case Call.Status.SEARCHING:
case Call.Status.CONNECTING: case Call.Status.CONNECTING:
case Call.Status.INCOMING_RINGING: case Call.Status.INCOMING_RINGING:
case Call.Status.OUTGOING_RINGING: case Call.Status.OUTGOING_RINGING:
return initialCallPageComponent;
default: default:
return 0; return null;
} }
} }
InitialCallPage { Component {
id: initialCallPageComponent
InitialCallPage {}
} }
OngoingCallPage {
id: ongoingCallPage Component {
id: ongoingCallPageComponent
OngoingCallPage {}
} }
} }
} }

View file

@ -61,17 +61,9 @@ Item {
opacity: 0 opacity: 0
// (un)subscribe to an app-wide mouse move event trap filtered Component.onCompleted: CallOverlayModel.setEventFilterActive(appWindow, this, true)
// for the overlay's geometry Component.onDestruction: CallOverlayModel.setEventFilterActive(appWindow, this, false)
function setupFilter() { onVisibleChanged: CallOverlayModel.setEventFilterActive(appWindow, this, visible)
if (visible) {
CallOverlayModel.registerFilter(appWindow, this);
} else {
CallOverlayModel.unregisterFilter(appWindow, this);
}
}
Component.onCompleted: setupFilter()
onVisibleChanged: setupFilter()
Connections { Connections {
target: CallOverlayModel target: CallOverlayModel

View file

@ -18,7 +18,7 @@
#include "videodevices.h" #include "videodevices.h"
#include "api/devicemodel.h" #include "global.h"
VideoInputDeviceModel::VideoInputDeviceModel(LRCInstance* lrcInstance, VideoInputDeviceModel::VideoInputDeviceModel(LRCInstance* lrcInstance,
VideoDevices* videoDeviceInstance) VideoDevices* videoDeviceInstance)
@ -260,11 +260,10 @@ VideoDevices::stopDevice(const QString& id)
return; return;
} }
qInfo() << "Stopping device" << id;
if (lrcInstance_->avModel().stopPreview(id)) { if (lrcInstance_->avModel().stopPreview(id)) {
deviceOpen_ = false; deviceOpen_ = false;
} else { } else {
qWarning() << "Failed to stop device" << id; C_DBG << "Failed to stop device" << id;
} }
} }

View file

@ -25,6 +25,8 @@
#include "directrenderer.h" #include "directrenderer.h"
#else #else
#include "shmrenderer.h" #include "shmrenderer.h"
#include <csignal>
#include <thread>
#endif #endif
#include "callbackshandler.h" #include "callbackshandler.h"
#include "dbus/callmanager.h" #include "dbus/callmanager.h"
@ -42,11 +44,7 @@
#include <algorithm> // std::sort #include <algorithm> // std::sort
#include <chrono> #include <chrono>
#include <csignal>
#include <iomanip> // for std::put_time #include <iomanip> // for std::put_time
#include <fstream>
#include <mutex>
#include <thread>
#include <string> #include <string>
#include <sstream> #include <sstream>
@ -328,7 +326,7 @@ void
AVModel::setDeviceSettings(video::Settings& settings) AVModel::setDeviceSettings(video::Settings& settings)
{ {
MapStringString newSettings; 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); rate = rate.left(rate.length() - 1);
newSettings["channel"] = settings.channel; newSettings["channel"] = settings.channel;
newSettings["name"] = settings.name; newSettings["name"] = settings.name;
@ -357,7 +355,7 @@ AVModel::getDeviceIdFromName(const QString& deviceName) const
return settings.name == deviceName; return settings.name == deviceName;
}); });
if (iter == devices.end()) { if (iter == devices.end()) {
qWarning() << "Couldn't find device: " << deviceName; LC_WARN << "Couldn't find device: " << deviceName;
return {}; return {};
} }
return *iter; return *iter;
@ -491,7 +489,7 @@ void
AVModel::stopLocalRecorder(const QString& path) const AVModel::stopLocalRecorder(const QString& path) const
{ {
if (path.isEmpty()) { if (path.isEmpty()) {
qWarning("stopLocalRecorder: can't stop non existing recording"); LC_WARN << "stopLocalRecorder: can't stop non existing recording";
return; return;
} }
@ -664,7 +662,7 @@ AVModel::getListWindows() const
}); });
if (xcb_connection_has_error(c.get())) { if (xcb_connection_has_error(c.get())) {
qDebug() << "xcb connection has error"; LC_DBG << "xcb connection has error";
return ret; return ret;
} }
@ -690,7 +688,7 @@ AVModel::getListWindows() const
propertyPtr replyPropList(xcb_get_property_reply(c.get(), propCookieList, &e), propertyPtr replyPropList(xcb_get_property_reply(c.get(), propCookieList, &e),
[](auto* ptr) { free(ptr); }); [](auto* ptr) { free(ptr); });
if (e) { if (e) {
qDebug() << "Error: " << e->error_code; LC_DBG << "Error: " << e->error_code;
free(e); free(e);
} }
if (replyPropList.get()) { if (replyPropList.get()) {
@ -710,7 +708,7 @@ AVModel::getListWindows() const
free(ptr); free(ptr);
}}; }};
if (e) { if (e) {
qDebug() << "Error: " << e->error_code; LC_DBG << "Error: " << e->error_code;
free(e); free(e);
} }
if (replyProp.get()) { if (replyProp.get()) {
@ -987,7 +985,7 @@ AVModelPimpl::getDevice(int type) const
if (deviceIdx < devices.size()) if (deviceIdx < devices.size())
result = devices.at(deviceIdx); result = devices.at(deviceIdx);
} catch (std::bad_alloc& ba) { } catch (std::bad_alloc& ba) {
qWarning() << "bad_alloc caught: " << ba.what(); LC_WARN << "bad_alloc caught: " << ba.what();
return ""; return "";
} }
return result; return result;
@ -1060,7 +1058,7 @@ AVModelPimpl::removeRenderer(const QString& id)
QWriteLocker lk(&renderersMutex_); QWriteLocker lk(&renderersMutex_);
auto it = renderers_.find(id); auto it = renderers_.find(id);
if (it == renderers_.end()) { if (it == renderers_.end()) {
qWarning() << "Cannot remove renderer. " << id << "not found"; LC_DBG << "Cannot remove renderer. " << id << " not found";
return {}; return {};
} }
auto removed = std::move(it->second); auto removed = std::move(it->second);

View file

@ -19,8 +19,6 @@
***************************************************************************/ ***************************************************************************/
#include "api/lrc.h" #include "api/lrc.h"
#include <locale>
#if !defined(_MSC_VER) #if !defined(_MSC_VER)
#include <unistd.h> #include <unistd.h>
#endif #endif