diff --git a/bin/mac/libnst64.dylib b/bin/mac/libnst64.dylib index f112472a5f65..0e1a91df7fb0 100755 Binary files a/bin/mac/libnst64.dylib and b/bin/mac/libnst64.dylib differ diff --git a/native/MacTouchBar/src/Button.m b/native/MacTouchBar/src/Button.m index c901d3130838..5558a8a0c872 100644 --- a/native/MacTouchBar/src/Button.m +++ b/native/MacTouchBar/src/Button.m @@ -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]); diff --git a/native/MacTouchBar/src/Popover.m b/native/MacTouchBar/src/Popover.m index 6142f82fd903..977e6e39a440 100644 --- a/native/MacTouchBar/src/Popover.m +++ b/native/MacTouchBar/src/Popover.m @@ -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]); diff --git a/native/MacTouchBar/src/Scrubber.m b/native/MacTouchBar/src/Scrubber.m index 52f953cad5aa..db7ba7b5faba 100644 --- a/native/MacTouchBar/src/Scrubber.m +++ b/native/MacTouchBar/src/Scrubber.m @@ -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]); diff --git a/native/MacTouchBar/src/TouchBar.m b/native/MacTouchBar/src/TouchBar.m index c896ed12d710..a49998b502c4 100644 --- a/native/MacTouchBar/src/TouchBar.m +++ b/native/MacTouchBar/src/TouchBar.m @@ -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]; diff --git a/native/MacTouchBar/src/Utils.h b/native/MacTouchBar/src/Utils.h index 7ec2b9cc2e9b..d2ce1d4ee1dc 100644 --- a/native/MacTouchBar/src/Utils.h +++ b/native/MacTouchBar/src/Utils.h @@ -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); diff --git a/native/MacTouchBar/src/Utils.m b/native/MacTouchBar/src/Utils.m index 025dee97790b..58215a54ac66 100644 --- a/native/MacTouchBar/src/Utils.m +++ b/native/MacTouchBar/src/Utils.m @@ -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]; diff --git a/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java b/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java index fae59b8ae8c2..8da6cb81db22 100644 --- a/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java +++ b/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java @@ -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 stoppableDescriptors) { + private static void _showStopRunningBar(@NotNull List 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 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> descriptors = new ArrayList<>(stoppableDescriptors.size()); + for (RunContentDescriptor sd : stoppableDescriptors) + descriptors.add(Pair.create(sd, ()->ExecutionManagerImpl.stopProcess(sd))); + TouchBarsManager.showStopRunningBar(descriptors); } abstract static class HandlerItem { diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index 90f2a6bb7362..456ea2f02fc4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -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(); diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/BarContainer.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/BarContainer.java index 914863a136bc..129aab0e9ee4 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/BarContainer.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/BarContainer.java @@ -14,8 +14,6 @@ enum BarType { } class BarContainer { - - private final @NotNull BarType myType; private Map 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() { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/BuildUtils.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/BuildUtils.java new file mode 100644 index 000000000000..f8f59f543d7c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/BuildUtils.java @@ -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 getAltLayouts(@NotNull ActionGroup context) { + final String ctxId = _getActionId(context); + if (ctxId == null || ctxId.isEmpty()) { + LOG.error("unregistered ActionGroup: " + context); + return null; + } + + Map 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 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 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> 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 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); + } + } + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ItemListener.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ItemListener.java new file mode 100644 index 000000000000..5de053568ed6 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ItemListener.java @@ -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); +} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ItemsContainer.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ItemsContainer.java new file mode 100644 index 000000000000..3382b2b34254 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ItemsContainer.java @@ -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 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 proc) { + myItems.forEach((item -> { + if (item instanceof TBItemGroup) { + ((TBItemGroup)item).getContainer().forEachDeep(proc); + return; + } + proc.accept(item); + })); + } + + boolean anyMatchDeep(Predicate 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++); } +} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NST.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NST.java index ad0dfa0d52e4..e5b78c7514cd 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NST.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NST.java @@ -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 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 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 items) { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NSTLibrary.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NSTLibrary.java index 850f3293ff03..537f9239df32 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NSTLibrary.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NSTLibrary.java @@ -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(); } } diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/PlatformAction.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/PlatformAction.java deleted file mode 100644 index 9a7f70d7f8f1..000000000000 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/PlatformAction.java +++ /dev/null @@ -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); - } -} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.java index 6d4d2bdfb35f..7b3b25e67855 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/ProjectData.java @@ -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 strmod2alt = TouchBarActionBase.getAltLayouts(mainLayout); + final Map strmod2alt = BuildUtils.getAltLayouts(mainLayout); final Map 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 proc) { + ApplicationManager.getApplication().assertIsDispatchThread(); + myBars.forEach(proc); + } + int getDbgSessions() { return myActiveDebugSessions.get(); } private static long _str2mask(@NotNull String modifierId) { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItem.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItem.java index 51237958a722..47818fa678b7 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItem.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItem.java @@ -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 } diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemAnActionButton.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemAnActionButton.java index 3b9cee665993..b139a2a3dad6 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemAnActionButton.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemAnActionButton.java @@ -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; } diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemButton.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemButton.java index 322cf55f9f6a..64f0c9f5830e 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemButton.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemButton.java @@ -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) { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemGroup.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemGroup.java index 699ef44fa2c3..f443c6ff23e3 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemGroup.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemGroup.java @@ -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 myGroupItems = new ArrayList<>(); - - TBItemGroup(@NotNull String uid, @NotNull List items) { - super(uid); - myGroupItems.addAll(items); + TBItemGroup(@NotNull String uid, @Nullable ItemListener listener) { + super(uid, listener); + myGroupItems = new ItemsContainer(uid + "_group", listener); } - List 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(); + } } diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemPopover.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemPopover.java index 6b092c1cf55f..98956c2718ec 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemPopover.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemPopover.java @@ -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)); } diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemScrubber.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemScrubber.java index a53df80f6306..d23351bd94c9 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemScrubber.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TBItemScrubber.java @@ -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 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 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; diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBar.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBar.java index 957cf5f1662e..00f3d2049697 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBar.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBar.java @@ -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 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 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 proc) { myItems.forEachDeep(proc); } - void forEach(Consumer 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 +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarActionBase.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarActionBase.java deleted file mode 100644 index ad9fb1a143fb..000000000000 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarActionBase.java +++ /dev/null @@ -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 getAltLayouts(@NotNull ActionGroup context) { - final String ctxId = _getActionId(context); - if (ctxId == null || ctxId.isEmpty()) { - LOG.error("unregistered ActionGroup: " + context); - return null; - } - - Map 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); - } - } - } -} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarProjectBase.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarProjectBase.java deleted file mode 100644 index 0451a30553f9..000000000000 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarProjectBase.java +++ /dev/null @@ -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; - } -} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java index c2b6f39736a1..4b899b55c22f 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java @@ -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 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 jbuttons, Project project) { + public static @Nullable Runnable showDlgButtonsBar(List 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 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> 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 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 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 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 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())); diff --git a/platform/platform-tests/testSrc/com/intellij/ui/mac/touchbar/TouchbarTest.java b/platform/platform-tests/testSrc/com/intellij/ui/mac/touchbar/TouchbarTest.java index e8af3e7c6396..c25af39a8982 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/mac/touchbar/TouchbarTest.java +++ b/platform/platform-tests/testSrc/com/intellij/ui/mac/touchbar/TouchbarTest.java @@ -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 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 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() + "]"); }; } }