Merge PR #612: macOS themes: make spinner look like macOS stepper

This commit is contained in:
Karl Tauber
2022-11-16 10:59:10 +01:00
6 changed files with 152 additions and 36 deletions

View File

@@ -17,7 +17,10 @@
package com.formdev.flatlaf.ui;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.JSpinner;
import javax.swing.UIManager;
import javax.swing.plaf.SpinnerUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
/**
@@ -35,6 +38,19 @@ public class FlatRoundBorder
// only used via styling (not in UI defaults, but has likewise client properties)
/** @since 2 */ @Styleable protected Boolean roundRect;
@Override
public void paintBorder( Component c, Graphics g, int x, int y, int width, int height ) {
// make mac style spinner border smaller (border does not surround arrow buttons)
if( isMacStyleSpinner( c ) ) {
int macStyleButtonsWidth = ((FlatSpinnerUI)((JSpinner)c).getUI()).getMacStyleButtonsWidth();
width -= macStyleButtonsWidth;
if( !c.getComponentOrientation().isLeftToRight() )
x += macStyleButtonsWidth;
}
super.paintBorder( c, g, x, y, width, height );
}
@Override
protected int getArc( Component c ) {
if( isCellEditor( c ) )
@@ -43,6 +59,17 @@ public class FlatRoundBorder
Boolean roundRect = FlatUIUtils.isRoundRect( c );
if( roundRect == null )
roundRect = this.roundRect;
return roundRect != null ? (roundRect ? Short.MAX_VALUE : 0) : arc;
return roundRect != null
? (roundRect ? Short.MAX_VALUE : 0)
: (isMacStyleSpinner( c ) ? 0 : arc);
}
private boolean isMacStyleSpinner( Component c ) {
if( c instanceof JSpinner ) {
SpinnerUI ui = ((JSpinner)c).getUI();
if( ui instanceof FlatSpinnerUI )
return ((FlatSpinnerUI)ui).isMacStyle();
}
return false;
}
}

View File

@@ -340,20 +340,31 @@ public class FlatSpinnerUI
private Component createArrowButton( int direction, String name ) {
FlatArrowButton button = new FlatArrowButton( direction, arrowType, buttonArrowColor,
buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null );
buttonDisabledArrowColor, buttonHoverArrowColor, null, buttonPressedArrowColor, null )
{
@Override
public int getArrowWidth() {
return isMacStyle() ? 7 : super.getArrowWidth();
}
@Override
public float getArrowThickness() {
return isMacStyle() ? 1.5f : super.getArrowThickness();
}
@Override
public float getYOffset() {
return isMacStyle() ? 0 : super.getYOffset();
}
@Override
public boolean isRoundBorderAutoXOffset() {
return isMacStyle() ? false : super.isRoundBorderAutoXOffset();
}
};
button.setName( name );
button.setYOffset( (direction == SwingConstants.NORTH) ? 1.25f : -1.25f );
if( direction == SwingConstants.NORTH )
installNextButtonListeners( button );
else
installPreviousButtonListeners( button );
if( "mac".equals( buttonStyle ) ) {
button.setArrowWidth( 7 );
button.setArrowThickness( 1.5f );
button.setYOffset( (direction == SwingConstants.NORTH) ? 0.75f : -0.75f );
button.setRoundBorderAutoXOffset( false );
}
return button;
}
@@ -381,10 +392,13 @@ public class FlatSpinnerUI
int width = c.getWidth();
int height = c.getHeight();
boolean enabled = spinner.isEnabled();
boolean ltr = spinner.getComponentOrientation().isLeftToRight();
boolean isMacStyle = isMacStyle();
int macStyleButtonsWidth = isMacStyle ? getMacStyleButtonsWidth() : 0;
// paint background
g2.setColor( getBackground( enabled ) );
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc );
FlatUIUtils.paintComponentBackground( g2, ltr ? 0 : macStyleButtonsWidth, 0, width - macStyleButtonsWidth, height, focusWidth, arc );
// paint button background and separator
boolean paintButton = !"none".equals( buttonStyle );
@@ -393,22 +407,20 @@ public class FlatSpinnerUI
Component button = (handler.nextButton != null) ? handler.nextButton : handler.previousButton;
int arrowX = button.getX();
int arrowWidth = button.getWidth();
boolean isLeftToRight = spinner.getComponentOrientation().isLeftToRight();
Color separatorColor = enabled ? buttonSeparatorColor : buttonDisabledSeparatorColor;
if( "mac".equals( buttonStyle ) ) {
if( isMacStyle ) {
Insets insets = spinner.getInsets();
int gapX = scale( 3 );
int gapY = scale( 1 );
int bx = arrowX + gapX;
int by = insets.top + gapY;
int bw = arrowWidth - (gapX * 2);
int bh = height - insets.top - insets.bottom - (gapY * 2);
int lineWidth = Math.round( FlatUIUtils.getBorderLineWidth( spinner ) );
int bx = arrowX;
int by = insets.top - lineWidth;
int bw = arrowWidth;
int bh = height - insets.top - insets.bottom + (lineWidth * 2);
float lw = scale( buttonSeparatorWidth );
// buttons border
FlatUIUtils.paintOutlinedComponent( g2, bx, by, bw, bh,
0, 0, 0, lw, arc - focusWidth,
0, 0, 0, lw, scale( 12 ),
null, separatorColor, buttonBackground );
// separator between buttons
@@ -423,7 +435,7 @@ public class FlatSpinnerUI
if( enabled && buttonBackground != null ) {
g2.setColor( buttonBackground );
Shape oldClip = g2.getClip();
if( isLeftToRight )
if( ltr )
g2.clipRect( arrowX, 0, width - arrowX, height );
else
g2.clipRect( 0, 0, arrowX + arrowWidth, height );
@@ -435,7 +447,7 @@ public class FlatSpinnerUI
if( separatorColor != null && buttonSeparatorWidth > 0 ) {
g2.setColor( separatorColor );
float lw = scale( buttonSeparatorWidth );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
float lx = ltr ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
}
}
@@ -446,6 +458,19 @@ public class FlatSpinnerUI
FlatUIUtils.resetRenderingHints( g, oldRenderingHints );
}
boolean isMacStyle() {
return "mac".equals( buttonStyle );
}
int getMacStyleButtonsWidth() {
return (handler.nextButton != null || handler.previousButton != null)
? scale( MAC_STEPPER_GAP ) + scale( MAC_STEPPER_WIDTH )
: 0;
}
private static final int MAC_STEPPER_WIDTH = 15;
private static final int MAC_STEPPER_GAP = 3;
//---- class Handler ------------------------------------------------------
private class Handler
@@ -502,6 +527,7 @@ public class FlatSpinnerUI
Insets insets = parent.getInsets();
Rectangle r = FlatUIUtils.subtractInsets( new Rectangle( size ), insets );
// editor gets all space if there are no buttons
if( nextButton == null && previousButton == null ) {
if( editor != null )
editor.setBounds( r );
@@ -517,20 +543,36 @@ public class FlatSpinnerUI
int minButtonWidth = (maxButtonWidth * 3) / 4;
// make button area square (except if width is limited)
int buttonsWidth = Math.min( Math.max( buttonsRect.height, minButtonWidth ), maxButtonWidth );
buttonsRect.width = buttonsWidth;
boolean isMacStyle = isMacStyle();
int buttonsGap = isMacStyle ? scale( MAC_STEPPER_GAP ) : 0;
int prefButtonWidth = isMacStyle ? scale( MAC_STEPPER_WIDTH ) : buttonsRect.height;
int buttonsWidth = Math.min( Math.max( prefButtonWidth, minButtonWidth ), maxButtonWidth );
if( parent.getComponentOrientation().isLeftToRight() ) {
editorRect.width -= buttonsWidth;
buttonsRect.x += editorRect.width;
} else {
editorRect.x += buttonsWidth;
editorRect.width -= buttonsWidth;
// update editor and buttons bounds
buttonsRect.width = buttonsWidth;
editorRect.width -= buttonsWidth + buttonsGap;
boolean ltr = parent.getComponentOrientation().isLeftToRight();
if( ltr )
buttonsRect.x += editorRect.width + buttonsGap;
else
editorRect.x += buttonsWidth + buttonsGap;
// in mac button style increase buttons height and move to the right
// for exact alignment with border
if( isMacStyle ) {
int lineWidth = Math.round( FlatUIUtils.getBorderLineWidth( spinner ) );
if( lineWidth > 0 ) {
buttonsRect.x += ltr ? lineWidth : -lineWidth;
buttonsRect.y -= lineWidth;
buttonsRect.height += lineWidth * 2;
}
}
// set editor bounds
if( editor != null )
editor.setBounds( editorRect );
// set buttons bounds
int nextHeight = (buttonsRect.height / 2) + (buttonsRect.height % 2); // round up
if( nextButton != null )
nextButton.setBounds( buttonsRect.x, buttonsRect.y, buttonsRect.width, nextHeight );

View File

@@ -253,6 +253,9 @@ Slider.focusedColor = $Component.focusColor
Spinner.buttonStyle = mac
Spinner.disabledBackground = @disabledComponentBackground
Spinner.buttonBackground = @buttonBackground
Spinner.buttonArrowColor = @foreground
Spinner.buttonHoverArrowColor = lighten($Spinner.buttonArrowColor,10%,derived noAutoInverse)
Spinner.buttonPressedArrowColor = lighten($Spinner.buttonArrowColor,20%,derived noAutoInverse)
Spinner.buttonSeparatorWidth = 0

View File

@@ -982,12 +982,12 @@ SliderUI com.formdev.flatlaf.ui.FlatSliderUI
Spinner.arrowButtonSize 16,5 java.awt.Dimension
Spinner.background #282828 HSL 0 0 16 javax.swing.plaf.ColorUIResource [UI]
Spinner.border [lazy] 3,3,3,3 false com.formdev.flatlaf.ui.FlatRoundBorder [UI]
Spinner.buttonArrowColor #b7b7b7 HSL 0 0 72 javax.swing.plaf.ColorUIResource [UI]
Spinner.buttonArrowColor #dddddd HSL 0 0 87 javax.swing.plaf.ColorUIResource [UI]
Spinner.buttonBackground #565656 HSL 0 0 34 javax.swing.plaf.ColorUIResource [UI]
Spinner.buttonDisabledArrowColor #777777 HSL 0 0 47 javax.swing.plaf.ColorUIResource [UI]
Spinner.buttonDisabledSeparatorColor #ffffff0c 5% HSLA 0 0 100 5 javax.swing.plaf.ColorUIResource [UI]
Spinner.buttonHoverArrowColor #d1d1d1 HSL 0 0 82 com.formdev.flatlaf.util.DerivedColor [UI] lighten(10%)
Spinner.buttonPressedArrowColor #eaeaea HSL 0 0 92 com.formdev.flatlaf.util.DerivedColor [UI] lighten(20%)
Spinner.buttonHoverArrowColor #f7f7f7 HSL 0 0 97 com.formdev.flatlaf.util.DerivedColor [UI] lighten(10%)
Spinner.buttonPressedArrowColor #ffffff HSL 0 0 100 com.formdev.flatlaf.util.DerivedColor [UI] lighten(20%)
Spinner.buttonSeparatorColor #ffffff19 10% HSLA 0 0 100 10 javax.swing.plaf.ColorUIResource [UI]
Spinner.buttonSeparatorWidth 0
Spinner.buttonStyle mac

View File

@@ -45,6 +45,27 @@ public class FlatTextComponentsTest
FlatTextComponentsTest() {
initComponents();
updatePreferredSizes();
}
@Override
public void updateUI() {
super.updateUI();
if( comboBox5 != null )
updatePreferredSizes();
}
private void updatePreferredSizes() {
Dimension size40 = UIScale.scale( new Dimension( 60, 40 ) );
comboBox5.setPreferredSize( size40 );
spinner4.setPreferredSize( size40 );
Dimension size14 = UIScale.scale( new Dimension( 60, 14 ) );
comboBox6.setPreferredSize( size14 );
comboBox6.setMinimumSize( size14 );
spinner5.setPreferredSize( size14 );
spinner5.setMinimumSize( size14 );
}
private void editableChanged() {
@@ -216,18 +237,19 @@ public class FlatTextComponentsTest
JComboBox<String> comboBox3 = new JComboBox<>();
JLabel spinnerLabel = new JLabel();
JSpinner spinner1 = new JSpinner();
JSpinner spinner6 = new JSpinner();
JLabel label2 = new JLabel();
JComboBox<String> comboBox2 = new JComboBox<>();
JSpinner spinner2 = new JSpinner();
JLabel label1 = new JLabel();
JComboBox<String> comboBox5 = new JComboBox<>();
JSpinner spinner4 = new JSpinner();
comboBox5 = new JComboBox<>();
spinner4 = new JSpinner();
JLabel label3 = new JLabel();
JComboBox<String> comboBox4 = new JComboBox<>();
JSpinner spinner3 = new JSpinner();
JLabel label4 = new JLabel();
JComboBox<String> comboBox6 = new JComboBox<>();
JSpinner spinner5 = new JSpinner();
comboBox6 = new JComboBox<>();
spinner5 = new JSpinner();
JLabel label5 = new JLabel();
textField = new JTextField();
dragEnabledCheckBox = new JCheckBox();
@@ -563,6 +585,10 @@ public class FlatTextComponentsTest
spinner1.setComponentPopupMenu(popupMenu1);
add(spinner1, "cell 1 7,growx");
//---- spinner6 ----
spinner6.setBorder(BorderFactory.createEmptyBorder());
add(spinner6, "cell 2 7,growx");
//---- label2 ----
label2.setText("<html>Large row height:<br>(default pref height)</html>");
add(label2, "cell 0 8,aligny top,growy 0");
@@ -690,6 +716,10 @@ public class FlatTextComponentsTest
private JCheckBox trailingComponentVisibleCheckBox;
private JCheckBox showClearButtonCheckBox;
private JCheckBox showRevealButtonCheckBox;
private JComboBox<String> comboBox5;
private JSpinner spinner4;
private JComboBox<String> comboBox6;
private JSpinner spinner5;
private JTextField textField;
private JCheckBox dragEnabledCheckBox;
private JTextArea textArea;

View File

@@ -403,6 +403,12 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 7,growx"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "spinner6"
"border": new javax.swing.border.EmptyBorder( 0, 0, 0, 0 )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 7,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"text": "<html>Large row height:<br>(default pref height)</html>"
@@ -435,6 +441,7 @@ new FormModel {
"editable": true
auxiliary() {
"JavaCodeGenerator.typeParameters": "String"
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 10,growx"
@@ -442,6 +449,9 @@ new FormModel {
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "spinner4"
"preferredSize": new java.awt.Dimension( 60, 40 )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 11,growx"
} )
@@ -478,6 +488,7 @@ new FormModel {
"minimumSize": new java.awt.Dimension( 60, 14 )
auxiliary() {
"JavaCodeGenerator.typeParameters": "String"
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 14,growx"
@@ -486,6 +497,9 @@ new FormModel {
name: "spinner5"
"minimumSize": new java.awt.Dimension( 60, 14 )
"preferredSize": new java.awt.Dimension( 60, 14 )
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15,growx,hmax 14"
} )