FlatUIUtils: joined the 3 component painting methods (for focus border, border and background) into a single method paintOutlinedComponent()

- this allows optimized painting if focus color and border color are equal
- avoids duplicate code
- support focusWidthFraction for future animations
This commit is contained in:
Karl Tauber
2021-09-16 18:09:32 +02:00
parent c659638fb4
commit 14a9240c45
6 changed files with 526 additions and 279 deletions

View File

@@ -103,9 +103,11 @@ public class FlatBorder
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );
float focusWidth = scale( (float) getFocusWidth( c ) ); float focusWidth = scale( (float) getFocusWidth( c ) );
float focusInnerWidth = 0;
float borderWidth = scale( (float) getBorderWidth( c ) ); float borderWidth = scale( (float) getBorderWidth( c ) );
float arc = scale( (float) getArc( c ) ); float arc = scale( (float) getArc( c ) );
Color outlineColor = getOutlineColor( c ); Color outlineColor = getOutlineColor( c );
Color focusColor = null;
// paint outer border // paint outer border
if( outlineColor != null || isFocused( c ) ) { if( outlineColor != null || isFocused( c ) ) {
@@ -114,15 +116,16 @@ public class FlatBorder
: 0; : 0;
if( focusWidth > 0 || innerWidth > 0 ) { if( focusWidth > 0 || innerWidth > 0 ) {
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) ); focusColor = (outlineColor != null) ? outlineColor : getFocusColor( c );
FlatUIUtils.paintComponentOuterBorder( g2, x, y, width, height, focusInnerWidth = borderWidth + scale( innerWidth );
focusWidth, borderWidth + scale( innerWidth ), arc );
} }
} }
// paint border // paint border
g2.setPaint( (outlineColor != null) ? outlineColor : getBorderColor( c ) ); Paint borderColor = (outlineColor != null) ? outlineColor : getBorderColor( c );
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, focusWidth, borderWidth, arc ); FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
focusWidth, 1, focusInnerWidth, borderWidth, arc,
focusColor, borderColor, null );
} finally { } finally {
g2.dispose(); g2.dispose();
} }

View File

@@ -114,9 +114,10 @@ public class FlatButtonBorder
width -= spacing.left + spacing.right; width -= spacing.left + spacing.right;
height -= spacing.top + spacing.bottom; height -= spacing.top + spacing.bottom;
g2.setColor( (outlineColor != null) ? outlineColor : getFocusColor( c ) ); Color color = (outlineColor != null) ? outlineColor : getFocusColor( c );
// not using paintComponentOuterBorder() here because its round edges look too "thick" // not using focus border painting of paintOutlinedComponent() here
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0, focusWidth, arc ); // because its round edges look too "thick"
FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height, 0, 0, 0, focusWidth, arc, null, color, null );
} finally { } finally {
g2.dispose(); g2.dispose();
} }

View File

@@ -61,8 +61,8 @@ public class FlatLineBorder
Graphics2D g2 = (Graphics2D) g.create(); Graphics2D g2 = (Graphics2D) g.create();
try { try {
FlatUIUtils.setRenderingHints( g2 ); FlatUIUtils.setRenderingHints( g2 );
g2.setColor( getLineColor() ); FlatUIUtils.paintOutlinedComponent( g2, x, y, width, height,
FlatUIUtils.paintComponentBorder( g2, x, y, width, height, 0f, scale( getLineThickness() ), 0f ); 0, 0, 0, scale( getLineThickness() ), 0, null, getLineColor(), null );
} finally { } finally {
g2.dispose(); g2.dispose();
} }

View File

@@ -28,6 +28,7 @@ import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice; import java.awt.GraphicsDevice;
import java.awt.Insets; import java.awt.Insets;
import java.awt.KeyboardFocusManager; import java.awt.KeyboardFocusManager;
import java.awt.Paint;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.RenderingHints; import java.awt.RenderingHints;
import java.awt.Shape; import java.awt.Shape;
@@ -351,104 +352,6 @@ public class FlatUIUtils
: color; : color;
} }
/**
* Paints an outer border, which is usually a focus border.
* <p>
* The outside bounds of the painted border are {@code x,y,width,height}.
* The line thickness of the painted border is {@code focusWidth + lineWidth}.
* The given arc diameter refers to the inner rectangle ({@code x,y,width,height} minus {@code focusWidth}).
*
* @see #paintComponentBorder
* @see #paintComponentBackground
*/
public static void paintComponentOuterBorder( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc )
{
if( focusWidth + lineWidth == 0 )
return; // nothing to paint
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
paintComponentOuterBorderImpl( g2d, x2, y2, width2, height2,
(float) (focusWidth * scaleFactor), (float) (lineWidth * scaleFactor), (float) (arc * scaleFactor) );
} );
return;
}
paintComponentOuterBorderImpl( g, x, y, width, height, focusWidth, lineWidth, arc );
}
private static void paintComponentOuterBorderImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc )
{
float ow = focusWidth + lineWidth;
float outerArc = arc + (focusWidth * 2);
float innerArc = arc - (lineWidth * 2);
// reduce outer arc slightly for small arcs to make the curve slightly wider
if( focusWidth > 0 && arc > 0 && arc < UIScale.scale( 10 ) )
outerArc -= UIScale.scale( 2f );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( createComponentRectangle( x, y, width, height, outerArc ), false );
path.append( createComponentRectangle( x + ow, y + ow, width - (ow * 2), height - (ow * 2), innerArc ), false );
g.fill( path );
}
/**
* Draws the border of a component as round rectangle.
* <p>
* The outside bounds of the painted border are
* {@code x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)}.
* The line thickness of the painted border is {@code lineWidth}.
* The given arc diameter refers to the painted rectangle (and not to {@code x,y,width,height}).
*
* @see #paintComponentOuterBorder
* @see #paintComponentBackground
*/
public static void paintComponentBorder( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc )
{
if( lineWidth == 0 )
return; // nothing to paint
double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
paintComponentBorderImpl( g2d, x2, y2, width2, height2,
(float) (focusWidth * scaleFactor), (float) (lineWidth * scaleFactor), (float) (arc * scaleFactor) );
} );
return;
}
paintComponentBorderImpl( g, x, y, width, height, focusWidth, lineWidth, arc );
}
private static void paintComponentBorderImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc )
{
float x1 = x + focusWidth;
float y1 = y + focusWidth;
float width1 = width - focusWidth * 2;
float height1 = height - focusWidth * 2;
float arc2 = arc - (lineWidth * 2);
Shape r1 = createComponentRectangle( x1, y1, width1, height1, arc );
Shape r2 = createComponentRectangle(
x1 + lineWidth, y1 + lineWidth,
width1 - lineWidth * 2, height1 - lineWidth * 2, arc2 );
Path2D border = new Path2D.Float( Path2D.WIND_EVEN_ODD );
border.append( r1, false );
border.append( r2, false );
g.fill( border );
}
/** /**
* Fills the background of a component with a round rectangle. * Fills the background of a component with a round rectangle.
* <p> * <p>
@@ -456,32 +359,201 @@ public class FlatUIUtils
* {@code x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)}. * {@code x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)}.
* The given arc diameter refers to the painted rectangle (and not to {@code x,y,width,height}). * The given arc diameter refers to the painted rectangle (and not to {@code x,y,width,height}).
* *
* @see #paintComponentOuterBorder * @see #paintOutlinedComponent
* @see #paintComponentBorder
*/ */
public static void paintComponentBackground( Graphics2D g, int x, int y, int width, int height, public static void paintComponentBackground( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float arc ) float focusWidth, float arc )
{
paintOutlinedComponent( g, x, y, width, height, focusWidth, 0, 0, 0, arc, null, null, g.getPaint() );
}
/**
* Paints an outlined component with rounded corners, consisting of following parts:
* <ul>
* <li>an (optional) outer border, which is usually a focus indicator
* <li>an (optional) component border
* <li>the (optional) component background
* </ul>
* Each part is painted only if the corresponding part color is not {@code null}.
* The parts are painted in this order:
* <p>
* <ol>
* <li>background
* <li>focus border
* <li>border
* </ol>
*
* <strong>Background</strong>:
* The bounds of the filled round rectangle are
* {@code [x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)]}.
* The focus border and the border may paint over the background.
* <p>
*
* <strong>Focus border</strong>:
* The outside bounds of the painted focus border are {@code [x, y, width, height]}.
* The thickness of the painted focus border is {@code (focusWidth * focusWidthFraction) + focusInnerWidth}.
* The border may paint over the focus border if {@code focusInnerWidth > 0}.
* <p>
*
* <strong>Border</strong>:
* The outside bounds of the painted border are
* {@code [x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)]}.
* The thickness of the painted border is {@code lineWidth}.
*
* @param g the graphics context used for painting
* @param x the x coordinate of the component
* @param y the y coordinate of the component
* @param width the width of the component
* @param height the height of the component
* @param focusWidth the width of the focus border, or {@code 0}
* @param focusWidthFraction specified how much of the focus border is painted (in range 0 - 1);
* can be used for animation;
* the painted thickness of the focus border is {@code (focusWidth * focusWidthFraction) + focusInnerWidth}
* @param focusInnerWidth the inner width of the focus border, or {@code 0};
* if a border is painted then {@code focusInnerWidth} needs to be larger
* than {@code lineWidth} to be not hidden by the border
* @param borderWidth the width of the border, or {@code 0}
* @param arc the arc diameter used for the outside shape of the component border;
* the other needed arc diameters are computed from this arc diameter
* @param focusColor the color of the focus border, or {@code null}
* @param borderColor the color of the border, or {@code null}
* @param background the background color of the component, or {@code null}
*
* @since 2
*/
public static void paintOutlinedComponent( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background )
{ {
double systemScaleFactor = UIScale.getSystemScaleFactor( g ); double systemScaleFactor = UIScale.getSystemScaleFactor( g );
if( systemScaleFactor != 1 && systemScaleFactor != 2 ) { if( systemScaleFactor != 1 && systemScaleFactor != 2 ) {
// paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175% // paint at scale 1x to avoid clipping on right and bottom edges at 125%, 150% or 175%
HiDPIUtils.paintAtScale1x( g, x, y, width, height, HiDPIUtils.paintAtScale1x( g, x, y, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> { (g2d, x2, y2, width2, height2, scaleFactor) -> {
paintComponentBackgroundImpl( g2d, x2, y2, width2, height2, paintOutlinedComponentImpl( g2d, x2, y2, width2, height2,
(float) (focusWidth * scaleFactor), (float) (arc * scaleFactor) ); (float) (focusWidth * scaleFactor), focusWidthFraction, (float) (focusInnerWidth * scaleFactor),
(float) (borderWidth * scaleFactor), (float) (arc * scaleFactor),
focusColor, borderColor, background );
} ); } );
return; return;
} }
paintComponentBackgroundImpl( g, x, y, width, height, focusWidth, arc ); paintOutlinedComponentImpl( g, x, y, width, height, focusWidth, focusWidthFraction, focusInnerWidth,
borderWidth, arc, focusColor, borderColor, background );
} }
private static void paintComponentBackgroundImpl( Graphics2D g, int x, int y, int width, int height, private static void paintOutlinedComponentImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float arc ) float focusWidth, float focusWidthFraction, float focusInnerWidth, float borderWidth, float arc,
Paint focusColor, Paint borderColor, Paint background )
{ {
g.fill( createComponentRectangle( // outside bounds of the border and the background
x + focusWidth, y + focusWidth, float x1 = x + focusWidth;
width - focusWidth * 2, height - focusWidth * 2, arc ) ); float y1 = y + focusWidth;
float w1 = width - focusWidth * 2;
float h1 = height - focusWidth * 2;
// fill background
// bounds: x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)
// arc diameter: arc
if( background != null ) {
g.setPaint( background );
g.fill( createComponentRectangle( x1, y1, w1, h1, arc ) );
}
// optimization: paint focus border and border in single operation if colors are equal
if( borderColor != null && borderColor.equals( focusColor ) ) {
borderColor = null;
focusInnerWidth = Math.max( focusInnerWidth, borderWidth );
}
// paint focus border
// outer bounds: x, y, width, height
// thickness: focusWidth + focusInnerWidth
// outer arc diameter: arc + (focusWidth * 2)
// inner arc diameter: arc - (focusInnerWidth * 2)
float paintedFocusWidth = (focusWidth * focusWidthFraction) + focusInnerWidth;
if( focusColor != null && paintedFocusWidth != 0 ) {
// outside bounds of the focus border
float inset = focusWidth - (focusWidth * focusWidthFraction);
float x2 = x + inset;
float y2 = y + inset;
float w2 = width - (inset * 2);
float h2 = height - (inset * 2);
float outerArc = arc + (focusWidth * 2);
float innerArc = arc - (focusInnerWidth * 2);
// reduce outer arc slightly for small arcs to make the curve slightly wider
if( focusWidth > 0 && arc > 0 && arc < UIScale.scale( 10 ) )
outerArc -= UIScale.scale( 2f );
// consider focus width fraction
if( focusWidthFraction != 1 )
outerArc = arc + ((outerArc - arc) * focusWidthFraction);
g.setPaint( focusColor );
paintOutline( g, x2, y2, w2, h2, paintedFocusWidth, outerArc, innerArc );
}
// paint border
// outer bounds: x + focusWidth, y + focusWidth, width - (focusWidth * 2), height - (focusWidth * 2)
// thickness: borderWidth
// outer arc diameter: arc
// inner arc diameter: arc - (borderWidth * 2)
if( borderColor != null && borderWidth != 0 ) {
g.setPaint( borderColor );
paintOutline( g, x1, y1, w1, h1, borderWidth, arc );
}
}
/**
* Paints an outline at the given bounds using the given line width.
* Depending on the given arc, a rectangle, rounded rectangle or circle (if w == h) is painted.
*
* @param g the graphics context used for painting
* @param x the x coordinate of the outline
* @param y the y coordinate of the outline
* @param w the width of the outline
* @param h the height of the outline
* @param lineWidth the width of the outline
* @param arc the arc diameter used for the outside shape of the outline
*
* @since 2
*/
public static void paintOutline( Graphics2D g, float x, float y, float w, float h,
float lineWidth, float arc )
{
paintOutline( g, x, y, w, h, lineWidth, arc, arc - (lineWidth * 2) );
}
/**
* Paints an outline at the given bounds using the given line width.
* Depending on the given arc, a rectangle, rounded rectangle or circle (if w == h) is painted.
*
* @param g the graphics context used for painting
* @param x the x coordinate of the outline
* @param y the y coordinate of the outline
* @param w the width of the outline
* @param h the height of the outline
* @param lineWidth the width of the outline
* @param arc the arc diameter used for the outside shape of the outline
* @param innerArc the arc diameter used for the inside shape of the outline
*
* @since 2
*/
public static void paintOutline( Graphics2D g, float x, float y, float w, float h,
float lineWidth, float arc, float innerArc )
{
if( lineWidth == 0 || w <= 0 || h <= 0 )
return;
float t = lineWidth;
float t2x = t * 2;
Path2D border = new Path2D.Float( Path2D.WIND_EVEN_ODD );
border.append( createComponentRectangle( x, y, w, h, arc ), false );
border.append( createComponentRectangle( x + t, y + t, w - t2x, h - t2x, innerArc ), false );
g.fill( border );
} }
/** /**
@@ -492,6 +564,9 @@ public class FlatUIUtils
if( arc <= 0 ) if( arc <= 0 )
return new Rectangle2D.Float( x, y, w, h ); return new Rectangle2D.Float( x, y, w, h );
if( w == h && arc >= w )
return new Ellipse2D.Float( x, y, w, h );
arc = Math.min( arc, Math.min( w, h ) ); arc = Math.min( arc, Math.min( w, h ) );
return new RoundRectangle2D.Float( x, y, w, h, arc, arc ); return new RoundRectangle2D.Float( x, y, w, h, arc, arc );
} }

View File

@@ -22,6 +22,7 @@ import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.Hashtable;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
import com.formdev.flatlaf.ui.FlatArrowButton; import com.formdev.flatlaf.ui.FlatArrowButton;
@@ -45,6 +46,11 @@ public class FlatPaintingTest
FlatPaintingTest() { FlatPaintingTest() {
initComponents(); initComponents();
Hashtable<Integer, JLabel> labels = new Hashtable<>();
for( int i = 0; i <= 5; i++ )
labels.put( i * 10, new JLabel( Integer.toString( i ) ) );
focusInnerWidthSlider.setLabelTable( labels );
} }
@Override @Override
@@ -55,6 +61,39 @@ public class FlatPaintingTest
getVerticalScrollBar().setUnitIncrement( UIScale.scale( 25 ) ); getVerticalScrollBar().setUnitIncrement( UIScale.scale( 25 ) );
} }
private void focusWidthFractionChanged() {
float focusWidthFraction = focusWidthFractionSlider.getValue() / 100f;
FlatTestFrame.updateComponentsRecur( (Container) getViewport().getView(), (c, type) -> {
if( c instanceof BorderPainter )
((BorderPainter)c).focusWidthFraction = focusWidthFraction;
} );
repaint();
}
private void focusInnerWidthChanged() {
float focusInnerWidth = focusInnerWidthSlider.getValue() / 10f;
FlatTestFrame.updateComponentsRecur( (Container) getViewport().getView(), (c, type) -> {
if( c instanceof BorderPainter )
((BorderPainter)c).focusInnerWidth = focusInnerWidth;
} );
repaint();
}
private void translucentChanged() {
boolean translucent = translucentCheckBox.isSelected();
FlatTestFrame.updateComponentsRecur( (Container) getViewport().getView(), (c, type) -> {
if( c instanceof BorderPainter )
((BorderPainter)c).translucent = translucent;
} );
repaint();
}
private void arrowSizeChanged() { private void arrowSizeChanged() {
int width = (int) arrowWidthSpinner.getValue(); int width = (int) arrowWidthSpinner.getValue();
int height = (int) arrowHeightSpinner.getValue(); int height = (int) arrowHeightSpinner.getValue();
@@ -167,17 +206,24 @@ public class FlatPaintingTest
JPanel panel2 = new JPanel(); JPanel panel2 = new JPanel();
arrowPainter7 = new FlatPaintingTest.ArrowPainter(); arrowPainter7 = new FlatPaintingTest.ArrowPainter();
arrowPainter8 = new FlatPaintingTest.ArrowPainter(); arrowPainter8 = new FlatPaintingTest.ArrowPainter();
JPanel panel6 = new JPanel();
JPanel panel7 = new JPanel();
JLabel focusWidthFractionLabel = new JLabel();
focusWidthFractionSlider = new JSlider();
JLabel focusInnerWidthLabel = new JLabel();
focusInnerWidthSlider = new JSlider();
translucentCheckBox = new JCheckBox();
JPanel panel5 = new JPanel(); JPanel panel5 = new JPanel();
JLabel arrowWidthLabel = new JLabel(); JLabel arrowWidthLabel = new JLabel();
arrowWidthSpinner = new JSpinner(); arrowWidthSpinner = new JSpinner();
vectorCheckBox = new JCheckBox();
JLabel arrowHeightLabel = new JLabel(); JLabel arrowHeightLabel = new JLabel();
arrowHeightSpinner = new JSpinner(); arrowHeightSpinner = new JSpinner();
buttonCheckBox = new JCheckBox();
JLabel arrowSizeLabel = new JLabel(); JLabel arrowSizeLabel = new JLabel();
arrowSizeSpinner = new JSpinner(); arrowSizeSpinner = new JSpinner();
JLabel offsetLabel = new JLabel(); JLabel offsetLabel = new JLabel();
offsetSpinner = new JSpinner(); offsetSpinner = new JSpinner();
vectorCheckBox = new JCheckBox();
buttonCheckBox = new JCheckBox();
FlatPaintingTest.ArrowPainter arrowPainter9 = new FlatPaintingTest.ArrowPainter(); FlatPaintingTest.ArrowPainter arrowPainter9 = new FlatPaintingTest.ArrowPainter();
FlatPaintingTest.ArrowPainter arrowPainter10 = new FlatPaintingTest.ArrowPainter(); FlatPaintingTest.ArrowPainter arrowPainter10 = new FlatPaintingTest.ArrowPainter();
FlatPaintingTest.ArrowPainter arrowPainter11 = new FlatPaintingTest.ArrowPainter(); FlatPaintingTest.ArrowPainter arrowPainter11 = new FlatPaintingTest.ArrowPainter();
@@ -527,69 +573,122 @@ public class FlatPaintingTest
} }
flatTestPanel1.add(panel2, "cell 5 5,align left top,grow 0 0"); flatTestPanel1.add(panel2, "cell 5 5,align left top,grow 0 0");
//======== panel5 ======== //======== panel6 ========
{ {
panel5.setBorder(new TitledBorder("Arrow Control")); panel6.setLayout(new MigLayout(
panel5.setLayout(new MigLayout( "insets 0,hidemode 3",
"hidemode 3",
// columns // columns
"[fill]" + "[grow,fill]",
"[fill]",
// rows // rows
"[]" + "[]unrel" +
"[]" +
"[]" +
"[]" +
"[]" +
"[]")); "[]"));
//---- arrowWidthLabel ---- //======== panel7 ========
arrowWidthLabel.setText("Width:"); {
panel5.add(arrowWidthLabel, "cell 0 0"); panel7.setBorder(new TitledBorder("Outlined Component Control"));
panel7.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[fill]",
// rows
"[]" +
"[]" +
"[]"));
//---- arrowWidthSpinner ---- //---- focusWidthFractionLabel ----
arrowWidthSpinner.setModel(new SpinnerNumberModel(20, 0, null, 1)); focusWidthFractionLabel.setText("Focus width fraction:");
arrowWidthSpinner.addChangeListener(e -> arrowSizeChanged()); panel7.add(focusWidthFractionLabel, "cell 0 0");
panel5.add(arrowWidthSpinner, "cell 1 0");
//---- arrowHeightLabel ---- //---- focusWidthFractionSlider ----
arrowHeightLabel.setText("Height:"); focusWidthFractionSlider.setValue(100);
panel5.add(arrowHeightLabel, "cell 0 1"); focusWidthFractionSlider.setMajorTickSpacing(25);
focusWidthFractionSlider.setPaintLabels(true);
focusWidthFractionSlider.addChangeListener(e -> focusWidthFractionChanged());
panel7.add(focusWidthFractionSlider, "cell 1 0");
//---- arrowHeightSpinner ---- //---- focusInnerWidthLabel ----
arrowHeightSpinner.setModel(new SpinnerNumberModel(20, 0, null, 1)); focusInnerWidthLabel.setText("Focus inner width:");
arrowHeightSpinner.addChangeListener(e -> arrowSizeChanged()); panel7.add(focusInnerWidthLabel, "cell 0 1");
panel5.add(arrowHeightSpinner, "cell 1 1");
//---- arrowSizeLabel ---- //---- focusInnerWidthSlider ----
arrowSizeLabel.setText("Arrow Size:"); focusInnerWidthSlider.setPaintLabels(true);
panel5.add(arrowSizeLabel, "cell 0 2"); focusInnerWidthSlider.setValue(10);
focusInnerWidthSlider.setMaximum(50);
focusInnerWidthSlider.addChangeListener(e -> focusInnerWidthChanged());
panel7.add(focusInnerWidthSlider, "cell 1 1");
//---- arrowSizeSpinner ---- //---- translucentCheckBox ----
arrowSizeSpinner.setModel(new SpinnerNumberModel(8, 2, null, 1)); translucentCheckBox.setText("translucent");
arrowSizeSpinner.addChangeListener(e -> arrowSizeChanged()); translucentCheckBox.addActionListener(e -> translucentChanged());
panel5.add(arrowSizeSpinner, "cell 1 2"); panel7.add(translucentCheckBox, "cell 0 2 2 1,alignx left,growx 0");
}
panel6.add(panel7, "cell 0 0");
//---- offsetLabel ---- //======== panel5 ========
offsetLabel.setText("Offset:"); {
panel5.add(offsetLabel, "cell 0 3"); panel5.setBorder(new TitledBorder("Arrow Control"));
panel5.setLayout(new MigLayout(
"hidemode 3",
// columns
"[fill]" +
"[fill]" +
"[fill]",
// rows
"[]" +
"[]" +
"[]" +
"[]"));
//---- offsetSpinner ---- //---- arrowWidthLabel ----
offsetSpinner.setModel(new SpinnerNumberModel(1.0F, null, null, 0.05F)); arrowWidthLabel.setText("Width:");
offsetSpinner.addChangeListener(e -> offsetChanged()); panel5.add(arrowWidthLabel, "cell 0 0");
panel5.add(offsetSpinner, "cell 1 3");
//---- vectorCheckBox ---- //---- arrowWidthSpinner ----
vectorCheckBox.setText("vector"); arrowWidthSpinner.setModel(new SpinnerNumberModel(20, 0, null, 1));
vectorCheckBox.addActionListener(e -> vectorChanged()); arrowWidthSpinner.addChangeListener(e -> arrowSizeChanged());
panel5.add(vectorCheckBox, "cell 0 4 2 1,alignx left,growx 0"); panel5.add(arrowWidthSpinner, "cell 1 0");
//---- buttonCheckBox ---- //---- vectorCheckBox ----
buttonCheckBox.setText("FlatArrowButton"); vectorCheckBox.setText("vector");
buttonCheckBox.addActionListener(e -> arrowButtonChanged()); vectorCheckBox.addActionListener(e -> vectorChanged());
panel5.add(buttonCheckBox, "cell 0 5 2 1,alignx left,growx 0"); panel5.add(vectorCheckBox, "cell 2 0,alignx left,growx 0");
//---- arrowHeightLabel ----
arrowHeightLabel.setText("Height:");
panel5.add(arrowHeightLabel, "cell 0 1");
//---- arrowHeightSpinner ----
arrowHeightSpinner.setModel(new SpinnerNumberModel(20, 0, null, 1));
arrowHeightSpinner.addChangeListener(e -> arrowSizeChanged());
panel5.add(arrowHeightSpinner, "cell 1 1");
//---- buttonCheckBox ----
buttonCheckBox.setText("FlatArrowButton");
buttonCheckBox.addActionListener(e -> arrowButtonChanged());
panel5.add(buttonCheckBox, "cell 2 1,alignx left,growx 0");
//---- arrowSizeLabel ----
arrowSizeLabel.setText("Arrow Size:");
panel5.add(arrowSizeLabel, "cell 0 2");
//---- arrowSizeSpinner ----
arrowSizeSpinner.setModel(new SpinnerNumberModel(8, 2, null, 1));
arrowSizeSpinner.addChangeListener(e -> arrowSizeChanged());
panel5.add(arrowSizeSpinner, "cell 1 2");
//---- offsetLabel ----
offsetLabel.setText("Offset:");
panel5.add(offsetLabel, "cell 0 3");
//---- offsetSpinner ----
offsetSpinner.setModel(new SpinnerNumberModel(1.0F, null, null, 0.05F));
offsetSpinner.addChangeListener(e -> offsetChanged());
panel5.add(offsetSpinner, "cell 1 3");
}
panel6.add(panel5, "cell 0 1");
} }
flatTestPanel1.add(panel5, "cell 6 5 1 2,aligny top,growy 0"); flatTestPanel1.add(panel6, "cell 6 5 3 2,aligny top,growy 0");
//---- arrowPainter9 ---- //---- arrowPainter9 ----
arrowPainter9.setScale(8.0F); arrowPainter9.setScale(8.0F);
@@ -670,12 +769,15 @@ public class FlatPaintingTest
private FlatPaintingTest.ArrowPainter arrowPainter6; private FlatPaintingTest.ArrowPainter arrowPainter6;
private FlatPaintingTest.ArrowPainter arrowPainter7; private FlatPaintingTest.ArrowPainter arrowPainter7;
private FlatPaintingTest.ArrowPainter arrowPainter8; private FlatPaintingTest.ArrowPainter arrowPainter8;
private JSlider focusWidthFractionSlider;
private JSlider focusInnerWidthSlider;
private JCheckBox translucentCheckBox;
private JSpinner arrowWidthSpinner; private JSpinner arrowWidthSpinner;
private JCheckBox vectorCheckBox;
private JSpinner arrowHeightSpinner; private JSpinner arrowHeightSpinner;
private JCheckBox buttonCheckBox;
private JSpinner arrowSizeSpinner; private JSpinner arrowSizeSpinner;
private JSpinner offsetSpinner; private JSpinner offsetSpinner;
private JCheckBox vectorCheckBox;
private JCheckBox buttonCheckBox;
private FlatPaintingTest.ArrowPainter arrowPainter13; private FlatPaintingTest.ArrowPainter arrowPainter13;
private FlatPaintingTest.ArrowPainter arrowPainter14; private FlatPaintingTest.ArrowPainter arrowPainter14;
private FlatPaintingTest.ArrowPainter arrowPainter15; private FlatPaintingTest.ArrowPainter arrowPainter15;
@@ -687,6 +789,9 @@ public class FlatPaintingTest
public static class BorderPainter public static class BorderPainter
extends JComponent extends JComponent
{ {
private static final Color TRANSLUCENT_BLUE = new Color( 0x880000ff, true );
private static final Color TRANSLUCENT_RED = new Color( 0x88ff0000, true );
private int w = 20; private int w = 20;
private int h = 20; private int h = 20;
private int focusWidth = 2; private int focusWidth = 2;
@@ -697,6 +802,10 @@ public class FlatPaintingTest
private boolean paintBorder = true; private boolean paintBorder = true;
private boolean paintFocus = true; private boolean paintFocus = true;
float focusWidthFraction = 1;
float focusInnerWidth = 1;
boolean translucent;
public BorderPainter() { public BorderPainter() {
} }
@@ -793,23 +902,17 @@ public class FlatPaintingTest
int width = UIScale.scale( w ); int width = UIScale.scale( w );
int height = UIScale.scale( h ); int height = UIScale.scale( h );
float focusWidth = UIScale.scale( (float) this.focusWidth ); float focusWidth = UIScale.scale( (float) this.focusWidth );
float focusInnerWidth = UIScale.scale( this.focusInnerWidth );
float lineWidth = UIScale.scale( (float) this.lineWidth ); float lineWidth = UIScale.scale( (float) this.lineWidth );
float arc = UIScale.scale( (float) this.arc ); float arc = UIScale.scale( (float) this.arc );
if( paintBackground ) { Color background = paintBackground ? Color.green : null;
g.setColor( Color.green ); Color focusColor = paintFocus ? (translucent ? TRANSLUCENT_BLUE : Color.blue) : null;
FlatUIUtils.paintComponentBackground( g2, 0, 0, width, height, focusWidth, arc ); Color borderColor = paintBorder ? (translucent ? TRANSLUCENT_RED : Color.red) : null;
}
if( paintFocus ) { FlatUIUtils.paintOutlinedComponent( g2, 0, 0, width, height,
g.setColor( Color.blue ); focusWidth, focusWidthFraction, focusInnerWidth, lineWidth, arc,
FlatUIUtils.paintComponentOuterBorder( g2, 0, 0, width, height, focusWidth, lineWidth, arc ); focusColor, borderColor, background );
}
if( paintBorder ) {
g.setColor( Color.red );
FlatUIUtils.paintComponentBorder( g2, 0, 0, width, height, focusWidth, lineWidth, arc );
}
HiDPIUtils.paintAtScale1x( g2, 0, 0, width, height, HiDPIUtils.paintAtScale1x( g2, 0, 0, width, height,
(g2d, x2, y2, width2, height2, scaleFactor) -> { (g2d, x2, y2, width2, height2, scaleFactor) -> {

View File

@@ -428,110 +428,175 @@ new FormModel {
"value": "cell 5 5,align left top,grow 0 0" "value": "cell 5 5,align left top,grow 0 0"
} ) } )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3" "$columnConstraints": "[grow,fill]"
"$columnConstraints": "[fill][fill]" "$rowConstraints": "[]unrel[]"
"$rowConstraints": "[][][][][][]" "$layoutConstraints": "insets 0,hidemode 3"
} ) { } ) {
name: "panel5" name: "panel6"
"border": new javax.swing.border.TitledBorder( "Arrow Control" ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
add( new FormComponent( "javax.swing.JLabel" ) { "$layoutConstraints": "hidemode 3"
name: "arrowWidthLabel" "$columnConstraints": "[fill][fill]"
"text": "Width:" "$rowConstraints": "[][][]"
} ) {
name: "panel7"
"border": new javax.swing.border.TitledBorder( "Outlined Component Control" )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "focusWidthFractionLabel"
"text": "Focus width fraction:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "focusWidthFractionSlider"
"value": 100
"majorTickSpacing": 25
"paintLabels": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "focusWidthFractionChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "focusInnerWidthLabel"
"text": "Focus inner width:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JSlider" ) {
name: "focusInnerWidthSlider"
"paintLabels": true
"value": 10
"maximum": 50
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "focusInnerWidthChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "translucentCheckBox"
"text": "translucent"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "translucentChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2 2 1,alignx left,growx 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0" "value": "cell 0 0"
} ) } )
add( new FormComponent( "javax.swing.JSpinner" ) { add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
name: "arrowWidthSpinner" "$layoutConstraints": "hidemode 3"
"model": new javax.swing.SpinnerNumberModel { "$columnConstraints": "[fill][fill][fill]"
minimum: 0 "$rowConstraints": "[][][][]"
value: 20 } ) {
} name: "panel5"
auxiliary() { "border": new javax.swing.border.TitledBorder( "Arrow Control" )
"JavaCodeGenerator.variableLocal": false add( new FormComponent( "javax.swing.JLabel" ) {
} name: "arrowWidthLabel"
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "arrowSizeChanged", false ) ) "text": "Width:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0" "value": "cell 0 0"
} ) } )
add( new FormComponent( "javax.swing.JLabel" ) { add( new FormComponent( "javax.swing.JSpinner" ) {
name: "arrowHeightLabel" name: "arrowWidthSpinner"
"text": "Height:" "model": new javax.swing.SpinnerNumberModel {
minimum: 0
value: 20
}
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "arrowSizeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "vectorCheckBox"
"text": "vector"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "vectorChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "arrowHeightLabel"
"text": "Height:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "arrowHeightSpinner"
"model": new javax.swing.SpinnerNumberModel {
minimum: 0
value: 20
}
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "arrowSizeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "buttonCheckBox"
"text": "FlatArrowButton"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "arrowButtonChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 1,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "arrowSizeLabel"
"text": "Arrow Size:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "arrowSizeSpinner"
"model": new javax.swing.SpinnerNumberModel {
minimum: 2
value: 8
}
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "arrowSizeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "offsetLabel"
"text": "Offset:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "offsetSpinner"
"model": new javax.swing.SpinnerNumberModel {
stepSize: 0.05f
value: 1.0f
}
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "offsetChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 1" "value": "cell 0 1"
} ) } )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "arrowHeightSpinner"
"model": new javax.swing.SpinnerNumberModel {
minimum: 0
value: 20
}
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "arrowSizeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "arrowSizeLabel"
"text": "Arrow Size:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "arrowSizeSpinner"
"model": new javax.swing.SpinnerNumberModel {
minimum: 2
value: 8
}
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "arrowSizeChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "offsetLabel"
"text": "Offset:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 3"
} )
add( new FormComponent( "javax.swing.JSpinner" ) {
name: "offsetSpinner"
"model": new javax.swing.SpinnerNumberModel {
stepSize: 0.05f
value: 1.0f
}
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "offsetChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 3"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "vectorCheckBox"
"text": "vector"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "vectorChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 4 2 1,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "buttonCheckBox"
"text": "FlatArrowButton"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "arrowButtonChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 5 2 1,alignx left,growx 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 6 5 1 2,aligny top,growy 0" "value": "cell 6 5 3 2,aligny top,growy 0"
} ) } )
add( new FormComponent( "com.formdev.flatlaf.testing.FlatPaintingTest$ArrowPainter" ) { add( new FormComponent( "com.formdev.flatlaf.testing.FlatPaintingTest$ArrowPainter" ) {
name: "arrowPainter9" name: "arrowPainter9"