diff --git a/platform/platform-api/resources/messages/IdeBundle.properties b/platform/platform-api/resources/messages/IdeBundle.properties index 914753372b38..a30fea9ce62b 100644 --- a/platform/platform-api/resources/messages/IdeBundle.properties +++ b/platform/platform-api/resources/messages/IdeBundle.properties @@ -1867,6 +1867,7 @@ notification.title.desktop.entry.created=Desktop entry created link.cancel=Cancel dialog.title.customize.0=Customize {0} button.back=Back +dialog.apply=Apply github.project.generator.version=&Version: github.project.generator.versions.not.loaded.error=Versions have not been loaded yet. diff --git a/platform/platform-impl/api-dump-unreviewed.txt b/platform/platform-impl/api-dump-unreviewed.txt index 834ac7864cbb..9b97addc3082 100644 --- a/platform/platform-impl/api-dump-unreviewed.txt +++ b/platform/platform-impl/api-dump-unreviewed.txt @@ -9335,6 +9335,7 @@ c:com.intellij.ide.ui.customization.CustomizableActionsPanel - p:getRestoreGroup():com.intellij.openapi.actionSystem.ActionGroup - isModified():Z - p:needExpandAll():Z +- p:onModified():V - p:patchActionsTreeCorrespondingToSchema(javax.swing.tree.DefaultMutableTreeNode):V - reset():V - resetToDefaults():V diff --git a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.java b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.java index 47397aa20e3a..2b7d75f52c9e 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizableActionsPanel.java @@ -94,6 +94,7 @@ public class CustomizableActionsPanel { ActionToolbarImpl addGroupToolbar = (ActionToolbarImpl)ActionManager.getInstance() .createActionToolbar(ActionPlaces.TOOLBAR, new DefaultActionGroup(new AddActionActionTreeSelectionAction()), true); + addGroupToolbar.setTargetComponent(myPanel); addGroupToolbar.setActionButtonBorder(new JBEmptyBorder(0)); addGroupToolbar.setBorder(new JBEmptyBorder(0)); container.add(addGroupToolbar, BorderLayout.WEST); @@ -168,6 +169,7 @@ public class CustomizableActionsPanel { private void addCustomizedAction(ActionUrl url) { mySelectedSchema.addAction(url); + onModified(); } private static boolean isMoveSupported(JTree tree, int dir) { @@ -262,8 +264,11 @@ public class CustomizableActionsPanel { } TreeUtil.selectPaths(myActionsTree, toTreePaths(root, selectedIds)); TreeUtil.ensureSelection(myActionsTree); + onModified(); } + protected void onModified() { } + private static List toActionIDs(List paths) { return ContainerUtil.map(paths, path -> getActionId((DefaultMutableTreeNode)path.getLastPathComponent())); } @@ -282,7 +287,13 @@ public class CustomizableActionsPanel { } public boolean isModified() { - CustomizationUtil.optimizeSchema(myActionsTree, mySelectedSchema); + return isModified(true); + } + + boolean isModified(boolean optimized) { + if (optimized) { + CustomizationUtil.optimizeSchema(myActionsTree, mySelectedSchema); + } return CustomActionsSchema.getInstance().isModified(mySelectedSchema); } @@ -292,6 +303,7 @@ public class CustomizableActionsPanel { mySelectedSchema.fillCorrectedActionGroups(root); } ((DefaultTreeModel)myActionsTree.getModel()).reload(); + onModified(); } private static final class TreePathStringFunction implements Function { @@ -1044,6 +1056,7 @@ public class CustomizableActionsPanel { final List treePaths = TreeUtil.collectExpandedPaths(myActionsTree); patchActionsTreeCorrespondingToSchema((DefaultMutableTreeNode)myActionsTree.getModel().getRoot()); restorePathsAfterTreeOptimization(treePaths); + onModified(); } @Override diff --git a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java index a7821698b3c4..4d586e1a106d 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/customization/CustomizationUtil.java @@ -23,6 +23,8 @@ import com.intellij.openapi.ui.JBPopupMenu; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.Strings; +import com.intellij.openapi.wm.IdeFrame; +import com.intellij.openapi.wm.WindowManager; import com.intellij.ui.ClientProperty; import com.intellij.ui.PopupHandler; import com.intellij.ui.PopupMenuListenerAdapter; @@ -46,11 +48,10 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.awt.*; +import java.awt.event.ActionEvent; import java.awt.event.MouseListener; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; @@ -575,13 +576,33 @@ public final class CustomizationUtil { } @Override - protected void doOKAction() { + protected Action @NotNull [] createActions() { + List actions = new ArrayList<>(Arrays.asList(super.createActions())); + Action applyAction = new DialogWrapperAction(IdeBundle.message("dialog.apply")) { + @Override + protected void doAction(ActionEvent e) { + apply(); + setEnabled(false); + } + }; + actions.add(applyAction); + panel.setApplyAction(applyAction); + + return actions.toArray(Action[]::new); + } + + private void apply() { try { panel.apply(); } catch (ConfigurationException ex) { LOG.error(ex); } + } + + @Override + protected void doOKAction() { + apply(); close(OK_EXIT_CODE); } @@ -606,12 +627,56 @@ public final class CustomizationUtil { private static final class ToolbarCustomizableActionsPanel extends CustomizableActionsPanel { private final @NotNull String myGroupID; private final @Nls @NotNull String myGroupName; + private @Nullable Action myApplyAction; private ToolbarCustomizableActionsPanel(@NotNull String groupID, @Nls @NotNull String groupName) { myGroupID = groupID; myGroupName = groupName; } + @Override + public void apply() throws ConfigurationException { + super.apply(); + updateActionToolbars(); + onModified(); + } + + private void setApplyAction(@Nullable Action applyAction) { + this.myApplyAction = applyAction; + if (applyAction != null) { + applyAction.setEnabled(isModified(false)); + } + } + + private void updateActionToolbars() { + HashSet editedChildrenSet = new HashSet<>(); + if (ActionManager.getInstance().getAction(myGroupID) instanceof ActionGroup group) { + editedChildrenSet.addAll(Arrays.stream(group.getChildren(null)).map(action -> { + return ActionManager.getInstance().getId(action); + }).toList()); + } + + for (IdeFrame frame: WindowManager.getInstance().getAllProjectFrames()) { + for (Component c : UIUtil.uiTraverser(frame.getComponent()).traverse()) { + if (c instanceof ActionToolbar toolbar) { + AnAction foundGroup = ActionUtil.getDelegateChainRootAction(toolbar.getActionGroup()); + + String foundId = ActionManager.getInstance().getId(foundGroup); + if (myGroupID.equals(foundId) || editedChildrenSet.contains(foundId)) { + toolbar.updateActionsAsync(); + } + } + } + } + } + + @Override + protected void onModified() { + if (myApplyAction != null) { + myApplyAction.setEnabled(CustomActionsSchema.getInstance().isModified(mySelectedSchema)); + } + } + @Override protected @NotNull ActionGroup getRestoreGroup() { return new DefaultActionGroup(new DumbAwareAction(IdeBundle.messagePointer("button.restore.last.state")) {