Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8232927

ToolTipManager is unable to force heavy weight popups

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Currently every system / Java is affected

      A DESCRIPTION OF THE PROBLEM :
      A developer should be able to force the tooltips to be displayed in a heavyweight popup rather than in a medium weight popup.

      At the moment you can use illegal reflective access to force the PopupManager to display the popup in a heavyweight popup with the following code:

      protected void installDefaults(JPanel p) {
          super.installDefaults(p);
          p.setFont(AbstractLookAndFeel.getTheme().getControlTextFont());

          // We don't want medium weight popups for tool tips, so we try to force heavy weight popups.
          try {
              Class clazz = Class.forName("javax.swing.ClientPropertyKey");
              Field field = clazz.getDeclaredField("PopupFactory_FORCE_HEAVYWEIGHT_POPUP");
              field.setAccessible(true);
              p.putClientProperty(field.get(null), Boolean.TRUE);
          } catch (Exception ex) {
          }
      }

      But since such a reflective access to the enum javax.swing.ClientPropertyKey is illegal (and will be removed in future releases) we do not have any chance to force the tooltips to be shown in a heavyweight popup.

      In the JPopupMenu you have the option to set "setLightWeightPopupEnabled(false)" to force a heavywight popup because of

      if (isLightWeightPopupEnabled()) {
          popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
      } else {
          popupFactory.setPopupType(PopupFactory.HEAVY_WEIGHT_POPUP);
      }

      but if you do the same in the "ToolTipManager.setLightWeightPopupEnabled(false)" you can only force a medium weight popup because of

      if (lightWeightPopupEnabled) {
          int y = getPopupFitHeight(popupRect, insideComponent);
          int x = getPopupFitWidth(popupRect,insideComponent);
          if (x>0 || y>0) {
              popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
          } else {
              popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
          }
      }
      else {
          popupFactory.setPopupType(PopupFactory.MEDIUM_WEIGHT_POPUP);
      }

      In addition, there is also a field "heavyWeightPopupEnabled" inside the ToolTipManager which is not used..
      There should be at least one way to force a heavy weight popup in the ToolTipManager. Furthermore a different behavior between the JPopupMenu.setLightWeightPopupEnabled(false) and ToolTipManager.setLightWeightPopupEnabled(false) should be avoided!

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Just add a JLabel to a JPanel and set a tooltip to the JLabel. Check if the tooltip is being rendered in a lightweight or mediumweight popup.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      you should be able to force the tooltip to be rendered in a heavyweight popup either via

      ToolTipManager.setLightWeightPopupEnabled(false) (to be consistent with the JPopupMenu) or add a new method ToolTipManager.setHeavyWeightPopupEnabled(false) (to use the unused field heavyWeightPopupEnabled inside the ToolTipManager)
      ACTUAL -
      you need reflection to achieve the tooltip to be displayed in a heavyweight popup which will not be possible any more in future releases

      ---------- BEGIN SOURCE ----------
      // -*- mode:java; encoding:utf-8 -*-
      // vim:set fileencoding=utf-8:
      // @homepage@

      package example;

      import java.awt.*;
      import java.lang.reflect.Field;
      import java.security.AccessController;
      import java.security.PrivilegedAction;
      import javax.swing.*;

      public final class MainPanel extends JPanel {
        private MainPanel() {
          super();
          JLabel label = makeLabel("FORCE_HEAVYWEIGHT_POPUP", Color.PINK);

          ToolTipManager.sharedInstance().setLightWeightPopupEnabled(false);
          AccessController.doPrivileged(new PrivilegedAction<Void>() {
            @Override public Void run() {
              try {
                Field field;
                if (System.getProperty("java.version").startsWith("1.6.0")) {
                  // https://community.oracle.com/thread/1357949 ComboBox scroll and selected/highlight on glasspane
                  // Class<?> clazz = Class.forName("javax.swing.PopupFactory"); // errorprone: LiteralClassName
                  // field = clazz.getDeclaredField("forceHeavyWeightPopupKey");
                  field = PopupFactory.class.getDeclaredField("forceHeavyWeightPopupKey");
                } else { // JDK 1.7.0, 1.8.0
                  Class<?> clazz = Class.forName("javax.swing.ClientPropertyKey");
                  field = clazz.getDeclaredField("PopupFactory_FORCE_HEAVYWEIGHT_POPUP");
                }
                field.setAccessible(true);
                label.putClientProperty(field.get(null), Boolean.TRUE);
              } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException ex) {
                throw new UnsupportedOperationException(ex);
              }
              return null;
            }
          });

          JComponent glass = new JPanel(new BorderLayout()) {
            private final Color backgroundColor = new Color(0x64_64_64_C8, true);
            @Override protected void paintComponent(Graphics g) {
              g.setColor(backgroundColor);
              g.fillRect(0, 0, getWidth(), getHeight());
              super.paintComponent(g);
            }
          };
          glass.setOpaque(false);
          glass.add(makeLabel("Default: ToolTipText", Color.ORANGE), BorderLayout.WEST);
          glass.add(label, BorderLayout.EAST);
          glass.add(Box.createVerticalStrut(60), BorderLayout.SOUTH);
          EventQueue.invokeLater(() -> {
            getRootPane().setGlassPane(glass);
            getRootPane().getGlassPane().setVisible(true);
          });
          setPreferredSize(new Dimension(320, 240));
        }

        private static JLabel makeLabel(String title, Color color) {
          JLabel label = new JLabel(title);
          label.setOpaque(true);
          label.setBackground(color);
          label.setToolTipText("1234567890");
          return label;
        }

        public static void main(String[] args) {
          EventQueue.invokeLater(MainPanel::createAndShowGui);
        }

        private static void createAndShowGui() {
          try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
          } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
            Toolkit.getDefaultToolkit().beep();
          }
          JFrame frame = new JFrame("@title@");
          frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
          frame.getContentPane().add(new MainPanel());
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      actual workaround is reflection, but this will not work in future releases

      FREQUENCY : always


            psadhukhan Prasanta Sadhukhan
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: