@@ -1682,6 +1682,7 @@ private static class SvgBackgroundJTextArea extends JTextArea {
16821682 private final FlatSVGIcon svgIcon ;
16831683 private FlatSVGIcon derivedIcon = null ;
16841684 private int derivedIconWidth = 0 ;
1685+ private float osScale = 1f ;
16851686
16861687 SvgBackgroundJTextArea (final String svgFileName ) {
16871688 this .svgIcon = new FlatSVGIcon ("gui/" + svgFileName );
@@ -1696,19 +1697,26 @@ public void addNotify() {
16961697 // Compute the derived icon here (not in paintComponent) to avoid feedback
16971698 // loops between icon size, insets, wrapping, and component height
16981699 super .addNotify ();
1699- // Size the icon to 3 text rows: getRowHeight() is font-based and should be stable
1700- final int iconHeight = getRowHeight () * 3 ;
1700+ // osScale = FlatLaF uiScale / AWT device scale
1701+ // It seems that on macOS and Windows, Java handles HiDPI natively: AWT deviceScale == uiScale,
1702+ // so osScale == 1 and all sizes are already in FlatLaF logical units. On Linux with GDK_SCALE, the OS
1703+ // scales everything _before_(!?) Java sees it: AWT deviceScale stays at 1 while uiScale absorbs
1704+ // the GDK factor, so sizes become in device pixels and must be divided by osScale to get FlatLaF logical
1705+ // units. FlatSVGIcon.derive() and getInsets() both expect FlatLaF logical units on the icon side
1706+ osScale = osScale (this );
1707+ final int iconHeight = (int ) (getRowHeight () * 3 / osScale );
17011708 final float scale = (float ) iconHeight / svgIcon .getIconHeight ();
17021709 derivedIconWidth = Math .round (svgIcon .getIconWidth () * scale );
17031710 derivedIcon = svgIcon .derive (derivedIconWidth , iconHeight );
17041711 }
17051712
17061713 @ Override
17071714 public Insets getInsets () {
1708- // Reserve left space equal to icon width + one 'M' gap for readability
1715+ // derivedIconWidth is in FlatLaF logical units; multiply by osScale to convert to Swing layout coordinates.
1716+ // charWidth is also in FlatLaF logical units (it seems GDK_SCALE is already applied on Linux)
17091717 final Insets base = super .getInsets ();
1710- final int gap = getFontMetrics (getFont ()).stringWidth ( "M" );
1711- return new Insets (base .top , base .left + derivedIconWidth + gap , base .bottom , base .right );
1718+ final int gap = ( int ) ( getFontMetrics (getFont ()).charWidth ( 'm' ) * osScale );
1719+ return new Insets (base .top , base .left + ( int )( derivedIconWidth * osScale ) + gap , base .bottom , base .right );
17121720 }
17131721
17141722 @ Override
@@ -1912,6 +1920,47 @@ public static double uiScale() {
19121920 return Math .max (1.0 , ij .Prefs .getGuiScale ());
19131921 }
19141922
1923+ /**
1924+ * Returns the OS-level scale factor that is NOT already accounted for by Java's HiDPI awareness.
1925+ * <p>
1926+ * On macOS and Windows, Java intercepts HiDPI natively: so this returns 1.0. On Linux with GDK_SCALE, the OS seems
1927+ * to scale everything before Java sees it: AWT/Swing stay at 1.0 scaling while {@link #uiScale()} absorbs the GDK
1928+ * factor, so font sizes, etc. are in device pixels and must be divided by the returned value to reach FlatLaF
1929+ * logical units.
1930+ * </p>
1931+ * Uses the default screen device; prefer {@link #osScale(Component)} when a
1932+ * component is available so multi-monitor setups are handled correctly.
1933+ */
1934+ public static float osScale () {
1935+ try {
1936+ final double deviceScale = GraphicsEnvironment .getLocalGraphicsEnvironment ()
1937+ .getDefaultScreenDevice ()
1938+ .getDefaultConfiguration ()
1939+ .getDefaultTransform ()
1940+ .getScaleX ();
1941+ return (float ) (uiScale () / Math .max (1.0 , deviceScale ));
1942+ } catch (final Exception ignored ) {
1943+ return 1f ;
1944+ }
1945+ }
1946+
1947+ /**
1948+ * Component-aware variant of {@link #osScale()}: uses the component's own
1949+ * {@link java.awt.GraphicsConfiguration} so the correct scale is returned on
1950+ * multi-monitor setups where screens may have different DPI.
1951+ */
1952+ public static float osScale (final Component c ) {
1953+ if (c == null ) return osScale ();
1954+ try {
1955+ final java .awt .GraphicsConfiguration gc = c .getGraphicsConfiguration ();
1956+ if (gc == null ) return osScale ();
1957+ final double deviceScale = gc .getDefaultTransform ().getScaleX ();
1958+ return (float ) (uiScale () / Math .max (1.0 , deviceScale ));
1959+ } catch (final Exception ignored ) {
1960+ return 1f ;
1961+ }
1962+ }
1963+
19151964 public static void initSplashScreen () {
19161965 splashScreen = new SplashScreen ();
19171966 splashScreen .addMouseListener (new MouseAdapter () {
0 commit comments