mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
Binary file not shown.
@@ -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]);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() + "]"); };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user