IDEA-163208: refactoring (with fix of leak)

extract ItemsContainer into separate class to use with both java-wrappers for 'group' and 'touchbar' (fixed memory leak)
composite actions of button (to implement auto-close bars)
remove TouchBarProjectBase, TouchBarActionBase (unnecessary layers)
simplify TBItem's api with code cleanup
remove unnecessary syncronized-blocks and NSAutoreleasePool
This commit is contained in:
Artem Bochkarev
2018-06-12 19:00:12 +07:00
parent 81bb385439
commit 8d304bc944
28 changed files with 884 additions and 831 deletions
Binary file not shown.
+4 -4
View File
@@ -92,7 +92,7 @@ static void _setButtonData(NSButtonJAction *button, int updateOptions, int butto
}
}
// NOTE: called from AppKit (creation when TB becomes visible)
// NOTE: called from AppKit-thread (creation when TB becomes visible), uses default autorelease-pool (create before event processing)
id createButton(
const char *uid,
int buttonWidth,
@@ -101,7 +101,7 @@ id createButton(
const char *raster4ByteRGBA, int w, int h,
execute jaction
) {
NSString *nsUid = getString(uid);
NSString *nsUid = createStringFromUTF8(uid);
nstrace(@"create button [%@] (thread: %@)", nsUid, [NSThread currentThread]);
NSCustomTouchBarItem *customItemForButton = [[NSCustomTouchBarItem alloc] initWithIdentifier:nsUid]; // create non-autorelease object to be owned by java-wrapper
@@ -109,7 +109,7 @@ id createButton(
[button setBezelStyle:NSRoundedBezelStyle];
NSImage *img = createImgFrom4ByteRGBA((const unsigned char *) raster4ByteRGBA, w, h);
NSString *nstext = getString(text);
NSString *nstext = createStringFromUTF8(text);
_setButtonData(button, BUTTON_UPDATE_ALL, buttonWidth, buttonFlags, nstext, img, jaction);
customItemForButton.view = button; // NOTE: view is strong
@@ -137,7 +137,7 @@ void updateButton(
NSAutoreleasePool *edtPool = [NSAutoreleasePool new];
NSImage *img = createImgFrom4ByteRGBA((const unsigned char *) raster4ByteRGBA, w, h);
NSString *nstext = getString(text);
NSString *nstext = createStringFromUTF8(text);
if ([NSThread isMainThread]) {
nstrace(@"sync update button [%@] (main thread: %@)", container.identifier, [NSThread currentThread]);
+4 -4
View File
@@ -13,14 +13,14 @@ static void _setPopoverData(NSPopoverTouchBarItem * popoverItem, int itemWidth,
popoverItem.pressAndHoldTouchBar = ((TouchBar*)tbObjTapAndHold).touchBar;
}
// NOTE: called from AppKit (creation when TB becomes visible)
// NOTE: called from AppKit-thread (creation when TB becomes visible), uses default autorelease-pool (create before event processing)
id createPopover(const char * uid, int itemWidth, const char * text, const char * raster4ByteRGBA, int w, int h, id tbObjExpand, id tbObjTapAndHold) {
NSString * nsUid = getString(uid);
NSString * nsUid = createStringFromUTF8(uid);
nstrace(@"create popover [%@] (thread: %@)", nsUid, [NSThread currentThread]);
NSPopoverTouchBarItem * popoverItem = [[NSPopoverTouchBarItem alloc] initWithIdentifier:nsUid]; // create non-autorelease object to be owned by java-wrapper
NSImage * img = createImgFrom4ByteRGBA((const unsigned char *)raster4ByteRGBA, w, h);
NSString * nstext = getString(text);
NSString * nstext = createStringFromUTF8(text);
_setPopoverData(popoverItem, itemWidth, nstext, img, tbObjExpand, tbObjTapAndHold);
return popoverItem;
}
@@ -31,7 +31,7 @@ void updatePopover(id popoverObj, int itemWidth, const char * text, const char *
nstrace(@"async update popover [%@] (thread: %@)", popoverItem.identifier, [NSThread currentThread]);
NSAutoreleasePool * edtPool = [[NSAutoreleasePool alloc] init];
NSImage * img = createImgFrom4ByteRGBA((const unsigned char *)raster4ByteRGBA, w, h);
NSString * nstext = getString(text);
NSString * nstext = createStringFromUTF8(text);
dispatch_async(dispatch_get_main_queue(), ^{
// NOTE: block is copied, img/text objects is automatically retained
// nstrace(@"\tperform update popover [%@] (thread: %@)", popoverItem.identifier, [NSThread currentThread]);
+5 -5
View File
@@ -28,8 +28,8 @@ static NSMutableArray * _convertItems(ScrubberItemData * items, int count) {
NSMutableArray * nsarray = [[[NSMutableArray alloc] initWithCapacity:count] autorelease];
for (int c = 0; c < count; ++c) {
ScrubberItem * si = [[[ScrubberItem alloc] init] autorelease];
si.text = getText(&items[c]);
si.img = getImg(&items[c]);
si.text = createString(&items[c]);
si.img = createImg(&items[c]);
si.jaction = items[c].action;
[nsarray addObject:si];
}
@@ -45,7 +45,7 @@ static NSMutableArray * _convertItems(ScrubberItemData * items, int count) {
}
- (NSScrubberItemView *)scrubber:(NSScrubber *)scrubber viewForItemAtIndex:(NSInteger)itemIndex {
// NOTE: called from AppKit
// NOTE: called from AppKit-thread (creation when TB becomes visible), uses default autorelease-pool (create before event processing)
nstrace(@"scrubber [%@]: create viewForItemAtIndex %d", self.identifier, itemIndex);
ScrubberItemView *itemView = [scrubber makeItemWithIdentifier:g_scrubberItemIdentifier owner:nil];
@@ -60,7 +60,7 @@ static NSMutableArray * _convertItems(ScrubberItemData * items, int count) {
}
- (NSSize)scrubber:(NSScrubber *)scrubber layout:(NSScrubberFlowLayout *)layout sizeForItemAtIndex:(NSInteger)itemIndex {
// NOTE: called from AppKit (when update layout)
// NOTE: called from AppKit-thread (creation when TB becomes visible), uses default autorelease-pool (create before event processing)
ScrubberItem * itemData = [self.items objectAtIndex:itemIndex];
if (itemData == nil) {
nserror(@"scrubber [%@]: null item-data at index %d", self.identifier, itemIndex);
@@ -98,7 +98,7 @@ static NSMutableArray * _convertItems(ScrubberItemData * items, int count) {
@end
// NOTE: called from AppKit (when TB becomes visible)
// NOTE: called from AppKit-thread (creation when TB becomes visible), uses default autorelease-pool (create before event processing)
id createScrubber(const char* uid, int itemWidth, ScrubberItemData * items, int count) {
NSString * nsid = [NSString stringWithUTF8String:uid];
nstrace(@"create scrubber [%@] (thread: %@)", nsid, [NSThread currentThread]);
+8 -4
View File
@@ -22,7 +22,7 @@
}
- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier {
// NOTE: called from AppKit-thread
// NOTE: called from AppKit-thread, uses default autorelease-pool (create before event processing)
if (_jcreator == nil) {
nserror(@"tb [%@]: called makeTouchBarItem for '%@' but creator is null", self.name, identifier);
return nil;
@@ -66,28 +66,32 @@ void selectItemsToShow(id touchBar, const char** ppIds, int count) {
[edtPool release];
}
// NOTE: called from EDT (when java-wrapper of touchbar created)
id createTouchBar(const char * name, createItem jcreator, const char * escId) {
NSAutoreleasePool * edtPool = [[NSAutoreleasePool alloc] init];
TouchBar * result = [[TouchBar alloc] init:getString(name) jcreator:jcreator customEscId:getString(escId)]; // creates non-autorelease obj to be owned by java-wrapper
TouchBar * result = [[TouchBar alloc] init:createStringFromUTF8(name) jcreator:jcreator customEscId:createStringFromUTF8(escId)]; // creates non-autorelease obj to be owned by java-wrapper
[edtPool release];
return result;
}
void setPrincipal(id tbobj, const char * uid) {
NSAutoreleasePool * edtPool = [[NSAutoreleasePool alloc] init];
TouchBar * tb = (TouchBar *)tbobj; // TODO: check types
[tb.touchBar setPrincipalItemIdentifier:getString(uid)];
[tb.touchBar setPrincipalItemIdentifier:createStringFromUTF8(uid)];
[edtPool release];
}
void releaseTouchBar(id tbobj) {
[tbobj release];
}
// NOTE: called from AppKit-thread (creation when TB becomes visible), uses default autorelease-pool (create before event processing)
id createGroupItem(const char * uid, id * items, int count) {
NSMutableArray *allItems = [NSMutableArray arrayWithCapacity:count];
for (int c = 0; c < count; ++c)
[allItems addObject:items[c]];
NSGroupTouchBarItem * result = [NSGroupTouchBarItem groupItemWithIdentifier:getString(uid) items:allItems];
NSGroupTouchBarItem * result = [NSGroupTouchBarItem groupItemWithIdentifier:createStringFromUTF8(uid) items:allItems];
// NOTE: should create non-autorelease object to be owned by java-wrapper
// the simplest way to create working NSGroupTouchBarItem with fixed item set is to call groupItemWithIdentifier, which creates autorelease object, so do retain..
[result retain];
+6 -4
View File
@@ -5,8 +5,10 @@
void nstrace(NSString *format, ...);
void nserror(NSString *format, ...);
NSImage * createImgFrom4ByteRGBA(const unsigned char *bytes, int w, int h); // Creates autorelease image
NSImage * getImg(ScrubberItemData * jdata);
NSString * getText(ScrubberItemData * jdata);
NSString * getString(const char * utf8);
// Next functions create autorelease image/strings
NSImage * createImgFrom4ByteRGBA(const unsigned char *bytes, int w, int h);
NSImage * createImg(ScrubberItemData *jdata);
NSString * createString(ScrubberItemData *jdata);
NSString * createStringFromUTF8(const char *utf8);
+3 -3
View File
@@ -55,19 +55,19 @@ NSImage * createImgFrom4ByteRGBA(const unsigned char *bytes, int w, int h) {
return nsimg;
}
NSImage * getImg(ScrubberItemData * jdata) {
NSImage * createImg(ScrubberItemData *jdata) {
if (jdata == NULL)
return nil;
return createImgFrom4ByteRGBA((const unsigned char *)jdata->raster4ByteRGBA, jdata->rasterW, jdata->rasterH);
}
NSString * getText(ScrubberItemData * jdata) {
NSString * createString(ScrubberItemData *jdata) {
if (jdata == NULL || jdata->text == NULL)
return nil;
return [NSString stringWithUTF8String:jdata->text];
}
NSString * getString(const char * utf8) {
NSString * createStringFromUTF8(const char *utf8) {
if (utf8 == NULL)
return nil;
return [NSString stringWithUTF8String:utf8];
@@ -17,9 +17,6 @@ import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.reference.SoftReference;
import com.intellij.ui.mac.touchbar.NSAutoreleaseLock;
import com.intellij.ui.mac.touchbar.TBItemScrubber;
import com.intellij.ui.mac.touchbar.TouchBar;
import com.intellij.ui.mac.touchbar.TouchBarsManager;
import com.intellij.ui.popup.list.GroupedItemsListRenderer;
import com.intellij.util.IconUtil;
@@ -104,7 +101,7 @@ public class StopAction extends DumbAwareAction implements AnAction.TransparentU
return;
}
if (e.getPlace().equals(ActionPlaces.TOUCHBAR_GENERAL)) {
if (e.getPlace().equals(ActionPlaces.TOUCHBAR_GENERAL) && !stoppableDescriptors.isEmpty()) {
_showStopRunningBar(stoppableDescriptors);
return;
}
@@ -262,30 +259,14 @@ public class StopAction extends DumbAwareAction implements AnAction.TransparentU
|| processHandler instanceof KillableProcess && ((KillableProcess)processHandler).canKillProcess());
}
private static void _showStopRunningBar(List<RunContentDescriptor> stoppableDescriptors) {
private static void _showStopRunningBar(@NotNull List<RunContentDescriptor> stoppableDescriptors) {
if (!TouchBarsManager.isTouchBarAvailable())
return;
final TouchBar tb;
try (NSAutoreleaseLock lock = new NSAutoreleaseLock()) {
tb = new TouchBar("select_running_to_stop", true, true);
tb.addButton().setText("Stop all").setAction(() -> {
for (RunContentDescriptor sd: stoppableDescriptors)
ExecutionManagerImpl.stopProcess(sd);
TouchBarsManager.closeTouchBar(tb, true);
});
final TBItemScrubber stopScrubber = tb.addScrubber();
List<TBItemScrubber.ItemData> scrubItems = new ArrayList<>();
for (RunContentDescriptor sd : stoppableDescriptors) {
scrubItems.add(new TBItemScrubber.ItemData(sd.getIcon(), sd.getDisplayName(), () -> {
ExecutionManagerImpl.stopProcess(sd);
TouchBarsManager.closeTouchBar(tb, true);
}));
}
stopScrubber.setItems(scrubItems);
}
TouchBarsManager.showStopRunningBar(tb);
List<Pair<RunContentDescriptor, Runnable>> descriptors = new ArrayList<>(stoppableDescriptors.size());
for (RunContentDescriptor sd : stoppableDescriptors)
descriptors.add(Pair.create(sd, ()->ExecutionManagerImpl.stopProcess(sd)));
TouchBarsManager.showStopRunningBar(descriptors);
}
abstract static class HandlerItem {
@@ -430,7 +430,7 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer {
myDialog.getWindow().setAutoRequestFocus(true);
if (myTouchBarButtons != null && myProject != null)
myTouchBarCloser = TouchBarsManager.showDlgButtonsBar(myTouchBarButtons, myProject);
myTouchBarCloser = TouchBarsManager.showDlgButtonsBar(myTouchBarButtons);
try {
myDialog.show();
@@ -14,8 +14,6 @@ enum BarType {
}
class BarContainer {
private final @NotNull BarType myType;
private Map<Long, TouchBar> myKeyMask2Alt;
private @NotNull TouchBar myMain;
@@ -55,10 +53,9 @@ class BarContainer {
boolean isTemporary() { return myType == BarType.POPUP || myType == BarType.DIALOG; }
void setComponent(Component component) {
if (myMain instanceof TouchBarActionBase)
((TouchBarActionBase)myMain).setComponent(component);
myMain.setComponent(component);
if (myKeyMask2Alt != null)
myKeyMask2Alt.values().forEach(tb -> { if (tb instanceof TouchBarActionBase) ((TouchBarActionBase)tb).setComponent(component); });
myKeyMask2Alt.values().forEach(tb -> { tb.setComponent(component); });
}
void release() {
@@ -0,0 +1,360 @@
// 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.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.ide.ui.customization.CustomActionsSchema;
import com.intellij.ide.ui.customization.CustomisedActionGroup;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.impl.LaterInvocator;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.OptionAction;
import com.intellij.openapi.ui.popup.ListPopupStep;
import com.intellij.openapi.ui.popup.MnemonicNavigationFilter;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.ui.components.JBOptionButton;
import com.intellij.ui.popup.list.ListPopupImpl;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class BuildUtils {
private static final Logger LOG = Logger.getInstance(Utils.class);
private static final String ourLargeSeparatorText = "type.big";
private static final String ourFlexibleSeparatorText = "type.flexible";
private static final int ourRunConfigurationPopoverWidth = 143;
private static final String RUNNERS_GROUP_TOUCHBAR = "RunnerActionsTouchbar";
static {
_initExecutorsGroup();
}
static void addCustomizedActionGroup(ItemsContainer out, ActionGroup customizedGroup) {
final String groupId = _getActionId(customizedGroup);
if (groupId == null) {
LOG.error("unregistered group: " + customizedGroup);
return;
}
addActionGroupButtons(out, customizedGroup, null, TBItemAnActionButton.SHOWMODE_IMAGE_ONLY_IF_PRESENTED,
nodeId -> nodeId.contains(groupId + "_"), null);
}
static void addActionGroupButtons(ItemsContainer out,
ActionGroup actionGroup,
ModalityState modality,
int showMode,
INodeFilter filter,
ICustomizer customizer) {
_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) out.addSpacing(false);
else if (mySeparatorCounter == 2) out.addSpacing(true);
else out.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 = out.addAnActionButton(act, false, mode, modality);
if (isRunConfigPopover)
butt.setWidth(ourRunConfigurationPopoverWidth);
if (customizer != null)
customizer.customize(butt);
}
}, filter);
}
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;
}
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;
}
static TouchBar createMessageDlgBar(@NotNull String[] buttons, @NotNull Runnable[] actions, String defaultButton) {
final TouchBar result = new TouchBar("message_dlg_bar", false, true, false);
final TBItemGroup gr = result.addGroup();
final ItemsContainer group = gr.getContainer();
// NOTE: buttons are placed from right to left, see SheetController.layoutButtons
int defIndex = -1;
final int len = Math.min(buttons.length, actions.length);
for (int c = len - 1; c >= 0; --c) {
final String sb = buttons[c];
final boolean isDefault = Comparing.equal(sb, defaultButton);
if (isDefault) {
defIndex = c;
continue;
}
group.addButton().setText(DialogWrapper.extractMnemonic(sb).second).setThreadSafeAction(actions[c]);
}
if (defIndex >= 0)
group.addButton().setText(DialogWrapper.extractMnemonic(buttons[defIndex]).second).setThreadSafeAction(actions[defIndex])
.setFlags(false, false, true);
result.setPrincipal(gr);
result.selectVisibleItemsToShow();
return result;
}
// creates releaseOnClose touchbar
static TouchBar createButtonsBar(List<JButton> jbuttons) {
final TouchBar result = new TouchBar("dialog_buttons", false, true, false);
final ModalityState ms = LaterInvocator.getCurrentModalityState();
// 1. add option buttons (at left)
byte prio = -1;
for (JButton jb : jbuttons) {
if (jb instanceof JBOptionButton) {
final JBOptionButton ob = (JBOptionButton)jb;
final Action[] opts = ob.getOptions();
for (Action a : opts) {
if (a == null)
continue;
final AnAction anAct = _createAnAction(a, ob, true);
if (anAct == null)
continue;
// NOTE: must set different priorities for items, otherwise system can hide all items with the same priority (but some of them is able to be placed)
result.addAnActionButton(anAct, false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(ob).setPriority(--prio);
}
}
}
// 2. add main buttons and make principal
final TBItemGroup gr = result.addGroup();
final ItemsContainer group = gr.getContainer();
JButton jbdef = null;
for (JButton jb : jbuttons) {
// NOTE: can be true: jb.getAction().isEnabled() && !jb.isEnabled()
final boolean isDefault = jb.getAction().getValue(DialogWrapper.DEFAULT_ACTION) != null;
if (isDefault) {
jbdef = jb;
continue;
}
final AnAction anAct = _createAnAction(jb.getAction(), jb, false);
if (anAct == null)
continue;
group.addAnActionButton(anAct, false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(jb);
}
if (jbdef != null) {
final AnAction anAct = _createAnAction(jbdef.getAction(), jbdef, false);
if (anAct != null)
group.addAnActionButton(anAct, false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms).setComponent(jbdef).setFlags(false, false, true);
}
result.selectVisibleItemsToShow();
result.setPrincipal(gr);
return result;
}
// creates releaseOnClose touchbar
static TouchBar createScrubberBarFromPopup(@NotNull ListPopupImpl listPopup) {
final TouchBar result = new TouchBar("popup_scrubber_bar" + listPopup, false, true, false);
final TBItemScrubber scrub = result.addScrubber();
@NotNull ListPopupStep listPopupStep = listPopup.getListStep();
for (Object obj : listPopupStep.getValues()) {
final Icon ic = listPopupStep.getIconFor(obj);
String txt = listPopupStep.getTextFor(obj);
if (listPopupStep.isMnemonicsNavigationEnabled()) {
final MnemonicNavigationFilter<Object> filter = listPopupStep.getMnemonicNavigationFilter();
final int pos = filter == null ? -1 : filter.getMnemonicPos(obj);
if (pos != -1)
txt = txt.substring(0, pos) + txt.substring(pos + 1);
}
final Runnable action = () -> {
listPopup.getList().setSelectedValue(obj, false);
listPopup.handleSelect(true);
};
scrub.addItem(ic, txt, () -> ApplicationManager.getApplication().invokeLater(() -> action.run()));
}
result.selectVisibleItemsToShow();
return result;
}
// creates releaseOnClose touchbar
static TouchBar createStopRunningBar(List<Pair<RunContentDescriptor, Runnable>> stoppableDescriptors) {
final TouchBar tb = new TouchBar("select_running_configuration_to_stop", true, true, true);
tb.addButton().setText("Stop all").setActionOnEDT(() -> {
stoppableDescriptors.forEach((pair) -> { pair.second.run(); });
});
final TBItemScrubber stopScrubber = tb.addScrubber();
for (Pair<RunContentDescriptor, Runnable> sd : stoppableDescriptors)
stopScrubber.addItem(sd.first.getIcon(), sd.first.getDisplayName(), sd.second);
tb.selectVisibleItemsToShow();
return tb;
}
interface INodeFilter {
boolean skip(String nodeId);
}
interface ICustomizer {
void customize(TBItem item);
}
interface ILeafVisitor {
void visit(AnAction leaf);
}
private static String _getActionId(AnAction act) {
return ActionManager.getInstance().getId(act instanceof CustomisedActionGroup ? ((CustomisedActionGroup)act).getOrigin() : act);
}
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 AnAction _createAnAction(@NotNull Action action, JButton fromButton, boolean useTextFromAction /*for optional buttons*/) {
final Object anAct = action.getValue(OptionAction.AN_ACTION);
if (anAct == null) {
// LOG.warn("null AnAction in action: '" + action + "', use wrapper");
return new DumbAwareAction() {
{
setEnabledInModalContext(true);
if (useTextFromAction) {
final Object name = action.getValue(Action.NAME);
getTemplatePresentation().setText(name != null && name instanceof String ? (String)name : "");
}
}
@Override
public void actionPerformed(AnActionEvent e) {
// also can be used something like: ApplicationManager.getApplication().invokeLater(() -> jb.doClick(), ms)
action.actionPerformed(new ActionEvent(fromButton, ActionEvent.ACTION_PERFORMED, null));
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(action.isEnabled());
if (!useTextFromAction)
e.getPresentation().setText(DialogWrapper.extractMnemonic(fromButton.getText()).second);
}
};
}
if (!(anAct instanceof AnAction)) {
// LOG.warn("unknown type of awt.Action's property: " + anAct.getClass().toString());
return null;
}
return (AnAction)anAct;
}
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);
}
}
}
}
@@ -0,0 +1,7 @@
// 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;
public interface ItemListener {
// NOTE: called from AppKit thread
void onItemEvent(TBItem src, int evcode);
}
@@ -0,0 +1,127 @@
// 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.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.ModalityState;
import com.intellij.ui.mac.foundation.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
class ItemsContainer {
private final @NotNull String myName; // just for logging/debugging
private final ItemListener myListener;
private final List<TBItem> myItems = new ArrayList<>();
private long myCounter = 0; // for unique id generation
ItemsContainer(@NotNull String name, ItemListener listener) { myName = name; myListener = listener; }
boolean isEmpty() { return myItems.isEmpty(); }
boolean hasAnActionItems() { return anyMatchDeep(item -> item instanceof TBItemAnActionButton); }
@Override
public String toString() { return myName; }
@NotNull TBItemButton addButton() {
final TBItemButton butt = new TBItemButton(_genNewID("button"), myListener);
myItems.add(butt);
return butt;
}
@NotNull TBItemAnActionButton addAnActionButton(@NotNull AnAction act, boolean hiddenWhenDisabled, int showMode, ModalityState modality) {
final String uid = String.format("%s.anActionButton.%d.%s", myName, myCounter++, ActionManager.getInstance().getId(act));
final TBItemAnActionButton butt = new TBItemAnActionButton(uid, myListener, act, hiddenWhenDisabled, showMode, modality);
myItems.add(butt);
return butt;
}
@NotNull TBItemGroup addGroup() {
final TBItemGroup group = new TBItemGroup(_genNewID("group"), myListener);
myItems.add(group);
return group;
}
@NotNull TBItemPopover addPopover(Icon icon, String text, int width, TouchBar expandTB, TouchBar tapAndHoldTB) {
final TBItemPopover popover = new TBItemPopover(_genNewID("popover"), myListener, icon, text, width, expandTB, tapAndHoldTB);
myItems.add(popover);
return popover;
}
@NotNull TBItemScrubber addScrubber() {
final int defaultScrubberWidth = 500;
final TBItemScrubber scrubber = new TBItemScrubber(_genNewID("scrubber"), myListener, defaultScrubberWidth);
myItems.add(scrubber);
return scrubber;
}
void addSpacing(boolean large) {
final SpacingItem spacing = new SpacingItem(large ? "static_touchbar_item_large_space" : "static_touchbar_item_small_space");
myItems.add(spacing);
}
void addFlexibleSpacing() {
final SpacingItem spacing = new SpacingItem("static_touchbar_item_flexible_space");
myItems.add(spacing);
}
void releaseAll() {
myItems.forEach(item -> item.releaseNativePeer());
myItems.clear();
}
@NotNull String[] getVisibleIds() {
final String[] ids = new String[myItems.size()];
int c = 0;
for (TBItem item : myItems) {
if (item.isVisible())
ids[c++] = item.myUid;
}
return c == myItems.size() ? ids : Arrays.copyOf(ids, c);
}
@NotNull ID[] getVisibleNativePeers() {
final ID[] ids = new ID[myItems.size()];
int c = 0;
for (TBItem item : myItems) {
if (item.isVisible())
ids[c++] = item.getNativePeer();
}
return c == myItems.size() ? ids : Arrays.copyOf(ids, c);
}
void forEachDeep(Consumer<? super TBItem> proc) {
myItems.forEach((item -> {
if (item instanceof TBItemGroup) {
((TBItemGroup)item).getContainer().forEachDeep(proc);
return;
}
proc.accept(item);
}));
}
boolean anyMatchDeep(Predicate<? super TBItem> proc) {
return myItems.stream().anyMatch(item -> {
if (item instanceof TBItemGroup)
return ((TBItemGroup)item).getContainer().anyMatchDeep(proc);
return proc.test(item);
});
}
@Nullable
TBItem findItem(String uid) {
for (TBItem item : myItems)
if (item.myUid.equals(uid))
return item;
return null;
}
private @NotNull String _genNewID(String desc) { return String.format("%s.%s.%d", myName, desc, myCounter++); }
}
@@ -86,7 +86,7 @@ public class NST {
public static boolean isAvailable() { return ourNSTLibrary != null; }
public static ID createTouchBar(String name, NSTLibrary.ItemCreator creator, String escID) {
return ourNSTLibrary.createTouchBar(name, creator, escID);
return ourNSTLibrary.createTouchBar(name, creator, escID); // creates autorelease-pool internally
}
public static void releaseTouchBar(ID tbObj) {
@@ -99,11 +99,11 @@ public class NST {
public static void selectItemsToShow(ID tbObj, String[] ids, int count) {
_assertIsDispatchThread();
ourNSTLibrary.selectItemsToShow(tbObj, ids, count);
ourNSTLibrary.selectItemsToShow(tbObj, ids, count); // creates autorelease-pool internally
}
public static void setPrincipal(ID tbObj, String uid) {
ourNSTLibrary.setPrincipal(tbObj, uid);
ourNSTLibrary.setPrincipal(tbObj, uid); // creates autorelease-pool internally
}
public static ID createButton(String uid,
@@ -116,7 +116,7 @@ public class NST {
final byte[] raster4ByteRGBA = _getRaster(img);
final int w = _getImgW(img);
final int h = _getImgH(img);
return ourNSTLibrary.createButton(uid, buttWidth, buttFlags, text, raster4ByteRGBA, w, h, action);
return ourNSTLibrary.createButton(uid, buttWidth, buttFlags, text, raster4ByteRGBA, w, h, action); // called from AppKit, uses per-event autorelease-pool
}
public static ID createPopover(String uid,
@@ -129,16 +129,16 @@ public class NST {
final byte[] raster4ByteRGBA = _getRaster(img);
final int w = _getImgW(img);
final int h = _getImgH(img);
return ourNSTLibrary.createPopover(uid, itemWidth, text, raster4ByteRGBA, w, h, tbObjExpand, tbObjTapAndHold);
return ourNSTLibrary.createPopover(uid, itemWidth, text, raster4ByteRGBA, w, h, tbObjExpand, tbObjTapAndHold); // called from AppKit, uses per-event autorelease-pool
}
public static ID createScrubber(String uid, int itemWidth, List<TBItemScrubber.ItemData> items) {
final NSTLibrary.ScrubberItemData[] vals = _makeItemsArray2(items);
return ourNSTLibrary.createScrubber(uid, itemWidth, vals, vals != null ? vals.length : 0);
return ourNSTLibrary.createScrubber(uid, itemWidth, vals, vals != null ? vals.length : 0); // called from AppKit, uses per-event autorelease-pool
}
public static ID createGroupItem(String uid, ID[] items, int count) {
return ourNSTLibrary.createGroupItem(uid, items, count);
return ourNSTLibrary.createGroupItem(uid, items, count); // called from AppKit, uses per-event autorelease-pool
}
public static void updateButton(ID buttonObj,
@@ -153,7 +153,7 @@ public class NST {
final byte[] raster4ByteRGBA = _getRaster(img);
final int w = _getImgW(img);
final int h = _getImgH(img);
ourNSTLibrary.updateButton(buttonObj, updateOptions, buttWidth, buttonFlags, text, raster4ByteRGBA, w, h, action);
ourNSTLibrary.updateButton(buttonObj, updateOptions, buttWidth, buttonFlags, text, raster4ByteRGBA, w, h, action); // creates autorelease-pool internally
}
public static void updatePopover(ID popoverObj,
@@ -166,13 +166,13 @@ public class NST {
final byte[] raster4ByteRGBA = _getRaster(img);
final int w = _getImgW(img);
final int h = _getImgH(img);
ourNSTLibrary.updatePopover(popoverObj, itemWidth, text, raster4ByteRGBA, w, h, tbObjExpand, tbObjTapAndHold);
ourNSTLibrary.updatePopover(popoverObj, itemWidth, text, raster4ByteRGBA, w, h, tbObjExpand, tbObjTapAndHold); // creates autorelease-pool internally
}
public static void updateScrubber(ID scrubObj, int itemWidth, List<TBItemScrubber.ItemData> items) {
_assertIsDispatchThread();
final NSTLibrary.ScrubberItemData[] vals = _makeItemsArray2(items);
ourNSTLibrary.updateScrubber(scrubObj, itemWidth, vals, vals != null ? vals.length : 0);
ourNSTLibrary.updateScrubber(scrubObj, itemWidth, vals, vals != null ? vals.length : 0); // creates autorelease-pool internally
}
private static NSTLibrary.ScrubberItemData[] _makeItemsArray2(List<TBItemScrubber.ItemData> items) {
@@ -68,6 +68,4 @@ public interface NSTLibrary extends Library {
void updateScrubber(ID scrubObj, int itemWidth, ScrubberItemData[] items, int count);
static int priority2mask(byte prio) { return (prio + 128) << BUTTON_PRIORITY_SHIFT; }
static Action run2act(Runnable run) { return ()->run.run(); }
}
@@ -1,48 +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.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import static java.awt.event.ComponentEvent.COMPONENT_FIRST;
public class PlatformAction implements NSTLibrary.Action {
final AnAction myAction;
final String myActionId;
public PlatformAction(@NotNull String actionId) {
final ActionManagerEx actionManagerEx = ActionManagerEx.getInstanceEx();
myAction = ActionManager.getInstance().getAction(actionId);
if (myAction == null) {
TouchBar.LOG.error("can't find action by id '%s'", actionId);
}
myActionId = actionId;
}
public PlatformAction(@NotNull AnAction act) {
myAction = act;
myActionId = ActionManager.getInstance().getId(myAction);
}
@Override
public void execute() {
ApplicationManager.getApplication().invokeLater(() -> _performAction());
}
private void _performAction() {
final ActionManagerEx actionManagerEx = ActionManagerEx.getInstanceEx();
final KeyboardFocusManager focusManager=KeyboardFocusManager.getCurrentKeyboardFocusManager();
final Component focusOwner = focusManager.getFocusedWindow();
final InputEvent ie = new KeyEvent(focusOwner, COMPONENT_FIRST, System.currentTimeMillis(), 0, 0, '\0');
actionManagerEx.tryToExecute(myAction, ie, focusOwner, ActionPlaces.UNKNOWN, false);
}
}
@@ -17,6 +17,7 @@ import java.awt.event.InputEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
class ProjectData {
private static final Logger LOG = Logger.getInstance(ProjectData.class);
@@ -78,13 +79,13 @@ class ProjectData {
return;
}
final ActionGroup mainLayout = TouchBarActionBase.getCustomizedGroup(barId);
final ActionGroup mainLayout = BuildUtils.getCustomizedGroup(barId);
if (mainLayout == null) {
LOG.info("can't create touchbar because corresponding ActionGroup isn't defined (seems that user deleted it), context: " + barId);
return;
}
final Map<String, ActionGroup> strmod2alt = TouchBarActionBase.getAltLayouts(mainLayout);
final Map<String, ActionGroup> strmod2alt = BuildUtils.getAltLayouts(mainLayout);
final Map<Long, TouchBar> alts = new HashMap<>();
if (strmod2alt != null && !strmod2alt.isEmpty()) {
for (String modId: strmod2alt.keySet()) {
@@ -93,11 +94,11 @@ class ProjectData {
// System.out.println("ERROR: zero mask for modId="+modId);
continue;
}
alts.put(mask, new TouchBarActionBase(type.name() + "_" + modId, myProject, strmod2alt.get(modId), replaceEsc));
alts.put(mask, TouchBar.buildFromGroup(type.name() + "_" + modId, strmod2alt.get(modId), replaceEsc));
}
}
container.set(new TouchBarActionBase(type.name(), myProject, mainLayout, replaceEsc), alts);
container.set(TouchBar.buildFromGroup(type.name(), mainLayout, replaceEsc), alts);
}
void releaseAll() {
@@ -114,6 +115,11 @@ class ProjectData {
});
}
void forEach(BiConsumer<? super BarType, ? super BarContainer> proc) {
ApplicationManager.getApplication().assertIsDispatchThread();
myBars.forEach(proc);
}
int getDbgSessions() { return myActiveDebugSessions.get(); }
private static long _str2mask(@NotNull String modifierId) {
@@ -4,17 +4,22 @@ package com.intellij.ui.mac.touchbar;
import com.intellij.ui.mac.foundation.Foundation;
import com.intellij.ui.mac.foundation.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
abstract class TBItem {
final @NotNull String myUid;
final @Nullable ItemListener myListener;
protected @NotNull ID myNativePeer = ID.NIL; // java wrapper holds native object
protected boolean myIsVisible = true;
TBItem(@NotNull String uid) { myUid = uid; }
TBItem(@NotNull String uid, ItemListener listener) { myUid = uid; myListener = listener; }
void setVisible(boolean visible) { myIsVisible = visible; }
boolean isVisible() { return myIsVisible; }
@Override
public String toString() { return myUid; }
ID getNativePeer() {
// called from AppKit (when NSTouchBarDelegate create items)
if (myNativePeer == ID.NIL)
@@ -26,16 +31,13 @@ abstract class TBItem {
return;
_updateNativePeer();
}
final void releaseNativePeer() {
void releaseNativePeer() {
if (myNativePeer == ID.NIL)
return;
_releaseChildBars();
Foundation.invoke(myNativePeer, "release");
myNativePeer = ID.NIL;
}
protected abstract void _updateNativePeer(); // called from EDT
protected abstract ID _createNativePeer(); // called from AppKit
protected void _releaseChildBars() {} // called from EDT
}
@@ -4,11 +4,11 @@ package com.intellij.ui.mac.touchbar;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -17,7 +17,7 @@ import java.awt.event.KeyEvent;
import static java.awt.event.ComponentEvent.COMPONENT_FIRST;
public class TBItemAnActionButton extends TBItemButton {
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;
@@ -36,24 +36,22 @@ public class TBItemAnActionButton extends TBItemButton {
private Component myComponent;
TBItemAnActionButton(@NotNull String uid, @NotNull AnAction action, boolean hiddenWhenDisabled, int showMode, ModalityState modality) {
super(uid);
TBItemAnActionButton(@NotNull String uid, @Nullable ItemListener listener, @NotNull AnAction action, boolean hiddenWhenDisabled, int showMode, ModalityState modality) {
super(uid, listener);
myAnAction = action;
myActionId = ActionManager.getInstance().getId(myAnAction);
myAction = () -> {
if (modality != null)
ApplicationManager.getApplication().invokeLater(() -> _performAction(), modality);
else
ApplicationManager.getApplication().invokeLater(() -> _performAction());
};
setAction(this::_performAction, true, modality);
myAutoVisibility = true;
myHiddenWhenDisabled = hiddenWhenDisabled;
myIsVisible = false;
myShowMode = showMode;
}
void setComponent(Component component/*for DataCtx*/) { myComponent = component; }
@Override
public String toString() { return String.format("%s [%s]", myActionId, myUid); }
TBItemAnActionButton setComponent(Component component/*for DataCtx*/) { myComponent = component; return this; }
void updateAnAction(Presentation presentation) {
final DataContext dctx = DataManager.getInstance().getDataContext(_getComponent());
@@ -69,7 +67,7 @@ public class TBItemAnActionButton extends TBItemButton {
}
boolean isAutoVisibility() { return myAutoVisibility; }
public void setAutoVisibility(boolean autoVisibility) { myAutoVisibility = autoVisibility; }
void setAutoVisibility(boolean autoVisibility) { myAutoVisibility = autoVisibility; }
AnAction getAnAction() { return myAnAction; }
@@ -82,7 +80,7 @@ public class TBItemAnActionButton extends TBItemButton {
final boolean visibilityChanged = isVisible != myIsVisible;
if (visibilityChanged) {
myIsVisible = isVisible;
// LOG.info(String.format("[%s:%s] visibility changed: now is %s", myUid, myActionId, isVisible ? "visible" : "hidden"));
// System.out.println(String.format("%s: visibility changed, now is [%s]", toString(), isVisible ? "visible" : "hidden"));
}
return visibilityChanged;
}
@@ -1,6 +1,8 @@
// 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.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.util.Comparing;
import com.intellij.ui.mac.foundation.ID;
import org.jetbrains.annotations.NotNull;
@@ -8,18 +10,20 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class TBItemButton extends TBItem {
protected @Nullable NSTLibrary.Action myAction;
class TBItemButton extends TBItem {
protected @Nullable Icon myIcon;
protected @Nullable String myText;
protected int myWidth = -1;
protected int myFlags = 0;
private @Nullable Runnable myAction;
private @Nullable NSTLibrary.Action myNativeCallback;
private int myUpdateOptions;
TBItemButton(@NotNull String uid) { super(uid); }
TBItemButton(@NotNull String uid, @Nullable ItemListener listener) { super(uid, listener); }
public TBItemButton setIcon(Icon icon) {
TBItemButton setIcon(Icon icon) {
if (!_equals(icon, myIcon)) {
myIcon = icon;
if (myNativePeer != ID.NIL) {
@@ -31,7 +35,7 @@ public class TBItemButton extends TBItem {
return this;
}
public TBItemButton setText(String text) {
TBItemButton setText(String text) {
if (!Comparing.equal(text, myText)) {
myText = text;
if (myNativePeer != ID.NIL) {
@@ -43,9 +47,31 @@ public class TBItemButton extends TBItem {
return this;
}
public TBItemButton setAction(NSTLibrary.Action action) {
TBItemButton setActionOnEDT(Runnable action) { return setAction(action, true, null);}
TBItemButton setThreadSafeAction(Runnable action) { return setAction(action, false, null);}
TBItemButton setAction(Runnable action, boolean executeOnEDT, ModalityState modality) {
if (action != myAction) {
myAction = action;
if (myAction == null)
myNativeCallback = null;
else
myNativeCallback = ()->{
// NOTE: executed from AppKit thread
if (executeOnEDT) {
if (modality != null)
ApplicationManager.getApplication().invokeLater(myAction, modality);
else
ApplicationManager.getApplication().invokeLater(myAction);
} else {
myAction.run();
}
if (myListener != null)
myListener.onItemEvent(this, 0);
};
if (myNativePeer != ID.NIL) {
myUpdateOptions |= NSTLibrary.BUTTON_UPDATE_ACTION;
_updateNativePeer();
@@ -55,7 +81,7 @@ public class TBItemButton extends TBItem {
return this;
}
public TBItemButton setWidth(int width) {
TBItemButton setWidth(int width) {
if (width != myWidth) {
myWidth = width;
if (myNativePeer != ID.NIL) {
@@ -68,7 +94,7 @@ public class TBItemButton extends TBItem {
}
// [-128, 127], 0 is the default value
public TBItemButton setPriority(byte prio) {
TBItemButton setPriority(byte prio) {
final int flags = myFlags | NSTLibrary.priority2mask(prio);
if (flags != myFlags) {
myFlags = flags;
@@ -81,9 +107,10 @@ public class TBItemButton extends TBItem {
return this;
}
public TBItemButton setFlags(boolean isSelected, boolean isDisabled) {
TBItemButton setFlags(boolean isSelected, boolean isDisabled, boolean isColored) {
int flags = _applyFlag(myFlags, isSelected, NSTLibrary.BUTTON_FLAG_SELECTED);
flags = _applyFlag(flags, isDisabled, NSTLibrary.BUTTON_FLAG_DISABLED);
flags = _applyFlag(flags, isColored, NSTLibrary.BUTTON_FLAG_COLORED);
if (flags != myFlags) {
myFlags = flags;
if (myNativePeer != ID.NIL) {
@@ -107,7 +134,7 @@ public class TBItemButton extends TBItem {
return ic0 != null ? ic0.equals(ic1) : ic1.equals(ic0);
}
synchronized private void _update(Icon icon, String text, NSTLibrary.Action action, int buttWidth, int buttFlags) {
synchronized private void _update(Icon icon, String text, Runnable action, int buttWidth, int buttFlags) {
if (myNativePeer != ID.NIL) {
if (!_equals(icon, myIcon)) {
// NOTE: some of layered buttons (like 'stop' or 'debug') can change the icon-object permanently (every second) without any visible differences
@@ -136,15 +163,16 @@ public class TBItemButton extends TBItem {
protected void _updateNativePeer() {
final Icon icon = (myUpdateOptions & NSTLibrary.BUTTON_UPDATE_IMG) != 0 ? myIcon : null;
final String text = (myUpdateOptions & NSTLibrary.BUTTON_UPDATE_TEXT) != 0 ? myText : null;
final NSTLibrary.Action callback = (myUpdateOptions & NSTLibrary.BUTTON_UPDATE_ACTION) != 0 ? myNativeCallback : null;
// System.out.printf("_updateNativePeer, button [%s]: updateOptions 0x%X\n", myUid, myUpdateOptions);
NST.updateButton(myNativePeer, myUpdateOptions, myWidth, myFlags, text, icon, myAction);
NST.updateButton(myNativePeer, myUpdateOptions, myWidth, myFlags, text, icon, callback);
myUpdateOptions = 0;
}
@Override
synchronized protected ID _createNativePeer() {
// System.out.printf("_createNativePeer, button [%s]\n", myUid);
return NST.createButton(myUid, myWidth, myFlags, myText, myIcon, myAction);
return NST.createButton(myUid, myWidth, myFlags, myText, myIcon, myNativeCallback);
}
private static int _applyFlag(int src, boolean include, int flag) {
@@ -3,29 +3,35 @@ package com.intellij.ui.mac.touchbar;
import com.intellij.ui.mac.foundation.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
class TBItemGroup extends TBItem {
private final ItemsContainer myGroupItems;
public class TBItemGroup extends TBItem {
private final List<TBItem> myGroupItems = new ArrayList<>();
TBItemGroup(@NotNull String uid, @NotNull List<TBItem> items) {
super(uid);
myGroupItems.addAll(items);
TBItemGroup(@NotNull String uid, @Nullable ItemListener listener) {
super(uid, listener);
myGroupItems = new ItemsContainer(uid + "_group", listener);
}
List<TBItem> getGroupItems() { return myGroupItems; }
ItemsContainer getContainer() { return myGroupItems; }
@Override
protected void _updateNativePeer() { myGroupItems.forEach(item->item._updateNativePeer()); }
protected void _updateNativePeer() {
myGroupItems.forEachDeep(item->item._updateNativePeer());
}
@Override
protected ID _createNativePeer() {
if (myGroupItems.isEmpty())
return ID.NIL;
final ID[] ids = myGroupItems.stream().map(item->item.getNativePeer()).toArray(size -> new ID[size]);
final ID[] ids = myGroupItems.getVisibleNativePeers();
return NST.createGroupItem(myUid, ids, ids.length);
}
@Override
void releaseNativePeer() {
myGroupItems.releaseAll();
super.releaseNativePeer();
}
}
@@ -1,13 +1,13 @@
// 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.ui.mac.foundation.Foundation;
import com.intellij.ui.mac.foundation.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class TBItemPopover extends TBItem {
class TBItemPopover extends TBItem {
private Icon myIcon;
private String myText;
private int myWidthPix;
@@ -15,8 +15,8 @@ public class TBItemPopover extends TBItem {
private TouchBar myTapAndHoldTB;
// NOTE: make popover with 'flexible' width when widthInPix <= 0
public TBItemPopover(@NotNull String uid, Icon icon, String text, int widthInPix, TouchBar expandTB, TouchBar tapAndHoldTB) {
super(uid);
TBItemPopover(@NotNull String uid, @Nullable ItemListener listener, Icon icon, String text, int widthInPix, TouchBar expandTB, TouchBar tapAndHoldTB) {
super(uid, listener);
myIcon = icon;
myText = text;
myWidthPix = widthInPix;
@@ -24,21 +24,8 @@ public class TBItemPopover extends TBItem {
myTapAndHoldTB = tapAndHoldTB;
}
synchronized public void update(Icon icon, String text) {
myIcon = icon;
myText = text;
updateNativePeer();
}
public void dismiss() {
if (myNativePeer == ID.NIL)
return;
Foundation.invoke(myNativePeer, "dismissPopover:", ID.NIL);
}
@Override
protected void _releaseChildBars() {
void releaseNativePeer() {
// called from EDT
if (myExpandTB != null)
myExpandTB.release();
@@ -47,15 +34,18 @@ public class TBItemPopover extends TBItem {
myExpandTB = null;
myTapAndHoldTB = null;
super.releaseNativePeer();
}
// NOTE: popover is immutable (at this moment) => update doesn't called => _create/_update can be unsyncronized
@Override
protected void _updateNativePeer() {
NST.updatePopover(myNativePeer, myWidthPix, myText, myIcon, getNativePeer(myExpandTB), getNativePeer(myTapAndHoldTB));
}
@Override
synchronized protected ID _createNativePeer() {
protected ID _createNativePeer() {
return NST.createPopover(myUid, myWidthPix, myText, myIcon, getNativePeer(myExpandTB), getNativePeer(myTapAndHoldTB));
}
@@ -1,34 +1,48 @@
// 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.application.ApplicationManager;
import com.intellij.ui.mac.foundation.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class TBItemScrubber extends TBItem {
class TBItemScrubber extends TBItem {
private final int myWidth;
private List<ItemData> myItems;
// NOTE: make scrubber with 'flexible' width when scrubWidth <= 0
public TBItemScrubber(@NotNull String uid, int scrubWidth) {
super(uid);
TBItemScrubber(@NotNull String uid, @Nullable ItemListener listener, int scrubWidth) {
super(uid, listener);
myWidth = scrubWidth;
}
synchronized public void setItems(List<ItemData> items) {
myItems = items;
TBItemScrubber addItem(Icon icon, String text, Runnable action) {
if (myItems == null)
myItems = new ArrayList<>();
final NSTLibrary.Action nativeAction = action == null && myListener == null ? null : ()-> {
if (action != null)
ApplicationManager.getApplication().invokeLater(action);
if (myListener != null)
myListener.onItemEvent(this, 0);
};
myItems.add(new ItemData(icon, text, nativeAction));
updateNativePeer();
return this;
}
// NOTE: scrubber is immutable (at this moment) => update doesn't called => _create/_update can be unsyncronized
@Override
protected void _updateNativePeer() { NST.updateScrubber(myNativePeer, myWidth, myItems); }
@Override
synchronized protected ID _createNativePeer() { return NST.createScrubber(myUid, myWidth, myItems); }
protected ID _createNativePeer() { return NST.createScrubber(myUid, myWidth, myItems); }
public static class ItemData {
static class ItemData {
final Icon myIcon;
final String myText;
final NSTLibrary.Action myAction;
@@ -2,43 +2,57 @@
package com.intellij.ui.mac.touchbar;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.PresentationFactory;
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.ui.mac.foundation.ID;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import java.awt.*;
import java.util.function.Consumer;
public class TouchBar implements NSTLibrary.ItemCreator {
static final Logger LOG = Logger.getInstance(TouchBar.class);
class TouchBar implements NSTLibrary.ItemCreator {
private static final Logger LOG = Logger.getInstance(TouchBar.class);
protected final @NotNull String myName; // just for logging/debugging
protected ID myNativePeer; // java wrapper holds native object
protected long myCounter = 0;
protected final List<TBItem> myItems = new ArrayList<>();
protected final boolean myReleaseOnClose;
private final ItemsContainer myItems;
private final ItemListener myItemListener;
private final boolean myReleaseOnClose;
private final TBItemButton myCustomEsc;
private final PresentationFactory myPresentationFactory = new PresentationFactory();
protected final TBItemButton myCustomEsc;
private ID myNativePeer; // java wrapper holds native object
private TimerListener myTimerListener;
public static final TouchBar EMPTY = new TouchBar();
private TouchBar() {
myName = "EMPTY_STUB_TOUCHBAR";
myItems = new ItemsContainer("EMPTY_STUB_TOUCHBAR", null);
myCustomEsc = null;
myNativePeer = ID.NIL;
myReleaseOnClose = false;
myItemListener = null;
}
public TouchBar(@NotNull String touchbarName, boolean replaceEsc) {
this(touchbarName, replaceEsc, false);
TouchBar(@NotNull String touchbarName, boolean replaceEsc) {
this(touchbarName, replaceEsc, false, false);
}
public TouchBar(@NotNull String touchbarName, boolean replaceEsc, boolean releaseOnClose) {
myName = touchbarName;
TouchBar(@NotNull String touchbarName, boolean replaceEsc, boolean releaseOnClose, boolean autoClose) {
if (autoClose) {
myItemListener = (src, evcode) -> {
// NOTE: called from AppKit thread
_closeSelf();
};
} else
myItemListener = null;
myItems = new ItemsContainer(touchbarName, myItemListener);
if (replaceEsc)
myCustomEsc = new TBItemButton(genNewID("esc")).setIcon(AllIcons.Actions.Cancel).setAction(this::_closeSelf);
myCustomEsc = new TBItemButton(touchbarName + "_custom_esc_button", myItemListener).setIcon(AllIcons.Actions.Cancel).setThreadSafeAction(this::_closeSelf);
else
myCustomEsc = null;
@@ -46,17 +60,25 @@ public class TouchBar implements NSTLibrary.ItemCreator {
myReleaseOnClose = releaseOnClose;
}
public boolean isManualClose() { return myCustomEsc != null; }
static TouchBar buildFromGroup(@NotNull String touchbarName, @NotNull ActionGroup customizedGroup, boolean replaceEsc) {
final TouchBar result = new TouchBar(touchbarName, replaceEsc);
BuildUtils.addCustomizedActionGroup(result.myItems, customizedGroup);
result.selectVisibleItemsToShow();
return result;
}
boolean isManualClose() { return myCustomEsc != null; }
boolean isEmpty() { return myItems.isEmpty(); }
@Override
public String toString() { return myName + "_" + myNativePeer; }
public String toString() { return myItems.toString() + "_" + myNativePeer; }
@Override
public ID createItem(@NotNull String uid) { // called from AppKit (when NSTouchBarDelegate create items)
if (myCustomEsc != null && myCustomEsc.myUid.equals(uid))
return myCustomEsc.getNativePeer();
TBItem item = findItem(uid);
TBItem item = myItems.findItem(uid);
if (item == null) {
LOG.error("can't find TBItem with uid '%s'", uid);
return ID.NIL;
@@ -68,103 +90,113 @@ public class TouchBar implements NSTLibrary.ItemCreator {
ID getNativePeer() { return myNativePeer; }
void release() {
for (TBItem item : myItems)
item.releaseNativePeer();
NST.releaseTouchBar(myNativePeer);
myItems.clear();
myNativePeer = ID.NIL;
myItems.releaseAll();
if (!myNativePeer.equals(ID.NIL)) {
NST.releaseTouchBar(myNativePeer);
myNativePeer = ID.NIL;
}
_stopTimer();
}
//
// NOTE: must call 'selectVisibleItemsToShow' after touchbar filling
//
public TBItemButton addButton() {
final TBItemButton butt = new TBItemButton(genNewID("button"));
myItems.add(butt);
return butt;
@NotNull TBItemButton addButton() { return myItems.addButton(); }
@NotNull TBItemAnActionButton addAnActionButton(@NotNull AnAction act, boolean hiddenWhenDisabled, int showMode, ModalityState modality) {
return myItems.addAnActionButton(act, hiddenWhenDisabled, showMode, modality);
}
public TBItemGroup addGroup(List<TBItem> items) {
final TBItemGroup group = new TBItemGroup(genNewID("group"), items);
myItems.add(group);
return group;
@NotNull TBItemGroup addGroup() { return myItems.addGroup(); }
@NotNull TBItemScrubber addScrubber() { return myItems.addScrubber(); }
@NotNull TBItemPopover addPopover(Icon icon, String text, int width, TouchBar expandTB, TouchBar tapAndHoldTB) {
return myItems.addPopover(icon, text, width, expandTB, tapAndHoldTB);
}
@NotNull void addSpacing(boolean large) { myItems.addSpacing(large); }
@NotNull void addFlexibleSpacing() { myItems.addFlexibleSpacing(); }
public TBItemPopover addPopover(Icon icon, String text, int width, TouchBar expandTB, TouchBar tapAndHoldTB) {
final TBItemPopover popover = new TBItemPopover(genNewID("popover"), icon, text, width, expandTB, tapAndHoldTB);
myItems.add(popover);
return popover;
}
public TBItemScrubber addScrubber() {
final int defaultScrubberWidth = 500;
final TBItemScrubber scrubber = new TBItemScrubber(genNewID("scrubber"), defaultScrubberWidth);
myItems.add(scrubber);
return scrubber;
}
public void addSpacing(boolean large) {
final SpacingItem spacing = new SpacingItem(large ? "static_touchbar_item_large_space" : "static_touchbar_item_small_space");
myItems.add(spacing);
}
public void addFlexibleSpacing() {
final SpacingItem spacing = new SpacingItem("static_touchbar_item_flexible_space");
myItems.add(spacing);
}
public void selectVisibleItemsToShow() {
void selectVisibleItemsToShow() {
if (myItems.isEmpty())
return;
// TODO: cache prev ids and compare with new list
final String[] ids = new String[myItems.size()];
int c = 0;
for (TBItem item : myItems) {
if (item.isVisible())
ids[c++] = item.myUid;
}
final String[] ids = myItems.getVisibleIds();
NST.selectItemsToShow(myNativePeer, ids, ids.length);
}
public void setPrincipal(@NotNull TBItem item) { NST.setPrincipal(myNativePeer, item.myUid); }
void setPrincipal(@NotNull TBItem item) { NST.setPrincipal(myNativePeer, item.myUid); }
public void onBeforeShow() {}
public void onHide() {
void onBeforeShow() {
if (myItems.hasAnActionItems()) {
updateActionItems();
if (myTimerListener == null) {
myTimerListener = new TimerListener() {
@Override
public ModalityState getModalityState() { return ModalityState.current(); }
@Override
public void run() { updateActionItems(); }
};
}
ActionManager.getInstance().addTransparentTimerListener(500/*delay param doesn't affect anything*/, myTimerListener);
}
}
void onHide() { _stopTimer(); }
void onClose() {
_stopTimer();
if (myReleaseOnClose)
release();
}
String genNewID(String desc) { return String.format("%s.%s.%d", myName, desc, myCounter++); }
void forEachDeep(Consumer<? super TBItem> proc) { myItems.forEachDeep(proc); }
void forEach(Consumer<? super TBItem> proc) {
myItems.forEach((item -> {
if (item instanceof TBItemGroup) {
((TBItemGroup)item).getGroupItems().forEach(proc);
void updateActionItems() {
ApplicationManager.getApplication().assertIsDispatchThread();
final boolean[] layoutChanged = new boolean[]{false};
forEachDeep(tbitem->{
if (!(tbitem instanceof TBItemAnActionButton))
return;
final TBItemAnActionButton item = (TBItemAnActionButton)tbitem;
final Presentation presentation = myPresentationFactory.getPresentation(item.getAnAction());
try {
item.updateAnAction(presentation);
} catch (IndexNotReadyException e1) {
presentation.setEnabled(false);
presentation.setVisible(false);
}
proc.accept(item);
}));
if (item.isAutoVisibility()) {
final boolean itemVisibilityChanged = item.updateVisibility(presentation);
if (itemVisibilityChanged)
layoutChanged[0] = true;
}
item.updateView(presentation);
});
if (layoutChanged[0])
selectVisibleItemsToShow();
}
private TBItem findItem(String uid) {
for (TBItem item : myItems)
if (item.myUid.equals(uid))
return item;
return null;
void setComponent(Component component/*for DataContext*/) {
myItems.forEachDeep(item -> {
if (item instanceof TBItemAnActionButton)
((TBItemAnActionButton)item).setComponent(component);
});
}
private void _closeSelf() { TouchBarsManager.closeTouchBar(this, false); }
private void _closeSelf() { TouchBarsManager.closeTouchBar(this); }
private static class SpacingItem extends TBItem {
SpacingItem(@NotNull String uid) { super(uid); }
@Override
protected void _updateNativePeer() {} // mustn't be called
@Override
protected ID _createNativePeer() { return null; } // mustn't be called
private void _stopTimer() {
if (myTimerListener != null) {
ActionManager.getInstance().removeTransparentTimerListener(myTimerListener);
myTimerListener = null;
}
}
}
class SpacingItem extends TBItem {
SpacingItem(@NotNull String uid) { super(uid, null); }
@Override
protected void _updateNativePeer() {} // mustn't be called
@Override
protected ID _createNativePeer() { return null; } // mustn't be called
}
@@ -1,274 +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.Executor;
import com.intellij.execution.ExecutorRegistry;
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.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.openapi.wm.ToolWindowId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
public class TouchBarActionBase extends TouchBarProjectBase {
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;
static {
_initExecutorsGroup();
}
public TouchBarActionBase(@NotNull String touchbarName, @NotNull Project project) { this(touchbarName, project, false); }
public TouchBarActionBase(@NotNull String touchbarName, @NotNull Project project, @NotNull ActionGroup customizedGroup, boolean replaceEsc) {
this(touchbarName, project, replaceEsc);
final String groupId = _getActionId(customizedGroup);
if (groupId == null) {
LOG.error("unregistered group: " + customizedGroup);
return;
}
addActionGroupButtons(customizedGroup, null, TBItemAnActionButton.SHOWMODE_IMAGE_ONLY_IF_PRESENTED, nodeId -> nodeId.contains(groupId + "_"), null);
}
private TouchBarActionBase(@NotNull String touchbarName, @NotNull Project project, boolean replaceEsc) {
super(touchbarName, project, replaceEsc);
myTimerListener = new TimerListener() {
@Override
public ModalityState getModalityState() { return ModalityState.current(); }
@Override
public void run() { updateActionItems(); }
};
}
@Override
public void release() {
super.release();
ActionManager.getInstance().removeTransparentTimerListener(myTimerListener);
}
@Override
public void onBeforeShow() {
updateActionItems();
ActionManager.getInstance().addTransparentTimerListener(500/*delay param doesn't affect anything*/, myTimerListener);
}
@Override
public void onHide() { ActionManager.getInstance().removeTransparentTimerListener(myTimerListener); }
private TBItemAnActionButton _addAnActionButton(@NotNull AnAction act, boolean hiddenWhenDisabled, int showMode, ModalityState modality) {
final String uid = String.format("%s.anActionButton.%d.%s", myName, myCounter++, ActionManager.getInstance().getId(act));
final TBItemAnActionButton butt = new TBItemAnActionButton(uid, act, hiddenWhenDisabled, showMode, modality);
myItems.add(butt);
return butt;
}
public void setComponent(Component component/*for DataContext*/) {
myItems.forEach(item -> {
if (item instanceof TBItemAnActionButton)
((TBItemAnActionButton)item).setComponent(component);
});
}
public void addActionGroupButtons(ActionGroup actionGroup, ModalityState modality, int showMode, INodeFilter filter, ICustomizer customizer) {
_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, modality);
if (isRunConfigPopover)
butt.setWidth(ourRunConfigurationPopoverWidth);
if (customizer != null)
customizer.customize(butt);
}
}, filter);
}
void updateActionItems() {
ApplicationManager.getApplication().assertIsDispatchThread();
final boolean[] layoutChanged = new boolean[]{false};
forEach(tbitem->{
if (!(tbitem instanceof TBItemAnActionButton))
return;
final TBItemAnActionButton item = (TBItemAnActionButton)tbitem;
final Presentation presentation = myPresentationFactory.getPresentation(item.getAnAction());
try {
item.updateAnAction(presentation);
} catch (IndexNotReadyException e1) {
presentation.setEnabled(false);
presentation.setVisible(false);
}
if (item.isAutoVisibility()) {
final boolean itemVisibilityChanged = item.updateVisibility(presentation);
if (itemVisibilityChanged)
layoutChanged[0] = true;
}
item.updateView(presentation);
});
if (layoutChanged[0])
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)
LOG.error("can't find action by id: " + actId);
return act;
}
protected interface INodeFilter {
boolean skip(String nodeId);
}
protected interface ICustomizer {
void customize(TBItem item);
}
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,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;
public class TouchBarProjectBase extends TouchBar {
protected final @NotNull Project myProject;
public TouchBarProjectBase(@NotNull String touchbarName, @NotNull Project project) { this(touchbarName, project, false); }
public TouchBarProjectBase(@NotNull String touchbarName, @NotNull Project project, boolean replaceEsc) {
super(touchbarName, replaceEsc);
myProject = project;
}
}
@@ -5,31 +5,21 @@ import com.intellij.execution.ExecutionListener;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.impl.LaterInvocator;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.FocusChangeListener;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.OptionAction;
import com.intellij.openapi.ui.popup.JBPopupListener;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.ui.popup.ListPopupStep;
import com.intellij.openapi.ui.popup.MnemonicNavigationFilter;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
import com.intellij.ui.components.JBOptionButton;
import com.intellij.ui.popup.list.ListPopupImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -37,7 +27,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.MouseWheelEvent;
@@ -45,7 +34,6 @@ import java.util.*;
import java.util.List;
public class TouchBarsManager {
private static final Logger LOG = Logger.getInstance(TouchBarsManager.class);
private static final ArrayDeque<BarContainer> ourTouchBarStack = new ArrayDeque<>();
private static final TouchBarHolder ourTouchBarHolder = new TouchBarHolder();
private static long ourCurrentKeyMask;
@@ -62,7 +50,7 @@ public class TouchBarsManager {
// System.out.println("opened project " + project + ", set default touchbar");
final ProjectData pd = _getProjData(project);
showTouchBar(pd.get(BarType.DEFAULT));
_showContainer(pd.get(BarType.DEFAULT));
project.getMessageBus().connect().subscribe(ToolWindowManagerListener.TOPIC, new ToolWindowManagerListener() {
@Override
@@ -74,7 +62,7 @@ public class TouchBarsManager {
if (pd.getDbgSessions() <= 0)
return;
showTouchBar(pd.get(BarType.DEBUGGER));
_showContainer(pd.get(BarType.DEBUGGER));
}
}
});
@@ -96,15 +84,15 @@ public class TouchBarsManager {
if (isDebugger) {
if (executorId.equals(ToolWindowId.DEBUG) || executorId.equals(ToolWindowId.RUN_DASHBOARD)) {
// System.out.println("processTerminated, dbgSessionsCount=" + pd.getDbgSessions());
final boolean hasDebugSession = _hasAnyActiveSession(project, handler);
if (!hasDebugSession || pd.getDbgSessions() <= 0)
closeTouchBar(top);
final boolean hasDebugSession = _hasAnyActiveSession(project, handler) && pd.getDbgSessions() > 0;
if (!hasDebugSession)
_closeContainer(top);
}
}
}
if (curr instanceof TouchBarActionBase)
ApplicationManager.getApplication().invokeLater(() -> { ((TouchBarActionBase)curr).updateActionItems(); });
if (curr != null)
ApplicationManager.getApplication().invokeLater(() -> curr.updateActionItems());
}
});
}
@@ -112,13 +100,22 @@ public class TouchBarsManager {
public void projectClosed(Project project) {
// System.out.println("closed project " + project + ", hide touchbar");
final ProjectData pd = _getProjData(project);
closeTouchBar(pd.get(BarType.DEFAULT));
synchronized (TouchBarsManager.class) {
pd.forEach((btype, bc) -> {
ourTouchBarStack.remove(bc);
});
if (!ourTouchBarStack.isEmpty())
_setBarContainer(ourTouchBarStack.peek());
}
pd.releaseAll();
ourProjectData.remove(project);
}
});
}
public static boolean isTouchBarAvailable() { return NST.isAvailable(); }
synchronized
public static void reloadAll() {
if (!isTouchBarAvailable())
return;
@@ -129,28 +126,12 @@ public class TouchBarsManager {
_setBarContainer(ourTouchBarStack.peek());
}
public static boolean isTouchBarAvailable() { return NST.isAvailable(); }
synchronized public static void showTouchBar(BarContainer bar) {
if (bar == null)
return;
final BarContainer top = ourTouchBarStack.peek();
if (top == bar)
return;
ourTouchBarStack.remove(bar);
ourTouchBarStack.push(bar);
_setBarContainer(bar);
}
synchronized public static void closeTouchBar(TouchBar tb, boolean doRelease) {
synchronized
static void closeTouchBar(TouchBar tb) {
if (tb == null)
return;
if (doRelease)
tb.release();
tb.onClose();
if (ourTouchBarStack.isEmpty())
return;
@@ -171,10 +152,12 @@ public class TouchBarsManager {
if (e instanceof MouseWheelEvent)
return;
if (ourCurrentKeyMask != e.getModifiersEx()) {
// LOG.debug("change current mask: 0x%X -> 0x%X\n", ourCurrentKeyMask, e.getModifiersEx());
ourCurrentKeyMask = e.getModifiersEx();
_setBarContainer(ourTouchBarStack.peek());
synchronized (TouchBarsManager.class) {
if (ourCurrentKeyMask != e.getModifiersEx()) {
// System.out.printf("change current mask: 0x%X -> 0x%X\n", ourCurrentKeyMask, e.getModifiersEx());
ourCurrentKeyMask = e.getModifiersEx();
_setBarContainer(ourTouchBarStack.peek());
}
}
}
@@ -188,6 +171,11 @@ public class TouchBarsManager {
return;
ourProjectData.forEach((project, data) -> {
if (project.isDisposed())
return;
if (data.getDbgSessions() <= 0)
return;
final ToolWindowManagerEx twm = ToolWindowManagerEx.getInstanceEx(project);
if (twm == null)
return;
@@ -205,7 +193,7 @@ public class TouchBarsManager {
|| (compD != null && SwingUtilities.isDescendingFrom((Component)e.getSource(), compD))
|| (compR != null && SwingUtilities.isDescendingFrom((Component)e.getSource(), compR))
)
showTouchBar(data.get(BarType.DEBUGGER));
_showContainer(data.get(BarType.DEBUGGER));
});
}
}
@@ -221,7 +209,9 @@ public class TouchBarsManager {
editor.addFocusListener(new FocusChangeListener() {
@Override
public void focusGained(Editor editor) {
_elevateTouchBar(_getProjData(proj).get(BarType.DEFAULT));
final boolean hasDebugSession = _getProjData(proj).getDbgSessions() > 0;
if (!hasDebugSession)
_elevateTouchBar(_getProjData(proj).get(BarType.DEFAULT));
}
@Override
public void focusLost(Editor editor) {}
@@ -233,83 +223,81 @@ public class TouchBarsManager {
return;
listPopup.addPopupListener(new JBPopupListener() {
private TouchBar myPopupBar = _createScrubberBarFromPopup(listPopup);
private TouchBar myPopupBar = BuildUtils.createScrubberBarFromPopup(listPopup);
@Override
public void beforeShown(LightweightWindowEvent event) {
_showTempTouchBar(myPopupBar, BarType.POPUP);
}
@Override
public void onClosed(LightweightWindowEvent event) {
closeTouchBar(myPopupBar, true);
closeTouchBar(myPopupBar);
myPopupBar = null;
}
}
);
}
public static @Nullable Runnable showDlgButtonsBar(List<JButton> jbuttons, Project project) {
public static @Nullable Runnable showDlgButtonsBar(List<JButton> jbuttons) {
if (!isTouchBarAvailable())
return null;
final TouchBar tb = _createButtonsBar(jbuttons, project);
final TouchBar tb = BuildUtils.createButtonsBar(jbuttons);
_showTempTouchBar(tb, BarType.DIALOG);
return ()->{closeTouchBar(tb, true);};
return ()->{closeTouchBar(tb);};
}
synchronized public static void showStopRunningBar(TouchBar tb) {
_showTempTouchBar(tb, BarType.DIALOG);
}
synchronized public static Runnable showMessageDlgBar(@NotNull String[] buttons, @NotNull Runnable[] actions, String defaultButton) {
public static Runnable showMessageDlgBar(@NotNull String[] buttons, @NotNull Runnable[] actions, String defaultButton) {
if (!isTouchBarAvailable())
return null;
// NOTE: buttons are placed from right to left, see SheetController.layoutButtons
final List<TBItem> groupButtons = new ArrayList<>();
int defIndex = -1;
final int len = Math.min(buttons.length, actions.length);
for (int c = 0; c < len; ++c) {
final String sb = buttons[c];
final boolean isDefault = Comparing.equal(sb, defaultButton);
if (isDefault) {
defIndex = c;
continue;
}
groupButtons.add(
new TBItemButton("message_dlg_bar_group_item_" + c)
.setText(DialogWrapper.extractMnemonic(sb).second)
.setAction(NSTLibrary.run2act(actions[c])));
}
Collections.reverse(groupButtons);
if (defIndex >= 0)
groupButtons.add(
new TBItemButton("message_dlg_bar_group_item_default")
.setText(DialogWrapper.extractMnemonic(buttons[defIndex]).second)
.setAction(NSTLibrary.run2act(actions[defIndex]))
.setFlags(true, false));
final TouchBar tb;
try (NSAutoreleaseLock lock = new NSAutoreleaseLock()) {
tb = new TouchBar("message_dlg_bar", false);
final TBItemGroup gr = tb.addGroup(groupButtons);
tb.setPrincipal(gr);
}
final TouchBar tb = BuildUtils.createMessageDlgBar(buttons, actions, defaultButton);
_showTempTouchBar(tb, BarType.DIALOG);
return ()->{closeTouchBar(tb, true);};
return ()->{closeTouchBar(tb);};
}
synchronized private static void _showTempTouchBar(TouchBar tb, BarType type) {
if (tb == null)
public static void showStopRunningBar(List<Pair<RunContentDescriptor, Runnable>> stoppableDescriptors) {
final TouchBar tb = BuildUtils.createStopRunningBar(stoppableDescriptors);
_showTempTouchBar(tb, BarType.DIALOG);
}
synchronized
private static void _showContainer(BarContainer bar) {
if (bar == null)
return;
tb.selectVisibleItemsToShow();
BarContainer container = new BarContainer(type, tb, null);
showTouchBar(container);
final BarContainer top = ourTouchBarStack.peek();
if (top == bar)
return;
ourTouchBarStack.remove(bar);
ourTouchBarStack.push(bar);
_setBarContainer(bar);
}
synchronized private static void _elevateTouchBar(BarContainer bar) {
synchronized
private static void _closeContainer(BarContainer tb) {
if (tb == null || ourTouchBarStack.isEmpty())
return;
BarContainer top = ourTouchBarStack.peek();
if (top == tb) {
ourTouchBarStack.pop();
_setBarContainer(ourTouchBarStack.peek());
} else {
ourTouchBarStack.remove(tb);
}
}
synchronized
private static void _showTempTouchBar(TouchBar tb, BarType type) {
if (tb == null)
return;
BarContainer container = new BarContainer(type, tb, null);
_showContainer(container);
}
synchronized
private static void _elevateTouchBar(BarContainer bar) {
if (bar == null)
return;
@@ -330,20 +318,8 @@ public class TouchBarsManager {
}
}
synchronized public static void closeTouchBar(BarContainer tb) {
if (tb == null || ourTouchBarStack.isEmpty())
return;
BarContainer top = ourTouchBarStack.peek();
if (top == tb) {
ourTouchBarStack.pop();
_setBarContainer(ourTouchBarStack.peek());
} else {
ourTouchBarStack.remove(tb);
}
}
synchronized private static void _setBarContainer(BarContainer barContainer) {
synchronized
private static void _setBarContainer(BarContainer barContainer) {
if (barContainer == null) {
ourTouchBarHolder.setTouchBar(null);
return;
@@ -369,8 +345,8 @@ public class TouchBarsManager {
}
synchronized void updateCurrent() {
if (myCurrentBar instanceof TouchBarActionBase)
((TouchBarActionBase)myCurrentBar).updateActionItems();
if (myCurrentBar != null)
myCurrentBar.updateActionItems();
}
synchronized private void _setNextTouchBar() {
@@ -387,135 +363,6 @@ public class TouchBarsManager {
}
}
private static TouchBar _createButtonsBar(List<JButton> jbuttons, Project project) {
try (NSAutoreleaseLock lock = new NSAutoreleaseLock()) {
final TouchBarActionBase result = new TouchBarActionBase("dialog_buttons", project);
final ModalityState ms = LaterInvocator.getCurrentModalityState();
// 1. add option buttons (at left)
for (JButton jb : jbuttons) {
if (jb instanceof JBOptionButton) {
final JBOptionButton ob = (JBOptionButton)jb;
final Action[] opts = ob.getOptions();
for (Action a : opts) {
if (a == null)
continue;
final AnAction anAct = _createAnAction(a, ob, true);
if (anAct == null)
continue;
final TBItemAnActionButton butt = new TBItemAnActionButton(result.genNewID(a.toString()), anAct, false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms);
butt.setComponent(ob);
result.myItems.add(butt);
}
}
}
// 2. set different priorities for items, otherwise system can hide all items with the same priority (but some of them is able to be placed)
byte prio = -1;
for (TBItem item: result.myItems) {
if (item instanceof TBItemButton)
((TBItemButton)item).setPriority(--prio);
}
// 3. add main buttons and make principal
final List<TBItem> groupButtons = new ArrayList<>();
TBItemAnActionButton def = null;
for (JButton jb : jbuttons) {
// TODO: make correct processing for disabled buttons, add them and update state by timer
// NOTE: can be true: jb.getAction().isEnabled() && !jb.isEnabled()
final AnAction anAct = _createAnAction(jb.getAction(), jb, false);
if (anAct == null)
continue;
final int index = jbuttons.indexOf(jb);
final TBItemAnActionButton butt = new TBItemAnActionButton("dialog_buttons_group_item_" + index, anAct, false, TBItemAnActionButton.SHOWMODE_TEXT_ONLY, ms);
butt.setComponent(jb);
final boolean isDefault = jb.getAction().getValue(DialogWrapper.DEFAULT_ACTION) != null;
if (isDefault) {
def = butt;
def.myFlags |= NSTLibrary.BUTTON_FLAG_COLORED;
continue;
}
groupButtons.add(butt);
}
if (def != null)
groupButtons.add(def);
final TBItemGroup gr = result.addGroup(groupButtons);
result.setPrincipal(gr);
return result;
}
}
private static AnAction _createAnAction(@NotNull Action action, JButton fromButton, boolean useTextFromAction /*for optional buttons*/) {
final Object anAct = action.getValue(OptionAction.AN_ACTION);
if (anAct == null) {
// LOG.warn("null AnAction in action: '" + action + "', use wrapper");
return new DumbAwareAction() {
{
setEnabledInModalContext(true);
if (useTextFromAction) {
final Object name = action.getValue(Action.NAME);
getTemplatePresentation().setText(name != null && name instanceof String ? (String)name : "");
}
}
@Override
public void actionPerformed(AnActionEvent e) {
// also can be used something like: ApplicationManager.getApplication().invokeLater(() -> jb.doClick(), ms)
action.actionPerformed(new ActionEvent(fromButton, ActionEvent.ACTION_PERFORMED, null));
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(action.isEnabled());
if (!useTextFromAction)
e.getPresentation().setText(DialogWrapper.extractMnemonic(fromButton.getText()).second);
}
};
}
if (!(anAct instanceof AnAction)) {
// LOG.warn("unknown type of awt.Action's property: " + anAct.getClass().toString());
return null;
}
return (AnAction)anAct;
}
private static TouchBar _createScrubberBarFromPopup(@NotNull ListPopupImpl listPopup) {
try (NSAutoreleaseLock lock = new NSAutoreleaseLock()) {
final TouchBar result = new TouchBar("popup_scrubber_bar" + listPopup, false);
List<TBItemScrubber.ItemData> items = new ArrayList<>();
@NotNull ListPopupStep listPopupStep = listPopup.getListStep();
for (Object obj : listPopupStep.getValues()) {
final Icon ic = listPopupStep.getIconFor(obj);
String txt = listPopupStep.getTextFor(obj);
if (listPopupStep.isMnemonicsNavigationEnabled()) {
final MnemonicNavigationFilter<Object> filter = listPopupStep.getMnemonicNavigationFilter();
final int pos = filter == null ? -1 : filter.getMnemonicPos(obj);
if (pos != -1)
txt = txt.substring(0, pos) + txt.substring(pos + 1);
}
final Runnable action = () -> {
listPopup.getList().setSelectedValue(obj, false);
listPopup.handleSelect(true);
};
items.add(new TBItemScrubber.ItemData(ic, txt, () -> ApplicationManager.getApplication().invokeLater(() -> action.run())));
}
final TBItemScrubber scrub = result.addScrubber();
scrub.setItems(items);
result.selectVisibleItemsToShow();
return result;
}
}
private static boolean _hasAnyActiveSession(Project proj, ProcessHandler handler/*already terminated*/) {
final ProcessHandler[] processes = ExecutionManager.getInstance(proj).getRunningProcesses();
return Arrays.stream(processes).anyMatch(h -> h != null && h != handler && (!h.isProcessTerminated() && !h.isProcessTerminating()));
@@ -19,11 +19,9 @@ public class TouchbarTest {
Foundation.init();
NST.loadLibrary();
try (NSAutoreleaseLock lock = new NSAutoreleaseLock()) {
final TouchBar testTB = _createTestScrubberTouchbar();
testTB.selectVisibleItemsToShow();
NST.setTouchBar(testTB);
}
final TouchBar testTB = _createTestScrubberTouchbar();
testTB.selectVisibleItemsToShow();
NST.setTouchBar(testTB);
final JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
@@ -33,9 +31,9 @@ public class TouchbarTest {
private static TouchBar _createTestButtonsTouchbar() {
final TouchBar testTB = new TouchBar("test", false);
testTB.addButton().setText("test1").setAction(createPrintTextCallback("pressed test1 button"));
testTB.addButton().setText("test2").setAction(createPrintTextCallback("pressed test2 button"));
testTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowRun).setAction(createPrintTextCallback("pressed image button"));
testTB.addButton().setText("test1").setThreadSafeAction(createPrintTextCallback("pressed test1 button"));
testTB.addButton().setText("test2").setThreadSafeAction(createPrintTextCallback("pressed test2 button"));
testTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowRun).setThreadSafeAction(createPrintTextCallback("pressed image button"));
return testTB;
}
@@ -44,16 +42,15 @@ public class TouchbarTest {
testTB.addSpacing(true);
final TBItemScrubber scrubber = testTB.addScrubber();
List<TBItemScrubber.ItemData> scrubberItems = new ArrayList<>();
for (int c = 0; c < 11; ++c) {
String txt;
if (c == 3) txt = "very very long text";
else txt = String.format("r%1.2f", Math.random());
int finalC = c;
scrubberItems.add(new TBItemScrubber.ItemData(AllIcons.Toolwindows.ToolWindowPalette, txt,
() -> System.out.println("performed action of scrubber item at index " + finalC + " [thread:" + Thread.currentThread() + "]")));
scrubber.addItem(AllIcons.Toolwindows.ToolWindowPalette, txt, () -> {
System.out.println("performed action of scrubber item at index " + finalC + " [thread:" + Thread.currentThread() + "]");
});
}
scrubber.setItems(scrubberItems);
return testTB;
}
@@ -61,17 +58,17 @@ public class TouchbarTest {
private static TouchBar _createTestAllTouchbar() {
final TouchBar testTB = new TouchBar("test", false);
testTB.addSpacing(true);
testTB.addButton().setText("test1").setAction(createPrintTextCallback("pressed test1 button"));
testTB.addButton().setText("test2").setAction(createPrintTextCallback("pressed test2 button"));
testTB.addButton().setText("test1").setThreadSafeAction(createPrintTextCallback("pressed test1 button"));
testTB.addButton().setText("test2").setThreadSafeAction(createPrintTextCallback("pressed test2 button"));
testTB.addSpacing(false);
testTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowRun).setAction(createPrintTextCallback("pressed image button"));
testTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowRun).setThreadSafeAction(createPrintTextCallback("pressed image button"));
final TouchBar tapHoldTB = new TouchBar("test_popover_tap_and_hold", false);
final TouchBar expandTB = new TouchBar("test_configs_popover_expand", false);
final int configPopoverWidth = 143;
testTB.addPopover(AllIcons.Toolwindows.ToolWindowBuild, "test-popover", configPopoverWidth, expandTB, tapHoldTB);
expandTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowDebugger).setAction(createPrintTextCallback("pressed popover-image button"));
expandTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowDebugger).setThreadSafeAction(createPrintTextCallback("pressed popover-image button"));
final TBItemScrubber scrubber = expandTB.addScrubber();
List<TBItemScrubber.ItemData> scrubberItems = new ArrayList<>();
for (int c = 0; c < 15; ++c) {
@@ -84,19 +81,14 @@ public class TouchbarTest {
}
expandTB.selectVisibleItemsToShow();
tapHoldTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowPalette).setAction(createPrintTextCallback("pressed tap-hold-image button"));
tapHoldTB.addButton().setIcon(AllIcons.Toolwindows.ToolWindowPalette).setThreadSafeAction(createPrintTextCallback("pressed tap-hold-image button"));
tapHoldTB.selectVisibleItemsToShow();
return testTB;
}
private static NSTLibrary.Action createPrintTextCallback(String text) {
return new NSTLibrary.Action() {
@Override
public void execute() {
System.out.println(text + " [thread:" + Thread.currentThread() + "]");
}
};
private static Runnable createPrintTextCallback(String text) {
return ()->{ System.out.println(text + " [thread:" + Thread.currentThread() + "]"); };
}
}