You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by eb...@apache.org on 2022/12/16 12:20:26 UTC

[netbeans] branch master updated: Have "Close All" and friends honor "Cancel" in save prompt dialogs

This is an automated email from the ASF dual-hosted git repository.

ebakke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 9f212e884d Have "Close All" and friends honor "Cancel" in save prompt dialogs
9f212e884d is described below

commit 9f212e884d314ae221e25fcc721aaeb7dcad40c6
Author: Eirik Bakke <eb...@ultorg.com>
AuthorDate: Sat Nov 5 16:20:12 2022 -0400

    Have "Close All" and friends honor "Cancel" in save prompt dialogs
    
    When invoking "Close All" on a tab, some tabs may pop up confirmation dialogs asking whether their work should be saved or not. Pressing the "Cancel" button in any dialog, as opposed to "Save" or "Discard", should stop showing further dialogs, and stop the overall operation. This was not previously done. (In some cases, a bunch of dialogs would be shown on top of each other simultaneously, creating visual artifacts.)
    
    This change was made to the Close All and Close Other actions, as well as for the newly introduced "Close whole documents list at once" button in the document list dropdown (PR #4792).
    
    In a previous discussion, it was suggested to make the whole operation atomic (i.e. don't close any tabs at all if the user presses Cancel after several tabs have already been iterated), but this is not possible given the existing APIs. (We don't know if Cancel was pressed until TopComponent.close() was called.) Moreover, the behavior implemented in this PR is more standard in any case; it matches the behavior in e.g. Photoshop and VSCode.
    
    It was also suggested to implement similar behavior in the save prompt that shows up when exiting the IDE, but this is a rather different operation that does not actually close any tabs (they remain "open" and persisted for the next time the IDE starts). I'm not quite sure how to improve it; it might require more discussion. See the "Savable" API and the o.n.core.ExitDialog class.
    
    I would also have liked to focus each TopComponent in turn when prompts to save come up, but this is a bit more work due to the various levels of abstractions involved. More work on this can be done later. See MultiViewFactory.resolveCloseOperation for a relevant starting point.
---
 .../core/multitabs/impl/DocumentSwitcherTable.java | 27 +++++++-
 .../netbeans/core/windows/actions/ActionUtils.java | 71 ++++++++--------------
 2 files changed, 50 insertions(+), 48 deletions(-)

diff --git a/platform/core.multitabs/src/org/netbeans/core/multitabs/impl/DocumentSwitcherTable.java b/platform/core.multitabs/src/org/netbeans/core/multitabs/impl/DocumentSwitcherTable.java
index 006a3afc8f..4ff3786b3a 100644
--- a/platform/core.multitabs/src/org/netbeans/core/multitabs/impl/DocumentSwitcherTable.java
+++ b/platform/core.multitabs/src/org/netbeans/core/multitabs/impl/DocumentSwitcherTable.java
@@ -49,6 +49,7 @@ import org.netbeans.swing.tabcontrol.TabData;
 import org.netbeans.swing.tabcontrol.TabbedContainer;
 import org.netbeans.swing.tabcontrol.event.TabActionEvent;
 import org.openide.awt.CloseButtonFactory;
+import org.openide.windows.TopComponent;
 
 /**
  * Slightly enhanced switcher table which adds close button to selected item
@@ -196,15 +197,35 @@ class DocumentSwitcherTable extends SwitcherTable {
         for ( TabData tab : tabs ) {  
             ProjectProxy projectForTab = projectSupport.getProjectForTab( tab );
             if (( project == null && projectForTab == null ) || ( projectForTab != null && projectForTab.equals( project ))) {
-                int tabIndex = controller.getTabModel().indexOf( tab );
-                TabActionEvent tae = new TabActionEvent( this, TabbedContainer.COMMAND_CLOSE, tabIndex );
-                controller.postActionEvent( tae );
+                Component tabComponent = tab.getComponent();
+                if (tabComponent instanceof TopComponent) {
+                    TopComponent curTC = (TopComponent) tabComponent;
+                    /* As in o.n.core.windows.actions.ActionUtils.closeAll(Iterable<TopComponent>).
+                    We have to be a little more general here since, theoretically, there could be
+                    tab components that do not extend from TopComponent. */
+                    if(!isClosingEnabled(curTC)) {
+                        continue;
+                    }
+                    curTC.putClientProperty("inCloseAll", Boolean.TRUE);
+                    if (!curTC.close()) {
+                        break;
+                    }
+                } else {
+                    int tabIndex = controller.getTabModel().indexOf( tab );
+                    TabActionEvent tae = new TabActionEvent( this, TabbedContainer.COMMAND_CLOSE, tabIndex );
+                    controller.postActionEvent( tae );
+                }
             } else {
                 numOfOtherTabs++;
             }
         }
         return numOfOtherTabs == 0;
     }
+
+    // Copied from o.n.core.windows.Switches.isClosingEnabled (in private package).
+    private static boolean isClosingEnabled( TopComponent tc ) {
+        return !Boolean.TRUE.equals(tc.getClientProperty(TopComponent.PROP_CLOSING_DISABLED));
+    }
     
     private JButton createCloseButton() {
         JButton res = CloseButtonFactory.createBigCloseButton();
diff --git a/platform/core.windows/src/org/netbeans/core/windows/actions/ActionUtils.java b/platform/core.windows/src/org/netbeans/core/windows/actions/ActionUtils.java
index e13aed3bb7..043b889edf 100644
--- a/platform/core.windows/src/org/netbeans/core/windows/actions/ActionUtils.java
+++ b/platform/core.windows/src/org/netbeans/core/windows/actions/ActionUtils.java
@@ -29,7 +29,6 @@ import org.netbeans.core.windows.*;
 import org.netbeans.core.windows.view.ui.slides.SlideController;
 import org.openide.DialogDisplayer;
 import org.openide.NotifyDescriptor;
-import org.openide.awt.Actions;
 import org.openide.awt.Mnemonics;
 import org.openide.cookies.SaveCookie;
 import org.openide.util.*;
@@ -467,30 +466,15 @@ public abstract class ActionUtils {
      * otherwise closes all documents in the system
      */
     public static void closeAllDocuments (boolean isContext) {
-        if (isContext) {
-            TopComponent activeTC = TopComponent.getRegistry().getActivated();
-            List<TopComponent> tcs = getOpened(activeTC);
-
-            closeAll( tcs.toArray(new TopComponent[tcs.size()]) );
-        } else {
-            TopComponent[] tcs = WindowManagerImpl.getInstance().getEditorTopComponents();
-            closeAll( tcs );
-        }
-    }
-
-    private static void closeAll( TopComponent[] tcs ) {
-        for( TopComponent tc: tcs ) {
-            if( !Switches.isClosingEnabled(tc) )
-                continue;
-            final TopComponent toBeClosed = tc;
-            SwingUtilities.invokeLater( new Runnable() {
-                @Override
-                public void run() {
-                    toBeClosed.putClientProperty("inCloseAll", Boolean.TRUE);
-                    toBeClosed.close();
-                }
-            });
-        }
+        /* Historically, the closeAll method wrapped calls to TopComponent.close() in an
+        invokeLater, so keep doing that. This is probably a good idea e.g. for cases where the
+        caller is a button press handler and where TopComponent.close() ends up showing a modal
+        dialog (which could perhaps delay the update of the button's visual state). */
+        SwingUtilities.invokeLater(() -> {
+            closeAll(isContext
+                ? getOpened(TopComponent.getRegistry().getActivated())
+                : Arrays.asList(WindowManagerImpl.getInstance().getEditorTopComponents()));
+        });
     }
 
     /** Closes all documents except given param, according to isContext flag
@@ -500,27 +484,24 @@ public abstract class ActionUtils {
      * given
      */
     public static void closeAllExcept (TopComponent tc, boolean isContext) {
-        if (isContext) {
-            List<TopComponent> tcs = getOpened(tc);
-
-            for(TopComponent curTC: tcs) {
-                if( !Switches.isClosingEnabled(curTC) )
-                    continue;
-                if (curTC != tc) {
-                    curTC.putClientProperty("inCloseAll", Boolean.TRUE);
-                    curTC.close();
-                }
-            }
-        } else {
-            TopComponent[] tcs = WindowManagerImpl.getInstance().getEditorTopComponents();
+        // See closeAllDocuments.
+        SwingUtilities.invokeLater(() -> {
+            List<TopComponent> tcs = new ArrayList<>(isContext
+                ? getOpened(tc)
+                : Arrays.asList(WindowManagerImpl.getInstance().getEditorTopComponents()));
+            tcs.remove(tc);
+            closeAll(tcs);
+        });
+    }
 
-            for(TopComponent curTC: tcs) {
-                if( !Switches.isClosingEnabled(curTC) )
-                    continue;
-                if (curTC != tc) {
-                    curTC.putClientProperty("inCloseAll", Boolean.TRUE);
-                    curTC.close();
-                }
+    private static void closeAll( Iterable<TopComponent> tcs ) {
+        for (TopComponent curTC : tcs) {
+            if( !Switches.isClosingEnabled(curTC) ) {
+                continue;
+            }
+            curTC.putClientProperty("inCloseAll", Boolean.TRUE);
+            if (!curTC.close()) {
+                break;
             }
         }
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists