linux-menubar: show swing-menu in full screen mode
authorArtem Bochkarev <artem.bochkarev@jetbrains.com>
Wed, 14 Nov 2018 11:37:12 +0000 (18:37 +0700)
committerArtem Bochkarev <artem.bochkarev@jetbrains.com>
Thu, 15 Nov 2018 09:50:32 +0000 (16:50 +0700)
fixed IDEA-201291 Linux Native menu bar: if 'windows-style' menu (i.e. located on the window title bar) is used, menu can't be used in the Full Screen mode

bin/linux/libdbm64.so
native/LinuxGlobalMenu/DbusMenuWrapper.c
native/LinuxGlobalMenu/DbusMenuWrapper.h
platform/platform-impl/src/com/intellij/openapi/wm/impl/GlobalMenuLinux.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameDecorator.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeMenuBar.java

index 7fc851dcc1112708391218f5da7debb0d7cd4879..8d086450278368ad09fdb5ca370fcc074756dfb0 100755 (executable)
Binary files a/bin/linux/libdbm64.so and b/bin/linux/libdbm64.so differ
index 2601e201381dbffce4d8b515ec2f8dc9e708dc41..42e0b70773fc8505f0bf57d5f3883af05f284abf 100644 (file)
@@ -30,6 +30,7 @@ typedef struct _WndInfo {
   DbusmenuServer *server;
   DbusmenuMenuitem *menuroot;
   jeventcallback jhandler;
+  jrunnable onReleaseCallback;
   GList* linkedXids;
 } WndInfo;
 
@@ -211,6 +212,10 @@ static void _releaseWindow(WndInfo *wi) {
     wi->linkedXids = NULL;
   }
 
+  if (wi->onReleaseCallback != NULL) {
+    (*wi->onReleaseCallback)();
+    wi->onReleaseCallback = NULL;
+  }
   free(wi);
 }
 
@@ -340,8 +345,10 @@ static gboolean _execReleaseWindow(gpointer user_data) {
   return FALSE;
 }
 
-void releaseWindowOnMainLoop(WndInfo *wi) {
+void releaseWindowOnMainLoop(WndInfo *wi, jrunnable onReleased) {
   // _info("scheduled releaseWindowOnMainLoop");
+  if (wi != NULL)
+    wi->onReleaseCallback = onReleased;
   g_idle_add(_execReleaseWindow, wi);
 }
 
index f1b459d23292274b073fddd92345d586da196795..42264b053749724e5f70d6dbc42e82ae32effe58 100644 (file)
@@ -40,7 +40,7 @@ void runMainLoop(jlogger jlogger, jrunnable onAppmenuServiceAppeared, jrunnable
 void execOnMainLoop(jrunnable run);
 
 WndInfo* registerWindow(long windowXid, jeventcallback handler); // creates menu-server and binds to xid
-void releaseWindowOnMainLoop(WndInfo* wi);
+void releaseWindowOnMainLoop(WndInfo* wi, jrunnable onReleased);
 
 void bindNewWindow(WndInfo * wi, long windowXid);
 void unbindWindow(WndInfo * wi, long windowXid);
index d02c815b7517a960752b6aa4ca97843817aaa107..f5d49caa2e8523cd3827d37f95ec0abc008aeb92 100644 (file)
@@ -24,7 +24,6 @@ import javax.imageio.ImageIO;
 import javax.swing.Timer;
 import javax.swing.*;
 import java.awt.*;
-import java.awt.event.KeyEvent;
 import java.awt.image.BufferedImage;
 import java.awt.peer.ComponentPeer;
 import java.io.ByteArrayOutputStream;
@@ -45,7 +44,7 @@ interface GlobalMenuLib extends Library {
   void execOnMainLoop(JRunnable run);
 
   Pointer registerWindow(long windowXid, EventHandler handler);
-  void releaseWindowOnMainLoop(Pointer wi);
+  void releaseWindowOnMainLoop(Pointer wi, JRunnable onReleased);
 
   void bindNewWindow(Pointer wi, long windowXid); // can be called from EDT (invokes only g_dbus_proxy_call, stateless)
   void unbindWindow(Pointer wi, long windowXid);  // can be called from EDT (invokes only g_dbus_proxy_call, stateless)
@@ -109,7 +108,7 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
   private static final Logger LOG = Logger.getInstance(GlobalMenuLinux.class);
   private static final GlobalMenuLib ourLib;
   private static final GlobalMenuLib.JLogger ourGLogger;
-  private static final GlobalMenuLib.JRunnable ourProcessQueue;
+  private static final GlobalMenuLib.JRunnable ourUpdateAllRoots;
   private static final GlobalMenuLib.JRunnable ourOnAppmenuServiceAppeared;
   private static final GlobalMenuLib.JRunnable ourOnAppmenuServiceVanished;
   private static final Map<Long, GlobalMenuLinux> ourInstances = new ConcurrentHashMap<>();
@@ -119,8 +118,11 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
   private final @NotNull JFrame myFrame;
   private List<MenuItemInternal> myRoots;
   private Pointer myWindowHandle;
-  private boolean myIsProcessed = false;
+  private boolean myIsRootsUpdated = false;
+  private boolean myIsEnabled = true;
+  private boolean myIsDisposed = false;
 
+  private final GlobalMenuLib.JRunnable myOnWindowReleased;
   private final EventFilter myEventFilter = new EventFilter();
 
   static {
@@ -137,19 +139,19 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
           LOG.error(msg);
         }
       };
-      ourProcessQueue = () -> {
+      ourUpdateAllRoots = () -> {
         // exec at glib-thread
         if (!ourIsServiceAvailable)
           return;
 
         for (GlobalMenuLinux gml: ourInstances.values())
-          gml._processRoots();
+          gml._updateRoots();
       };
       ourOnAppmenuServiceAppeared = () -> {
         // exec at glib-thread
         LOG.info("Appeared dbus-service 'com.canonical.AppMenu.Registrar'");
         ourIsServiceAvailable = true;
-        ourProcessQueue.run();
+        ourUpdateAllRoots.run();
       };
       ourOnAppmenuServiceVanished = () -> {
         // exec at glib-thread
@@ -177,7 +179,7 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
       }
     } else {
       ourGLogger = null;
-      ourProcessQueue = null;
+      ourUpdateAllRoots = null;
       ourOnAppmenuServiceAppeared = null;
       ourOnAppmenuServiceVanished = null;
     }
@@ -192,22 +194,40 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
     LOG.info("created instance of GlobalMenuLinux for xid=0x" + Long.toHexString(xid));
     myXid = xid;
     myFrame = frame;
+    myOnWindowReleased = () -> {
+      // exec at glib-thread
+      myWindowHandle = null;
+      if (myRoots != null) {
+        for (MenuItemInternal root : myRoots) {
+          root.nativePeer = null;
+          root.children.clear();
+        }
+      }
+      if (myIsDisposed)
+        ourInstances.remove(myXid);
+    };
     ourInstances.put(myXid, this);
   }
 
   @Override
   public void dispose() {
-    if (ourLib == null)
+    // exec at EDT
+    if (ourLib == null || myIsDisposed)
       return;
 
+    myIsDisposed = true;
+
     if (myWindowHandle != null) {
-      LOG.info("scheduled destroying of GlobalMenuLinux for xid=0x" + Long.toHexString(myXid));
-      ourLib.releaseWindowOnMainLoop(myWindowHandle);
+      _trace("dispose frame, scheduled destroying of GlobalMenuLinux for xid=0x%X", myXid);
+      ourLib.releaseWindowOnMainLoop(myWindowHandle, myOnWindowReleased);
     }
   }
 
   public void bindNewWindow(@NotNull Window frame) {
     // exec at EDT
+    if (ourLib == null)
+      return;
+
     final long xid = _getX11WindowXid(frame);
     if (xid == 0) {
       LOG.warn("can't obtain XID of window: " + frame + ", skip global menu binding");
@@ -221,6 +241,9 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
 
   public void unbindWindow(@NotNull Window frame) {
     // exec at EDT
+    if (ourLib == null)
+      return;
+
     final long xid = _getX11WindowXid(frame);
     if (xid == 0) {
       LOG.warn("can't obtain XID of window: " + frame + ", skip global menu unbinding");
@@ -233,6 +256,7 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
   }
 
   public void setRoots(List<ActionMenu> roots) {
+    // exec at EDT
     if (ourLib == null)
       return;
 
@@ -252,16 +276,16 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
 
     myRoots = newRoots;
     _trace("set new menu roots, count=%d", size);
-    myIsProcessed = false;
-    ourLib.execOnMainLoop(ourProcessQueue);
+    myIsRootsUpdated = false;
+    ourLib.execOnMainLoop(ourUpdateAllRoots);
   }
 
-  private void _processRoots() {
+  private void _updateRoots() {
     // exec at glib-thread
-    if (myIsProcessed)
+    if (myIsRootsUpdated || !myIsEnabled || myIsDisposed)
       return;
 
-    myIsProcessed = true;
+    myIsRootsUpdated = true;
 
     if (myWindowHandle == null) {
       myWindowHandle = ourLib.registerWindow(myXid, this);
@@ -281,7 +305,35 @@ public class GlobalMenuLinux implements GlobalMenuLib.EventHandler, Disposable {
       mi.nativePeer = ourLib.addRootMenu(myWindowHandle, mi.uid, mi.txt);
 
     if (!SHOW_SWING_MENU)
-      ApplicationManager.getApplication().invokeLater(()->myFrame.getJMenuBar().setVisible(false));
+      ApplicationManager.getApplication().invokeLater(()->{
+        if (myIsEnabled)
+          myFrame.getJMenuBar().setVisible(false);
+      });
+  }
+
+  public void toggle(boolean enabled) {
+    if (ourLib == null || myIsDisposed)
+      return;
+
+    if (myIsEnabled == enabled)
+      return;
+
+    myIsEnabled = enabled;
+
+    if (enabled) {
+      _trace("enable global-menu");
+      myIsRootsUpdated = false;
+      ourLib.execOnMainLoop(ourUpdateAllRoots);
+    } else {
+      if (myWindowHandle != null) {
+        _trace("disable global menu, scheduled destroying of GlobalMenuLinux for xid=0x%X", myXid);
+        ourLib.releaseWindowOnMainLoop(myWindowHandle, myOnWindowReleased);
+      }
+
+      final JMenuBar frameMenu = myFrame.getJMenuBar();
+      if (frameMenu != null)
+        frameMenu.setVisible(true);
+    }
   }
 
   private MenuItemInternal _findMenuItem(int uid) {
index 411bc316c0edc9d7e34d0b25bd2d02d8e2b2d74b..f82072fcf6aaa5890a15fa6801b5a01a589c85f2 100644 (file)
@@ -152,6 +152,11 @@ public abstract class IdeFrameDecorator implements Disposable {
       if (myFrame != null) {
         myRequestedState = state;
         X11UiUtil.toggleFullScreenMode(myFrame);
+
+        if (myFrame.getJMenuBar() instanceof IdeMenuBar) {
+          final IdeMenuBar frameMenuBar = (IdeMenuBar)myFrame.getJMenuBar();
+          frameMenuBar.onToggleFullScreen(state);
+        }
       }
       return ActionCallback.DONE;
     }
index 4e74be871173747e774109ded2b8afad4957602b..42f66e1e6235664706f5e8dc01dab53abfdbcda4 100644 (file)
@@ -600,6 +600,13 @@ public class IdeMenuBar extends JMenuBar implements IdeEventQueue.EventDispatche
     }
   }
 
+  public void onToggleFullScreen(boolean isFullScreen) {
+    if (myGlobalMenuLinux == null)
+      return;
+
+     myGlobalMenuLinux.toggle(!isFullScreen);
+  }
+
   private static class MyExitFullScreenButton extends JButton {
     private MyExitFullScreenButton() {
       setFocusable(false);