diff --git a/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java b/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java index 7b065e0d0466..7807ebe19984 100644 --- a/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java +++ b/platform/platform-impl/src/com/intellij/ide/MacOSApplicationProvider.java @@ -22,7 +22,7 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.platform.PlatformProjectOpenProcessor; import com.intellij.ui.mac.foundation.Foundation; import com.intellij.ui.mac.foundation.ID; -import com.intellij.ui.mac.touchbar.TouchBarManager; +import com.intellij.ui.mac.touchbar.TouchBarsManager; import com.sun.jna.Callback; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -138,7 +138,7 @@ public class MacOSApplicationProvider { }); installAutoUpdateMenu(); - TouchBarManager.initialize(); + TouchBarsManager.initialize(); } private static void installAutoUpdateMenu() { diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java index 344f0aceae6e..673ef49fe429 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java @@ -49,7 +49,8 @@ import com.intellij.packageDependencies.ui.TreeExpansionMonitor; import com.intellij.ui.DoubleClickListener; import com.intellij.ui.FilterComponent; import com.intellij.ui.awt.RelativePoint; -import com.intellij.ui.mac.touchbar.TouchBarManager; +import com.intellij.ui.mac.touchbar.TouchBarsManager; +import com.intellij.ui.mac.touchbar.SystemSettingsTouchBar; import com.intellij.util.Alarm; import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.JBUI; @@ -212,11 +213,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi } }); - if (TouchBarManager.isTouchBarAvailable()) { - final JCheckBox useFn = new JCheckBox("Always show FN-keys at TouchBar", TouchBarManager.isShowFnKeysEnabled()); + if (TouchBarsManager.isTouchBarAvailable()) { + final JCheckBox useFn = new JCheckBox("Always show FN-keys at TouchBar", SystemSettingsTouchBar.isShowFnKeysEnabled()); useFn.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { - TouchBarManager.setShowFnKeysEnabled(useFn.isSelected()); + SystemSettingsTouchBar.setShowFnKeysEnabled(useFn.isSelected()); } }); panel.add(useFn, BorderLayout.SOUTH); 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 new file mode 100644 index 000000000000..7511f868c80d --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/NST.java @@ -0,0 +1,109 @@ +// 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.diagnostic.Logger; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.ui.mac.foundation.ID; +import com.intellij.util.lang.UrlClassLoader; +import com.sun.jna.Native; + +import java.util.HashMap; +import java.util.Map; + +public class NST { + private static final Logger LOG = Logger.getInstance(NST.class); + private static final NSTLibrary ourNSTLibrary; + + static { + final boolean isSystemSupportTouchbar = SystemInfo.isMac && SystemInfo.isOsVersionAtLeast("10.12.2"); + NSTLibrary lib = null; + if (isSystemSupportTouchbar && Registry.is("ide.mac.touchbar.use", false) && SystemSettingsTouchBar.isTouchBarServerRunning()) { + try { + UrlClassLoader.loadPlatformLibrary("nst"); + + // Set JNA to convert java.lang.String to char* using UTF-8, and match that with + // the way we tell CF to interpret our char* + // May be removed if we use toStringViaUTF16 + System.setProperty("jna.encoding", "UTF8"); + + final Map nstOptions = new HashMap<>(); + lib = Native.loadLibrary("nst", NSTLibrary.class, nstOptions); + } catch (Throwable e) { + LOG.error("Failed to load nst library for touchbar: ", e); + } + } + ourNSTLibrary = lib; + } + + public static boolean isAvailable() { return ourNSTLibrary != null; } + + // + // TODO: syncronization between AppKit and EDT will be implemented in next commit + // + + public static ID createTouchBar(String name, NSTLibrary.ItemCreator creator) { + return ourNSTLibrary.createTouchBar(name, creator); + } + + public static void releaseTouchBar(ID tbObj) { + ourNSTLibrary.releaseTouchBar(tbObj); + } + + public static void setTouchBar(TouchBar tb) { + ourNSTLibrary.setTouchBar(tb == null ? ID.NIL : tb.getNativePeer()); + } + + public static void selectItemsToShow(ID tbObj, String[] ids, int count) { + ourNSTLibrary.selectItemsToShow(tbObj, ids, count); + } + + public static ID createButton(String uid, + int buttWidth, + String text, + byte[] raster4ByteRGBA, + int w, + int h, + NSTLibrary.Action action) { + return ourNSTLibrary.createButton(uid, buttWidth, text, raster4ByteRGBA, w, h, action); + } + + public static ID createPopover(String uid, + int itemWidth, + String text, + byte[] raster4ByteRGBA, + int w, + int h, + ID tbObjExpand, + ID tbObjTapAndHold) { + return ourNSTLibrary.createPopover(uid, itemWidth, text, raster4ByteRGBA, w, h, tbObjExpand, tbObjTapAndHold); + } + + public static ID createScrubber(String uid, + int itemWidth, + NSTLibrary.ScrubberItemsSource source, + NSTLibrary.ScrubberItemsCount count, + NSTLibrary.ActionWithIndex actions) { + return ourNSTLibrary.createScrubber(uid, itemWidth, source, count, actions); + } + + public static void updateButton(ID buttonObj, + int buttWidth, + String text, + byte[] raster4ByteRGBA, + int w, + int h, + NSTLibrary.Action action) { + ourNSTLibrary.updateButton(buttonObj, buttWidth, text, raster4ByteRGBA, w, h, action); + } + + public static void updatePopover(ID popoverObj, + int itemWidth, + String text, + byte[] raster4ByteRGBA, + int w, + int h, + ID tbObjExpand, ID tbObjTapAndHold) { + ourNSTLibrary.updatePopover(popoverObj, itemWidth, text, raster4ByteRGBA, w, h, tbObjExpand, tbObjTapAndHold); + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/SystemSettingsTouchBar.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/SystemSettingsTouchBar.java new file mode 100644 index 000000000000..f48d9e5b40e8 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/SystemSettingsTouchBar.java @@ -0,0 +1,76 @@ +// 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.ExecutionException; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.process.ProcessOutput; +import com.intellij.execution.util.ExecUtil; +import com.intellij.openapi.application.impl.ApplicationInfoImpl; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.ui.mac.foundation.Foundation; +import com.intellij.ui.mac.foundation.ID; + +import java.io.IOException; + +public class SystemSettingsTouchBar { + private static final Logger LOG = Logger.getInstance(SystemSettingsTouchBar.class); + + private static final String ourTBServerProcessName = "TouchBarServer"; + private static final String ourDefaultsDomain = "com.apple.touchbar.agent"; + private static final String ourDefaultsNode = "PresentationModePerApp"; + private static final String ourDefaultsValue = "functionKeys"; + private static final String ourApplicationId; + + static { + // TODO: obtain "OS X Application identifier' via platform api + ourApplicationId = ApplicationInfoImpl.getInstanceEx().isEAP() ? "com.jetbrains.intellij-EAP" : "com.jetbrains.intellij"; + } + + static boolean isTouchBarServerRunning() { + final GeneralCommandLine cmdLine = new GeneralCommandLine("pgrep", ourTBServerProcessName); + try { + final ProcessOutput out = ExecUtil.execAndGetOutput(cmdLine); + return !out.getStdout().isEmpty(); + } catch (ExecutionException e) { + LOG.error(e); + } + return false; + } + + public static boolean isShowFnKeysEnabled() { + final ID defaults = Foundation.invoke("NSUserDefaults", "standardUserDefaults"); + final ID domain = Foundation.invoke(defaults, "persistentDomainForName:", Foundation.nsString(ourDefaultsDomain)); + final ID node = Foundation.invoke(domain, "objectForKey:", Foundation.nsString(ourDefaultsNode)); + final ID val = Foundation.invoke(node, "objectForKey:", Foundation.nsString(ourApplicationId)); + final String sval = Foundation.toStringViaUTF8(val); + return sval != null && sval.equals(ourDefaultsValue); + } + + public static void setShowFnKeysEnabled(boolean val) { + final ID defaults = Foundation.invoke("NSUserDefaults", "standardUserDefaults"); + final ID domain = Foundation.invoke(defaults, "persistentDomainForName:", Foundation.nsString(ourDefaultsDomain)); + final ID node = Foundation.invoke(domain, "objectForKey:", Foundation.nsString(ourDefaultsNode)); + final ID nsVal = Foundation.invoke(node, "objectForKey:", Foundation.nsString(ourApplicationId)); + final String sval = Foundation.toStringViaUTF8(nsVal); + final boolean settingEnabled = sval != null && sval.equals(ourDefaultsValue); + if (val == settingEnabled) + return; + + final ID mdomain = Foundation.invoke(domain, "mutableCopy"); + final ID mnode = Foundation.invoke(node, "mutableCopy"); + if (val) + Foundation.invoke(mnode, "setObject:forKey:", Foundation.nsString(ourDefaultsValue), Foundation.nsString(ourApplicationId)); + else + Foundation.invoke(mnode, "removeObjectForKey:", Foundation.nsString(ourApplicationId)); + Foundation.invoke(mdomain, "setObject:forKey:", mnode, Foundation.nsString(ourDefaultsNode)); + Foundation.invoke(defaults, "setPersistentDomain:forName:", mdomain, Foundation.nsString(ourDefaultsDomain)); + + try { + ExecUtil.sudo(new GeneralCommandLine("pkill", ourTBServerProcessName), ""); + } catch (ExecutionException e) { + LOG.error(e); + } catch (IOException e) { + LOG.error(e); + } + } +} 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 99f0ef4299b6..df00f90274d7 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 @@ -33,11 +33,11 @@ public class TBItemButton extends TBItem { @Override protected void _updateNativePeer() { - TouchBarManager.getNSTLibrary().updateButton(myNativePeer, myWidth, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), myAction); + NST.updateButton(myNativePeer, myWidth, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), myAction); } @Override protected ID _createNativePeer() { - return TouchBarManager.getNSTLibrary().createButton(myUid, myWidth, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), myAction); + return NST.createButton(myUid, myWidth, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), myAction); } } 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 ac633e7df2a9..a3a4aca93c58 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 @@ -51,12 +51,12 @@ public class TBItemPopover extends TBItem { @Override protected void _updateNativePeer() { - TouchBarManager.getNSTLibrary().updatePopover(myNativePeer, myWidthPix, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), getNativePeer(myExpandTB), getNativePeer(myTapAndHoldTB)); + NST.updatePopover(myNativePeer, myWidthPix, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), getNativePeer(myExpandTB), getNativePeer(myTapAndHoldTB)); } @Override protected ID _createNativePeer() { - return TouchBarManager.getNSTLibrary().createPopover(myUid, myWidthPix, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), getNativePeer(myExpandTB), getNativePeer(myTapAndHoldTB)); + return NST.createPopover(myUid, myWidthPix, myText, getRaster(myIcon), getIconW(myIcon), getIconH(myIcon), getNativePeer(myExpandTB), getNativePeer(myTapAndHoldTB)); } private static ID getNativePeer(TouchBar tb) { return tb == null ? ID.NIL : tb.getNativePeer(); } 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 0b7de4a179f0..6ae3a2f9899d 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 @@ -42,7 +42,7 @@ public class TBItemScrubber extends TBItem { @Override protected ID _createNativePeer() { - return TouchBarManager.getNSTLibrary().createScrubber(myUid, myWidth, mySource, myCount, myActions); + return NST.createScrubber(myUid, myWidth, mySource, myCount, myActions); } public void executeAt(int index) { 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 12f70f592733..910464160440 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 @@ -18,7 +18,7 @@ public class TouchBar implements NSTLibrary.ItemCreator { TouchBar(String touchbarName) { myName = touchbarName; - myNativePeer = TouchBarManager.getNSTLibrary().createTouchBar(touchbarName, this); + myNativePeer = NST.createTouchBar(touchbarName, this); } @Override @@ -37,7 +37,7 @@ public class TouchBar implements NSTLibrary.ItemCreator { void release() { for (TBItem item : myItems) item.releaseNativePeer(); - TouchBarManager.getNSTLibrary().releaseTouchBar(myNativePeer); + NST.releaseTouchBar(myNativePeer); myItems.clear(); myNativePeer = ID.NIL; @@ -94,7 +94,7 @@ public class TouchBar implements NSTLibrary.ItemCreator { for (TBItem item : myItems) ids[c++] = item.myUid; - TouchBarManager.getNSTLibrary().selectItemsToShow(myNativePeer, ids, ids.length); + NST.selectItemsToShow(myNativePeer, ids, ids.length); } private TBItem findItem(String uid) { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarGeneral.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarGeneral.java new file mode 100644 index 000000000000..c2b18ced34d1 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarGeneral.java @@ -0,0 +1,105 @@ +// 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.ExecutionListener; +import com.intellij.execution.ExecutionManager; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.IconLoader; +import com.intellij.ui.mac.foundation.Foundation; +import com.intellij.ui.mac.foundation.ID; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TouchBarGeneral extends TouchBar { + private static final Map ourInstances = new HashMap<>(); + + private TBItemButton myButtonStop = null; + + private TouchBarGeneral(Project project) { + super("general"); + + addButton(AllIcons.Toolwindows.ToolWindowBuild, null, "CompileDirty"); // NOTE: IdeActions.ACTION_COMPILE doesn't work + + final RunManager rm = RunManager.getInstance(project); + final RunnerAndConfigurationSettings selected = rm.getSelectedConfiguration(); + if (selected != null) { + final TouchBar tapHoldTB = new TouchBar("run_configs_popover_tap_and_hold"); + final TouchBar expandTB = new TouchBar("run_configs_popover_expand"); + final TBItemPopover popover = addPopover(selected.getConfiguration().getIcon(), selected.getName(), 143, expandTB, tapHoldTB); + + tapHoldTB.addFlexibleSpacing(); + tapHoldTB.addButton(selected.getConfiguration().getIcon(), selected.getName(), (NSTLibrary.Action)null); + tapHoldTB.selectAllItemsToShow(); + + expandTB.addButton(AllIcons.Actions.EditSource, null, IdeActions.ACTION_EDIT_RUN_CONFIGURATIONS); + final TBItemScrubber scrubber = expandTB.addScrubber(500); + List allRunCongigs = rm.getAllSettings(); + for (RunnerAndConfigurationSettings rc : allRunCongigs) { + final Icon iconRc = rc.getConfiguration().getIcon(); + scrubber.addItem(iconRc, rc.getName(), () -> { + rm.setSelectedConfiguration(rc); + popover.update(iconRc, rc.getName()); + popover.dismiss(); + }); + } + expandTB.addFlexibleSpacing(); + expandTB.selectAllItemsToShow(); + + addButton(AllIcons.Toolwindows.ToolWindowRun, null, IdeActions.ACTION_DEFAULT_RUNNER); + addButton(AllIcons.Toolwindows.ToolWindowDebugger, null, IdeActions.ACTION_DEFAULT_DEBUGGER); + myButtonStop = addButton(IconLoader.getDisabledIcon(AllIcons.Actions.Suspend), null, (NSTLibrary.Action)null); + } + + addSpacing(true); + addButton(AllIcons.Actions.CheckOut, null, new PlatformAction("Vcs.UpdateProject")); // NOTE: IdeActions.ACTION_CVS_CHECKOUT doesn't works + addButton(AllIcons.Actions.Commit, null, new PlatformAction("CheckinProject")); // NOTE: IdeActions.ACTION_CVS_COMMIT doesn't works + + selectAllItemsToShow(); + + ApplicationManager.getApplication().getMessageBus().connect().subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionListener() { + @Override + public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) { + myButtonStop.update(AllIcons.Actions.Suspend, null, new PlatformAction("Stop")); + // TODO: implement other items update + } + @Override + public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler, int exitCode) { + myButtonStop.update(IconLoader.getDisabledIcon(AllIcons.Actions.Suspend), null, (NSTLibrary.Action)null); + // TODO: implement other items update + } + }); + } + + public static @NotNull TouchBarGeneral instance(@NotNull Project project) { + // NOTE: called from EDT only + TouchBarGeneral result = ourInstances.get(project); + if (result == null) { + final ID pool = Foundation.invoke("NSAutoreleasePool", "new"); + try { + result = new TouchBarGeneral(project); + ourInstances.put(project, result); + } finally { + Foundation.invoke(pool, "release"); + } + } + return result; + } + + public static void release(@NotNull Project project) { + // NOTE: called from EDT only + TouchBarGeneral result = ourInstances.remove(project); + if (result != null) + result.release(); + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarManager.java b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarManager.java deleted file mode 100644 index 61a63dd1ad4a..000000000000 --- a/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarManager.java +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.ui.mac.touchbar; - -import com.intellij.execution.*; -import com.intellij.execution.configurations.GeneralCommandLine; -import com.intellij.execution.process.ProcessHandler; -import com.intellij.execution.process.ProcessOutput; -import com.intellij.execution.runners.ExecutionEnvironment; -import com.intellij.execution.util.ExecUtil; -import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.impl.ApplicationInfoImpl; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.project.ProjectManagerListener; -import com.intellij.openapi.util.IconLoader; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.registry.Registry; -import com.intellij.ui.mac.foundation.Foundation; -import com.intellij.ui.mac.foundation.ID; -import com.intellij.util.lang.UrlClassLoader; -import com.sun.jna.Native; -import org.jetbrains.annotations.NotNull; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; -import java.awt.image.*; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class TouchBarManager { - private static final Logger ourLog = Logger.getInstance(TouchBar.class); - - private static final String ourTBServerProcessName = "TouchBarServer"; - private static final String ourDefaultsDomain = "com.apple.touchbar.agent"; - private static final String ourDefaultsNode = "PresentationModePerApp"; - private static final String ourDefaultsValue = "functionKeys"; - private static final String ourApplicationId; - private static final NSTLibrary ourNSTLibrary; - private static TouchBar ourTouchbar; - - static { - // NOTE: can also check existence of process 'ControlStrip' to determine touchbar availability - final boolean isSystemSupportTouchbar = SystemInfo.isMac && SystemInfo.isOsVersionAtLeast("10.12.1"); - NSTLibrary lib = null; - if (isSystemSupportTouchbar && Registry.is("ide.mac.touchbar.use", false) && isTouchBarServerRunning()) { - try { - UrlClassLoader.loadPlatformLibrary("nst"); - - // Set JNA to convert java.lang.String to char* using UTF-8, and match that with - // the way we tell CF to interpret our char* - // May be removed if we use toStringViaUTF16 - System.setProperty("jna.encoding", "UTF8"); - - final Map nstOptions = new HashMap<>(); - lib = Native.loadLibrary("nst", NSTLibrary.class, nstOptions); - } catch (Throwable e) { - ourLog.error("Failed to load nst library for touchbar: ", e); - } - } - ourNSTLibrary = lib; - - // TODO: obtain "OS X Application identifier' via platform api - ourApplicationId = ApplicationInfoImpl.getInstanceEx().isEAP() ? "com.jetbrains.intellij-EAP" : "com.jetbrains.intellij"; - } - - public enum TOUCHBARS { - main, - debug, - test; /*only for testing purposes*/ - - private TouchBar myTB = null; - - TouchBar get(Project project) { - if (myTB == null) { - final ID pool = Foundation.invoke("NSAutoreleasePool", "new"); - try { - if (this == main) { - if (project == null) { - ourLog.error("can't create 'main' touchbar: passed null project"); - return null; - } - - myTB = new TouchBar(name()); - myTB.addButton(AllIcons.Toolwindows.ToolWindowBuild, null, "CompileDirty"); // NOTE: IdeActions.ACTION_COMPILE doesn't work - - final RunManager rm = RunManager.getInstance(project); - final RunnerAndConfigurationSettings selected = rm.getSelectedConfiguration(); - if (selected != null) { - final TouchBar tapHoldTB = new TouchBar("run_configs_popover_tap_and_hold"); - final TouchBar expandTB = new TouchBar("run_configs_popover_expand"); - final TBItemPopover popover = myTB.addPopover(selected.getConfiguration().getIcon(), selected.getName(), 143, expandTB, tapHoldTB); - - tapHoldTB.addFlexibleSpacing(); - tapHoldTB.addButton(selected.getConfiguration().getIcon(), selected.getName(), () -> {}); // TODO: remove button, use NSView with 'image+text' - tapHoldTB.selectAllItemsToShow(); - - expandTB.addButton(AllIcons.Actions.EditSource, null, IdeActions.ACTION_EDIT_RUN_CONFIGURATIONS); - final TBItemScrubber scrubber = expandTB.addScrubber(500); - List allRunCongigs = rm.getAllSettings(); - for (RunnerAndConfigurationSettings rc : allRunCongigs) { - final Icon iconRc = rc.getConfiguration().getIcon(); - scrubber.addItem(iconRc, rc.getName(), () -> { - rm.setSelectedConfiguration(rc); - popover.update(iconRc, rc.getName()); - popover.dismiss(); - }); - } - expandTB.addFlexibleSpacing(); - expandTB.selectAllItemsToShow(); - - myTB.addButton(AllIcons.Toolwindows.ToolWindowRun, null, IdeActions.ACTION_DEFAULT_RUNNER); - myTB.addButton(AllIcons.Toolwindows.ToolWindowDebugger, null, IdeActions.ACTION_DEFAULT_DEBUGGER); - myTB.addButton(IconLoader.getDisabledIcon(AllIcons.Actions.Suspend), null, (NSTLibrary.Action)null); - } - - myTB.addSpacing(true); - myTB.addButton(AllIcons.Actions.CheckOut, null, new PlatformAction("Vcs.UpdateProject")); // NOTE: IdeActions.ACTION_CVS_CHECKOUT doesn't works - myTB.addButton(AllIcons.Actions.Commit, null, new PlatformAction("CheckinProject")); // NOTE: IdeActions.ACTION_CVS_COMMIT doesn't works - } - else if (this == debug) { - myTB = new TouchBar(name()); - myTB.addButton(null, "Step Over", "Step Over"); - myTB.addButton(null, "Step Into", "Step Into"); - myTB.addButton(AllIcons.Actions.Suspend, null, "Stop"); - } - else if (this == test) { - myTB = new TouchBar(name()); - myTB.addSpacing(true); - myTB.addButton(null, "test1", createPrintTextCallback("pressed test1 button")); - myTB.addButton(null, "test2", createPrintTextCallback("pressed test2 button")); - myTB.addSpacing(false); - myTB.addButton(AllIcons.Toolwindows.ToolWindowRun, null, createPrintTextCallback("pressed image button")); - - - final TouchBar tapHoldTB = new TouchBar("test_popover_tap_and_hold"); - final TouchBar expandTB = new TouchBar("test_configs_popover_expand"); - final int configPopoverWidth = 143; - myTB.addPopover(AllIcons.Toolwindows.ToolWindowBuild, "test-popover", configPopoverWidth, expandTB, tapHoldTB); - - expandTB.addButton(AllIcons.Toolwindows.ToolWindowDebugger, null, createPrintTextCallback("pressed pimage button")); - final TBItemScrubber scrubber = expandTB.addScrubber(450); - for (int c = 0; c < 15; ++c) { - String txt; - if (c == 7) txt = "very very long configuration name (debugging type)"; - else txt = String.format("r%1.2f", Math.random()); - int finalC = c; - scrubber.addItem(AllIcons.Toolwindows.ToolWindowPalette, txt, - () -> System.out.println("JAVA: performed action of scrubber item at index " + finalC + " [thread:" + Thread.currentThread() + "]")); - } - expandTB.selectAllItemsToShow(); - - tapHoldTB.addButton(AllIcons.Toolwindows.ToolWindowPalette, null, createPrintTextCallback("pressed pimage button")); - tapHoldTB.selectAllItemsToShow(); - } - - if (myTB != null) - myTB.selectAllItemsToShow(); - } - finally { - Foundation.invoke(pool, "release"); - } - } - return myTB; - } - - void release() { - if (myTB == null) - return; - - myTB.release(); - myTB = null; - } - } - - public static void initialize() { - if (!isTouchBarAvailable()) - return; - - final ID app = Foundation.invoke("NSApplication", "sharedApplication"); - Foundation.invoke(app, "setAutomaticCustomizeTouchBarMenuItemEnabled:", true); - - ApplicationManager.getApplication().getMessageBus().connect().subscribe(ProjectManager.TOPIC, new ProjectManagerListener() { - @Override - public void projectOpened(Project project) { - ourTouchbar = TOUCHBARS.main.get(project); - if (ourTouchbar == null) - return; - - ourNSTLibrary.setTouchBar(ourTouchbar.getNativePeer()); - } - }); - } - - public static void setCurrent(TOUCHBARS tbType) { - if (tbType == null || !isTouchBarAvailable()) - return; - - ourTouchbar = tbType.get(null); - if (ourTouchbar == null) - return; - - ourNSTLibrary.setTouchBar(ourTouchbar.getNativePeer()); - } - - static NSTLibrary getNSTLibrary() { return ourNSTLibrary; } - - public static boolean isTouchBarAvailable() { return ourNSTLibrary != null; } - - private static boolean isTouchBarServerRunning() { - final GeneralCommandLine cmdLine = new GeneralCommandLine("pgrep", ourTBServerProcessName); - try { - final ProcessOutput out = ExecUtil.execAndGetOutput(cmdLine); - return !out.getStdout().isEmpty(); - } catch (ExecutionException e) { - ourLog.error(e); - } - return false; - } - - public static boolean isShowFnKeysEnabled() { - final ID defaults = Foundation.invoke("NSUserDefaults", "standardUserDefaults"); - final ID domain = Foundation.invoke(defaults, "persistentDomainForName:", Foundation.nsString(ourDefaultsDomain)); - final ID node = Foundation.invoke(domain, "objectForKey:", Foundation.nsString(ourDefaultsNode)); - final ID val = Foundation.invoke(node, "objectForKey:", Foundation.nsString(ourApplicationId)); - final String sval = Foundation.toStringViaUTF8(val); - return sval != null && sval.equals(ourDefaultsValue); - } - - public static void setShowFnKeysEnabled(boolean val) { - final ID defaults = Foundation.invoke("NSUserDefaults", "standardUserDefaults"); - final ID domain = Foundation.invoke(defaults, "persistentDomainForName:", Foundation.nsString(ourDefaultsDomain)); - final ID node = Foundation.invoke(domain, "objectForKey:", Foundation.nsString(ourDefaultsNode)); - final ID nsVal = Foundation.invoke(node, "objectForKey:", Foundation.nsString(ourApplicationId)); - final String sval = Foundation.toStringViaUTF8(nsVal); - final boolean settingEnabled = sval != null && sval.equals(ourDefaultsValue); - if (val == settingEnabled) - return; - - final ID mdomain = Foundation.invoke(domain, "mutableCopy"); - final ID mnode = Foundation.invoke(node, "mutableCopy"); - if (val) - Foundation.invoke(mnode, "setObject:forKey:", Foundation.nsString(ourDefaultsValue), Foundation.nsString(ourApplicationId)); - else - Foundation.invoke(mnode, "removeObjectForKey:", Foundation.nsString(ourApplicationId)); - Foundation.invoke(mdomain, "setObject:forKey:", mnode, Foundation.nsString(ourDefaultsNode)); - Foundation.invoke(defaults, "setPersistentDomain:forName:", mdomain, Foundation.nsString(ourDefaultsDomain)); - - try { - ExecUtil.sudo(new GeneralCommandLine("pkill", ourTBServerProcessName), ""); - } catch (ExecutionException e) { - ourLog.error(e); - } catch (IOException e) { - ourLog.error(e); - } - } - - private static NSTLibrary.Action createPrintTextCallback(String text) { - return new NSTLibrary.Action() { - @Override - public void execute() { - System.out.println(text + " [thread:" + Thread.currentThread() + "]"); - } - }; - } -} 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 new file mode 100644 index 000000000000..c48090645e54 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/touchbar/TouchBarsManager.java @@ -0,0 +1,44 @@ +// 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.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.project.ProjectManagerListener; +import com.intellij.ui.mac.foundation.Foundation; +import com.intellij.ui.mac.foundation.ID; + +public class TouchBarsManager { + private final static boolean IS_LOGGING_ENABLED = false; + private static final Logger LOG = Logger.getInstance(TouchBar.class); + + public static void initialize() { + if (!isTouchBarAvailable()) + return; + + final ID app = Foundation.invoke("NSApplication", "sharedApplication"); + Foundation.invoke(app, "setAutomaticCustomizeTouchBarMenuItemEnabled:", true); + + ApplicationManager.getApplication().getMessageBus().connect().subscribe(ProjectManager.TOPIC, new ProjectManagerListener() { + @Override + public void projectOpened(Project project) { + trace("opened project %s, set general touchbar", project); + NST.setTouchBar(TouchBarGeneral.instance(project)); + } + @Override + public void projectClosed(Project project) { + trace("closed project %s, hide touchbar", project); + NST.setTouchBar(null); + TouchBarGeneral.release(project); + } + }); + } + + public static boolean isTouchBarAvailable() { return NST.isAvailable(); } + + private static void trace(String fmt, Object... args) { + if (IS_LOGGING_ENABLED) + LOG.trace(String.format(fmt, args)); + } +} 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 6b80aaa99a36..711cd40870bb 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 @@ -1,7 +1,9 @@ // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ui.mac.touchbar; +import com.intellij.icons.AllIcons; import com.intellij.ui.mac.foundation.Foundation; +import com.intellij.ui.mac.foundation.ID; import javax.swing.*; @@ -9,8 +11,40 @@ public class TouchbarTest { public static void main(String[] args) { SwingUtilities.invokeLater(() -> { Foundation.init(); - TouchBarManager.initialize(); - TouchBarManager.setCurrent(TouchBarManager.TOUCHBARS.test); + + final ID pool = Foundation.invoke("NSAutoreleasePool", "new"); + try { + final TouchBar testTB = new TouchBar("test"); + testTB.addSpacing(true); + testTB.addButton(null, "test1", createPrintTextCallback("pressed test1 button")); + testTB.addButton(null, "test2", createPrintTextCallback("pressed test2 button")); + testTB.addSpacing(false); + testTB.addButton(AllIcons.Toolwindows.ToolWindowRun, null, createPrintTextCallback("pressed image button")); + + final TouchBar tapHoldTB = new TouchBar("test_popover_tap_and_hold"); + final TouchBar expandTB = new TouchBar("test_configs_popover_expand"); + final int configPopoverWidth = 143; + testTB.addPopover(AllIcons.Toolwindows.ToolWindowBuild, "test-popover", configPopoverWidth, expandTB, tapHoldTB); + + expandTB.addButton(AllIcons.Toolwindows.ToolWindowDebugger, null, createPrintTextCallback("pressed pimage button")); + final TBItemScrubber scrubber = expandTB.addScrubber(450); + for (int c = 0; c < 15; ++c) { + String txt; + if (c == 7) txt = "very very long configuration name (debugging type)"; + else txt = String.format("r%1.2f", Math.random()); + int finalC = c; + scrubber.addItem(AllIcons.Toolwindows.ToolWindowPalette, txt, + () -> System.out.println("JAVA: performed action of scrubber item at index " + finalC + " [thread:" + Thread.currentThread() + "]")); + } + expandTB.selectAllItemsToShow(); + + tapHoldTB.addButton(AllIcons.Toolwindows.ToolWindowPalette, null, createPrintTextCallback("pressed pimage button")); + tapHoldTB.selectAllItemsToShow(); + + NST.setTouchBar(testTB); + } finally { + Foundation.invoke(pool, "release"); + } final JFrame f = new JFrame(); f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); @@ -18,5 +52,14 @@ public class TouchbarTest { f.setVisible(true); }); } + + private static NSTLibrary.Action createPrintTextCallback(String text) { + return new NSTLibrary.Action() { + @Override + public void execute() { + System.out.println(text + " [thread:" + Thread.currentThread() + "]"); + } + }; + } }