1

When using a HiDPI aware LAF like "GTK" instead of "metal" then components get scaled. The isssue I am having, how can I scale a custom component the same.

Here is an example where clicking on the buttons switch the look and feel, you can see the components change size, but my custom component stays the same.

What is a good way to have my custom component change size with the associated components.

import java.awt.*;
import javax.swing.*;
public class DpiScale{
    public static void setLaf( String s , JFrame frame){
        try{
            UIManager.setLookAndFeel(s);
            SwingUtilities.updateComponentTreeUI(frame);
            frame.pack();
        } catch(Exception e){
            //oh well
        }
    }
    
    static class CustomComponent extends JPanel{
        int cross = 25;
        @Override
        public Dimension getPreferredSize(){
            return new Dimension( cross, cross );
        }
        @Override
        public void paintComponent( Graphics g){
            g.setColor(Color.BLUE);
            g.drawLine(0, 0, cross, cross);
            g.drawLine(0, cross, cross, 0);
        }
    }
    
    public static void main(String[] args) throws Exception{
            for(UIManager.LookAndFeelInfo lafi: UIManager.getInstalledLookAndFeels()){
                System.out.println(lafi.getClassName());
            }
            
        JFrame frame = new JFrame();
        JButton gtk = new JButton("GTK");
        gtk.addActionListener( 
                evt->{ 
                    setLaf("com.sun.java.swing.plaf.gtk.GTKLookAndFeel", frame);
                    frame.validate(); 
                    }
                );
        
        JButton metal = new JButton("metal");
        metal.addActionListener( 
                evt->{ 
                    setLaf("javax.swing.plaf.metal.MetalLookAndFeel", frame);
                    frame.validate();
                } 
            );
        frame.add(gtk, BorderLayout.NORTH);
        frame.add(new CustomComponent(), BorderLayout.CENTER);
        frame.add(metal, BorderLayout.SOUTH);
        frame.pack();
        frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
        frame.setVisible(true);
    }
}

Here is the result, left is metal and right is gtk. The x does not change size.

Two instances of the program, left is metal and right is gtk.

Specifying GDK_SCALING causes the whole display to change, but I don't think that is an appropriate solution. My actual application is part of a larger application that uses the HiDPI scaling.

matt
  • 10,892
  • 3
  • 22
  • 34

1 Answers1

0

One way I found is to query the graphics object for a scale value.

    static class CustomComponent extends JPanel{
        int cross = 25;
        
        @Override
        public Dimension getPreferredSize(){
            int scaled = (int)(cross * getScale());            
            return new Dimension( scaled, scaled );
        }
        
        public float getScale(){
            Graphics g = getGraphics();
            if(g == null) return 1;
            float fontsize = g.getFont().getSize2D();
            return fontsize/12.0f;
        }
        
        @Override
        public void paintComponent( Graphics g){
            int scaled = (int)(cross*getScale());
            g.setColor(Color.BLUE);
            g.drawLine(0, 0, scaled, scaled);
            g.drawLine(0, scaled, scaled, 0);
            
        }
    }

That solves my issue, but I feel like there should be a better way because I'm using 12pt font as an arbitrary reference size.

matt
  • 10,892
  • 3
  • 22
  • 34
  • don't know how well swing nowadays copes with HiDPI - so using some font size (probably best gotten from the UIManager/LookAndFeel, forgot the gory details ;) might be a good option. Or there might be another option/scaling property buried inside the ui defaults which you could use (possibly going the whole way and make the custom component a "real" JComponent with a UIDelegate, so it will get updated automatically) – kleopatra Nov 25 '22 at 12:20
  • I'd suggest checking the font size of `JLabel` or `JButton` or another component and use it to calculate the size of your custom component. That's what you basically do. If `GDK_SCALING` is set, then the entire UI is scaled (the `Graphics` will be scaled by an `AffineTransform`). The UI appears larger in GTK L&F because the font size obtained from the system is larger in a HiDPI environment. – Alexey Ivanov Jan 02 '23 at 20:23