Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JRootPane glasspanes get incorrect bounds, breaking SwingUtilities.convertPoint (on Windows) #696

Closed
eirikbakke opened this issue Jun 29, 2023 · 3 comments

Comments

@eirikbakke
Copy link
Contributor

eirikbakke commented Jun 29, 2023

On Windows, in a JFrame with a JMenuBar and FlatLAF unified title/menu bar enabled, glass panes set via JRootPane.setGlassPane() end up having the wrong bounds. Specifically, the bounds appear shifted down by the height of a regular JMenuBar, even though the JMenuBar is rendered as part of the title bar and does not actually shift other components down.

This in turn causes SwingUtilities.convertPoint to yield the wrong coordinates when converting between the coordinate systems of the glass pane and other components in the window.

The bug was probably introduced by the change in #630 (commit 9101324 ), and can be worked around by setting putClientProperty("JRootPane.glassPaneFullHeight", true) on the JRootPane. (Great foresight putting risky changes behind client properties, by the way!👍)

In the NetBeans IDE (and NetBeans Platform applications such as Ultorg), the bug causes drag-and-drop indication lines that are implemented in org.openide.explorer.view.DropGlassPane to appear in the wrong Y position:

pointer

Possibly similar bug: #658

Here is an example JFrame that shows the problem:

import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLightLaf;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class FlatLafGlassPaneBugExhibit extends JFrame {
  public static boolean APPLY_BUG_WORKAROUND = false;

  private final JLabel label;
  private final JMenu menu1;
  private final JMenu menu2;
  private final JMenuBar menuBar;
  private final JMenuItem menuItem;

  public static void main(String args[]) {
    FlatLightLaf.setup();

    SwingUtilities.invokeLater(() -> {
      new FlatLafGlassPaneBugExhibit().setVisible(true);
    });
  }

  public FlatLafGlassPaneBugExhibit() {
    label = new JLabel();
    menuBar = new JMenuBar();
    menu1 = new JMenu();
    menuItem = new JMenuItem();
    menu2 = new JMenu();
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    label.setBackground(Color.YELLOW);
    label.setText("Label with yellow background, with glass pane drawing red border at bottom edge");
    label.setOpaque(true);
    getContentPane().add(label, java.awt.BorderLayout.NORTH);
    menu1.setText("File");
    menuItem.setText("menuItem1");
    menu1.add(menuItem);
    menuBar.add(menu1);
    menu2.setText("Edit");
    menuBar.add(menu2);
    setJMenuBar(menuBar);

    SomeGlassPane glassPane = new SomeGlassPane();
    glassPane.setOpaque(false);
    JRootPane ourRootPane = getRootPane();
    if (APPLY_BUG_WORKAROUND)
      ourRootPane.putClientProperty(FlatClientProperties.GLASS_PANE_FULL_HEIGHT, true);
    ourRootPane.setGlassPane(glassPane);
    glassPane.setVisible(true);
    setSize(500, 300);
  }

  final class SomeGlassPane extends JPanel {
    @Override
    public void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.BLUE);
      Rectangle bounds = getBounds();
      g.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height);
      int bottomOfLabelY =
          SwingUtilities.convertPoint(label, 0, label.getHeight(), label.getRootPane()).y;
      g.setColor(Color.RED);
      g.drawLine(0, bottomOfLabelY, bounds.x + bounds.width, bottomOfLabelY);
    }
  }
}

Screenshot with APPLY_BUG_WORKAROUND=false, showing the bug (red line should be at bottom edge of JLabel, but is not):

glasspane_bug_without_workaround_incorrect

Screenshot with APPLY_BUG_WORKAROUND=true, showing the correct/expected output:

glasspane_bug_with_workaround_correct
@DevCharly
Copy link
Collaborator

It is not a bug that glass pane is not at location 0,0 in root pane.

This is to avoid that the glass pane overlaps the FlatLaf window title bar and allowing the user to move/maximize/close a window even if a glass pane is active. On macOS/Linux, the glass pane also do not overlap the window title bar (which is not possible because it is provided by the operating system).

The real bug in NetBeans (and in your example above) is to convert coordinates to root pane and then use them for glass pane.

Better convert coordinates to glass pane when using them in glass pane 😉

Here is a fix for above example:

int bottomOfLabelY =
    SwingUtilities.convertPoint(label, 0, label.getHeight(), label.getRootPane().getGlassPane()).y;

@DevCharly DevCharly closed this as not planned Won't fix, can't repro, duplicate, stale Jul 1, 2023
@DevCharly
Copy link
Collaborator

BTW I've submitted a PR to fix location of drag-and-drop insert indicator lines in NetBeans: apache/netbeans#6142

@eirikbakke
Copy link
Contributor Author

Better convert coordinates to glass pane when using them in glass pane

Ah, that makes complete sense! Thank you so much for the NetBeans PR; I will review it and test it later today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants