From 9fbfcb7a07ad3908627db4e4a332639944335e47 Mon Sep 17 00:00:00 2001 From: Joseph Battelle Date: Mon, 1 Feb 2021 17:02:22 -0800 Subject: [PATCH 1/4] Per-window SourceViewPanel and Tab visitors In order to fix an issue with closing secondary windows with changed, inactive 'SourceViewPanel's we need a way to visit the tabs of just a specific Window. The current 'SecondaryAllowClose' logic that relies on checking 'mWidgetWindow' is broken. This commit just introduces the needed utility methods and refactors the all-window methods to use them. --- IDE/src/IDEApp.bf | 92 +++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index e2b03a36..f86ba07a 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -5560,30 +5560,32 @@ namespace IDE }); } - public void WithDocumentTabbedViews(delegate void(DarkTabbedView) func) - { - for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++) - { - var window = mWindows[windowIdx]; - var widgetWindow = window as WidgetWindow; - if (widgetWindow != null) - { - var darkDockingFrame = widgetWindow.mRootWidget as DarkDockingFrame; - if (widgetWindow == mMainWindow) - darkDockingFrame = mDockingFrame; + public void WithDocumentTabbedViewsOf(BFWindow window, delegate void(DarkTabbedView) func) + { + var widgetWindow = window as WidgetWindow; + if (widgetWindow != null) + { + var darkDockingFrame = widgetWindow.mRootWidget as DarkDockingFrame; + if (widgetWindow == mMainWindow) + darkDockingFrame = mDockingFrame; - if (darkDockingFrame != null) - { - darkDockingFrame.WithAllDockedWidgets(scope (dockedWidget) => - { - var tabbedView = dockedWidget as DarkTabbedView; - if (tabbedView != null) - func(tabbedView); - }); - } - } - } - } + if (darkDockingFrame != null) + { + darkDockingFrame.WithAllDockedWidgets(scope (dockedWidget) => + { + var tabbedView = dockedWidget as DarkTabbedView; + if (tabbedView != null) + func(tabbedView); + }); + } + } + } + + public void WithDocumentTabbedViews(delegate void(DarkTabbedView) func) + { + for (let window in mWindows) + WithDocumentTabbedViewsOf(window, func); + } public void EnsureDocumentArea() { @@ -5682,13 +5684,19 @@ namespace IDE return null; } - public void WithTabs(delegate void(TabbedView.TabButton) func) - { - WithDocumentTabbedViews(scope (documentTabbedView) => - { - documentTabbedView.WithTabs(func); - }); - } + public void WithTabsOf(BFWindow window, delegate void(TabbedView.TabButton) func) + { + WithDocumentTabbedViewsOf(window, scope (documentTabbedView) => + { + documentTabbedView.WithTabs(func); + }); + } + + public void WithTabs(delegate void(TabbedView.TabButton) func) + { + for (let window in mWindows) + WithTabsOf(window, func); + } public TabbedView.TabButton GetTab(Widget content) { @@ -5701,15 +5709,21 @@ namespace IDE return tab; } - public void WithSourceViewPanels(delegate void(SourceViewPanel) func) - { - WithTabs(scope (tab) => - { - var sourceViewPanel = tab.mContent as SourceViewPanel; - if (sourceViewPanel != null) - func(sourceViewPanel); - }); - } + public void WithSourceViewPanelsOf(BFWindow window, delegate void(SourceViewPanel) func) + { + WithTabsOf(window, scope (tab) => + { + var sourceViewPanel = tab.mContent as SourceViewPanel; + if (sourceViewPanel != null) + func(sourceViewPanel); + }); + } + + public void WithSourceViewPanels(delegate void(SourceViewPanel) func) + { + for (let window in mWindows) + WithSourceViewPanelsOf(window, func); + } TabbedView.TabButton SetupTab(TabbedView tabView, String name, float width, Widget content, bool ownsContent) // 2 { From 21f2edd8b0dbc00ce4e49d82be7c22eed072e42e Mon Sep 17 00:00:00 2001 From: Joseph Battelle Date: Mon, 1 Feb 2021 17:54:40 -0800 Subject: [PATCH 2/4] Fix IDE 'SecondaryAllowClose' on inactive tabs When closing secondary IDE Windows that have changes for inactive sourceViewPanels the current logic checks 'mWidgetWindow' against the window to decide if it should be included in the close query. Inactive tabs always have null mWidgetWindows so the changes are not seen. This fixes the 'Save' button case for inactive SourceViewPanels by using per-window visitors. --- IDE/src/IDEApp.bf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index f86ba07a..ca6b033b 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -1243,9 +1243,9 @@ namespace IDE List changedList = scope List(); defer ClearAndDeleteItems(changedList); - WithSourceViewPanels(scope (sourceViewPanel) => + WithSourceViewPanelsOf(window, scope (sourceViewPanel) => { - if ((sourceViewPanel.mWidgetWindow == window) && (sourceViewPanel.HasUnsavedChanges())) + if (sourceViewPanel.HasUnsavedChanges()) { var fileName = new String(); Path.GetFileName(sourceViewPanel.mFilePath, fileName); @@ -1260,9 +1260,9 @@ namespace IDE bool hadError = false; // We use a close delay after saving so the user can see we actually saved before closing down var _this = this; - WithSourceViewPanels(scope [&] (sourceViewPanel) => + WithSourceViewPanelsOf(window, scope [&] (sourceViewPanel) => { - if ((!hadError) && (sourceViewPanel.mWidgetWindow == window) && (sourceViewPanel.HasUnsavedChanges())) + if ((!hadError) && (sourceViewPanel.HasUnsavedChanges())) if (!_this.SaveFile(sourceViewPanel)) hadError = true; }); From 1f772f685e1cfb2fa5600b4e20432c1953cec889 Mon Sep 17 00:00:00 2001 From: Joseph Battelle Date: Mon, 1 Feb 2021 18:20:12 -0800 Subject: [PATCH 3/4] Fix 'Dont Save' on Secondary Windows When you select 'Dont Save' on the close dialog of secondary windows, the projectSource is left with changes and no SourceViewPanel, and then VerifyModifiedBuffers will fail. This fix uses refactored code from CloseDocument, 'RevertSourceViewPanel', to revert changes on close. --- IDE/src/IDEApp.bf | 115 +++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 53 deletions(-) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index ca6b033b..ef58e26a 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -1273,6 +1273,12 @@ namespace IDE }); aDialog.AddButton("Don't Save", new (evt) => { + var _this = this; + WithSourceViewPanelsOf(window, scope [&] (sourceViewPanel) => + { + if (sourceViewPanel.HasUnsavedChanges()) + _this.RevertSourceViewPanel(sourceViewPanel); + }); mMainWindow.SetForeground(); window.Close(true); }); @@ -6477,6 +6483,61 @@ namespace IDE aDialog.PopupWindow(mMainWindow); } + void RevertSourceViewPanel(SourceViewPanel sourceViewPanel) + { + // When we close a Beef file that has modified text, we need to revert by + // reparsing from the actual source file + if (sourceViewPanel.mIsBeefSource) + { + mBfResolveHelper.DeferReparse(sourceViewPanel.mFilePath, null); + //mBfResolveHelper.DeferRefreshVisibleViews(null); + } + + var projectSource = sourceViewPanel.mProjectSource; + if (projectSource != null) + { + var editData = GetEditData(projectSource, true); + if (editData != null) + { + var editWidgetContent = editData.mEditWidget.mEditWidgetContent; + + //TODO: Verify this, once we have multiple panes allowed within a single SourceViewContent + if (editWidgetContent.mData.mUsers.Count == 1) // Is last view of data... + { + if ((editData != null) && (editData.mHadRefusedFileChange)) + { + // If we didn't take an external file change then closing the file means we want to revert + // our data to the version on disk + sourceViewPanel.Reload(); + } + else + { + // Undo until we either get to whatever the last saved state was, or until we + // get to a global action like renaming a symbol - we need to leave those + // so the global undo actually works if invoked from another file + while (editData.HasTextChanged()) + { + var nextUndoAction = editWidgetContent.mData.mUndoManager.GetLastUndoAction(); + if (nextUndoAction == null) + break; + if (nextUndoAction is UndoBatchEnd) + { + var undoBatchEnd = (UndoBatchEnd)nextUndoAction; + if (undoBatchEnd.Name.StartsWith("#")) + { + break; + } + } + editWidgetContent.mData.mUndoManager.Undo(); + } + + editWidgetContent.mData.mTextIdData.Prepare(); + } + } + } + } + } + public void CloseDocument(Widget documentPanel) { bool hasFocus = false; @@ -6492,59 +6553,7 @@ namespace IDE hasFocus = sourceViewPanel.mEditWidget.mHasFocus;*/ if ((sourceViewPanel != null) && (sourceViewPanel.HasUnsavedChanges())) - { - // When we close a Beef file that has modified text, we need to revert by - // reparsing from the actual source file - if (sourceViewPanel.mIsBeefSource) - { - mBfResolveHelper.DeferReparse(sourceViewPanel.mFilePath, null); - //mBfResolveHelper.DeferRefreshVisibleViews(null); - } - - var projectSource = sourceViewPanel.mProjectSource; - if (projectSource != null) - { - var editData = GetEditData(projectSource, true); - if (editData != null) - { - var editWidgetContent = editData.mEditWidget.mEditWidgetContent; - - //TODO: Verify this, once we have multiple panes allowed within a single SourceViewContent - if (editWidgetContent.mData.mUsers.Count == 1) // Is last view of data... - { - if ((editData != null) && (editData.mHadRefusedFileChange)) - { - // If we didn't take an external file change then closing the file means we want to revert - // our data to the version on disk - sourceViewPanel.Reload(); - } - else - { - // Undo until we either get to whatever the last saved state was, or until we - // get to a global action like renaming a symbol - we need to leave those - // so the global undo actually works if invoked from another file - while (editData.HasTextChanged()) - { - var nextUndoAction = editWidgetContent.mData.mUndoManager.GetLastUndoAction(); - if (nextUndoAction == null) - break; - if (nextUndoAction is UndoBatchEnd) - { - var undoBatchEnd = (UndoBatchEnd)nextUndoAction; - if (undoBatchEnd.Name.StartsWith("#")) - { - break; - } - } - editWidgetContent.mData.mUndoManager.Undo(); - } - - editWidgetContent.mData.mTextIdData.Prepare(); - } - } - } - } - } + RevertSourceViewPanel(sourceViewPanel); DarkTabbedView tabbedView = null; DarkTabbedView.DarkTabButton tabButton = null; From dab68d045a87d5679e141f05bc60b083bb71e3b7 Mon Sep 17 00:00:00 2001 From: Joseph Battelle Date: Mon, 1 Feb 2021 23:44:06 -0800 Subject: [PATCH 4/4] Close tabs of Secondary Window on window close Prior to this change, for secondary windows, documents were in an inconsistent state when the window was closed. They also failed to re-open. --- IDE/src/IDEApp.bf | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index ef58e26a..beeda77b 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -1241,6 +1241,19 @@ namespace IDE if (mRunningTestScript) return true; + void CloseTabs() + { + WithDocumentTabbedViewsOf(window, scope (tabbedView) => { + tabbedView.CloseTabs(false, true); + }); + } + + void CloseWindow() + { + mMainWindow.SetForeground(); + window.Close(true); + } + List changedList = scope List(); defer ClearAndDeleteItems(changedList); WithSourceViewPanelsOf(window, scope (sourceViewPanel) => @@ -1252,8 +1265,11 @@ namespace IDE changedList.Add(fileName); } }); - if (changedList.Count == 0) + + if (changedList.Count == 0) { + CloseTabs(); return true; + } var aDialog = QuerySaveFiles(changedList, (WidgetWindow)window); aDialog.mDefaultButton = aDialog.AddButton("Save", new (evt) => { @@ -1268,8 +1284,8 @@ namespace IDE }); if (hadError) return; - mMainWindow.SetForeground(); - window.Close(true); + CloseTabs(); + CloseWindow(); }); aDialog.AddButton("Don't Save", new (evt) => { @@ -1279,8 +1295,8 @@ namespace IDE if (sourceViewPanel.HasUnsavedChanges()) _this.RevertSourceViewPanel(sourceViewPanel); }); - mMainWindow.SetForeground(); - window.Close(true); + CloseTabs(); + CloseWindow(); }); aDialog.mEscButton = aDialog.AddButton("Cancel");