IDEA-163208: make main panels configurable

save layouts (with alternatives) in xml
use CustomActionsSchema to configure
This commit is contained in:
Artem Bochkarev
2018-05-13 17:48:14 +07:00
parent 17d76ada38
commit 15ab6ffae6
17 changed files with 322 additions and 349 deletions
@@ -332,4 +332,6 @@ public interface IdeActions {
@NonNls String ACTION_UPDATE_RUNNING_APPLICATION = "UpdateRunningApplication";
@NonNls String ACTION_BRACE_OR_QUOTE_OUT = "BraceOrQuoteOut";
@NonNls String GROUP_TOUCHBAR = "TouchBar";
}
@@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.wm.ex.WindowManagerEx;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.ui.mac.touchbar.TouchBarsManager;
import com.intellij.util.ImageLoader;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.JBImageIcon;
@@ -69,6 +70,8 @@ public class CustomActionsSchema implements PersistentStateComponent<Element> {
myIdToName.put(IdeActions.GROUP_J2EE_VIEW_POPUP, ActionsTreeUtil.J2EE_POPUP);
myIdToName.put(IdeActions.GROUP_NAVBAR_POPUP, "Navigation Bar");
myIdToName.put("NavBarToolBar", "Navigation Bar Toolbar");
if (TouchBarsManager.isTouchBarAvailable())
myIdToName.put(IdeActions.GROUP_TOUCHBAR, "Touch Bar");
ArrayList<Couple<String>> extList = ContainerUtil.newArrayList();
CustomizableActionGroupProvider.CustomizableActionGroupRegistrar registrar =
@@ -84,4 +84,6 @@ public class CustomisedActionGroup extends ActionGroup {
final AnAction[] children = getChildren(null);
return children.length > 0 ? children[0] : null;
}
public ActionGroup getOrigin() { return myGroup; }
}
@@ -1,18 +1,21 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.event.InputEvent;
import java.util.*;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class ProjectBarsStorage {
private static final Logger LOG = Logger.getInstance(ProjectBarsStorage.class);
public static final String GENERAL = "general";
public static final String DEBUGGER = "debugger";
public static final String EDITOR = "editor";
@@ -24,31 +27,50 @@ public class ProjectBarsStorage {
ProjectBarsStorage(@NotNull Project project) { myProject = project; }
BarContainer createBarContainer(@NotNull String type, Component component) {
ApplicationManager.getApplication().assertIsDispatchThread ();
BarContainer result;
@Nullable BarContainer createBarContainer(@NotNull String type, Component component) {
ApplicationManager.getApplication().assertIsDispatchThread();
final String barId;
final String touchBarName;
final boolean replaceEsc;
if (type.equals(GENERAL)) {
result = new SingleBarContainer(new TouchBarGeneral(myProject, component, "GLOBAL"));
barId = "Default";
touchBarName = "default_global";
replaceEsc = false;
} else if (type.equals(DEBUGGER)) {
MultiBarContainer container = new MultiBarContainer(new TouchBarDebugger(myProject, component));
final long mask = InputEvent.ALT_DOWN_MASK;
container.registerAltByKeyMask(mask, new TouchBarDebuggerAlt(myProject, component));
result = container;
barId = "Debugger";
touchBarName = "debugger";
replaceEsc = true;
} else if (type.equals(EDITOR)) {
MultiBarContainer container = new MultiBarContainer(new TouchBarGeneral(myProject, component, "EDITOR"));
container.registerAltByKeyMask(InputEvent.ALT_DOWN_MASK, new TouchBarEditorAlt(myProject, component));
container.registerAltByKeyMask(InputEvent.META_DOWN_MASK, new TouchBarEditorCmd(myProject, component));
container.registerAltByKeyMask(InputEvent.ALT_DOWN_MASK | InputEvent.META_DOWN_MASK, new TouchBarEditorCmdAlt(myProject, component));
container.registerAltByKeyMask(InputEvent.SHIFT_DOWN_MASK, new TouchBarEditorShift(myProject, component));
result = container;
} else
throw new RuntimeException("unknown context of project-touchbar: " + type);
barId = "Default";
touchBarName = "default_editor";
replaceEsc = false;
} else {
LOG.error("can't create touchbar, unknown context: " + type);
return null;
}
myBars.add(result);
final ActionGroup mainLayout = TouchBarActionBase.getCustomizedGroup(barId);
if (mainLayout == null) {
LOG.error("can't create touchbar because corresponding ActionGroup isn't defined, context: " + barId);
return null;
}
return result;
final MultiBarContainer container = new MultiBarContainer(new TouchBarActionBase(touchBarName, myProject, mainLayout, component, replaceEsc));
final Map<String, ActionGroup> alts = type.equals(GENERAL) ? null : TouchBarActionBase.getAltLayouts(mainLayout);
if (alts != null && !alts.isEmpty()) {
for (String modId: alts.keySet()) {
final long mask = _str2mask(modId);
if (mask == 0) {
// System.out.println("ERROR: zero mask for modId="+modId);
continue;
}
container.registerAltByKeyMask(mask, new TouchBarActionBase(touchBarName + "_" + modId, myProject, alts.get(modId), component, replaceEsc));
}
}
myBars.add(container);
return container;
}
void releaseAll() {
@@ -67,4 +89,25 @@ public class ProjectBarsStorage {
}
return result;
}
private static long _str2mask(@NotNull String modifierId) {
if (!modifierId.contains(".")) {
if (modifierId.equalsIgnoreCase("alt"))
return InputEvent.ALT_DOWN_MASK;
if (modifierId.equalsIgnoreCase("cmd"))
return InputEvent.META_DOWN_MASK;
if (modifierId.equalsIgnoreCase("shift"))
return InputEvent.SHIFT_DOWN_MASK;
return 0;
}
final String[] spl = modifierId.split("\\.");
if (spl == null)
return 0;
long mask = 0;
for (String sub: spl)
mask |= _str2mask(sub);
return mask;
}
}
@@ -8,7 +8,6 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.IconLoader;
import com.intellij.util.ui.EmptyIcon;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -19,9 +18,12 @@ import java.awt.event.KeyEvent;
import static java.awt.event.ComponentEvent.COMPONENT_FIRST;
public class TBItemAnActionButton extends TBItemButton {
private static final boolean LOG_ICON_ERRORS = System.getProperty("touchbar.log.icon.errors", "false").equals("true");
public static final int SHOWMODE_IMAGE_ONLY = 0;
public static final int SHOWMODE_TEXT_ONLY = 1;
public static final int SHOWMODE_IMAGE_TEXT = 2;
public static final int SHOWMODE_IMAGE_ONLY_IF_PRESENTED = 3;
private static final Logger LOG = Logger.getInstance(TBItemAnActionButton.class);
@@ -104,19 +106,19 @@ public class TBItemAnActionButton extends TBItemButton {
if (icon == null)
icon = IconLoader.getDisabledIcon(presentation.getIcon());
}
if (icon == null) {
if (icon == null && LOG_ICON_ERRORS)
LOG.error("can't get icon, action " + myActionId + ", presentation = " + _printPresentation(presentation));
icon = EmptyIcon.ICON_18;
}
}
final String text = myShowMode == SHOWMODE_IMAGE_ONLY ? null : presentation.getText();
boolean isSelected = false;
if (myAnAction instanceof ToggleAction) {
final Object selectedProp = presentation.getClientProperty(Toggleable.SELECTED_PROPERTY);
isSelected = selectedProp != null && selectedProp == Boolean.TRUE;
}
final boolean hideText = myShowMode == SHOWMODE_IMAGE_ONLY || (myShowMode == SHOWMODE_IMAGE_ONLY_IF_PRESENTED && icon != null);
final String text = hideText ? null : presentation.getText();
update(icon, text, isSelected, !presentation.isEnabled());
}
@@ -32,6 +32,8 @@ public class TBItemButton extends TBItem {
myFlags = buttonFlags;
}
void setWidth(int width) { _update(myIcon, myText, myAction, width, myFlags); }
void update(Icon icon, String text, NSTLibrary.Action action) { _update(icon, text, action, myWidth, myFlags); }
void update(Icon icon, String text) { _update(icon, text, myAction, myWidth, myFlags); }
void update(Icon icon) { _update(icon, myText, myAction, myWidth, myFlags); }
@@ -3,35 +3,55 @@ package com.intellij.ui.mac.touchbar;
import com.intellij.execution.ExecutionListener;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.ide.DataManager;
import com.intellij.ide.ui.customization.CustomActionsSchema;
import com.intellij.ide.ui.customization.CustomisedActionGroup;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.PresentationFactory;
import com.intellij.openapi.actionSystem.impl.Utils;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.ui.components.JBOptionButton;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
public class TouchBarActionBase extends TouchBarProjectBase implements ExecutionListener {
private static final Logger LOG = Logger.getInstance(TouchBarActionBase.class);
private static final String ourLargeSeparatorText = "type.big";
private static final String ourFlexibleSeparatorText = "type.flexible";
private static final int ourRunConfigurationPopoverWidth = 143;
private final PresentationFactory myPresentationFactory = new PresentationFactory();
private final TimerListener myTimerListener;
private final Component myComponent;
static {
_initExecutorsGroup();
}
public TouchBarActionBase(@NotNull String touchbarName, @NotNull Project project, Component component) { this(touchbarName, project, component, false); }
public TouchBarActionBase(@NotNull String touchbarName, @NotNull Project project, @NotNull ActionGroup customizedGroup, Component component, boolean replaceEsc) {
this(touchbarName, project, component, replaceEsc);
final String groupId = _getActionId(customizedGroup);
if (groupId == null) {
LOG.error("unregistered group: " + customizedGroup);
return;
}
addActionGroupButtons(customizedGroup, component, null, TBItemAnActionButton.SHOWMODE_IMAGE_ONLY_IF_PRESENTED, nodeId -> nodeId.contains(groupId + "_"));
}
public TouchBarActionBase(@NotNull String touchbarName, @NotNull Project project, Component component, boolean replaceEsc) {
super(touchbarName, project, replaceEsc);
@@ -92,16 +112,43 @@ public class TouchBarActionBase extends TouchBarProjectBase implements Execution
return butt;
}
public void addActionGroupButtons(ActionGroup actionGroup, JBOptionButton forCtx, ModalityState modality) {
final DataContext dctx = DataManager.getInstance().getDataContext(forCtx);
List<AnAction> visibleActions = ContainerUtil.newArrayListWithCapacity(10);
Utils.expandActionGroup(false, actionGroup, visibleActions, myPresentationFactory, dctx, ActionPlaces.UNKNOWN, ActionManager.getInstance());
for (AnAction act: visibleActions) {
if (act == null || act instanceof Separator)
continue;
public void addActionGroupButtons(ActionGroup actionGroup, Component forCtx, ModalityState modality, int showMode) { addActionGroupButtons(actionGroup, forCtx, modality, showMode, null); }
_addAnActionButton(act, false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, forCtx, modality);
}
public void addActionGroupButtons(ActionGroup actionGroup, Component forCtx, ModalityState modality, int showMode, INodeFilter filter) {
_traverse(actionGroup, new ILeafVisitor() {
private int mySeparatorCounter = 0;
@Override
public void visit(AnAction act) {
if (act instanceof Separator) {
final Separator sep = (Separator)act;
int increment = 1;
if (sep.getText() != null) {
if (sep.getText().equals(ourLargeSeparatorText)) increment = 2;
if (sep.getText().equals(ourFlexibleSeparatorText)) increment = 3;
}
mySeparatorCounter += increment;
return;
}
if (mySeparatorCounter > 0) {
if (mySeparatorCounter == 1) addSpacing(false);
else if (mySeparatorCounter == 2) addSpacing(true);
else addFlexibleSpacing();
mySeparatorCounter = 0;
}
final String actId = _getActionId(act);
// if (actId == null || actId.isEmpty()) System.out.println("unregistered action: " + act);
final boolean isRunConfigPopover = actId != null && actId.contains("RunConfiguration");
final int mode = isRunConfigPopover ? TBItemAnActionButton.SHOWMODE_IMAGE_TEXT : showMode;
final TBItemAnActionButton butt = _addAnActionButton(act, false, mode, forCtx, modality);
if (isRunConfigPopover)
butt.setWidth(ourRunConfigurationPopoverWidth);
}
}, filter);
}
@Override
@@ -145,6 +192,52 @@ public class TouchBarActionBase extends TouchBarProjectBase implements Execution
selectVisibleItemsToShow();
}
private static String _getActionId(AnAction act) { return ActionManager.getInstance().getId(act instanceof CustomisedActionGroup ? ((CustomisedActionGroup)act).getOrigin() : act); }
public static ActionGroup getCustomizedGroup(@NotNull String barId) {
final ActionGroup actGroup = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_TOUCHBAR);
final AnAction[] kids = actGroup.getChildren(null);
final String childGroupId = barId.startsWith(IdeActions.GROUP_TOUCHBAR) ? barId : IdeActions.GROUP_TOUCHBAR + barId;
for (AnAction act: kids) {
if (!(act instanceof ActionGroup))
continue;
final String gid = _getActionId(act);
if (gid == null || gid.isEmpty()) {
LOG.error("unregistered ActionGroup: " + act);
continue;
}
if (gid.equals(childGroupId))
return (ActionGroup)act;
}
return null;
}
public static Map<String, ActionGroup> getAltLayouts(@NotNull ActionGroup context) {
final String ctxId = _getActionId(context);
if (ctxId == null || ctxId.isEmpty()) {
LOG.error("unregistered ActionGroup: " + context);
return null;
}
Map<String, ActionGroup> result = new HashMap<>();
final AnAction[] kids = context.getChildren(null);
for (AnAction act: kids) {
if (!(act instanceof ActionGroup))
continue;
final String gid = _getActionId(act);
if (gid == null || gid.isEmpty()) {
LOG.error("unregistered ActionGroup: " + act);
continue;
}
if (gid.startsWith(ctxId + "_"))
result.put(gid.substring(ctxId.length() + 1), (ActionGroup)act);
}
return result;
}
private static @Nullable AnAction _getActionById(String actId) {
final AnAction act = ActionManager.getInstance().getAction(actId);
if (act == null)
@@ -152,4 +245,64 @@ public class TouchBarActionBase extends TouchBarProjectBase implements Execution
return act;
}
protected interface INodeFilter {
boolean skip(String nodeId);
}
private interface ILeafVisitor {
void visit(AnAction leaf);
}
private static final String RUNNERS_GROUP_TOUCHBAR = "RunnerActionsTouchbar";
private static void _traverse(@NotNull ActionGroup group, ILeafVisitor visitor, INodeFilter filter) {
String groupId = _getActionId(group);
if (groupId == null) groupId = "unregistered";
final AnAction[] children = group.getChildren(null);
for (int i = 0; i < children.length; i++) {
AnAction child = children[i];
if (child == null) {
LOG.error(String.format("action is null: i=%d, group='%s', group id='%s'",i, group.toString(), groupId));
continue;
}
String childId = _getActionId(child);
if (childId == null) childId = "unregistered";
if (child instanceof ActionGroup) {
ActionGroup actionGroup = (ActionGroup)child;
if (actionGroup.isPopup()) {
LOG.error(String.format("children with isPopup=true aren't supported now: i=%d, childId='%s', group='%s', group id='%s'", i, childId, group.toString(), groupId));
continue;
}
if (filter != null && filter.skip(childId)) {
// System.out.printf("filter child group: i=%d, childId='%s', group='%s', group id='%s'\n", i, childId, group.toString(), groupId);
continue;
}
_traverse((ActionGroup)child, visitor, filter);
} else
visitor.visit(child);
}
}
private static void _initExecutorsGroup() {
final ActionManager am = ActionManager.getInstance();
final AnAction runButtons = am.getAction(RUNNERS_GROUP_TOUCHBAR);
if (runButtons == null) {
// System.out.println("ERROR: RunnersGroup for touchbar is unregistered");
return;
}
if (!(runButtons instanceof ActionGroup)) {
// System.out.println("ERROR: RunnersGroup for touchbar isn't a group");
return;
}
final ActionGroup g = (ActionGroup)runButtons;
for (Executor exec: ExecutorRegistry.getInstance().getRegisteredExecutors()) {
if (exec != null && (exec.getId().equals(ToolWindowId.RUN) || exec.getId().equals(ToolWindowId.DEBUG))) {
AnAction action = am.getAction(exec.getId());
((DefaultActionGroup)g).add(action);
}
}
}
}
@@ -1,31 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class TouchBarDebugger extends TouchBarActionBase {
TouchBarDebugger(@NotNull Project project, Component component) {
super("debugger", project, component, true);
addButton(AllIcons.Actions.Restart, null, new PlatformAction(IdeActions.ACTION_RERUN));
addAnActionButton("Pause");
addAnActionButton("Resume");
addAnActionButton("Stop", false);
addSpacing(false);
addAnActionButton("XDebugger.MuteBreakpoints", false);
addSpacing(true);
addAnActionButton("StepOver");
addAnActionButton("StepInto");
addAnActionButton("StepOut");
addSpacing(true);
addAnActionButton("EvaluateExpression");
}
}
@@ -1,21 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class TouchBarDebuggerAlt extends TouchBarActionBase {
public TouchBarDebuggerAlt(@NotNull Project project, Component component) {
super("debugger_alt", project, component, true);
addFlexibleSpacing();
addAnActionButton("ViewBreakpoints", false);
addSpacing(true);
addAnActionButton("ForceStepOver");
addAnActionButton("ForceStepInto");
addAnActionButton("SmartStepInto");
addAnActionButton("RunToCursor");
}
}
@@ -1,16 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class TouchBarEditorAlt extends TouchBarActionBase {
TouchBarEditorAlt(@NotNull Project project, Component component) {
super("editor_alt", project, component);
addFlexibleSpacing();
addAnActionButton("Terminal.OpenInTerminal", false, TBItemAnActionButton.SHOWMODE_IMAGE_TEXT);
}
}
@@ -1,25 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class TouchBarEditorCmd extends TouchBarActionBase {
TouchBarEditorCmd(@NotNull Project project, Component component) {
super("editor_cmd", project, component);
// Use Cmd modifier key to show Navigation buttons, Bookmark and Breakpoint toggles and to Show Usages
// Cmd key is used, because it’s used in most of the action shortcuts: Back/Forward, Breakpoint and Find Usages.
addAnActionButton("Back", false);
addAnActionButton("Forward", false);
addSpacing(true);
addAnActionButton("ToggleBookmark", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY); // TODO: make with custom icon (doesn't defined in template presentation)
addAnActionButton("ToggleLineBreakpoint", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY); // TODO: make with custom icon (doesn't defined in template presentation)
addFlexibleSpacing();
addAnActionButton("FindUsages", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
}
}
@@ -1,18 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class TouchBarEditorCmdAlt extends TouchBarActionBase {
TouchBarEditorCmdAlt(@NotNull Project project, Component component) {
super("editor_cmd_alt", project, component);
addFlexibleSpacing();
addAnActionButton("SwitchCoverage", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
addSpacing(true);
addAnActionButton("FindUsages", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
}
}
@@ -1,22 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class TouchBarEditorShift extends TouchBarActionBase {
TouchBarEditorShift(@NotNull Project project, Component component) {
super("editor_shift", project, component);
// Use Shift to show Refactor actions
addAnActionButton("RenameElement", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
addAnActionButton("ChangeSignature", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
addAnActionButton("ChangeTypeSignature", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
addFlexibleSpacing();
addAnActionButton("CopyElement", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
addAnActionButton("Move", false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
}
}
@@ -1,171 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.mac.touchbar;
import com.intellij.execution.*;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.util.messages.MessageBus;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class TouchBarGeneral extends TouchBarActionBase {
private TBItemButton myButtonAddRunConf;
private TBItemPopover myPopoverRunConf;
private TouchBar myPopoverRunConfExpandTB;
private TBItemButton myPopoverRunConfTapAndHold;
private TBItemScrubber myScrubberRunConf;
private TBItemButton myButtonRun;
private TBItemAnActionButton myButtonDebug;
private TBItemAnActionButton myButtonStop;
TouchBarGeneral(@NotNull Project project, Component component, String desc) {
super("general_"+desc, project, component);
addAnActionButton("CompileDirty", false); // NOTE: IdeActions.ACTION_COMPILE doesn't work
myButtonAddRunConf = addButton(AllIcons.General.Add, "Add Configuration", IdeActions.ACTION_EDIT_RUN_CONFIGURATIONS);
{
final TouchBar tapHoldTB = new TouchBar("run_configs_popover_tap_and_hold", false);
tapHoldTB.addFlexibleSpacing();
myPopoverRunConfTapAndHold = tapHoldTB.addButton(null, null, (NSTLibrary.Action)null);
tapHoldTB.selectVisibleItemsToShow();
myPopoverRunConfExpandTB = new TouchBar("run_configs_popover_expand", false);
myPopoverRunConfExpandTB.addButton(AllIcons.Actions.EditSource, null, IdeActions.ACTION_EDIT_RUN_CONFIGURATIONS);
myScrubberRunConf = myPopoverRunConfExpandTB.addScrubber(500);
myPopoverRunConfExpandTB.addFlexibleSpacing();
myPopoverRunConfExpandTB.selectVisibleItemsToShow();
myPopoverRunConf = addPopover(null, null, 143, myPopoverRunConfExpandTB, tapHoldTB);
}
{
myButtonRun = addButton();
myButtonDebug = addAnActionButton(IdeActions.ACTION_DEFAULT_DEBUGGER);
myButtonDebug.setAutoVisibility(false);
myButtonStop = addAnActionButton(IdeActions.ACTION_STOP_PROGRAM);
myButtonStop.setAutoVisibility(false);
}
addSpacing(true);
addAnActionButton("Vcs.UpdateProject", false); // NOTE: IdeActions.ACTION_CVS_CHECKOUT doesn't works
addAnActionButton("CheckinProject", false); // NOTE: IdeActions.ACTION_CVS_COMMIT doesn't works
_updateRunConfigs();
_updateRunButtons();
final MessageBus mb = myProject.getMessageBus();
mb.connect().subscribe(RunManagerListener.TOPIC, new RunManagerListener() {
@Override
public void runConfigurationSelected() {
_updateSelectedConf();
}
@Override
public void runConfigurationAdded(@NotNull RunnerAndConfigurationSettings settings) {
_updateRunConfigs();
}
@Override
public void runConfigurationRemoved(@NotNull RunnerAndConfigurationSettings settings) {
_updateRunConfigs();
}
});
}
@Override
public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) {
super.processStarted(executorId, env, handler);
_updateRunButtons();
}
@Override
public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) {
super.processTerminated(executorId, env, handler, exitCode);
ApplicationManager.getApplication().invokeLater(()->_updateRunButtons());
}
private void _updateSelectedConf() {
final Icon icon;
final String text;
final RunManager rm = RunManager.getInstance(myProject);
final RunnerAndConfigurationSettings selected = rm.getSelectedConfiguration();
if (selected != null) {
icon = selected.getConfiguration().getIcon();
text = selected.getName();
} else {
icon = null;
text = "Select configuration";
}
myPopoverRunConf.update(icon, text);
myPopoverRunConfTapAndHold.update(icon, text);
}
private void _updateRunConfigs() {
final RunManager rm = RunManager.getInstance(myProject);
final List<RunnerAndConfigurationSettings> allRunCongigs = rm.getAllSettings();
if (allRunCongigs == null || allRunCongigs.isEmpty()) {
myButtonAddRunConf.setVisible(true);
myPopoverRunConf.setVisible(false);
myButtonRun.setVisible(false);
myButtonDebug.setVisible(false);
myButtonStop.setVisible(false);
} else {
myButtonAddRunConf.setVisible(false);
myPopoverRunConf.setVisible(true);
myButtonRun.setVisible(true);
myButtonDebug.setVisible(true);
myButtonStop.setVisible(true);
_updateSelectedConf();
List<TBItemScrubber.ItemData> scrubberItems = new ArrayList<>();
for (RunnerAndConfigurationSettings rc : allRunCongigs) {
final Icon iconRc = rc.getConfiguration().getIcon();
scrubberItems.add(new TBItemScrubber.ItemData(iconRc, rc.getName(), () -> {
// NOTE: executed at AppKit-thread
ApplicationManager.getApplication().invokeLater(()->{ rm.setSelectedConfiguration(rc); });
myPopoverRunConf.dismiss();
}));
}
myScrubberRunConf.setItems(scrubberItems);
}
selectVisibleItemsToShow();
}
private static boolean _canBeStopped(ProcessHandler ph) {
return ph != null && !ph.isProcessTerminated()
&& (!ph.isProcessTerminating() || ph instanceof KillableProcess && ((KillableProcess)ph).canKillProcess());
}
private void _updateRunButtons() {
if (!myButtonRun.isVisible())
return;
if (myProject.isDisposed())
return;
final ExecutionManager em = ExecutionManager.getInstance(myProject);
final ProcessHandler[] allRunning = em.getRunningProcesses();
int runningCount = 0;
for (ProcessHandler ph : allRunning)
if (_canBeStopped(ph))
++runningCount;
if (runningCount == 0)
myButtonRun.update(AllIcons.Actions.Execute, null, new PlatformAction(IdeActions.ACTION_DEFAULT_RUNNER));
else
myButtonRun.update(AllIcons.Actions.Restart, null, new PlatformAction(IdeActions.ACTION_RERUN));
}
}
@@ -174,7 +174,10 @@ public class TouchBarsManager {
ourTouchBarStack.removeIf(bc -> bc.isTemporary() && bc.get() == tb);
}
synchronized public static void showTouchBar(@NotNull BarContainer bar) {
synchronized public static void showTouchBar(BarContainer bar) {
if (bar == null)
return;
final BarContainer top = ourTouchBarStack.peek();
if (top == bar)
return;
@@ -184,7 +187,10 @@ public class TouchBarsManager {
_setBarContainer(bar);
}
synchronized private static void _elevateTouchBar(@NotNull BarContainer bar) {
synchronized private static void _elevateTouchBar(BarContainer bar) {
if (bar == null)
return;
final BarContainer top = ourTouchBarStack.peek();
if (top == bar)
return;
@@ -282,7 +288,7 @@ public class TouchBarsManager {
}
if (ag.getChildrenCount() > 0)
result.addActionGroupButtons(ag, ob, ms);
result.addActionGroupButtons(ag, ob, ms, TBItemAnActionButton.SHOWMODE_TEXT_ONLY);
}
}
@@ -1097,5 +1097,32 @@
<keyboard-shortcut first-keystroke="control F10" keymap="$default"/>
<add-to-group group-id="DebugMainMenu" anchor="before" relative-to-action="StepOver"/>
</action>
<group id="TouchBar">
<group id="TouchBarDebugger">
<reference ref="Rerun"/>
<reference ref="Pause"/>
<reference ref="Resume"/>
<reference ref="Stop"/>
<separator/>
<reference ref="XDebugger.MuteBreakpoints"/>
<separator/>
<reference ref="StepOver"/>
<reference ref="StepInto"/>
<reference ref="StepOut"/>
<separator/>
<reference ref="EvaluateExpression"/>
<group id="TouchBarDebugger_alt">
<separator/>
<reference ref="ViewBreakpoints"/>
<separator/>
<reference ref="ForceStepOver"/>
<reference ref="ForceStepInto"/>
<reference ref="SmartStepInto"/>
<reference ref="RunToCursor"/>
</group>
<group id="TouchBarDebugger_cmd"/>
</group>
</group>
</actions>
</idea-plugin>
@@ -418,5 +418,42 @@
<add-to-group group-id="ChangesView.GroupBy"/>
</action>
<group id="TouchBarDefault">
<reference ref="CompileDirty"/>
<reference ref="RunConfiguration"/>
<group id="RunnerActionsTouchbar"/> <!--filled from ExecutorRegistry-->
<reference ref="Stop"/>
<separator/>
<reference ref="Vcs.UpdateProject"/>
<reference ref="CheckinProject"/>
<add-to-group group-id="TouchBar" anchor="before" relative-to-action="TouchBarDebugger"/>
<group id="TouchBarDefault_alt">
<separator text="type.flexible"/>
<reference ref="RevealGroup"/>
</group>
<group id="TouchBarDefault_cmd">
<!--Use Cmd modifier key to show Navigation buttons, Bookmark and Breakpoint toggles and to Show Usages/-->
<!--Cmd key is used, because it’s used in most of the action shortcuts: Back/Forward, Breakpoint and Find Usages./-->
<reference ref="Back"/>
<reference ref="Forward"/>
<separator text="type.big"/>
<reference ref="ToggleBookmark"/>
<reference ref="ToggleLineBreakpoint"/>
<separator text="type.flexible"/>
<reference ref="FindUsages"/>
</group>
<group id="TouchBarDefault_cmd.alt">
<separator text="type.flexible"/>
<reference ref="ShowUsages"/>
</group>
<group id="TouchBarDefault_shift">
<!--Use Shift to show Refactor actions/-->
<reference ref="RenameElement"/>
<reference ref="ChangeSignature"/>
<separator text="type.flexible"/>
<reference ref="CopyElement"/>
<reference ref="Move"/>
</group>
</group>
</actions>
</idea-plugin>