java - 使用非 Aqua 外观时,OS X 上的工具提示隐藏在 JOGL GLCanvas 后面

标签 java macos swing tooltip jogl

在以下程序(取决于 JOGL)中,当工具提示“适合”在 内时,JLabel 的工具提示隐藏在重量级 GLCanvas 后面。 GLCanvas.

import java.awt.*;

import javax.swing.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;

import com.jogamp.opengl.awt.GLCanvas;

public class HeavyWeightTooltipTest {

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
        try {
          UIManager.setLookAndFeel(NimbusLookAndFeel.class.getName());
        } catch (Exception aE) {
          aE.printStackTrace();
        }
        showUI();
      }
    });
  }

  private static void showUI(){
    JFrame frame = new JFrame("TestFrame");

    JLabel label = new JLabel("Label with tooltip");
    label.setToolTipText("A very long tooltip to ensure it overlaps with the heavyweight component");
    frame.add(label, BorderLayout.WEST);

    GLCanvas glCanvas = new GLCanvas();
    frame.add(glCanvas, BorderLayout.CENTER);

    frame.setVisible(true);
    frame.setSize(300,300);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  }
}

观察

  • 只有在不使用 Aqua 外观和感觉时才会发生这种情况。我可以用 Nimbus 和 Metal 的外观和感觉来重现它,但不能用 Aqua 的外观和感觉来重现它。
  • 使用常规 java.awt.Canvas 时不会发生这种情况,仅使用 JOGL GLCanvas (它是 java.awt.Canvas 的扩展)时不会发生这种情况)
  • 当工具提示比 GLCanvas 宽时,工具提示会正确呈现。一旦工具提示适合 GLCanvas,问题就开始了(请参阅帖子末尾的屏幕截图)
  • 是否调用 ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false) 并不重要。问题总是可重现的
  • 它适用于 Linux 和 Windows
  • 如果相关的话,我使用的是 JOGL 版本 2.3.2 和 Java 版本 1.8.0_65

    java version "1.8.0_65"
    Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
    Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
    

正确显示工具提示 Tooltip correctly shown 隐藏在 GLCanvas 后面的工具提示 Tooltip hidden behind GLCanvas

编辑:我在 JOGL 的错误跟踪器中将其记录为 bug 1306 .

最佳答案

似乎强制 PopupFactory 使用重量级工具提示(而不是中等重量工具提示)可以解决该问题。 这并不简单,需要您编写自己的 PopupFactory 或使用反射来调用 PopupFactory#setPopupType

由于我不太热衷于编写自己的 PopupFactory,因此我使用了反射:

final class HeavyWeightTooltipEnforcerMac {

  private static final Object LOCK = new Object();
  private static PropertyChangeListener sUIManagerListener;

  private HeavyWeightTooltipEnforcerMac() {
  }

  /**
   * <p>
   *   Tooltips which overlap with the GLCanvas
   *   will be painted behind the heavyweight component when the bounds of the tooltip are contained
   *   in the bounds of the application.
   * </p>
   *
   * <p>
   *   In that case, {@code javax.swing.PopupFactory#MEDIUM_WEIGHT_POPUP} instances are used, and
   *   they suffer from this bug.
   *   Always using {@code javax.swing.PopupFactory#HEAVY_WEIGHT_POPUP} instances fixes the issue.
   * </p>
   *
   * <p>
   *   Note that the bug is only present when not using the Aqua look-and-feel.
   * Aqua uses its own {@code PopupFactory} which does not suffer from this.
   * </p>
   *
   */
  static void install() {
    synchronized (LOCK) {
      if (sUIManagerListener == null && isMacOS()) {
        installCustomPopupFactoryIfNeeded();
        sUIManagerListener = new LookAndFeelChangeListener();
        UIManager.addPropertyChangeListener(sUIManagerListener);
      }
    }
  }

  private static void installCustomPopupFactoryIfNeeded() {
    if (!isAquaLookAndFeel()) {
      PopupFactory.setSharedInstance(new AlwaysUseHeavyWeightPopupsFactory());
    }
  }

  private static final class LookAndFeelChangeListener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      String propertyName = evt.getPropertyName();
      if ("lookAndFeel".equals(propertyName)) {
        installCustomPopupFactoryIfNeeded();
      }
    }
  }

  private static class AlwaysUseHeavyWeightPopupsFactory extends PopupFactory {
    private boolean couldEnforceHeavyWeightComponents = true;

    @Override
    public Popup getPopup(Component owner, Component contents, int x, int y) throws IllegalArgumentException {
      enforceHeavyWeightComponents();
      return super.getPopup(owner, contents, x, y);
    }

    private void enforceHeavyWeightComponents() {
      if (!couldEnforceHeavyWeightComponents) {
        return;
      }
      try {
        Method setPopupTypeMethod = PopupFactory.class.getDeclaredMethod("setPopupType", int.class);
        setPopupTypeMethod.setAccessible(true);
        setPopupTypeMethod.invoke(this, 2);
      } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException aE) {
        //If it fails once, it will fail every time. Do not try again
        //Consequence is that tooltips which overlap with a heavyweight component will be painted behind that component iso
        //on top of it
        couldEnforceHeavyWeightComponents = false;
      }
    }
  }
}

类似的修复可以在 IntelliJ 社区版中找到:LafManagerImpl类在 fixPopupWeight 方法中设置自己的工厂,该方法强制执行重量级弹出窗口。

关于java - 使用非 Aqua 外观时,OS X 上的工具提示隐藏在 JOGL GLCanvas 后面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37001959/

相关文章:

java - 如何在数据表条目中添加路径

java - JPQL有 "SUBSET"函数吗

java - 异常 "System can' t 找不到指定的文件“file.createNewFile()

java - 如何获取 Java Frame 对象以在 Reset 方法中使用?

java - getTableCellRendererComponent 参数的含义

java - 在 JLabel 上的 ImageIcon 周围创建边框,而不是在 Jlabel 周围

java - Android 图像按钮 fragment

c++ - Xcode 5 'forgetting' 头文件..?

cocoa - 多行 NSTextField

java - 如何在 Mac 上设置 Logback?