Skip to content

Commit

Permalink
support changing default font used for all components with automatic …
Browse files Browse the repository at this point in the history
…scaling UI if using larger font
  • Loading branch information
DevCharly committed Mar 31, 2020
1 parent 60c6c5b commit af89dd1
Show file tree
Hide file tree
Showing 12 changed files with 398 additions and 190 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ FlatLaf Change Log
- Linux: Fixed scaling if `GDK_SCALE` environment variable is set or if running
on JetBrains Runtime. (issue #69)
- Tree: Fixed repainting wide selection on focus gained/lost.
- Support changing default font used for all components with automatic scaling
UI if using larger font. Use `UIManager.put( "defaultFont", myFont );`
- No longer use system property `sun.java2d.uiScale`. (Java 8 only)
- Demo: Support using own FlatLaf themes (`.properties` files) that are located
in working directory of Demo application. Shown in the "Themes" list under
Expand Down
52 changes: 50 additions & 2 deletions flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.text.html.HTMLEditorKit;
import com.formdev.flatlaf.util.SystemInfo;
Expand Down Expand Up @@ -345,14 +347,22 @@ private void initFonts( UIDefaults defaults ) {

uiFont = UIScale.applyCustomScaleFactor( uiFont );

// use active value for all fonts to allow changing fonts in all components
// (similar as in Nimbus L&F) with:
// UIManager.put( "defaultFont", myFont );
Object activeFont = new ActiveFont( 1 );

// override fonts
for( Object key : defaults.keySet() ) {
if( key instanceof String && (((String)key).endsWith( ".font" ) || ((String)key).endsWith( "Font" )) )
defaults.put( key, uiFont );
defaults.put( key, activeFont );
}

// use smaller font for progress bar
defaults.put( "ProgressBar.font", UIScale.scaleFont( uiFont, 0.85f ) );
defaults.put( "ProgressBar.font", new ActiveFont( 0.85f ) );

// set default font
defaults.put( "defaultFont", uiFont );
}

/**
Expand Down Expand Up @@ -562,4 +572,42 @@ private static boolean hasMnemonic( Component c ) {

return false;
}

//---- class ActiveFont ---------------------------------------------------

private static class ActiveFont
implements ActiveValue
{
private final float scaleFactor;

// cache (scaled) font
private Font font;
private Font lastDefaultFont;

ActiveFont( float scaleFactor ) {
this.scaleFactor = scaleFactor;
}

@Override
public Object createValue( UIDefaults table ) {
Font defaultFont = UIManager.getFont( "defaultFont" );

if( lastDefaultFont != defaultFont ) {
lastDefaultFont = defaultFont;

if( scaleFactor != 1 ) {
// scale font
int newFontSize = Math.round( defaultFont.getSize() * scaleFactor );
font = new FontUIResource( defaultFont.deriveFont( (float) newFontSize ) );
} else {
// make sure that font is a UIResource for LaF switching
font = (defaultFont instanceof UIResource)
? defaultFont
: new FontUIResource( defaultFont );
}
}

return font;
}
}
}
75 changes: 47 additions & 28 deletions flatlaf-core/src/main/java/com/formdev/flatlaf/util/UIScale.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.awt.Insets;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.Method;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
Expand All @@ -48,19 +49,36 @@
*
* 2) user scaling mode
*
* This mode is mainly for Java 8 compatibility, but is also used on Linux.
* This mode is mainly for Java 8 compatibility, but is also used on Linux
* 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.
* There is only one user scale factor for all displays.
* The user scale factor may change if the active LaF or "Label.font" has changed.
* 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.
*
* @author Karl Tauber
*/
public class UIScale
{
private static final boolean DEBUG = false;

private static PropertyChangeSupport changeSupport;

public static void addPropertyChangeListener( PropertyChangeListener listener ) {
if( changeSupport == null )
changeSupport = new PropertyChangeSupport( UIScale.class );
changeSupport.addPropertyChangeListener( listener );
}

public static void removePropertyChangeListener( PropertyChangeListener listener ) {
if( changeSupport == null )
return;
changeSupport.removePropertyChangeListener( listener );
}

//---- system scaling (Java 9) --------------------------------------------

private static Boolean jreHiDPI;
Expand Down Expand Up @@ -110,27 +128,33 @@ private static void initialize() {
return;
initialized = true;

if( isUserScalingEnabled() ) {
// listener to update scale factor if LaF changed or if Label.font changed
// (e.g. option "Override default fonts" in IntelliJ IDEA)
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
String propName = e.getPropertyName();
if( "lookAndFeel".equals( propName ) ) {
if( !isUserScalingEnabled() )
return;

// 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
if( e.getNewValue() instanceof LookAndFeel )
UIManager.getLookAndFeelDefaults().addPropertyChangeListener( this );
updateScaleFactor();
} else if( "Label.font".equals( propName ) )
break;

case "defaultFont":
case "Label.font":
updateScaleFactor();
break;
}
};
UIManager.addPropertyChangeListener( listener );
UIManager.getLookAndFeelDefaults().addPropertyChangeListener( listener );
}
};
UIManager.addPropertyChangeListener( listener );
UIManager.getDefaults().addPropertyChangeListener( listener );
UIManager.getLookAndFeelDefaults().addPropertyChangeListener( listener );

updateScaleFactor();
}
updateScaleFactor();
}

private static void updateScaleFactor() {
Expand All @@ -141,7 +165,9 @@ private static void updateScaleFactor() {
// 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)
Font font = UIManager.getFont( "Label.font" );
Font font = UIManager.getFont( "defaultFont" );
if( font == null )
font = UIManager.getFont( "Label.font" );

setUserScaleFactor( computeScaleFactor( font ) );
}
Expand All @@ -168,9 +194,6 @@ private static float computeScaleFactor( Font font ) {
}

private static boolean isUserScalingEnabled() {
if( isSystemScalingEnabled() && !SystemInfo.IS_LINUX )
return false; // disable user scaling if JRE scales

// same as in IntelliJ IDEA
String hidpi = System.getProperty( "hidpi" );
return (hidpi != null) ? Boolean.parseBoolean( hidpi ) : true;
Expand All @@ -197,14 +220,6 @@ public static FontUIResource applyCustomScaleFactor( FontUIResource font ) {
return new FontUIResource( font.deriveFont( (float) newFontSize ) );
}

/**
* Scales the given font.
*/
public static FontUIResource scaleFont( FontUIResource font, float scaleFactor ) {
int newFontSize = Math.round( font.getSize() * scaleFactor );
return new FontUIResource( font.deriveFont( (float) newFontSize ) );
}

/**
* Similar to sun.java2d.SunGraphicsEnvironment.getScaleFactor(String)
*/
Expand Down Expand Up @@ -242,10 +257,14 @@ private static void setUserScaleFactor( float scaleFactor ) {
else // round scale factor to 1/4
scaleFactor = Math.round( scaleFactor * 4f ) / 4f;

float oldScaleFactor = UIScale.scaleFactor;
UIScale.scaleFactor = scaleFactor;

if( DEBUG )
System.out.println( "HiDPI scale factor " + scaleFactor );

if( changeSupport != null )
changeSupport.firePropertyChange( "userScaleFactor", oldScaleFactor, scaleFactor );
}

public static float scale( float value ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ else if( className.equals( NimbusLookAndFeel.class.getName() ) )
} );
}
} );

UIScale.addPropertyChangeListener( e -> {
// update info label because user scale factor may change
updateInfoLabel();
} );
}

void initialize( JFrame frame, JTabbedPane tabbedPane ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.demo.intellijthemes.*;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import net.miginfocom.swing.*;
Expand Down Expand Up @@ -52,12 +53,53 @@ private void selectedTabChanged() {
DemoPrefs.getState().putInt( FlatLafDemo.KEY_TAB, tabbedPane.getSelectedIndex() );
}

private void menuItemActionPerformed(ActionEvent e) {
private void menuItemActionPerformed( ActionEvent e ) {
SwingUtilities.invokeLater( () -> {
JOptionPane.showMessageDialog( this, e.getActionCommand(), "Menu Item", JOptionPane.PLAIN_MESSAGE );
} );
}

private void fontFamilyChanged( ActionEvent e ) {
String fontFamily = e.getActionCommand();

Font font = UIManager.getFont( "defaultFont" );
Font newFont = new Font( fontFamily, font.getStyle(), font.getSize() );
UIManager.put( "defaultFont", newFont );

FlatLaf.updateUI();
}

private void fontSizeChanged( ActionEvent e ) {
String fontSizeStr = e.getActionCommand();

Font font = UIManager.getFont( "defaultFont" );
Font newFont = font.deriveFont( (float) Integer.parseInt( fontSizeStr ) );
UIManager.put( "defaultFont", newFont );

FlatLaf.updateUI();
}

private void restoreFont() {
UIManager.put( "defaultFont", null );
FlatLaf.updateUI();
}

private void incrFont() {
Font font = UIManager.getFont( "defaultFont" );
Font newFont = font.deriveFont( (float) (font.getSize() + 1) );
UIManager.put( "defaultFont", newFont );

FlatLaf.updateUI();
}

private void decrFont() {
Font font = UIManager.getFont( "defaultFont" );
Font newFont = font.deriveFont( (float) Math.max( font.getSize() - 1, 8 ) );
UIManager.put( "defaultFont", newFont );

FlatLaf.updateUI();
}

private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JMenuBar menuBar1 = new JMenuBar();
Expand Down Expand Up @@ -86,6 +128,10 @@ private void initComponents() {
JRadioButtonMenuItem radioButtonMenuItem1 = new JRadioButtonMenuItem();
JRadioButtonMenuItem radioButtonMenuItem2 = new JRadioButtonMenuItem();
JRadioButtonMenuItem radioButtonMenuItem3 = new JRadioButtonMenuItem();
fontMenu = new JMenu();
JMenuItem restoreFontMenuItem = new JMenuItem();
JMenuItem incrFontMenuItem = new JMenuItem();
JMenuItem decrFontMenuItem = new JMenuItem();
JMenu helpMenu = new JMenu();
JMenuItem aboutMenuItem = new JMenuItem();
JToolBar toolBar1 = new JToolBar();
Expand Down Expand Up @@ -285,6 +331,30 @@ private void initComponents() {
}
menuBar1.add(viewMenu);

//======== fontMenu ========
{
fontMenu.setText("Font");

//---- restoreFontMenuItem ----
restoreFontMenuItem.setText("Restore Font");
restoreFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, KeyEvent.CTRL_MASK));
restoreFontMenuItem.addActionListener(e -> restoreFont());
fontMenu.add(restoreFontMenuItem);

//---- incrFontMenuItem ----
incrFontMenuItem.setText("Increase Font Size");
incrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_MASK));
incrFontMenuItem.addActionListener(e -> incrFont());
fontMenu.add(incrFontMenuItem);

//---- decrFontMenuItem ----
decrFontMenuItem.setText("Decrease Font Size");
decrFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, KeyEvent.CTRL_MASK));
decrFontMenuItem.addActionListener(e -> decrFont());
fontMenu.add(decrFontMenuItem);
}
menuBar1.add(fontMenu);

//======== helpMenu ========
{
helpMenu.setText("Help");
Expand Down Expand Up @@ -387,9 +457,30 @@ private void initComponents() {
cutMenuItem.addActionListener( new DefaultEditorKit.CutAction() );
copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() );
pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() );

// add font families
fontMenu.addSeparator();
String[] fontFamilies = { "Arial", "Comic Sans MS", "Courier New", "Dialog",
"Monospaced", "SansSerif", "Serif", "Tahoma", "Verdana" };
for( String fontFamily : fontFamilies ) {
JMenuItem fontItem = new JMenuItem( fontFamily );
fontItem.addActionListener( this::fontFamilyChanged );
fontMenu.add( fontItem );
}

// add font sizes
fontMenu.addSeparator();
int[] fontSizes = { 8, 10, 12, 14, 16, 18, 20, 24, 28 };
for( int fontSize : fontSizes ) {
String fontSizeStr = Integer.toString( fontSize );
JMenuItem fontItem = new JMenuItem( fontSizeStr );
fontItem.addActionListener( this::fontSizeChanged );
fontMenu.add( fontItem );
}
}

// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JMenu fontMenu;
private JTabbedPane tabbedPane;
private ControlBar controlBar;
// JFormDesigner - End of variables declaration //GEN-END:variables
Expand Down
Loading

0 comments on commit af89dd1

Please sign in to comment.