From ee1e6ca50821ace4323a0838452dda59b1a352a2 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 23 Oct 2020 13:02:09 +0200 Subject: [PATCH] EditorManager: Avoid changing the editor in between when closing editors Do not call setCurrentEditor(nullptr) since this sends a currentEditorChanged signal which claims that no editor is active, which can lead to unnecessary updates. In the specific case, the git branch view was updating with the project directory as a new repository, and then, after the actual new current editor was set updated again with the repository of the editor. This can be needlessly expensive if the previous and new editor are in the same repository, but the project repository is something else, like when having the Qt Creator super-repository open, or when editing files unrelated to the project. Change-Id: I515c3c40a86bfa2713ff13460ddfda974bc08c59 Reviewed-by: David Schulz --- .../editormanager/editormanager.cpp | 111 +++++++++++------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 12ae70bf033..36c1ae3634d 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -1650,62 +1650,91 @@ bool EditorManagerPrivate::closeEditors(const QList &editors, CloseFla if (acceptedEditors.isEmpty()) return false; - QList closedViews; - EditorView *focusView = nullptr; - - // remove the editors - foreach (IEditor *editor, acceptedEditors) { - emit m_instance->editorAboutToClose(editor); - if (!editor->document()->filePath().isEmpty() - && !editor->document()->isTemporary()) { + // save editor states + for (IEditor *editor : qAsConst(acceptedEditors)) { + if (!editor->document()->filePath().isEmpty() && !editor->document()->isTemporary()) { QByteArray state = editor->saveState(); if (!state.isEmpty()) d->m_editorStates.insert(editor->document()->filePath().toString(), QVariant(state)); } + } + EditorView *focusView = nullptr; + + // Remove accepted editors from document model/manager and context list, + // and sort them per view, so we can remove them from views in an orderly + // manner. + QMultiHash editorsPerView; + for (IEditor *editor : qAsConst(acceptedEditors)) { + emit m_instance->editorAboutToClose(editor); removeEditor(editor, flag != CloseFlag::Suspend); if (EditorView *view = viewForEditor(editor)) { - if (QApplication::focusWidget() && QApplication::focusWidget() == editor->widget()->focusWidget()) + editorsPerView.insert(view, editor); + if (QApplication::focusWidget() + && QApplication::focusWidget() == editor->widget()->focusWidget()) { focusView = view; - if (editor == view->currentEditor()) - closedViews += view; - if (d->m_currentEditor == editor) { - // avoid having a current editor without view - setCurrentView(view); - setCurrentEditor(nullptr); } - view->removeEditor(editor); } } + QTC_CHECK(!focusView || focusView == currentView); - // TODO doesn't work as expected with multiple areas in main window and some other cases - // instead each view should have its own file history and handle solely themselves - // which editor is shown if their current editor closes - EditorView *forceViewToShowEditor = nullptr; - if (!closedViews.isEmpty() && EditorManager::visibleEditors().isEmpty()) { - if (closedViews.contains(currentView)) - forceViewToShowEditor = currentView; - else - forceViewToShowEditor = closedViews.first(); - } - foreach (EditorView *view, closedViews) { - IEditor *newCurrent = view->currentEditor(); - if (!newCurrent && forceViewToShowEditor == view) - newCurrent = pickUnusedEditor(); - if (newCurrent) { - activateEditor(view, newCurrent, EditorManager::DoNotChangeCurrentEditor); - } else if (forceViewToShowEditor == view) { - DocumentModel::Entry *entry = DocumentModelPrivate::firstSuspendedEntry(); - if (entry) { - activateEditorForEntry(view, entry, EditorManager::DoNotChangeCurrentEditor); - } else { // no "suspended" ones, so any entry left should have a document - const QList documents = DocumentModel::entries(); - if (!documents.isEmpty()) { - if (IDocument *document = documents.last()->document) { - activateEditorForDocument(view, document, EditorManager::DoNotChangeCurrentEditor); + // Go through views, remove the editors from them. + // Sort such that views for which the current editor is closed come last, + // and if the global current view is one of them, that comes very last. + // When handling the last view in the list we handle the case where all + // visible editors are closed, and we need to e.g. revive an invisible or + // a suspended editor + QList views = editorsPerView.keys(); + Utils::sort(views, [editorsPerView, currentView](EditorView *a, EditorView *b) { + if (a == b) + return false; + const bool aHasCurrent = editorsPerView.values(a).contains(a->currentEditor()); + const bool bHasCurrent = editorsPerView.values(b).contains(b->currentEditor()); + const bool aHasGlobalCurrent = (a == currentView && aHasCurrent); + const bool bHasGlobalCurrent = (b == currentView && bHasCurrent); + if (bHasGlobalCurrent && !aHasGlobalCurrent) + return true; + if (bHasCurrent && !aHasCurrent) + return true; + return false; + }); + for (EditorView *view : qAsConst(views)) { + QList editors = editorsPerView.values(view); + // handle current editor in view last + IEditor *viewCurrentEditor = view->currentEditor(); + if (editors.contains(viewCurrentEditor) && editors.last() != viewCurrentEditor) { + editors.removeAll(viewCurrentEditor); + editors.append(viewCurrentEditor); + } + for (IEditor *editor : qAsConst(editors)) { + if (editor == viewCurrentEditor && view == views.last()) { + // Avoid removing the globally current editor from its view, + // set a new current editor before. + const EditorManager::OpenEditorFlags flags = view != currentView + ? EditorManager::DoNotChangeCurrentEditor + : EditorManager::NoFlags; + const QList viewEditors = view->editors(); + IEditor *newCurrent = viewEditors.size() > 1 ? viewEditors.at(viewEditors.size() - 2) + : nullptr; + if (!newCurrent) + newCurrent = pickUnusedEditor(); + if (newCurrent) { + activateEditor(view, newCurrent, flags); + } else { + DocumentModel::Entry *entry = DocumentModelPrivate::firstSuspendedEntry(); + if (entry) { + activateEditorForEntry(view, entry, flags); + } else { // no "suspended" ones, so any entry left should have a document + const QList documents = DocumentModel::entries(); + if (!documents.isEmpty()) { + if (IDocument *document = documents.last()->document) { + activateEditorForDocument(view, document, flags); + } + } } } } + view->removeEditor(editor); } }