mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 11:28:07 +00:00
Rework timelines to avoid GUI crashes when changing conversation.
This commit is contained in:
parent
f894631885
commit
bf9c76a02c
10 changed files with 133 additions and 48 deletions
|
|
@ -53,8 +53,32 @@ TimelineListModel::TimelineListModel (QObject *parent) : ProxyListModel(parent)
|
|||
updateTimelines ();
|
||||
}
|
||||
|
||||
TimelineListModel::TimelineListModel(const TimelineListModel* model){
|
||||
mSelectedCount = model->mSelectedCount;
|
||||
CoreHandlers* coreHandlers= CoreManager::getInstance()->getHandlers().get();
|
||||
connect(coreHandlers, &CoreHandlers::chatRoomStateChanged, this, &TimelineListModel::onChatRoomStateChanged);
|
||||
connect(coreHandlers, &CoreHandlers::messagesReceived, this, &TimelineListModel::update);
|
||||
connect(coreHandlers, &CoreHandlers::messagesReceived, this, &TimelineListModel::updated);
|
||||
|
||||
QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &TimelineListModel::onCallStateChanged);
|
||||
QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &TimelineListModel::onCallCreated);
|
||||
|
||||
connect(CoreManager::getInstance()->getSettingsModel(), &SettingsModel::hideEmptyChatRoomsChanged, this, &TimelineListModel::update);
|
||||
connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultRegistrationChanged, this, &TimelineListModel::update);
|
||||
for(auto item : model->mList) {
|
||||
auto newItem = qobject_cast<TimelineModel*>(item)->clone();
|
||||
connect(newItem.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
|
||||
connect(newItem->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
|
||||
mList << newItem;
|
||||
}
|
||||
}
|
||||
|
||||
TimelineListModel::~TimelineListModel(){
|
||||
}
|
||||
|
||||
TimelineListModel* TimelineListModel::clone() const{
|
||||
return new TimelineListModel(this);
|
||||
}
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void TimelineListModel::reset(){
|
||||
|
|
@ -109,7 +133,7 @@ QSharedPointer<TimelineModel> TimelineListModel::getTimeline(std::shared_ptr<lin
|
|||
}
|
||||
}
|
||||
if(create){
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(chatRoom);
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(this, chatRoom);
|
||||
if(model){
|
||||
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
|
||||
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
|
||||
|
|
@ -157,7 +181,7 @@ QSharedPointer<ChatRoomModel> TimelineListModel::getChatRoomModel(std::shared_pt
|
|||
return model;
|
||||
}
|
||||
if(create){
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(chatRoom);
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(this, chatRoom);
|
||||
if(model){
|
||||
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
|
||||
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
|
||||
|
|
@ -198,8 +222,13 @@ void TimelineListModel::onSelectedHasChanged(bool selected){
|
|||
}else
|
||||
setSelectedCount(mSelectedCount+1);
|
||||
emit selectedChanged(qobject_cast<TimelineModel*>(sender()));
|
||||
} else
|
||||
} else {
|
||||
if( this == CoreManager::getInstance()->getTimelineListModel()) {// Clean memory only if the selection is about the main list.
|
||||
auto timeline = qobject_cast<TimelineModel*>(sender());
|
||||
timeline->getChatRoomModel()->resetData();// Cleanup leaving chat room
|
||||
}
|
||||
setSelectedCount(mSelectedCount-1);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineListModel::updateTimelines () {
|
||||
|
|
@ -252,7 +281,7 @@ void TimelineListModel::updateTimelines () {
|
|||
auto haveTimeline = getTimeline(dbChatRoom, false);
|
||||
if(!haveTimeline && dbChatRoom){// Create a new Timeline if needed
|
||||
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(dbChatRoom, callLogs);
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(this, dbChatRoom, callLogs);
|
||||
if( model){
|
||||
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
|
||||
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
|
||||
|
|
@ -302,7 +331,7 @@ void TimelineListModel::select(ChatRoomModel * chatRoomModel){
|
|||
void TimelineListModel::onChatRoomStateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,linphone::ChatRoom::State state){
|
||||
if( state == linphone::ChatRoom::State::Created
|
||||
&& !getTimeline(chatRoom, false)){// Create a new Timeline if needed
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(chatRoom);
|
||||
QSharedPointer<TimelineModel> model = TimelineModel::create(this, chatRoom);
|
||||
if(model){
|
||||
connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool)));
|
||||
connect(model->getChatRoomModel(), &ChatRoomModel::allEntriesRemoved, this, &TimelineListModel::removeChatRoomModel);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ public:
|
|||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
|
||||
TimelineListModel (QObject *parent = Q_NULLPTR);
|
||||
TimelineListModel(const TimelineListModel* model);
|
||||
virtual ~TimelineListModel();
|
||||
TimelineListModel * clone() const;
|
||||
void reset();
|
||||
void selectAll(const bool& selected);
|
||||
TimelineModel * getAt(const int& index);
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ void TimelineModel::connectTo(ChatRoomListener * listener){
|
|||
}
|
||||
|
||||
// =============================================================================
|
||||
QSharedPointer<TimelineModel> TimelineModel::create(std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs, QObject *parent){
|
||||
if((!chatRoom || chatRoom->getState() != linphone::ChatRoom::State::Deleted) && (!CoreManager::getInstance()->getTimelineListModel() || !CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, false)) ) {
|
||||
QSharedPointer<TimelineModel> TimelineModel::create(TimelineListModel * mainList, std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs, QObject *parent){
|
||||
if((!chatRoom || chatRoom->getState() != linphone::ChatRoom::State::Deleted) && (!mainList || !mainList->getTimeline(chatRoom, false)) ) {
|
||||
QSharedPointer<TimelineModel> model = QSharedPointer<TimelineModel>::create(chatRoom, parent);
|
||||
if(model && model->getChatRoomModel()){
|
||||
|
||||
|
|
@ -122,6 +122,25 @@ TimelineModel::TimelineModel (std::shared_ptr<linphone::ChatRoom> chatRoom, QObj
|
|||
mSelected = false;
|
||||
}
|
||||
|
||||
TimelineModel::TimelineModel(const TimelineModel * model){
|
||||
App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
|
||||
mChatRoomModel = model->mChatRoomModel;
|
||||
if( mChatRoomModel ){
|
||||
QObject::connect(this, &TimelineModel::selectedChanged, this, &TimelineModel::updateUnreadCount);
|
||||
QObject::connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &TimelineModel::onDefaultAccountChanged);
|
||||
}
|
||||
if(mChatRoomModel->getChatRoom()){
|
||||
mChatRoomListener = model->mChatRoomListener;
|
||||
connectTo(mChatRoomListener.get());
|
||||
mChatRoomModel->getChatRoom()->addListener(mChatRoomListener);
|
||||
}
|
||||
mSelected = model->mSelected;
|
||||
}
|
||||
|
||||
QSharedPointer<TimelineModel> TimelineModel::clone()const{
|
||||
return QSharedPointer<TimelineModel>::create(this);
|
||||
}
|
||||
|
||||
TimelineModel::~TimelineModel(){
|
||||
if( mChatRoomModel->getChatRoom())
|
||||
mChatRoomModel->getChatRoom()->removeListener(mChatRoomListener);
|
||||
|
|
@ -152,7 +171,7 @@ ChatRoomModel *TimelineModel::getChatRoomModel() const{
|
|||
}
|
||||
|
||||
void TimelineModel::setSelected(const bool& selected){
|
||||
if(mChatRoomModel && selected != mSelected){
|
||||
if(mChatRoomModel && (selected != mSelected || selected)){
|
||||
mSelected = selected;
|
||||
if(mSelected){
|
||||
qInfo() << "Chat room selected : Subject :" << mChatRoomModel->getSubject()
|
||||
|
|
@ -166,10 +185,7 @@ void TimelineModel::setSelected(const bool& selected){
|
|||
<< ", canHandleParticipants:"<< mChatRoomModel->canHandleParticipants()
|
||||
<< ", isReadOnly:" << mChatRoomModel->isReadOnly()
|
||||
<< ", state:" << mChatRoomModel->getState();
|
||||
QQmlEngine *engine = App::getInstance()->getEngine();
|
||||
engine->clearComponentCache();
|
||||
}else
|
||||
mChatRoomModel->resetData();// Cleanup leaving chat room
|
||||
}
|
||||
emit selectedChanged(mSelected);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,15 +33,19 @@
|
|||
|
||||
class ChatRoomModel;
|
||||
class ChatRoomListener;
|
||||
class TimelineListModel;
|
||||
|
||||
class TimelineModel : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QSharedPointer<TimelineModel> create(std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs = std::list<std::shared_ptr<linphone::CallLog>>(), QObject *parent = Q_NULLPTR);
|
||||
static QSharedPointer<TimelineModel> create(TimelineListModel * mainList, std::shared_ptr<linphone::ChatRoom> chatRoom, const std::list<std::shared_ptr<linphone::CallLog>>& callLogs = std::list<std::shared_ptr<linphone::CallLog>>(), QObject *parent = Q_NULLPTR);
|
||||
TimelineModel (std::shared_ptr<linphone::ChatRoom> chatRoom, QObject *parent = Q_NULLPTR);
|
||||
TimelineModel(const TimelineModel * model);
|
||||
virtual ~TimelineModel();
|
||||
|
||||
QSharedPointer<TimelineModel> clone() const;
|
||||
|
||||
Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged)
|
||||
Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress NOTIFY fullLocalAddressChanged)
|
||||
Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel CONSTANT)
|
||||
|
|
|
|||
|
|
@ -37,32 +37,14 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
TimelineProxyModel::TimelineProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
|
||||
CoreManager *coreManager = CoreManager::getInstance();
|
||||
AccountSettingsModel *accountSettingsModel = coreManager->getAccountSettingsModel();
|
||||
TimelineListModel * model = CoreManager::getInstance()->getTimelineListModel();
|
||||
|
||||
connect(model, SIGNAL(selectedCountChanged(int)), this, SIGNAL(selectedCountChanged(int)));
|
||||
connect(model, &TimelineListModel::updated, this, &TimelineProxyModel::invalidate);
|
||||
connect(model, &TimelineListModel::selectedChanged, this, &TimelineProxyModel::selectedChanged);
|
||||
connect(model, &TimelineListModel::countChanged, this, &TimelineProxyModel::countChanged);
|
||||
|
||||
QObject::connect(accountSettingsModel, &AccountSettingsModel::defaultAccountChanged, this, [this]() {
|
||||
qobject_cast<TimelineListModel*>(sourceModel())->update();
|
||||
invalidate();
|
||||
});
|
||||
QObject::connect(coreManager->getSipAddressesModel(), &SipAddressesModel::sipAddressReset, this, [this]() {
|
||||
qobject_cast<TimelineListModel*>(sourceModel())->reset();
|
||||
invalidate();// Invalidate and reload GUI if the model has been reset
|
||||
});
|
||||
|
||||
setSourceModel(model);
|
||||
sort(0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void TimelineProxyModel::unselectAll(){
|
||||
qobject_cast<TimelineListModel*>(sourceModel())->selectAll(false);
|
||||
if( sourceModel())
|
||||
qobject_cast<TimelineListModel*>(sourceModel())->selectAll(false);
|
||||
}
|
||||
|
||||
void TimelineProxyModel::setFilterFlags(const int& filterFlags){
|
||||
|
|
@ -79,9 +61,49 @@ void TimelineProxyModel::setFilterText(const QString& text){
|
|||
emit filterTextChanged();
|
||||
}
|
||||
}
|
||||
|
||||
TimelineProxyModel::TimelineListSource TimelineProxyModel::getListSource() const{
|
||||
return mListSource;
|
||||
}
|
||||
|
||||
void TimelineProxyModel::setListSource(const TimelineListSource& source){
|
||||
if(source != mListSource) {
|
||||
TimelineListModel * model = nullptr;
|
||||
if( source != Undefined){
|
||||
CoreManager *coreManager = CoreManager::getInstance();
|
||||
AccountSettingsModel *accountSettingsModel = coreManager->getAccountSettingsModel();
|
||||
model = source == Main ? CoreManager::getInstance()->getTimelineListModel() : CoreManager::getInstance()->getTimelineListModel()->clone();
|
||||
|
||||
connect(model, SIGNAL(selectedCountChanged(int)), this, SIGNAL(selectedCountChanged(int)));
|
||||
connect(model, &TimelineListModel::updated, this, &TimelineProxyModel::invalidate);
|
||||
connect(model, &TimelineListModel::selectedChanged, this, &TimelineProxyModel::selectedChanged);
|
||||
connect(model, &TimelineListModel::countChanged, this, &TimelineProxyModel::countChanged);
|
||||
|
||||
QObject::connect(accountSettingsModel, &AccountSettingsModel::defaultAccountChanged, this, [this]() {
|
||||
qobject_cast<TimelineListModel*>(sourceModel())->update();
|
||||
invalidate();
|
||||
});
|
||||
QObject::connect(coreManager->getSipAddressesModel(), &SipAddressesModel::sipAddressReset, this, [this]() {
|
||||
qobject_cast<TimelineListModel*>(sourceModel())->reset();
|
||||
invalidate();// Invalidate and reload GUI if the model has been reset
|
||||
});
|
||||
}
|
||||
|
||||
if( mListSource != Main && sourceModel()){
|
||||
sourceModel()->deleteLater();
|
||||
}
|
||||
setSourceModel(model);
|
||||
sort(0);
|
||||
mListSource = source;
|
||||
emit listSourceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool TimelineProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const {
|
||||
if(!sourceModel())
|
||||
return false;
|
||||
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
auto timeline = sourceModel()->data(index).value<TimelineModel*>();
|
||||
if(!timeline || !timeline->getChatRoomModel() || timeline->getChatRoomModel()->getState() == (int)linphone::ChatRoom::State::Deleted)
|
||||
|
|
@ -116,6 +138,8 @@ bool TimelineProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sou
|
|||
}
|
||||
|
||||
bool TimelineProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
|
||||
if( !sourceModel())
|
||||
return false;
|
||||
const TimelineModel* a = sourceModel()->data(left).value<TimelineModel*>();
|
||||
const TimelineModel* b = sourceModel()->data(right).value<TimelineModel*>();
|
||||
bool aHaveUnread = a->getChatRoomModel()->getAllUnreadCount() > 0;
|
||||
|
|
|
|||
|
|
@ -45,8 +45,17 @@ public:
|
|||
};
|
||||
Q_ENUM(TimelineFilter)
|
||||
|
||||
enum TimelineListSource{
|
||||
Undefined,
|
||||
Main, // Timeline list comes from the singleton stored in CoreManager.
|
||||
Copy // Timeline list is created from Main but have no attach to the main list (aside of root items).
|
||||
};
|
||||
Q_ENUM(TimelineListSource)
|
||||
|
||||
TimelineProxyModel (QObject *parent = Q_NULLPTR);
|
||||
|
||||
Q_PROPERTY(TimelineListSource listSource READ getListSource WRITE setListSource NOTIFY listSourceChanged)
|
||||
|
||||
Q_PROPERTY(int filterFlags MEMBER mFilterFlags WRITE setFilterFlags NOTIFY filterFlagsChanged)
|
||||
Q_PROPERTY(QString filterText MEMBER mFilterText WRITE setFilterText NOTIFY filterTextChanged)
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
|
|
@ -55,12 +64,16 @@ public:
|
|||
Q_INVOKABLE void setFilterFlags(const int& filterFlags);
|
||||
Q_INVOKABLE void setFilterText(const QString& text);
|
||||
|
||||
TimelineListSource getListSource() const;
|
||||
void setListSource(const TimelineListSource& source);
|
||||
|
||||
signals:
|
||||
void countChanged();
|
||||
void selectedCountChanged(int selectedCount);
|
||||
void selectedChanged(TimelineModel * timelineModel);
|
||||
void filterFlagsChanged();
|
||||
void filterTextChanged();
|
||||
void listSourceChanged();
|
||||
|
||||
protected:
|
||||
|
||||
|
|
@ -74,6 +87,7 @@ protected:
|
|||
private:
|
||||
int mFilterFlags = 0;
|
||||
QString mFilterText;
|
||||
TimelineListSource mListSource = Undefined;
|
||||
};
|
||||
|
||||
#endif // TIMELINE_PROXY_MODEL_H_
|
||||
|
|
|
|||
|
|
@ -90,11 +90,12 @@ DialogPlus {
|
|||
Timeline {
|
||||
id: timeline
|
||||
showHistoryButton: false
|
||||
updateSelectionModels: false
|
||||
anchors.fill: parent
|
||||
model: TimelineProxyModel{}
|
||||
onEntryClicked:{
|
||||
if( entry ) {
|
||||
model: TimelineProxyModel{
|
||||
listSource: TimelineProxyModel.Copy
|
||||
}
|
||||
onEntrySelected:{
|
||||
if( entry) {
|
||||
mainItem.chatRoomSelectedCallback(entry.chatRoomModel)
|
||||
exit(1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,12 @@ Rectangle {
|
|||
property alias model: view.model
|
||||
property string _selectedSipAddress
|
||||
property bool showHistoryButton : CoreManager.callLogsCount
|
||||
property bool updateSelectionModels : true
|
||||
property bool isFilterVisible: searchView.visible || showFilterView
|
||||
property bool showFiltersButtons: view.count > 0 || timeline.isFilterVisible || timeline.model.filterFlags > 0
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
signal entrySelected (TimelineModel entry)
|
||||
signal entryClicked(TimelineModel entry)
|
||||
signal showHistoryRequest()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -52,7 +50,7 @@ Rectangle {
|
|||
timeline.entrySelected('')
|
||||
}
|
||||
}
|
||||
onSelectedChanged : if(timelineModel && timeline.updateSelectionModels) timeline.entrySelected(timelineModel)
|
||||
onSelectedChanged : if(timelineModel) timeline.entrySelected(timelineModel)
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// Legend.
|
||||
|
|
@ -358,7 +356,6 @@ Rectangle {
|
|||
|
||||
ScrollableListView {
|
||||
id: view
|
||||
property alias updateSelectionModels: timeline.updateSelectionModels
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
currentIndex: -1
|
||||
|
|
@ -370,8 +367,7 @@ Rectangle {
|
|||
Connections{
|
||||
target: $modelData
|
||||
onSelectedChanged:{
|
||||
gc()
|
||||
if(view.updateSelectionModels && selected) {
|
||||
if(selected) {
|
||||
view.currentIndex = index;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,10 +90,7 @@ Item {
|
|||
preventStealing: false
|
||||
onClicked: {
|
||||
if(mouse.button == Qt.LeftButton){
|
||||
timeline.entryClicked(mainItem.timelineModel)
|
||||
if(view.updateSelectionModels)
|
||||
mainItem.timelineModel.selected = true
|
||||
view.currentIndex = mainItem.modelIndex;
|
||||
mainItem.timelineModel.selected = true
|
||||
}else{
|
||||
mainItem.optionsToggled = !mainItem.optionsToggled
|
||||
}
|
||||
|
|
|
|||
|
|
@ -357,7 +357,9 @@ ApplicationWindow {
|
|||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
model: TimelineProxyModel{}
|
||||
model: TimelineProxyModel{
|
||||
listSource: TimelineProxyModel.Main
|
||||
}
|
||||
|
||||
onEntrySelected:{
|
||||
if( entry ) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue