fixed clipped borders at 125%, 150% and 175% scaling when outer focus width is zero (default in "Flat Light" and "Flat Dark" themes)

This commit is contained in:
Karl Tauber
2019-12-15 10:42:01 +01:00
parent 736c7b8377
commit 3fcb17931a
6 changed files with 168 additions and 3 deletions

View File

@@ -6,6 +6,8 @@ FlatLaf Change Log
- TabbedPane: Support background color for selected tabs.
- CheckBox: changed `CheckBox.arc` from radius to diameter to be consistent with
`Button.arc` and `Component.arc`
- Fixed clipped borders at 125%, 150% and 175% scaling when outer focus width is
zero (default in "Flat Light" and "Flat Dark" themes).
## 0.21

View File

@@ -335,7 +335,7 @@ public class FlatComboBoxUI
g2.setColor( enabled ? borderColor : disabledBorderColor );
float lw = scale( 1f );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) );
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2)) );
}
paint( g, c );

View File

@@ -285,7 +285,7 @@ public class FlatSpinnerUI
g2.setColor( enabled ? borderColor : disabledBorderColor );
float lw = scale( 1f );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) );
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
paint( g, c );
}

View File

@@ -39,6 +39,7 @@ import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ColorUIResource;
import com.formdev.flatlaf.util.DerivedColor;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.UIScale;
@@ -136,6 +137,23 @@ public class FlatUIUtils
*/
public static void paintComponentOuterBorder( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc )
{
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, systemScaleFactor,
(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 outerRadius = (arc > 0) ? arc + focusWidth - UIScale.scale( 2f ) : focusWidth;
float ow = focusWidth + lineWidth;
@@ -159,6 +177,23 @@ public class FlatUIUtils
*/
public static void paintComponentBorder( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float lineWidth, float arc )
{
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, systemScaleFactor,
(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 arc2 = arc > lineWidth ? arc - lineWidth : 0f;
@@ -187,6 +222,23 @@ public class FlatUIUtils
*/
public static void paintComponentBackground( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float arc )
{
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, systemScaleFactor,
(g2d, x2, y2, width2, height2, scaleFactor) -> {
paintComponentBackgroundImpl( g2d, x2, y2, width2, height2,
(float) (focusWidth * scaleFactor), (float) (arc * scaleFactor) );
} );
return;
}
paintComponentBackgroundImpl( g, x, y, width, height, focusWidth, arc );
}
private static void paintComponentBackgroundImpl( Graphics2D g, int x, int y, int width, int height,
float focusWidth, float arc )
{
g.fill( new RoundRectangle2D.Float(
x + focusWidth, y + focusWidth,

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2019 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
*
* http://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 java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
/**
* @author Karl Tauber
*/
public class HiDPIUtils
{
public interface Painter {
public void paint( Graphics2D g, int x, int y, int width, int height, double scaleFactor );
}
public static void paintAtScale1x( Graphics2D g, JComponent c, Painter painter ) {
paintAtScale1x( g, 0, 0, c.getWidth(), c.getHeight(), painter );
}
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height, Painter painter ) {
paintAtScale1x( g, x, y, width, height, UIScale.getSystemScaleFactor( g ), painter );
}
/**
* Paint at system scale factor 1x to avoid rounding issues at 125%, 150% and 175% scaling.
* <p>
* Scales the given Graphics2D down to 100% and invokes the
* given painter passing scaled x, y, width and height.
* <p>
* Uses the same scaling calculation as the JRE uses.
*/
public static void paintAtScale1x( Graphics2D g, int x, int y, int width, int height,
double scaleFactor, Painter painter )
{
if( scaleFactor == 1 ) {
painter.paint( g, x, y, width, height, 1 );
return;
}
// save original transform
AffineTransform transform = g.getTransform();
// scale rectangle
Rectangle2D.Double scaledRect = scale( transform, x, y, width, height );
try {
// unscale to factor 1.0
double scale = 1.0 / scaleFactor;
g.scale( scale, scale );
// compute origin delta x/y
double dx = Math.floor( scaledRect.x ) - transform.getTranslateX();
double dy = Math.floor( scaledRect.y ) - transform.getTranslateY();
// move origin to make sure that origin x/y are at whole numbers
if( dx != 0 || dy != 0 )
g.translate( dx, dy );
int swidth = (int) scaledRect.width;
int sheight = (int) scaledRect.height;
// paint
painter.paint( g, 0, 0, swidth, sheight, scaleFactor );
} finally {
// restore original transform
g.setTransform( transform );
}
}
/**
* Scales a rectangle in the same way as the JRE does in
* sun.java2d.pipe.PixelToParallelogramConverter.fillRectangle(),
* which is used by Graphics.fillRect().
*/
private static Rectangle2D.Double scale( AffineTransform transform, int x, int y, int width, int height ) {
double dx1 = transform.getScaleX();
double dy2 = transform.getScaleY();
double px = x * dx1 + transform.getTranslateX();
double py = y * dy2 + transform.getTranslateY();
dx1 *= width;
dy2 *= height;
double newx = normalize( px );
double newy = normalize( py );
dx1 = normalize( px + dx1 ) - newx;
dy2 = normalize( py + dy2 ) - newy;
return new Rectangle2D.Double( newx, newy, dx1, dy2 );
}
private static double normalize( double value ) {
return Math.floor( value + 0.25 ) + 0.25;
}
}

View File

@@ -255,7 +255,7 @@ public class FlatDatePickerUI
g2.setColor( enabled ? borderColor : disabledBorderColor );
float lw = scale( 1f );
float lx = isLeftToRight ? arrowX : arrowX + arrowWidth - lw;
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - (focusWidth * 2) ) );
g2.fill( new Rectangle2D.Float( lx, focusWidth, lw, height - 1 - (focusWidth * 2) ) );
paint( g, c );
}