Zooming API

This commit is contained in:
Karl Tauber
2025-10-26 18:50:24 +01:00
parent 0d4946230e
commit 0fb4c811f6
7 changed files with 800 additions and 40 deletions

View File

@@ -368,6 +368,22 @@ public abstract class FlatLaf
String.format( "a, address { color: #%06x; }", linkColor.getRGB() & 0xffffff ) );
}
};
// Initialize UIScale user scale factor immediately after FlatLaf was activated,
// which is necessary to ensure that UIScale.setZoomFactor(float)
// scales FlatLaf defaultDont correctly even if UIScale.scale() was not yet used.
// In other words: Without this, UIScale.setZoomFactor(float) would
// not work correctly if invoked between FlatLaf.setup() and crating UI.
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
if( "lookAndFeel".equals( e.getPropertyName() ) ) {
UIManager.removePropertyChangeListener( this );
UIScale.getUserScaleFactor();
}
}
};
UIManager.addPropertyChangeListener( listener );
}
@Override
@@ -707,11 +723,22 @@ public abstract class FlatLaf
uiFont = ((ActiveFont)defaultFont).derive( baseFont, fontSize -> {
return Math.round( fontSize * UIScale.computeFontScaleFactor( baseFont ) );
} );
}
} else if( defaultFont instanceof LazyValue )
uiFont = ActiveFont.toUIResource( (Font) ((LazyValue)defaultFont).createValue( defaults ) );
// increase font size if system property "flatlaf.uiScale" is set
uiFont = UIScale.applyCustomScaleFactor( uiFont );
// apply zoom factor to font size
float zoomFactor = UIScale.getZoomFactor();
if( zoomFactor != 1 ) {
// see also UIScale.setZoomFactor()
int unzoomedFontSize = uiFont.getSize();
defaults.put( "defaultFont.unzoomedSize", unzoomedFontSize );
int newFontSize = Math.max( Math.round( unzoomedFontSize * zoomFactor ), 1 );
uiFont = new FontUIResource( uiFont.deriveFont( (float) newFontSize ) );
}
// set default font
defaults.put( "defaultFont", uiFont );
}
@@ -1768,7 +1795,7 @@ public abstract class FlatLaf
return toUIResource( baseFont );
}
private FontUIResource toUIResource( Font font ) {
private static FontUIResource toUIResource( Font font ) {
// make sure that font is a UIResource for LaF switching
return (font instanceof FontUIResource)
? (FontUIResource) font

View File

@@ -27,7 +27,9 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.plaf.DimensionUIResource;
import javax.swing.plaf.FontUIResource;
@@ -61,16 +63,28 @@ import com.formdev.flatlaf.FlatSystemProperties;
* or if the default font is changed.
* The user scale factor is computed based on the used font.
* The JRE does not scale anything.
* So we have to invoke {@link #scale(float)} where necessary.
* So we have to invoke {@link #scale(int)} where necessary.
* There is only one user scale factor for all displays.
* The user scale factor may change if the active LaF, "defaultFont" or "Label.font" has changed.
* If system scaling mode is available the user scale factor is usually 1,
* but may be larger on Linux or if the default font is changed.
*
* <h2>Zooming</h2>
*
* Zooming allows appliations to easily zoom their UI, if FlatLaf is active Laf.
* This is done by changing user scale factor and default font.
* There are methods to increase, decrease and reset zoom factor.
* <p>
* Note: Only standard Swing components are zoomed.
* Custom components need to use {@link #scale(int)} to zoom their UI.
*
* @author Karl Tauber
*/
public class UIScale
{
/** @since 3.7 */ public static final String PROP_USER_SCALE_FACTOR = "userScaleFactor";
/** @since 3.7 */ public static final String PROP_ZOOM_FACTOR = "zoomFactor";
private static final boolean DEBUG = false;
private static PropertyChangeSupport changeSupport;
@@ -87,7 +101,7 @@ public class UIScale
changeSupport.removePropertyChangeListener( listener );
}
//---- system scaling (Java 9) --------------------------------------------
//---- system scaling (Java 9+) -------------------------------------------
private static Boolean jreHiDPI;
@@ -135,10 +149,13 @@ public class UIScale
return (isSystemScalingEnabled() && gc != null) ? gc.getDefaultTransform().getScaleX() : 1;
}
//---- user scaling (Java 8) ----------------------------------------------
//---- user scaling (Java 8 / zooming) ------------------------------------
private static float unzoomedScaleFactor = 1;
private static float scaleFactor = 1;
private static boolean initialized;
private static boolean listenerInitialized; // use extra flag for unit tests
private static boolean ignoreFontChange;
private static void initialize() {
if( initialized )
@@ -148,33 +165,43 @@ public class UIScale
if( !isUserScalingEnabled() )
return;
initializeListener();
updateScaleFactor( true );
}
private static void initializeListener() {
if( listenerInitialized )
return;
listenerInitialized = true;
// listener to update scale factor if LaF changed, "defaultFont" or "Label.font" changed
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
switch( e.getPropertyName() ) {
case "lookAndFeel":
// it is not necessary (and possible) to remove listener of old LaF defaults
// it is not possible (and necessary) to remove listener of old LaF defaults
// because it is not possible to access the UIDefault object of the old LaF
if( e.getNewValue() instanceof LookAndFeel )
UIManager.getLookAndFeelDefaults().addPropertyChangeListener( this );
updateScaleFactor();
updateScaleFactor( true );
break;
case "defaultFont":
case "Label.font":
updateScaleFactor();
if( !ignoreFontChange )
updateScaleFactor( false );
break;
}
}
};
UIManager.addPropertyChangeListener( listener );
UIManager.getDefaults().addPropertyChangeListener( listener );
UIManager.getLookAndFeelDefaults().addPropertyChangeListener( listener );
updateScaleFactor();
UIManager.addPropertyChangeListener( listener );
}
private static void updateScaleFactor() {
private static void updateScaleFactor( boolean lafChanged ) {
if( !isUserScalingEnabled() )
return;
@@ -185,17 +212,20 @@ public class UIScale
return;
}
// use font size to calculate scale factor (instead of DPI)
// because even if we are on a HiDPI display it is not sure
// that a larger font size is set by the current LaF
// (e.g. can avoid large icons with small text)
// get font that is used to calculate scale factor
Font font = null;
if( UIManager.getLookAndFeel() instanceof FlatLaf )
font = UIManager.getFont( "defaultFont" );
if( font == null )
font = UIManager.getFont( "Label.font" );
setUserScaleFactor( computeFontScaleFactor( font ), true );
float fontScaleFactor = computeFontScaleFactor( font );
if( lafChanged && UIManager.getLookAndFeel() instanceof FlatLaf ) {
// FlatLaf has applied zoom factor in FlatLaf.initDefaultFont() to defaultFont,
// so we need to take it into account to get correct user scale factor
fontScaleFactor /= zoomFactor;
}
setUserScaleFactor( fontScaleFactor, true );
}
/**
@@ -204,7 +234,7 @@ public class UIScale
* @since 2
*/
public static float computeFontScaleFactor( Font font ) {
if( SystemInfo.isWindows ) {
if( SystemInfo.isWindows && !inUnitTests ) {
// Special handling for Windows to be compatible with OS scaling,
// which distinguish between "screen scaling" and "text scaling".
// - Windows "screen scaling" scales everything (text, icon, gaps, etc.)
@@ -335,7 +365,7 @@ public class UIScale
}
/**
* Returns the user scale factor.
* Returns the user scale factor (including zoom factor).
*/
public static float getUserScaleFactor() {
initialize();
@@ -345,27 +375,49 @@ public class UIScale
/**
* Sets the user scale factor.
*/
private static void setUserScaleFactor( float scaleFactor, boolean normalize ) {
if( normalize ) {
if( scaleFactor < 1f ) {
scaleFactor = FlatSystemProperties.getBoolean( FlatSystemProperties.UI_SCALE_ALLOW_SCALE_DOWN, false )
? Math.round( scaleFactor * 10f ) / 10f // round small scale factor to 1/10
: 1f;
} else if( scaleFactor > 1f ) // round scale factor to 1/4
scaleFactor = Math.round( scaleFactor * 4f ) / 4f;
}
private static void setUserScaleFactor( float unzoomedScaleFactor, boolean normalize ) {
if( normalize )
unzoomedScaleFactor = normalizeScaleFactor( unzoomedScaleFactor );
// minimum scale factor
scaleFactor = Math.max( scaleFactor, 0.1f );
unzoomedScaleFactor = Math.max( unzoomedScaleFactor, 0.1f );
if( unzoomedScaleFactor == UIScale.unzoomedScaleFactor )
return;
if( DEBUG )
System.out.println( "Unzoomed scale factor " + UIScale.unzoomedScaleFactor + " --> " + unzoomedScaleFactor );
UIScale.unzoomedScaleFactor = unzoomedScaleFactor;
setScaleFactor( unzoomedScaleFactor * zoomFactor );
}
private static void setScaleFactor( float scaleFactor ) {
// round scale factor to 1/100
scaleFactor = Math.round( scaleFactor * 100f ) / 100f;
if( scaleFactor == UIScale.scaleFactor )
return;
float oldScaleFactor = UIScale.scaleFactor;
UIScale.scaleFactor = scaleFactor;
if( DEBUG )
System.out.println( "HiDPI scale factor " + scaleFactor );
System.out.println( "Scale factor " + oldScaleFactor + " --> " + scaleFactor + " (unzoomed " + UIScale.unzoomedScaleFactor + ")" );
if( changeSupport != null )
changeSupport.firePropertyChange( "userScaleFactor", oldScaleFactor, scaleFactor );
changeSupport.firePropertyChange( PROP_USER_SCALE_FACTOR, oldScaleFactor, scaleFactor );
}
private static float normalizeScaleFactor( float scaleFactor ) {
if( scaleFactor < 1f ) {
return FlatSystemProperties.getBoolean( FlatSystemProperties.UI_SCALE_ALLOW_SCALE_DOWN, false )
? Math.round( scaleFactor * 10f ) / 10f // round small scale factor to 1/10
: 1f;
} else if( scaleFactor > 1f ) // round scale factor to 1/4
return Math.round( scaleFactor * 4f ) / 4f;
else
return scaleFactor;
}
/**
@@ -451,4 +503,185 @@ public class UIScale
? new InsetsUIResource( scale( insets.top ), scale( insets.left ), scale( insets.bottom ), scale( insets.right ) )
: new Insets ( scale( insets.top ), scale( insets.left ), scale( insets.bottom ), scale( insets.right ) ));
}
//---- zoom ---------------------------------------------------------------
private static float zoomFactor = 1;
private static float[] supportedZoomFactors = { 1f, 1.1f, 1.25f, 1.5f, 1.75f, 2f };
/**
* Returns the current zoom factor. Default is {@code 1}.
*
* @since 3.7
*/
public static float getZoomFactor() {
return zoomFactor;
}
/**
* Sets the zoom factor.
* Also updates user scale factor and default font (if FlatLaf is active Laf).
* <p>
* UI needs to be updated if zoom factor has changed. E.g.:
* <pre>{@code
* if( UIScale.setZoomFactor( newZoomFactor ) )
* FlatLaf.updateUI();
* }</pre>
*
* @param zoomFactor new zoom factor
* @return {@code true} if zoom factor has changed
* @since 3.7
*/
public static boolean setZoomFactor( float zoomFactor ) {
// minimum zoom factor
zoomFactor = Math.max( zoomFactor, 0.1f );
if( UIScale.zoomFactor == zoomFactor )
return false;
float oldZoomFactor = UIScale.zoomFactor;
UIScale.zoomFactor = zoomFactor;
if( DEBUG )
System.out.println( "Zoom factor " + oldZoomFactor + " --> " + zoomFactor );
setScaleFactor( UIScale.unzoomedScaleFactor * zoomFactor );
if( initialized && UIManager.getLookAndFeel() instanceof FlatLaf ) {
// see also FlatLaf.initDefaultFont()
UIDefaults defaults = UIManager.getLookAndFeelDefaults();
Font font = defaults.getFont( "defaultFont" );
int unzoomedSize = defaults.getInt( "defaultFont.unzoomedSize" );
if( unzoomedSize == 0 ) {
unzoomedSize = font.getSize();
defaults.put( "defaultFont.unzoomedSize", unzoomedSize );
}
// update "defaultFont"
ignoreFontChange = true;
try {
// get application default font before updating Laf default font
Font appFont = UIManager.getFont( "defaultFont" );
// update Laf default font
int newFontSize = Math.max( Math.round( unzoomedSize * zoomFactor ), 1 );
defaults.put( "defaultFont", new FontUIResource( font.deriveFont( (float) newFontSize ) ) );
if( DEBUG )
System.out.println( "Zoom Laf font " + font.getSize() + " --> " + newFontSize + " (unzoomed " + unzoomedSize + ")" );
// check whether application has changed default font
if( appFont != font ) {
// application has own default font --> also zoom it
int newAppFontSize = Math.max( Math.round( (appFont.getSize() / oldZoomFactor) * zoomFactor ), 1 );
UIManager.put( "defaultFont", appFont.deriveFont( (float) newAppFontSize ) );
if( DEBUG )
System.out.println( "Zoom app font " + appFont.getSize() + " --> " + newAppFontSize );
}
} finally {
ignoreFontChange = false;
}
}
if( changeSupport != null )
changeSupport.firePropertyChange( PROP_ZOOM_FACTOR, oldZoomFactor, zoomFactor );
return true;
}
/**
* Increases zoom factor using next greater factor in supported factors array.
* <p>
* UI needs to be updated if zoom factor has changed. E.g.:
* <pre>{@code
* if( UIScale.zoomIn() )
* FlatLaf.updateUI();
* }</pre>
*
* @return {@code true} if zoom factor has changed
* @see #getSupportedZoomFactors()
* @since 3.7
*/
public static boolean zoomIn() {
int i = Arrays.binarySearch( supportedZoomFactors, zoomFactor );
int next = (i >= 0) ? i + 1 : -i - 1;
if( next >= supportedZoomFactors.length )
return false;
return setZoomFactor( supportedZoomFactors[next] );
}
/**
* Decreases zoom factor using next smaller factor in supported factors array.
* <p>
* UI needs to be updated if zoom factor has changed. E.g.:
* <pre>{@code
* if( UIScale.zoomOut() )
* FlatLaf.updateUI();
* }</pre>
*
* @return {@code true} if zoom factor has changed
* @see #getSupportedZoomFactors()
* @since 3.7
*/
public static boolean zoomOut() {
int i = Arrays.binarySearch( supportedZoomFactors, zoomFactor );
int prev = (i >= 0) ? i - 1 : -i - 2;
if( prev < 0 )
return false;
return setZoomFactor( supportedZoomFactors[prev] );
}
/**
* Resets zoom factor to {@code 1}.
* <p>
* UI needs to be updated if zoom factor has changed. E.g.:
* <pre>{@code
* if( UIScale.zoomReset() )
* FlatLaf.updateUI();
* }</pre>
*
* @return {@code true} if zoom factor has changed
* @since 3.7
*/
public static boolean zoomReset() {
return setZoomFactor( 1 );
}
/**
* Returns the supported zoom factors used for {@link #zoomIn()} and {@link #zoomOut()}.
* <p>
* Default is {@code [ 1f, 1.1f, 1.25f, 1.5f, 1.75f, 2f ]}.
*
* @since 3.7
*/
public static float[] getSupportedZoomFactors() {
return supportedZoomFactors.clone();
}
/**
* Sets the supported zoom factors used for {@link #zoomIn()} and {@link #zoomOut()}.
*
* @since 3.7
*/
public static void setSupportedZoomFactors( float[] supportedZoomFactors ) {
UIScale.supportedZoomFactors = supportedZoomFactors.clone();
Arrays.sort( UIScale.supportedZoomFactors );
if( Arrays.binarySearch( UIScale.supportedZoomFactors, 1f ) < 0 )
throw new IllegalArgumentException( "supportedZoomFactors array must contain value 1f" );
}
//---- unit testing -------------------------------------------------------
static boolean inUnitTests;
static void tests_uninitialize() {
initialized = false;
unzoomedScaleFactor = 1;
scaleFactor = 1;
zoomFactor = 1;
}
}

View File

@@ -0,0 +1,343 @@
/*
* Copyright 2025 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import java.awt.Font;
import java.util.Collections;
import java.util.Map;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.metal.MetalLookAndFeel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.formdev.flatlaf.FlatDarkLaf;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.FlatLightLaf;
import com.formdev.flatlaf.FlatSystemProperties;
/**
* @author Karl Tauber
*/
public class TestUIScale
{
private static Map<String, String> FONT_EXTRA_DEFAULTS_1x = Collections.singletonMap(
"defaultFont", "{instance}java.awt.Font,Dialog,0,12" );
private static Map<String, String> FONT_EXTRA_DEFAULTS_1_5x = Collections.singletonMap(
"defaultFont", "{instance}java.awt.Font,Dialog,0,18" );
@BeforeAll
static void setup() {
UIScale.inUnitTests = true;
// disable platform specific fonts
System.setProperty( "flatlaf.uiScale.fontSizeDivider", "12" );
FlatLaf.setGlobalExtraDefaults( FONT_EXTRA_DEFAULTS_1x );
}
@AfterAll
static void cleanup() throws UnsupportedLookAndFeelException {
System.clearProperty( "flatlaf.uiScale.fontSizeDivider" );
FlatLaf.setGlobalExtraDefaults( null );
UIScale.inUnitTests = false;
}
@AfterEach
void afterEach() throws UnsupportedLookAndFeelException {
UIManager.setLookAndFeel( new MetalLookAndFeel() );
UIManager.put( "defaultFont", null );
UIManager.put( "Label.font", null );
FlatLaf.setGlobalExtraDefaults( FONT_EXTRA_DEFAULTS_1x );
UIScale.tests_uninitialize();
}
@Test
void testCustomScaleFactor() {
System.setProperty( FlatSystemProperties.UI_SCALE, "1.25x" );
assertScaleFactor( 1.25f );
System.setProperty( FlatSystemProperties.UI_SCALE, "2x" );
UIScale.tests_uninitialize();
assertScaleFactor( 2f );
System.clearProperty( FlatSystemProperties.UI_SCALE );
}
@Test
void testLabelFontScaling() {
assertInstanceOf( MetalLookAndFeel.class, UIManager.getLookAndFeel() );
testLabelFont( 8, 1f );
testLabelFont( 9, 1f );
testLabelFont( 10, 1f );
testLabelFont( 11, 1f );
testLabelFont( 12, 1f );
testLabelFont( 13, 1f );
testLabelFont( 14, 1.25f );
testLabelFont( 15, 1.25f );
testLabelFont( 16, 1.25f );
testLabelFont( 17, 1.5f );
testLabelFont( 18, 1.5f );
testLabelFont( 19, 1.5f );
testLabelFont( 20, 1.75f );
testLabelFont( 21, 1.75f );
testLabelFont( 22, 1.75f );
testLabelFont( 23, 2f );
testLabelFont( 24, 2f );
testLabelFont( 25, 2f );
testLabelFont( 26, 2.25f );
}
private void testLabelFont( int fontSize, float expectedScaleFactor ) {
UIManager.put( "Label.font", new Font( Font.DIALOG, Font.PLAIN, fontSize ) );
assertScaleFactor( expectedScaleFactor );
}
@Test
void testDefaultFontScaling() {
FlatLightLaf.setup();
testDefaultFont( 8, 1f );
testDefaultFont( 9, 1f );
testDefaultFont( 10, 1f );
testDefaultFont( 11, 1f );
testDefaultFont( 12, 1f );
testDefaultFont( 13, 1f );
testDefaultFont( 14, 1.25f );
testDefaultFont( 15, 1.25f );
testDefaultFont( 16, 1.25f );
testDefaultFont( 17, 1.5f );
testDefaultFont( 18, 1.5f );
testDefaultFont( 19, 1.5f );
testDefaultFont( 20, 1.75f );
testDefaultFont( 21, 1.75f );
testDefaultFont( 22, 1.75f );
testDefaultFont( 23, 2f );
testDefaultFont( 24, 2f );
testDefaultFont( 25, 2f );
testDefaultFont( 26, 2.25f );
}
private void testDefaultFont( int fontSize, float expectedScaleFactor ) {
UIManager.put( "defaultFont", new Font( Font.DIALOG, Font.PLAIN, fontSize ) );
assertScaleFactor( expectedScaleFactor );
}
@Test
void testInitialScaleFactorAndFontSizes() {
FlatLightLaf.setup();
assertScaleFactorAndFontSizes( 1f, 12, -1 );
FlatLaf.setGlobalExtraDefaults( FONT_EXTRA_DEFAULTS_1_5x );
FlatDarkLaf.setup();
assertScaleFactorAndFontSizes( 1.5f, 18, -1 );
}
@Test
void zoom_Metal() {
UIScale.setZoomFactor( 1.1f );
assertScaleFactor( 1.1f );
UIScale.setZoomFactor( 1.3f );
assertScaleFactor( 1.3f );
UIScale.setZoomFactor( 2.3f );
assertScaleFactor( 2.3f );
}
@Test
void zoom_1x() {
FlatLightLaf.setup();
testZoom( 0.7f, 0.7f, 8, -1 );
testZoom( 0.75f, 0.75f, 9, -1 );
testZoom( 0.8f, 0.8f, 10, -1 );
testZoom( 0.9f, 0.9f, 11, -1 );
testZoom( 1f, 1f, 12, -1 );
testZoom( 1.1f, 1.1f, 13, -1 );
testZoom( 1.2f, 1.2f, 14, -1 );
testZoom( 1.25f, 1.25f, 15, -1 );
testZoom( 1.3f, 1.3f, 16, -1 );
testZoom( 1.4f, 1.4f, 17, -1 );
testZoom( 1.5f, 1.5f, 18, -1 );
testZoom( 1.6f, 1.6f, 19, -1 );
testZoom( 1.7f, 1.7f, 20, -1 );
testZoom( 1.75f, 1.75f, 21, -1 );
testZoom( 1.8f, 1.8f, 22, -1 );
testZoom( 1.9f, 1.9f, 23, -1 );
testZoom( 2f, 2f, 24, -1 );
testZoom( 2.25f, 2.25f, 27, -1 );
testZoom( 2.5f, 2.5f, 30, -1 );
testZoom( 2.75f, 2.75f, 33, -1 );
testZoom( 3f, 3f, 36, -1 );
testZoom( 4f, 4f, 48, -1 );
}
@Test
void zoom_1_5x() {
FlatLaf.setGlobalExtraDefaults( FONT_EXTRA_DEFAULTS_1_5x );
FlatLightLaf.setup();
testZoom( 0.7f, 1.05f, 13, -1 );
testZoom( 0.75f, 1.13f, 14, -1 );
testZoom( 0.8f, 1.2f, 14, -1 );
testZoom( 0.9f, 1.35f, 16, -1 );
testZoom( 1f, 1.5f, 18, -1 );
testZoom( 1.1f, 1.65f, 20, -1 );
testZoom( 1.2f, 1.8f, 22, -1 );
testZoom( 1.25f, 1.88f, 23, -1 );
testZoom( 1.3f, 1.95f, 23, -1 );
testZoom( 1.4f, 2.1f, 25, -1 );
testZoom( 1.5f, 2.25f, 27, -1 );
testZoom( 1.6f, 2.4f, 29, -1 );
testZoom( 1.7f, 2.55f, 31, -1 );
testZoom( 1.75f, 2.63f, 32, -1 );
testZoom( 1.8f, 2.7f, 32, -1 );
testZoom( 1.9f, 2.85f, 34, -1 );
testZoom( 2f, 3f, 36, -1 );
testZoom( 2.25f, 3.38f, 41, -1 );
testZoom( 2.5f, 3.75f, 45, -1 );
testZoom( 2.75f, 4.13f, 50, -1 );
testZoom( 3f, 4.5f, 54, -1 );
testZoom( 4f, 6f, 72, -1 );
}
@Test
void zoomAppFont_1x() {
FlatLightLaf.setup();
UIManager.put( "defaultFont", new Font( Font.DIALOG, Font.PLAIN, 14 ) );
testZoom( 1f, 1.25f, 12, 14 );
testZoom( 1.1f, 1.38f, 13, 15 );
testZoom( 1.25f, 1.56f, 15, 17 );
testZoom( 1.5f, 1.88f, 18, 20 );
testZoom( 1.75f, 2.19f, 21, 23 );
testZoom( 2f, 2.5f, 24, 26 );
testZoom( 1f, 1.25f, 12, 13 );
testZoom( 2f, 2.5f, 24, 26 );
}
@Test
void zoomWithLafChange() {
FlatLightLaf.setup();
assertScaleFactorAndFontSizes( 1f, 12, -1 );
testZoom( 1.1f, 1.1f, 13, -1 );
FlatDarkLaf.setup();
assertScaleFactorAndFontSizes( 1.1f, 13, -1 );
testZoom( 1.2f, 1.2f, 14, -1 );
FlatLightLaf.setup();
assertScaleFactorAndFontSizes( 1.2f, 14, -1 );
testZoom( 1.3f, 1.3f, 16, -1 );
FlatLaf.setGlobalExtraDefaults( FONT_EXTRA_DEFAULTS_1_5x );
FlatDarkLaf.setup();
assertScaleFactorAndFontSizes( 1.95f, 23, -1 );
testZoom( 1.4f, 2.1f, 25, -1 );
FlatLightLaf.setup();
assertScaleFactorAndFontSizes( 2.1f, 25, -1 );
testZoom( 1.5f, 2.25f, 27, -1 );
}
@Test
void zoomWithDefaultFontChange() {
FlatLightLaf.setup();
assertScaleFactorAndFontSizes( 1f, 12, -1 );
float zoom1 = 1.4f;
testZoom( zoom1, zoom1, 17, -1 );
testDefaultFont( 8, z( zoom1, 1f ) );
testDefaultFont( 9, z( zoom1, 1f ) );
testDefaultFont( 10, z( zoom1, 1f ) );
testDefaultFont( 11, z( zoom1, 1f ) );
testDefaultFont( 12, z( zoom1, 1f ) );
testDefaultFont( 13, z( zoom1, 1f ) );
testDefaultFont( 14, z( zoom1, 1.25f ) );
testDefaultFont( 15, z( zoom1, 1.25f ) );
testDefaultFont( 16, z( zoom1, 1.25f ) );
testDefaultFont( 17, z( zoom1, 1.5f ) );
testDefaultFont( 18, z( zoom1, 1.5f ) );
testDefaultFont( 19, z( zoom1, 1.5f ) );
testDefaultFont( 20, z( zoom1, 1.75f ) );
testDefaultFont( 21, z( zoom1, 1.75f ) );
testDefaultFont( 22, z( zoom1, 1.75f ) );
testDefaultFont( 23, z( zoom1, 2f ) );
testDefaultFont( 24, z( zoom1, 2f ) );
testDefaultFont( 25, z( zoom1, 2f ) );
testDefaultFont( 26, z( zoom1, 2.25f ) );
float zoom2 = 1.8f;
testZoom( zoom2, 4.05f, 22, 33 );
testDefaultFont( 8, z( zoom2, 1f ) );
testDefaultFont( 9, z( zoom2, 1f ) );
testDefaultFont( 10, z( zoom2, 1f ) );
testDefaultFont( 11, z( zoom2, 1f ) );
testDefaultFont( 12, z( zoom2, 1f ) );
testDefaultFont( 13, z( zoom2, 1f ) );
testDefaultFont( 14, z( zoom2, 1.25f ) );
testDefaultFont( 15, z( zoom2, 1.25f ) );
testDefaultFont( 16, z( zoom2, 1.25f ) );
testDefaultFont( 17, z( zoom2, 1.5f ) );
testDefaultFont( 18, z( zoom2, 1.5f ) );
testDefaultFont( 19, z( zoom2, 1.5f ) );
testDefaultFont( 20, z( zoom2, 1.75f ) );
testDefaultFont( 21, z( zoom2, 1.75f ) );
testDefaultFont( 22, z( zoom2, 1.75f ) );
testDefaultFont( 23, z( zoom2, 2f ) );
testDefaultFont( 24, z( zoom2, 2f ) );
testDefaultFont( 25, z( zoom2, 2f ) );
testDefaultFont( 26, z( zoom2, 2.25f ) );
}
private static float z( float zoom, float scale ) {
// round scale factor to 1/100
return Math.round( (zoom * scale) * 100f ) / 100f;
}
private static void testZoom( float zoomFactor, float expectedScaleFactor,
int expectedLafFontSize, int expectedAppFontSize )
{
UIScale.setZoomFactor( zoomFactor );
assertScaleFactorAndFontSizes( expectedScaleFactor, expectedLafFontSize, expectedAppFontSize );
}
private static void assertScaleFactorAndFontSizes( float expectedScaleFactor,
int expectedLafFontSize, int expectedAppFontSize )
{
assertScaleFactor( expectedScaleFactor );
Font lafFont = UIManager.getLookAndFeelDefaults().getFont( "defaultFont" );
Font appFont = UIManager.getFont( "defaultFont" );
assertEquals( expectedLafFontSize, lafFont.getSize() );
if( expectedAppFontSize > 0 ) {
assertNotEquals( lafFont, appFont );
assertEquals( expectedAppFontSize, appFont.getSize() );
} else
assertEquals( lafFont, appFont );
}
private static void assertScaleFactor( float expectedScaleFactor ) {
assertEquals( expectedScaleFactor, UIScale.getUserScaleFactor() );
}
}