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,38 +362,34 @@ 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 (isActive) {
if (watchedItems_.contains(item)) {
C_DBG << "Item already registered" << item;
}
} else {
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;
}
} else {
if (!watchedItems_.contains(item)) {
C_DBG << "Item not registered" << item;
}
} else {
watchedItems_.removeOne(item);
if (watchedItems_.size() == 0) {
window->removeEventFilter(this);
}
}
}
}
bool
CallOverlayModel::eventFilter(QObject* object, QEvent* event)

View file

@ -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:

View file

@ -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();

View file

@ -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();
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();
}
}

View file

@ -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 {}
}
}
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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);

View file

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