diff --git a/Linphone/core/conference/ConferenceInfoGui.cpp b/Linphone/core/conference/ConferenceInfoGui.cpp index 5a8b233fb..d065a8ade 100644 --- a/Linphone/core/conference/ConferenceInfoGui.cpp +++ b/Linphone/core/conference/ConferenceInfoGui.cpp @@ -29,6 +29,7 @@ ConferenceInfoGui::ConferenceInfoGui() { App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); if (isInLinphoneThread()) moveToThread(App::getInstance()->thread()); } + ConferenceInfoGui::ConferenceInfoGui(QSharedPointer core) { App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); mCore = core; diff --git a/Linphone/core/conference/ConferenceInfoList.cpp b/Linphone/core/conference/ConferenceInfoList.cpp index fb26a2d22..2d717c6b1 100644 --- a/Linphone/core/conference/ConferenceInfoList.cpp +++ b/Linphone/core/conference/ConferenceInfoList.cpp @@ -86,8 +86,7 @@ void ConferenceInfoList::setSelf(QSharedPointer me) { for (auto &item : *items) { connectItem(item); } - resetData(*items); - updateHaveCurrentDate(); + resetData(*items); delete items; if (isInitialization) { emit initialized(); @@ -99,52 +98,62 @@ void ConferenceInfoList::setSelf(QSharedPointer me) { // This is needed because account does not have a contact address until // it is connected, so we can't verify if it is the organizer of a deleted // conference (which must hidden) - auto connectModel = [this] { - mCoreModelConnection->invokeToModel([this]() { - if (mCurrentAccountCore) disconnect(mCurrentAccountCore.get()); - auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); - if (defaultAccount) { - mCurrentAccountCore = AccountCore::create(defaultAccount); - connect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this, - [this] { emit lUpdate(); }); - } - }); - }; - mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, connectModel); - connectModel(); - - auto addConference = [this](const std::shared_ptr &confInfo) { - auto list = getSharedList(); - auto haveConf = - std::find_if(list.begin(), list.end(), [confInfo](const QSharedPointer &item) { - std::shared_ptr confAddr = nullptr; - if (item) ToolModel::interpretUrl(item->getUri()); - return confInfo->getUri()->weakEqual(confAddr); - }); - if (haveConf == list.end()) { - auto confInfoCore = build(confInfo); - mCoreModelConnection->invokeToCore([this, confInfoCore] { - add(confInfoCore); - connectItem(confInfoCore); - updateHaveCurrentDate(); - emit confInfoInserted(getCount() - 1, new ConferenceInfoGui(confInfoCore)); - }); - } - }; + mCoreModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, + &ConferenceInfoList::updateCurrentAccount); + updateCurrentAccount(); mCoreModelConnection->makeConnectToModel( &CoreModel::conferenceInfoCreated, - [addConference](const std::shared_ptr &confInfo) { addConference(confInfo); }); + [this](const std::shared_ptr &confInfo) { addConference(confInfo); }); mCoreModelConnection->makeConnectToModel( &CoreModel::conferenceInfoReceived, - [this, addConference](const std::shared_ptr &core, - const std::shared_ptr &conferenceInfo) { + [this](const std::shared_ptr &core, + const std::shared_ptr &conferenceInfo) { lDebug() << log().arg("conference info received") << conferenceInfo->getSubject(); addConference(conferenceInfo->clone()); }); emit lUpdate(true); } +void ConferenceInfoList::resetData(QList> data) { + beginResetModel(); + mList.clear(); + for (auto i : data) + mList << i.template objectCast(); + updateHaveCurrentDate(); // Set have current date before resetting models to let proxy having this info. + endResetModel(); +} + +void ConferenceInfoList::addConference(const std::shared_ptr &confInfo) { + auto list = getSharedList(); + auto haveConf = std::find_if(list.begin(), list.end(), [confInfo](const QSharedPointer &item) { + std::shared_ptr confAddr = nullptr; + if (item) ToolModel::interpretUrl(item->getUri()); + return confInfo->getUri()->weakEqual(confAddr); + }); + if (haveConf == list.end()) { + auto confInfoCore = build(confInfo); + mCoreModelConnection->invokeToCore([this, confInfoCore] { + connectItem(confInfoCore); + add(confInfoCore); + updateHaveCurrentDate(); + emit confInfoInserted(confInfoCore); + }); + } +} + +void ConferenceInfoList::updateCurrentAccount() { + mCoreModelConnection->invokeToModel([this]() { + if (mCurrentAccountCore) disconnect(mCurrentAccountCore.get()); + auto defaultAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); + if (defaultAccount) { + mCurrentAccountCore = AccountCore::create(defaultAccount); + connect(mCurrentAccountCore.get(), &AccountCore::registrationStateChanged, this, + [this] { emit lUpdate(true); }); + } + }); +} + bool ConferenceInfoList::haveCurrentDate() const { return mHaveCurrentDate; } @@ -179,6 +188,19 @@ int ConferenceInfoList::getCurrentDateIndex() { return it == confInfoList.end() ? -1 : std::distance(confInfoList.begin(), it); } +QSharedPointer ConferenceInfoList::getCurrentDateConfInfo() { + auto today = QDate::currentDate(); + auto confInfoList = getSharedList(); + QList>::iterator it; + if (mHaveCurrentDate) { + it = std::find_if(confInfoList.begin(), confInfoList.end(), + [today](const QSharedPointer &item) { + return item && item->getDateTimeUtc().date() == today; + }); + } else it = std::find(confInfoList.begin(), confInfoList.end(), nullptr); + return it != confInfoList.end() ? *it : nullptr; +} + QSharedPointer ConferenceInfoList::build(const std::shared_ptr &conferenceInfo) { auto me = CoreModel::getInstance()->getCore()->getDefaultAccount()->getParams()->getIdentityAddress(); @@ -196,10 +218,11 @@ ConferenceInfoList::build(const std::shared_ptr &confe } void ConferenceInfoList::connectItem(QSharedPointer confInfoCore) { - connect(confInfoCore.get(), &ConferenceInfoCore::removed, this, [this](ConferenceInfoCore *confInfo) { - remove(confInfo); - updateHaveCurrentDate(); - }); + if (confInfoCore) + connect(confInfoCore.get(), &ConferenceInfoCore::removed, this, [this](ConferenceInfoCore *confInfo) { + remove(confInfo); + updateHaveCurrentDate(); + }); } QHash ConferenceInfoList::roleNames() const { @@ -221,10 +244,10 @@ QVariant ConferenceInfoList::data(const QModelIndex &index, int role) const { } } else { // Dummy date if (role == Qt::DisplayRole) { - return QVariant::fromValue(new ConferenceInfoGui()); + return QVariant::fromValue(new ConferenceInfoGui(nullptr)); } else if (role == Qt::DisplayRole + 1) { return Utils::toDateMonthString(QDateTime::currentDateTimeUtc()); } } return QVariant(); -} \ No newline at end of file +} diff --git a/Linphone/core/conference/ConferenceInfoList.hpp b/Linphone/core/conference/ConferenceInfoList.hpp index 723fc4474..d231951b8 100644 --- a/Linphone/core/conference/ConferenceInfoList.hpp +++ b/Linphone/core/conference/ConferenceInfoList.hpp @@ -39,13 +39,17 @@ public: ~ConferenceInfoList(); void setSelf(QSharedPointer me); + void resetData(QList> data); + + void addConference(const std::shared_ptr &confInfo); + void updateCurrentAccount(); bool haveCurrentDate() const; void setHaveCurrentDate(bool have); - void updateHaveCurrentDate(); int getCurrentDateIndex(); + QSharedPointer getCurrentDateConfInfo(); QSharedPointer build(const std::shared_ptr &conferenceInfo); void connectItem(QSharedPointer confInfoCore); @@ -60,7 +64,7 @@ signals: void addCurrentDateChanged(); void haveCurrentDateChanged(); void currentDateIndexChanged(int index); - void confInfoInserted(int index, ConferenceInfoGui *data); + void confInfoInserted(QSharedPointer data); private: QSharedPointer> mCoreModelConnection; diff --git a/Linphone/core/conference/ConferenceInfoProxy.cpp b/Linphone/core/conference/ConferenceInfoProxy.cpp index 940023506..25427ef20 100644 --- a/Linphone/core/conference/ConferenceInfoProxy.cpp +++ b/Linphone/core/conference/ConferenceInfoProxy.cpp @@ -32,27 +32,20 @@ ConferenceInfoProxy::ConferenceInfoProxy(QObject *parent) : LimitProxy(parent) { connect( mList.get(), &ConferenceInfoList::haveCurrentDateChanged, this, [this] { - setCurrentDateIndex(getCurrentDateIndex()); auto sortModel = dynamic_cast(sourceModel()); - sortModel->invalidate(); + sortModel->invalidate(); // New date => sort and filter change. + loadUntil(nullptr); }, Qt::QueuedConnection); - connect( - App::getInstance(), &App::currentDateChanged, this, [this] { setCurrentDateIndex(getCurrentDateIndex()); }, - Qt::QueuedConnection); connect( mList.get(), &ConferenceInfoList::confInfoInserted, this, - [this](int index, ConferenceInfoGui *data) { + [this](QSharedPointer data) { auto sortModel = dynamic_cast(sourceModel()); - if (sortModel) { - auto proxyIndex = sortModel->mapFromSource(mList->index(index, 0)).row(); - if (proxyIndex >= getMaxDisplayItems()) setMaxDisplayItems(proxyIndex + 1); - emit conferenceInfoCreated(proxyIndex); - } + sortModel->invalidate(); // New conf => sort change. Filter can change if on current date. + emit conferenceInfoCreated(new ConferenceInfoGui(data)); }, Qt::QueuedConnection); - connect(mList.get(), &ConferenceInfoList::initialized, this, - [this] { setCurrentDateIndex(getCurrentDateIndex()); }); + connect(mList.get(), &ConferenceInfoList::initialized, this, &ConferenceInfoProxy::initialized); } ConferenceInfoProxy::~ConferenceInfoProxy() { @@ -90,19 +83,35 @@ bool ConferenceInfoProxy::SortFilterList::filterAcceptsRow(int sourceRow, const } } -int ConferenceInfoProxy::getCurrentDateIndex() const { - auto sortModel = dynamic_cast(sourceModel()); - auto modelIndex = mList->getCurrentDateIndex(); - auto proxyIndex = sortModel->mapFromSource(mList->index(modelIndex, 0)).row(); - return proxyIndex; +void ConferenceInfoProxy::clear() { + mList->clearData(); } -void ConferenceInfoProxy::setCurrentDateIndex(int index) { - if (mCurrentDateIndex != index) { - if (index >= mMaxDisplayItems) setMaxDisplayItems(index + 1); - mCurrentDateIndex = index; - emit currentDateIndexChanged(index); +int ConferenceInfoProxy::loadUntil(ConferenceInfoGui *confInfo) { + return loadUntil(confInfo ? confInfo->mCore : nullptr); +} + +int ConferenceInfoProxy::loadUntil(QSharedPointer data) { + auto confInfoList = getListModel(); + if (confInfoList) { + int listIndex = -1; + // Get list index. + if (!data) listIndex = confInfoList->getCurrentDateIndex(); + else confInfoList->get(data.get(), &listIndex); + if (listIndex == -1) return -1; + // Get the index inside sorted/filtered list. + auto listModelIndex = + dynamic_cast(sourceModel())->mapFromSource(confInfoList->index(listIndex, 0)); + // Load enough items into LimitProxy. + if (mMaxDisplayItems < listModelIndex.row()) setMaxDisplayItems(listModelIndex.row() + mDisplayItemsStep); + // Get the new index inside sorted/filtered list. + listModelIndex = + dynamic_cast(sourceModel())->mapFromSource(confInfoList->index(listIndex, 0)); + // Get the index inside LimitProxy. + listIndex = mapFromSource(listModelIndex).row(); + return listIndex; } + return -1; } bool ConferenceInfoProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, diff --git a/Linphone/core/conference/ConferenceInfoProxy.hpp b/Linphone/core/conference/ConferenceInfoProxy.hpp index 6424039e0..6ef3f4515 100644 --- a/Linphone/core/conference/ConferenceInfoProxy.hpp +++ b/Linphone/core/conference/ConferenceInfoProxy.hpp @@ -25,12 +25,13 @@ #include "tool/AbstractObject.hpp" class ConferenceInfoList; +class ConferenceInfoGui; +class ConferenceInfoCore; class ConferenceInfoProxy : public LimitProxy, public AbstractObject { Q_OBJECT Q_PROPERTY(bool haveCurrentDate READ haveCurrentDate NOTIFY haveCurrentDateChanged) - // Q_PROPERTY(int currentDateIndex READ getCurrentDateIndex NOTIFY currentDateIndexChanged) public: enum ConferenceInfoFiltering { None = 0, Future = 1 }; @@ -43,17 +44,18 @@ public: bool haveCurrentDate() const; - Q_INVOKABLE int getCurrentDateIndex() const; - Q_INVOKABLE void setCurrentDateIndex(int index); - + Q_INVOKABLE void clear(); + Q_INVOKABLE int loadUntil(ConferenceInfoGui *confInfo); + int loadUntil(QSharedPointer data); signals: + void initialized(); void haveCurrentDateChanged(); - void conferenceInfoCreated(int index); - void currentDateIndexChanged(int index); + void conferenceInfoCreated(ConferenceInfoGui *confInfo); private: QSharedPointer mList; - int mCurrentDateIndex = -1; + ConferenceInfoCore *mCurrentConfInfo = nullptr; + // int mCurrentDateIndex = -1; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/core/search/MagicSearchProxy.cpp b/Linphone/core/search/MagicSearchProxy.cpp index b1d959293..bbe3c5674 100644 --- a/Linphone/core/search/MagicSearchProxy.cpp +++ b/Linphone/core/search/MagicSearchProxy.cpp @@ -111,7 +111,7 @@ int MagicSearchProxy::loadUntil(const QString &address) { if (listIndex == -1) return -1; listIndex = dynamic_cast(sourceModel())->mapFromSource(magicSearchList->index(listIndex, 0)).row(); - if (mMaxDisplayItems < listIndex) setMaxDisplayItems(listIndex + 1); + if (mMaxDisplayItems < listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); return listIndex; } return -1; diff --git a/Linphone/model/conference/ConferenceModel.cpp b/Linphone/model/conference/ConferenceModel.cpp index b8ed86568..dffc4ec64 100644 --- a/Linphone/model/conference/ConferenceModel.cpp +++ b/Linphone/model/conference/ConferenceModel.cpp @@ -143,11 +143,11 @@ void ConferenceModel::toggleScreenSharing() { if (enable) { params->setConferenceVideoLayout(linphone::Conference::Layout::ActiveSpeaker); params->enableVideo(true); - params->enableCamera(false); auto videoDirection = params->getVideoDirection(); - if (videoDirection != linphone::MediaDirection::SendOnly && - videoDirection != linphone::MediaDirection::SendRecv) - params->setVideoDirection(linphone::MediaDirection::SendOnly); + params->setVideoDirection(videoDirection == linphone::MediaDirection::RecvOnly || + videoDirection == linphone::MediaDirection::SendRecv + ? linphone::MediaDirection::SendRecv + : linphone::MediaDirection::SendOnly); } if (params->isValid()) mMonitor->getCall()->update(params); else lCritical() << log().arg("Cannot toggle screen sharing because parameters are invalid"); diff --git a/Linphone/view/Control/Container/Contact/ContactLayout.qml b/Linphone/view/Control/Container/Contact/ContactLayout.qml index 1451bca89..bd3ef1e05 100644 --- a/Linphone/view/Control/Container/Contact/ContactLayout.qml +++ b/Linphone/view/Control/Container/Contact/ContactLayout.qml @@ -26,7 +26,6 @@ ColumnLayout { Layout.fillWidth: true columns: mainItem.useVerticalLayout ? 1 : children.length rows: 1 - onColumnsChanged: console.log("columns changed", columns, rows) columnSpacing: 49 * DefaultStyle.dp rowSpacing: 27 * DefaultStyle.dp diff --git a/Linphone/view/Control/Display/Contact/AllContactListView.qml b/Linphone/view/Control/Display/Contact/AllContactListView.qml index 08a06c6b0..7d1bceeb9 100644 --- a/Linphone/view/Control/Display/Contact/AllContactListView.qml +++ b/Linphone/view/Control/Display/Contact/AllContactListView.qml @@ -100,6 +100,41 @@ Flickable{ contactsList.currentIndex = -1 suggestionsList.currentIndex = -1 } + + function findNextList(item, count, direction){ + if(count == 3) return null + var nextItem + switch(item){ + case suggestionsList:nextItem=(direction > 0 ? favoritesList : contactsList);break; + case contactsList:nextItem=(direction > 0 ? suggestionsList : favoritesList);break; + case favoritesList:nextItem=(direction > 0 ? contactsList : suggestionsList);break; + default: return null + } + if( nextItem.model.count > 0) return nextItem + else return findNextList(nextItem, count+1, direction) + } + + function updatePosition(list){ + var item = list.itemAtIndex(list.currentIndex) + var centerItemPos = 0 + if( item && list.expanded){ + // For debugging just in case + //var listPosition = item.mapToItem(favoriteList, item.x, item.y) + //var newPosition = favoriteList.mapToItem(mainItem, listPosition.x, listPosition.y) + //console.log("item pos: " +item.x + " / " +item.y) + //console.log("fav pos: " +favoriteList.x + " / " +favoriteList.y) + //console.log("fav content: " +favoriteList.contentX + " / " +favoriteList.contentY) + //console.log("main pos: " +mainItem.x + " / " +mainItem.y) + //console.log("main content: " +mainItem.contentX + " / " +mainItem.contentY) + //console.log("list pos: " +listPosition.x + " / " +listPosition.y) + //console.log("new pos: " +newPosition.x + " / " +newPosition.y) + //console.log("header pos: " +headerItem.x + " / " +headerItem.y) + //console.log("Moving to " + (headerItem.y+item.y)) + centerItemPos = item.y + list.y + list.headerHeight +item.height/2 + } + var centerPos = centerItemPos - height/2 + mainItem.contentY = Math.max(0, Math.min(centerPos, height, contentHeight-height)) + } onHighlightedContactChanged:{ favoritesList.highlightedContact = highlightedContact @@ -126,39 +161,6 @@ Flickable{ if( (contactsProxy.haveMore && contactList.expanded ) || mainItem.hideSuggestions) contactsProxy.displayMore() else suggestionsProxy.displayMore() } - function findNextList(item, count, direction){ - if(count == 3) return null - var nextItem - switch(item){ - case suggestionsList:nextItem=(direction > 0 ? favoritesList : contactsList);break; - case contactsList:nextItem=(direction > 0 ? suggestionsList : favoritesList);break; - case favoritesList:nextItem=(direction > 0 ? contactsList : suggestionsList);break; - default: return null - } - if( nextItem.model.count > 0) return nextItem - else return findNextList(nextItem, count+1, direction) - } - function updatePosition(list){ - var item = list.itemAtIndex(list.currentIndex) - var centerItemPos = 0 - if( item && list.expanded){ - // For debugging just in case - //var listPosition = item.mapToItem(favoriteList, item.x, item.y) - //var newPosition = favoriteList.mapToItem(mainItem, listPosition.x, listPosition.y) - //console.log("item pos: " +item.x + " / " +item.y) - //console.log("fav pos: " +favoriteList.x + " / " +favoriteList.y) - //console.log("fav content: " +favoriteList.contentX + " / " +favoriteList.contentY) - //console.log("main pos: " +mainItem.x + " / " +mainItem.y) - //console.log("main content: " +mainItem.contentX + " / " +mainItem.contentY) - //console.log("list pos: " +listPosition.x + " / " +listPosition.y) - //console.log("new pos: " +newPosition.x + " / " +newPosition.y) - //console.log("header pos: " +headerItem.x + " / " +headerItem.y) - //console.log("Moving to " + (headerItem.y+item.y)) - centerItemPos = item.y + list.y + list.headerHeight +item.height/2 - } - var centerPos = centerItemPos - height/2 - mainItem.contentY = Math.max(0, Math.min(centerPos, height, contentHeight-height)) - } Behavior on contentY{ NumberAnimation { duration: 500 diff --git a/Linphone/view/Control/Display/Meeting/MeetingListView.qml b/Linphone/view/Control/Display/Meeting/MeetingListView.qml index 9dc77391b..6494dc92a 100644 --- a/Linphone/view/Control/Display/Meeting/MeetingListView.qml +++ b/Linphone/view/Control/Display/Meeting/MeetingListView.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Effects +import QtQuick.Controls.Basic import Linphone import QtQml @@ -8,36 +9,121 @@ import UtilsCpp ListView { id: mainItem - visible: count > 0 - clip: true - property string searchBarText property bool hoverEnabled: true property var delegateButtons - property ConferenceInfoGui selectedConference: model && currentIndex != -1 ? model.getAt(currentIndex) : null + property ConferenceInfoGui selectedConference + property bool _moveToIndex: false + + visible: count > 0 + clip: true + cacheBuffer: height/2 spacing: 8 * DefaultStyle.dp - highlightFollowsCurrentItem: true - highlightMoveVelocity: 1500 - - onCountChanged: { - selectedConference = model && currentIndex != -1 && currentIndex < model.count ? model.getAt(currentIndex) : null + highlightFollowsCurrentItem: false + + function selectIndex(index){ + mainItem.currentIndex = index } - onCurrentIndexChanged: { - selectedConference = model.getAt(currentIndex) || null + + function resetSelections(){ + mainItem.selectedConference = null + mainItem.currentIndex = -1 + } + // Issues Notes: + // positionViewAtIndex: + // - if currentItem was in cache, it will not go to it (ex: contentY=63, currentItem.y=3143) + // - Animation don't work + function moveToCurrentItem(){ + var centerItemPos = 0 + if( currentItem){ + centerItemPos = currentItem.y + currentItem.height/2 + } + var centerPos = centerItemPos - height/2 + moveBehaviorTimer.startAnimation() + mainItem.contentY = Math.max(0, Math.min(centerPos, contentHeight-height)) + } + onCurrentItemChanged: { + moveToCurrentItem() + if(currentItem) { + mainItem.selectedConference = currentItem.itemGui + currentItem.forceActiveFocus() + } + } + // When cache is updating, contentHeight changes. Update position if we are moving the view. + onContentHeightChanged:{ + if(moveBehavior.enabled){ + moveToCurrentItem() + } } onAtYEndChanged: if(atYEnd) confInfoProxy.displayMore() + Timer{ + id: moveBehaviorTimer + interval: 501 + onTriggered: moveBehavior.enabled = false + function startAnimation(){ + moveBehavior.enabled = true + moveBehaviorTimer.restart() + } + } + + Behavior on contentY{ + id: moveBehavior + enabled: false + NumberAnimation { + duration: 500 + easing.type: Easing.OutExpo + onFinished: {// Not call if on Behavior. Callback just in case. + moveBehavior.enabled = false + } + } + } + Keys.onPressed: (event)=> { + if(event.key == Qt.Key_Up) { + if(currentIndex > 0 ) { + selectIndex(mainItem.currentIndex-1) + event.accepted = true + } else { + selectIndex(model.count - 1) + event.accepted = true + } + }else if(event.key == Qt.Key_Down){ + if(currentIndex < model.count - 1) { + selectIndex(currentIndex+1) + event.accepted = true + } else { + selectIndex(0) + event.accepted = true + } + } + } + model: ConferenceInfoProxy { id: confInfoProxy filterText: searchBarText filterType: ConferenceInfoProxy.None initialDisplayItems: mainItem.height / (63 * DefaultStyle.dp) + 5 displayItemsStep: initialDisplayItems/2 - onConferenceInfoCreated: (index) => { - mainItem.currentIndex = index + function selectData(confInfoGui){ + mainItem.currentIndex = loadUntil(confInfoGui) } - onCurrentDateIndexChanged: (index) => mainItem.currentIndex = index + onConferenceInfoCreated: (confInfoGui) => { + selectData(confInfoGui) + } + onInitialized: { + // Move to currentDate + selectData(null) + } + } + + ScrollBar.vertical: ScrollBar { + id: scrollbar + rightPadding: 8 * DefaultStyle.dp + + active: true + interactive: true + policy: mainItem.contentHeight > mainItem.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff } section { @@ -56,30 +142,31 @@ ListView { } property: '$sectionMonth' } - + delegate: FocusScope { id: itemDelegate - height: 63 * DefaultStyle.dp + height: 63 * DefaultStyle.dp + (!isFirst && dateDay.visible ? topOffset : 0) width: mainItem.width enabled: !isCanceled && haveModel - property var previousItem : mainItem.model.count > 0 && index > 0 ? mainItem.model.getAt(index-1) : null - property var dateTime: !!$modelData && $modelData.core.haveModel ? $modelData.core.dateTime : UtilsCpp.getCurrentDateTime() + + property var itemGui: $modelData + // Do not use itemAtIndex because of caching items. Using getAt ensure to have a GUI + property var previousConfInfoGui : mainItem.model.getAt(index-1) + property var dateTime: itemGui.core ? itemGui.core.dateTimeUtc : UtilsCpp.getCurrentDateTime() property string day : UtilsCpp.toDateDayNameString(dateTime) property string dateString: UtilsCpp.toDateString(dateTime) - property string previousDateString: previousItem ? UtilsCpp.toDateString(previousItem.core ? previousItem.core.dateTimeUtc : UtilsCpp.getCurrentDateTimeUtc()) : '' + property string previousDateString: previousConfInfoGui ? UtilsCpp.toDateString(previousConfInfoGui.core ? previousConfInfoGui.core.dateTimeUtc : UtilsCpp.getCurrentDateTimeUtc()) : '' property bool isFirst : ListView.previousSection !== ListView.section property int topOffset: (dateDay.visible && !isFirst? 8 * DefaultStyle.dp : 0) - property var endDateTime: $modelData ? $modelData.core.endDateTime : UtilsCpp.getCurrentDateTime() - property var haveModel: !!$modelData && $modelData != undefined && $modelData.core.haveModel || false - property bool isCanceled: $modelData?.core.state === LinphoneEnums.ConferenceInfoState.Cancelled || false - Component.onCompleted: if (!isFirst && dateDay.visible) { - height = (63+topOffset)*DefaultStyle.dp - delegateIn.anchors.topMargin = topOffset - } + property var endDateTime: itemGui.core ? itemGui.core.endDateTime : UtilsCpp.getCurrentDateTime() + property bool haveModel: itemGui.core ? itemGui.core.haveModel : false + property bool isCanceled: itemGui.core ? itemGui.core.state === LinphoneEnums.ConferenceInfoState.Cancelled : false + property bool isSelected: itemGui.core == mainItem.selectedConference?.core RowLayout{ id: delegateIn anchors.fill: parent + anchors.topMargin: !itemDelegate.isFirst && dateDay.visible ? itemDelegate.topOffset : 0 spacing: 0 Item{ Layout.preferredWidth: 32 * DefaultStyle.dp @@ -141,7 +228,7 @@ ListView { anchors.rightMargin: 5 // margin to avoid clipping shadows at right radius: 10 * DefaultStyle.dp visible: itemDelegate.haveModel || itemDelegate.activeFocus - color: mainItem.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.grey_0 + color: itemDelegate.isSelected ? DefaultStyle.main2_200 : DefaultStyle.grey_0 // mainItem.currentIndex === index ColumnLayout { anchors.fill: parent anchors.left: parent.left @@ -156,7 +243,7 @@ ListView { Layout.preferredHeight: 24 * DefaultStyle.dp } Text { - text: $modelData? $modelData.core.subject : "" + text: itemGui.core? itemGui.core.subject : "" Layout.fillWidth: true maximumLineCount: 1 font { @@ -203,8 +290,7 @@ ListView { cursorShape: Qt.PointingHandCursor visible: itemDelegate.haveModel onClicked: { - mainItem.currentIndex = index - itemDelegate.forceActiveFocus() + mainItem.selectIndex(index) } } } diff --git a/Linphone/view/Page/Main/Contact/ContactPage.qml b/Linphone/view/Page/Main/Contact/ContactPage.qml index 29df038f7..7a3b60fde 100644 --- a/Linphone/view/Page/Main/Contact/ContactPage.qml +++ b/Linphone/view/Page/Main/Contact/ContactPage.qml @@ -31,7 +31,6 @@ AbstractMainPage { if (rightPanelStackView.currentItem && rightPanelStackView.currentItem.objectName === "contactDetail") rightPanelStackView.clear() } } - signal forceListsUpdate() onNoItemButtonPressed: createContact("", "") diff --git a/Linphone/view/Page/Main/Meeting/MeetingPage.qml b/Linphone/view/Page/Main/Meeting/MeetingPage.qml index d12058c6f..b6698441d 100644 --- a/Linphone/view/Page/Main/Meeting/MeetingPage.qml +++ b/Linphone/view/Page/Main/Meeting/MeetingPage.qml @@ -8,30 +8,18 @@ import UtilsCpp // TODO : spacing AbstractMainPage { id: mainItem - noItemButtonText: qsTr("Créer une réunion") - emptyListText: qsTr("Aucune réunion") - newItemIconSource: AppIcons.plusCircle - rightPanelColor: selectedConference ? DefaultStyle.grey_0 : DefaultStyle.grey_100 + property ConferenceInfoGui selectedConference property int meetingListCount signal returnRequested() signal addParticipantsValidated(list selectedParticipants) - Component.onCompleted: rightPanelStackView.push(overridenRightPanel, Control.StackView.Immediate) + + noItemButtonText: qsTr("Créer une réunion") + emptyListText: qsTr("Aucune réunion") + newItemIconSource: AppIcons.plusCircle + rightPanelColor: selectedConference ? DefaultStyle.grey_0 : DefaultStyle.grey_100 showDefaultItem: leftPanelStackView.currentItem?.objectName === "listLayout" && meetingListCount === 0 - onVisibleChanged: if (!visible) { - leftPanelStackView.clear() - leftPanelStackView.push(leftPanelStackView.initialItem) - } - - onSelectedConferenceChanged: { - overridenRightPanelStackView.clear() - if (selectedConference && selectedConference.core.haveModel) { - if (!overridenRightPanelStackView.currentItem || overridenRightPanelStackView.currentItem != meetingDetail) overridenRightPanelStackView.replace(meetingDetail, Control.StackView.Immediate) - } - } - - onNoItemButtonPressed: editConference() function editConference(confInfoGui = null) { var isCreation = !confInfoGui @@ -49,6 +37,33 @@ AbstractMainPage { item.forceActiveFocus() } } + + + onVisibleChanged: if (!visible) { + leftPanelStackView.clear() + leftPanelStackView.push(leftPanelStackView.initialItem) + + } + + onSelectedConferenceChanged: { + overridenRightPanelStackView.clear() + if (selectedConference && selectedConference.core && selectedConference.core.haveModel) { + if (!overridenRightPanelStackView.currentItem || overridenRightPanelStackView.currentItem != meetingDetail) overridenRightPanelStackView.replace(meetingDetail, Control.StackView.Immediate) + } + } + + onNoItemButtonPressed: editConference() + + Component.onCompleted: rightPanelStackView.push(overridenRightPanel, Control.StackView.Immediate) + + leftPanelContent: Control.StackView { + id: leftPanelStackView + Layout.fillWidth: true + Layout.fillHeight: true + Layout.leftMargin: 45 * DefaultStyle.dp + initialItem: listLayout + clip: true + } Dialog { id: cancelAndDeleteConfDialog @@ -96,15 +111,6 @@ AbstractMainPage { ] } - leftPanelContent: Control.StackView { - id: leftPanelStackView - Layout.fillWidth: true - Layout.fillHeight: true - Layout.leftMargin: 45 * DefaultStyle.dp - initialItem: listLayout - clip: true - } - Item { id: overridenRightPanel Control.StackView { @@ -188,8 +194,13 @@ AbstractMainPage { Layout.fillWidth: true Layout.fillHeight: true - onCountChanged: mainItem.meetingListCount = count searchBarText: searchBar.text + + onCountChanged: mainItem.meetingListCount = count + onSelectedConferenceChanged: { + mainItem.selectedConference = selectedConference + } + Keys.onPressed: (event) => { if(event.key == Qt.Key_Escape){ searchBar.forceActiveFocus() @@ -199,18 +210,6 @@ AbstractMainPage { event.accepted = true } } - onSelectedConferenceChanged: { - mainItem.selectedConference = selectedConference - } - Control.ScrollBar.vertical: ScrollBar { - id: meetingsScrollbar - anchors.right: parent.right - anchors.rightMargin: 8 * DefaultStyle.dp - active: true - interactive: true - policy: Control.ScrollBar.AsNeeded - - } } } } @@ -579,7 +578,7 @@ AbstractMainPage { } Text { Layout.fillWidth: true - text: mainItem.selectedConference ? mainItem.selectedConference.core.subject : "" + text: mainItem.selectedConference ? mainItem.selectedConference.core?.subject : "" maximumLineCount: 1 font { pixelSize: 20 * DefaultStyle.dp @@ -591,7 +590,7 @@ AbstractMainPage { } Button { id: editButton - property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core.organizerAddress) + property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core?.organizerAddress) visible: mainItem.selectedConference && isMeObj && isMeObj.value || false Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp @@ -622,8 +621,8 @@ AbstractMainPage { textColor: DefaultStyle.danger_500main contentImageColor: DefaultStyle.danger_500main inversedColors: true - property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core.organizerAddress) - property bool canCancel: isMeObj && isMeObj.value && mainItem.selectedConference.core.state !== LinphoneEnums.ConferenceInfoState.Cancelled + property var isMeObj: UtilsCpp.isMe(mainItem.selectedConference?.core?.organizerAddress) + property bool canCancel: isMeObj && isMeObj.value && mainItem.selectedConference?.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled icon.source: AppIcons.trashCan icon.width: 24 * DefaultStyle.dp icon.height: 24 * DefaultStyle.dp @@ -670,7 +669,7 @@ AbstractMainPage { id: linkButton Layout.fillWidth: true font.bold: shadowEnabled - text: mainItem.selectedConference ? mainItem.selectedConference.core.uri : "" + text: mainItem.selectedConference ? mainItem.selectedConference.core?.uri : "" textSize: 14 * DefaultStyle.dp textWeight: 400 * DefaultStyle.dp underline: true @@ -719,10 +718,10 @@ AbstractMainPage { } Text { text: mainItem.selectedConference - ? UtilsCpp.toDateString(mainItem.selectedConference.core.dateTimeUtc) - + " | " + UtilsCpp.toDateHourString(mainItem.selectedConference.core.dateTimeUtc) + ? UtilsCpp.toDateString(mainItem.selectedConference.core?.dateTimeUtc) + + " | " + UtilsCpp.toDateHourString(mainItem.selectedConference.core?.dateTimeUtc) + " - " - + UtilsCpp.toDateHourString(mainItem.selectedConference.core.endDateTime) + + UtilsCpp.toDateHourString(mainItem.selectedConference.core?.endDateTime) : '' font { pixelSize: 14 * DefaultStyle.dp @@ -738,7 +737,7 @@ AbstractMainPage { source: AppIcons.globe } Text { - text: qsTr("Time zone: ") + (mainItem.selectedConference ? (mainItem.selectedConference.core.timeZoneModel.displayName + ", " + mainItem.selectedConference.core.timeZoneModel.countryName) : "") + text: qsTr("Time zone: ") + (mainItem.selectedConference ? (mainItem.selectedConference.core?.timeZoneModel.displayName + ", " + mainItem.selectedConference.core.timeZoneModel.countryName) : "") font { pixelSize: 14 * DefaultStyle.dp capitalization: Font.Capitalize @@ -748,7 +747,7 @@ AbstractMainPage { } } Section { - visible: mainItem.selectedConference && mainItem.selectedConference.core.description.length != 0 + visible: mainItem.selectedConference && mainItem.selectedConference.core?.description.length != 0 content: RowLayout { spacing: 8 * DefaultStyle.dp EffectImage { @@ -758,7 +757,7 @@ AbstractMainPage { colorizationColor: DefaultStyle.main2_600 } Text { - text: mainItem.selectedConference ? mainItem.selectedConference.core.description : "" + text: mainItem.selectedConference ? mainItem.selectedConference.core?.description : "" Layout.fillWidth: true font { pixelSize: 14 * DefaultStyle.dp @@ -779,10 +778,10 @@ AbstractMainPage { Avatar { Layout.preferredWidth: 45 * DefaultStyle.dp Layout.preferredHeight: 45 * DefaultStyle.dp - _address: mainItem.selectedConference ? mainItem.selectedConference.core.organizerAddress : "" + _address: mainItem.selectedConference ? mainItem.selectedConference.core?.organizerAddress : "" } Text { - text: mainItem.selectedConference ? mainItem.selectedConference.core.organizerName : "" + text: mainItem.selectedConference ? mainItem.selectedConference.core?.organizerName : "" font { pixelSize: 14 * DefaultStyle.dp capitalization: Font.Capitalize @@ -807,7 +806,7 @@ AbstractMainPage { id: participantList Layout.preferredHeight: Math.min(184 * DefaultStyle.dp, contentHeight) Layout.fillWidth: true - model: mainItem.selectedConference ? mainItem.selectedConference.core.participants : [] + model: mainItem.selectedConference ? mainItem.selectedConference.core?.participants : [] clip: true delegate: RowLayout { height: 56 * DefaultStyle.dp @@ -828,7 +827,7 @@ AbstractMainPage { } Text { text: qsTr("Organizer") - visible: mainItem.selectedConference && mainItem.selectedConference.core.organizerAddress === modelData.address + visible: mainItem.selectedConference && mainItem.selectedConference.core?.organizerAddress === modelData.address color: DefaultStyle.main2_400 font { pixelSize: 12 * DefaultStyle.dp @@ -841,7 +840,7 @@ AbstractMainPage { } Button { id: joinButton - visible: mainItem.selectedConference && mainItem.selectedConference.core.state !== LinphoneEnums.ConferenceInfoState.Cancelled + visible: mainItem.selectedConference && mainItem.selectedConference.core?.state !== LinphoneEnums.ConferenceInfoState.Cancelled Layout.fillWidth: true text: qsTr("Rejoindre la réunion") topPadding: 11 * DefaultStyle.dp diff --git a/external/linphone-sdk b/external/linphone-sdk index c8d895cb1..3e3edec28 160000 --- a/external/linphone-sdk +++ b/external/linphone-sdk @@ -1 +1 @@ -Subproject commit c8d895cb162e0d6782d339e2ea3950ba2b8ca3da +Subproject commit 3e3edec2889317585d5267d764885b2c25806aeb