From d21dad3611751c19522080ecef91a4b04df210c3 Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Tue, 20 Feb 2024 19:56:00 +0400 Subject: [PATCH] avoid calling actions update during the main menu calculation 2 In RemDev, the host updates menu groups directly without the enclosing `IdeMainMenuActionGroup` group. Its `ALWAYS_VISIBLE` setting is ignored. The solution is to use `ActionConfigurationCustomizer` that works both on client and host. Also, we now copy client properties from stub presentation to its action template presentation. See `Presentation.copyUnsetTemplateProperties`. GitOrigin-RevId: 153d76dc7a3d97e7355bad3236b7ca09b244d0e3 --- .../openapi/actionSystem/AnAction.java | 3 ++- .../openapi/actionSystem/Presentation.java | 21 +++++++++++++++---- .../openapi/actionSystem/ActionStub.java | 14 +++++-------- .../actionSystem/impl/ActionGroupStub.kt | 7 +++---- .../platform/ide/menu/IdeMenuBarHelper.kt | 20 +++++++++++------- .../src/META-INF/PlatformExtensions.xml | 1 + 6 files changed, 41 insertions(+), 25 deletions(-) diff --git a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java index 476f75ad04b5..3539a0db7473 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java @@ -120,7 +120,8 @@ public abstract class AnAction implements PossiblyDumbAware, ActionUpdateThreadA * @param icon the action's icon */ public AnAction(@Nullable @ActionText String text, @Nullable @ActionDescription String description, @Nullable Icon icon) { - this(() -> text, () -> description, icon); + this(text == null ? Presentation.NULL_STRING : () -> text, + description == null ? Presentation.NULL_STRING : () -> description, icon); } @ApiStatus.Experimental diff --git a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java index c08133b3ac9f..8fb40149b058 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java @@ -9,6 +9,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.util.SystemInfoRt; +import com.intellij.openapi.util.text.Strings; import com.intellij.openapi.util.text.TextWithMnemonic; import com.intellij.util.BitUtil; import com.intellij.util.SmartFMap; @@ -36,6 +37,7 @@ public final class Presentation implements Cloneable { private static final Logger LOG = Logger.getInstance(Presentation.class); public static final Supplier NULL_STRING = () -> null; + public static final Supplier NULL_TEXT_WITH_MNEMONIC = () -> null; // Property keys for the PropertyChangeListener API public static final @NonNls String PROP_TEXT = "text"; @@ -71,7 +73,7 @@ public final class Presentation implements Cloneable { private int myFlags = IS_ENABLED | IS_VISIBLE | IS_DISABLE_GROUP_IF_EMPTY; private @NotNull Supplier<@ActionDescription String> descriptionSupplier = NULL_STRING; - private @NotNull Supplier textWithMnemonicSupplier = () -> null; + private @NotNull Supplier textWithMnemonicSupplier = NULL_TEXT_WITH_MNEMONIC; private @NotNull SmartFMap myUserMap = SmartFMap.emptyMap(); private @Nullable Supplier icon; @@ -169,7 +171,10 @@ public final class Presentation implements Cloneable { public @NotNull Supplier getTextWithMnemonic(@NotNull Supplier<@Nls(capitalization = Nls.Capitalization.Title) String> text, boolean mayContainMnemonic) { - if (mayContainMnemonic) { + if (text == NULL_STRING) { + return NULL_TEXT_WITH_MNEMONIC; + } + else if (mayContainMnemonic) { return () -> { String s = text.get(); if (s == null) { @@ -283,10 +288,18 @@ public final class Presentation implements Cloneable { return icon; } - public void copyIconIfUnset(@NotNull Presentation other) { - if (icon == null && other.icon != null) { + @ApiStatus.Internal // do not expose + void copyUnsetTemplateProperties(@NotNull Presentation other) { + if (icon == null) { icon = other.icon; } + if (Strings.isEmpty(getText()) && Strings.isNotEmpty(other.getText())) { + textWithMnemonicSupplier = other.textWithMnemonicSupplier; + } + if (Strings.isEmpty(descriptionSupplier.get()) && Strings.isNotEmpty(other.descriptionSupplier.get())) { + descriptionSupplier = other.descriptionSupplier; + } + myUserMap = myUserMap.plusAll(other.myUserMap); } public void setIcon(@Nullable Icon icon) { diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java index b04f9f89b8b5..9360bad66de4 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionStub.java @@ -4,7 +4,6 @@ package com.intellij.openapi.actionSystem; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.project.ProjectType; -import com.intellij.openapi.util.text.Strings; import com.intellij.util.SmartList; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -105,13 +104,10 @@ public final class ActionStub extends AnAction implements ActionStubBase { } } - public static void copyTemplatePresentation(Presentation sourcePresentation, Presentation targetPresentation) { - targetPresentation.copyIconIfUnset(sourcePresentation); - if (Strings.isEmpty(targetPresentation.getText()) && sourcePresentation.getText() != null) { - targetPresentation.setTextWithMnemonic(sourcePresentation.getTextWithPossibleMnemonic()); - } - if (targetPresentation.getDescription() == null && sourcePresentation.getDescription() != null) { - targetPresentation.setDescription(sourcePresentation.getDescription()); - } + public static void copyTemplatePresentation(@NotNull Presentation sourcePresentation, + @NotNull Presentation targetPresentation) { + // Note: actions can update templatePresentation in constructor. + // We apply stub properties only after that. It could be confusing. + targetPresentation.copyUnsetTemplateProperties(sourcePresentation); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionGroupStub.kt b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionGroupStub.kt index 0bca24ae9627..c3c5006415b1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionGroupStub.kt +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionGroupStub.kt @@ -17,8 +17,11 @@ internal class ActionGroupStub(override val id: String, fun initGroup(target: ActionGroup, actionToId: Function) { ActionStub.copyTemplatePresentation(templatePresentation, target.templatePresentation) + if (popupDefinedInXml) target.isPopup = isPopup + copyActionTextOverrides(target) + target.isSearchable = isSearchable target.shortcutSet = shortcutSet val children = childActionsOrStubs if (children.isNotEmpty()) { @@ -29,9 +32,5 @@ internal class ActionGroupStub(override val id: String, target.addAction(action, Constraints.LAST, actionToId) } } - if (popupDefinedInXml) { - target.isPopup = isPopup - } - target.isSearchable = isSearchable } } diff --git a/platform/platform-impl/src/com/intellij/platform/ide/menu/IdeMenuBarHelper.kt b/platform/platform-impl/src/com/intellij/platform/ide/menu/IdeMenuBarHelper.kt index 3745b451d9cb..f0ce54eb6385 100644 --- a/platform/platform-impl/src/com/intellij/platform/ide/menu/IdeMenuBarHelper.kt +++ b/platform/platform-impl/src/com/intellij/platform/ide/menu/IdeMenuBarHelper.kt @@ -7,6 +7,7 @@ import com.intellij.ide.ui.UISettingsListener import com.intellij.ide.ui.customization.CustomActionsSchema import com.intellij.openapi.actionSystem.* import com.intellij.openapi.actionSystem.ex.ActionManagerEx +import com.intellij.openapi.actionSystem.ex.ActionRuntimeRegistrar import com.intellij.openapi.actionSystem.impl.* import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.EDT @@ -188,17 +189,22 @@ private suspend fun expandMainActionGroup(mainActionGroup: ActionGroup, @ApiStatus.Internal suspend fun IdeMainMenuActionGroup(): ActionGroup? { val group = CustomActionsSchema.getInstanceAsync().getCorrectedActionAsync(IdeActions.GROUP_MAIN_MENU) ?: return null - // enforce the "always-visible" flag for all main menu items - // without forcing everyone to employ custom groups in their plugin.xml files. return object : ActionGroupWrapper(group) { - override fun getChildren(e: AnActionEvent?): Array { - return super.getChildren(e).onEach { it.templatePresentation.putClientProperty(ActionMenu.ALWAYS_VISIBLE, true) } - } - override fun postProcessVisibleChildren(visibleChildren: List, updateSession: UpdateSession): List { return super.postProcessVisibleChildren(visibleChildren, updateSession) .filterIsInstance() } } -} \ No newline at end of file +} + +class IdeMainMenuActionCustomizer : ActionConfigurationCustomizer, ActionConfigurationCustomizer.LightCustomizeStrategy { + // enforce the "always-visible" flag for all main menu items + // without forcing everyone to employ custom groups in their plugin.xml files. + override suspend fun customize(actionRegistrar: ActionRuntimeRegistrar) { + val group = actionRegistrar.getActionOrStub(IdeActions.GROUP_MAIN_MENU) as? DefaultActionGroup + group?.childActionsOrStubs?.forEach { + it.templatePresentation.putClientProperty(ActionMenu.ALWAYS_VISIBLE, true) + } + } +} diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 7af793eec284..dd59f144acd6 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -1383,6 +1383,7 @@ +