Merge PR #864: HiDPI: fix incomplete component repainting at 125% or 175% scaling on Windows

This commit is contained in:
Karl Tauber
2024-07-15 18:43:46 +02:00
29 changed files with 773 additions and 85 deletions

View File

@@ -29,6 +29,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.FocusEvent;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.util.Map;
@@ -61,6 +62,7 @@ import com.formdev.flatlaf.icons.FlatHelpButtonIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -312,11 +314,11 @@ public class FlatButtonUI
case BUTTON_TYPE:
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
case OUTLINE:
b.repaint();
HiDPIUtils.repaint( b );
break;
case STYLE:
@@ -328,7 +330,7 @@ public class FlatButtonUI
} else
installStyle( b );
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}
@@ -915,7 +917,7 @@ public class FlatButtonUI
@Override
public void stateChanged( ChangeEvent e ) {
super.stateChanged( e );
HiDPIUtils.repaint( b );
// if button is in toolbar, repaint button groups
AbstractButton b = (AbstractButton) e.getSource();
@@ -927,5 +929,17 @@ public class FlatButtonUI
((FlatToolBarUI)ui).repaintButtonGroup( b );
}
}
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
HiDPIUtils.repaint( b );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
HiDPIUtils.repaint( b );
}
}
}

View File

@@ -78,6 +78,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
@@ -220,7 +221,7 @@ public class FlatComboBoxUI
private void repaintArrowButton() {
if( arrowButton != null && !comboBox.isEditable() )
arrowButton.repaint();
HiDPIUtils.repaint( arrowButton );
}
};
comboBox.addMouseListener( hoverListener );
@@ -351,15 +352,15 @@ public class FlatComboBoxUI
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
if( comboBox != null && comboBox.isEditable() )
comboBox.repaint();
if( comboBox != null )
HiDPIUtils.repaint( comboBox );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
if( comboBox != null && comboBox.isEditable() )
comboBox.repaint();
if( comboBox != null )
HiDPIUtils.repaint( comboBox );
}
};
}
@@ -386,12 +387,12 @@ public class FlatComboBoxUI
switch( propertyName ) {
case PLACEHOLDER_TEXT:
if( editor != null )
editor.repaint();
HiDPIUtils.repaint( editor );
break;
case COMPONENT_ROUND_RECT:
case OUTLINE:
comboBox.repaint();
HiDPIUtils.repaint( comboBox );
break;
case MINIMUM_WIDTH:
@@ -402,7 +403,7 @@ public class FlatComboBoxUI
case STYLE_CLASS:
installStyle();
comboBox.revalidate();
comboBox.repaint();
HiDPIUtils.repaint( comboBox );
break;
}
}

View File

@@ -171,7 +171,7 @@ public class FlatEditorPaneUI
case FlatClientProperties.STYLE_CLASS:
installStyle.run();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
}
}

View File

@@ -124,7 +124,7 @@ public class FlatLabelUI
} else
installStyle( label );
label.revalidate();
label.repaint();
HiDPIUtils.repaint( label );
}
super.propertyChange( e );

View File

@@ -43,6 +43,7 @@ import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -182,7 +183,7 @@ public class FlatListUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
list.revalidate();
list.repaint();
HiDPIUtils.repaint( list );
break;
}
};
@@ -205,7 +206,7 @@ public class FlatListUI
Rectangle r = getCellBounds( list, firstIndex, lastIndex );
if( r != null ) {
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
list.repaint( r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
HiDPIUtils.repaint( list, r.x - arc, r.y - arc, r.width + (arc * 2), r.height + (arc * 2) );
}
}
};

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicMenuUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -167,7 +168,7 @@ public class FlatMenuUI
JMenu menu = (JMenu) e.getSource();
if( menu.isTopLevelMenu() && menu.isRolloverEnabled() ) {
menu.getModel().setRollover( rollover );
menu.repaint();
HiDPIUtils.repaint( menu );
}
}
};

View File

@@ -31,6 +31,7 @@ import javax.swing.plaf.basic.BasicPanelUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -111,7 +112,7 @@ public class FlatPanelUI
} else
installStyle( c );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER:

View File

@@ -43,6 +43,7 @@ import javax.swing.text.View;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.icons.FlatCapsLockIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -163,7 +164,7 @@ public class FlatPasswordFieldUI
}
private void repaint( KeyEvent e ) {
if( e.getKeyCode() == KeyEvent.VK_CAPS_LOCK ) {
e.getComponent().repaint();
HiDPIUtils.repaint( e.getComponent() );
scrollCaretToVisible();
}
}
@@ -326,7 +327,7 @@ public class FlatPasswordFieldUI
if( visible != revealButton.isVisible() ) {
revealButton.setVisible( visible );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
if( !visible ) {
revealButton.setSelected( false );

View File

@@ -133,14 +133,14 @@ public class FlatProgressBarUI
case PROGRESS_BAR_LARGE_HEIGHT:
case PROGRESS_BAR_SQUARE:
progressBar.revalidate();
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
break;
case STYLE:
case STYLE_CLASS:
installStyle();
progressBar.revalidate();
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
break;
}
};
@@ -294,6 +294,6 @@ public class FlatProgressBarUI
// Only solution is to repaint whole progress bar.
double systemScaleFactor = UIScale.getSystemScaleFactor( progressBar.getGraphicsConfiguration() );
if( (int) systemScaleFactor != systemScaleFactor )
progressBar.repaint();
HiDPIUtils.repaint( progressBar );
}
}

View File

@@ -47,6 +47,7 @@ import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -173,7 +174,7 @@ public class FlatRadioButtonUI
} else
installStyle( b );
b.revalidate();
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}

View File

@@ -43,6 +43,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableField;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableLookupProvider;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -212,14 +213,14 @@ public class FlatScrollBarUI
switch( e.getPropertyName() ) {
case FlatClientProperties.SCROLL_BAR_SHOW_BUTTONS:
scrollbar.revalidate();
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
installStyle();
scrollbar.revalidate();
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
break;
case "componentOrientation":
@@ -492,7 +493,7 @@ public class FlatScrollBarUI
private void repaint() {
if( scrollbar.isEnabled() )
scrollbar.repaint();
HiDPIUtils.repaint( scrollbar );
}
}

View File

@@ -55,6 +55,7 @@ import javax.swing.plaf.basic.BasicScrollPaneUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -297,11 +298,11 @@ public class FlatScrollPaneUI
JScrollBar hsb = scrollpane.getHorizontalScrollBar();
if( vsb != null ) {
vsb.revalidate();
vsb.repaint();
HiDPIUtils.repaint( vsb );
}
if( hsb != null ) {
hsb.revalidate();
hsb.repaint();
HiDPIUtils.repaint( hsb );
}
break;
@@ -321,14 +322,14 @@ public class FlatScrollPaneUI
break;
case FlatClientProperties.OUTLINE:
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
break;
case FlatClientProperties.STYLE:
case FlatClientProperties.STYLE_CLASS:
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
break;
case "border":
@@ -339,7 +340,7 @@ public class FlatScrollPaneUI
borderShared = null;
installStyle();
scrollpane.revalidate();
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
break;
}
@@ -538,14 +539,14 @@ public class FlatScrollPaneUI
public void focusGained( FocusEvent e ) {
// necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
@Override
public void focusLost( FocusEvent e ) {
// necessary to update focus border
if( scrollpane.getBorder() instanceof FlatBorder )
scrollpane.repaint();
HiDPIUtils.repaint( scrollpane );
}
}

View File

@@ -32,6 +32,7 @@ import javax.swing.plaf.basic.BasicSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -134,7 +135,7 @@ public class FlatSeparatorUI
} else
installStyle( s );
s.revalidate();
s.repaint();
HiDPIUtils.repaint( s );
break;
}
}

View File

@@ -25,6 +25,8 @@ import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
@@ -191,6 +193,23 @@ public class FlatSliderUI
return new FlatTrackListener();
}
@Override
protected FocusListener createFocusListener( JSlider slider ) {
return new BasicSliderUI.FocusHandler() {
@Override
public void focusGained( FocusEvent e ) {
super.focusGained( e );
HiDPIUtils.repaint( slider );
}
@Override
public void focusLost( FocusEvent e ) {
super.focusLost( e );
HiDPIUtils.repaint( slider );
}
};
}
@Override
protected PropertyChangeListener createPropertyChangeListener( JSlider slider ) {
return FlatStylingSupport.createPropertyChangeListener( slider, this::installStyle,
@@ -579,15 +598,15 @@ debug*/
@Override
public void setThumbLocation( int x, int y ) {
// set new thumb location and compute union of old and new thumb bounds
Rectangle r = new Rectangle( thumbRect );
thumbRect.setLocation( x, y );
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
if( !isRoundThumb() ) {
// the needle of the directional thumb is painted outside of thumbRect
// --> must increase repaint rectangle
// set new thumb location and compute union of old and new thumb bounds
Rectangle r = new Rectangle( thumbRect );
thumbRect.setLocation( x, y );
SwingUtilities.computeUnion( thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, r );
// increase union rectangle for repaint
int extra = (int) Math.ceil( UIScale.scale( focusWidth ) * 0.4142f );
if( slider.getOrientation() == JSlider.HORIZONTAL )
@@ -597,10 +616,9 @@ debug*/
if( !slider.getComponentOrientation().isLeftToRight() )
r.x -= extra;
}
}
slider.repaint( r );
} else
super.setThumbLocation( x, y );
HiDPIUtils.repaint( slider, r );
}
//---- class FlatTrackListener --------------------------------------------
@@ -688,21 +706,21 @@ debug*/
!UIManager.getBoolean( "Slider.snapToTicksOnReleased" ) )
{
calculateThumbLocation();
slider.repaint();
HiDPIUtils.repaint( slider );
}
}
protected void setThumbHover( boolean hover ) {
if( hover != thumbHover ) {
thumbHover = hover;
slider.repaint( thumbRect );
HiDPIUtils.repaint( slider, thumbRect );
}
}
protected void setThumbPressed( boolean pressed ) {
if( pressed != thumbPressed ) {
thumbPressed = pressed;
slider.repaint( thumbRect );
HiDPIUtils.repaint( slider, thumbRect );
}
}

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicSpinnerUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -586,7 +587,7 @@ public class FlatSpinnerUI
@Override
public void focusGained( FocusEvent e ) {
// necessary to update focus border
spinner.repaint();
HiDPIUtils.repaint( spinner );
// if spinner gained focus, transfer it to the editor text field
if( e.getComponent() == spinner ) {
@@ -599,7 +600,7 @@ public class FlatSpinnerUI
@Override
public void focusLost( FocusEvent e ) {
// necessary to update focus border
spinner.repaint();
HiDPIUtils.repaint( spinner );
}
//---- interface PropertyChangeListener ----
@@ -614,7 +615,7 @@ public class FlatSpinnerUI
case FlatClientProperties.COMPONENT_ROUND_RECT:
case FlatClientProperties.OUTLINE:
spinner.repaint();
HiDPIUtils.repaint( spinner );
break;
case FlatClientProperties.MINIMUM_WIDTH:
@@ -625,7 +626,7 @@ public class FlatSpinnerUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
spinner.revalidate();
spinner.repaint();
HiDPIUtils.repaint( spinner );
break;
}
}

View File

@@ -40,6 +40,7 @@ import javax.swing.UIManager;
import javax.swing.border.Border;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.StringUtils;
import com.formdev.flatlaf.util.SystemInfo;
@@ -709,7 +710,7 @@ public class FlatStylingSupport
case FlatClientProperties.STYLE_CLASS:
installStyle.run();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
}
};

View File

@@ -98,6 +98,7 @@ import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.Animator;
import com.formdev.flatlaf.util.CubicBezierEasing;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.JavaCompatibility;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.StringUtils;
@@ -895,7 +896,7 @@ public class FlatTabbedPaneUI
}
}
tabPane.repaint( r );
HiDPIUtils.repaint( tabPane, r );
}
private boolean inCalculateEqual;
@@ -2581,19 +2582,19 @@ debug*/
@Override
public void popupMenuWillBecomeVisible( PopupMenuEvent e ) {
popupVisible = true;
repaint();
HiDPIUtils.repaint( this );
}
@Override
public void popupMenuWillBecomeInvisible( PopupMenuEvent e ) {
popupVisible = false;
repaint();
HiDPIUtils.repaint( this );
}
@Override
public void popupMenuCanceled( PopupMenuEvent e ) {
popupVisible = false;
repaint();
HiDPIUtils.repaint( this );
}
}
@@ -3102,7 +3103,7 @@ debug*/
case TABBED_PANE_SHOW_TAB_SEPARATORS:
case TABBED_PANE_TAB_TYPE:
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
case TABBED_PANE_SHOW_CONTENT_SEPARATOR:
@@ -3125,14 +3126,14 @@ debug*/
case TABBED_PANE_TAB_ICON_PLACEMENT:
case TABBED_PANE_TAB_CLOSABLE:
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
case TABBED_PANE_LEADING_COMPONENT:
uninstallLeadingComponent();
installLeadingComponent();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
ensureSelectedTabIsVisibleLater();
break;
@@ -3140,7 +3141,7 @@ debug*/
uninstallTrailingComponent();
installTrailingComponent();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
ensureSelectedTabIsVisibleLater();
break;
@@ -3148,7 +3149,7 @@ debug*/
case STYLE_CLASS:
installStyle();
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
}
}
@@ -3172,7 +3173,7 @@ debug*/
case TABBED_PANE_TAB_ALIGNMENT:
case TABBED_PANE_TAB_CLOSABLE:
tabPane.revalidate();
tabPane.repaint();
HiDPIUtils.repaint( tabPane );
break;
}
}

View File

@@ -45,6 +45,7 @@ import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -234,8 +235,8 @@ public class FlatTableHeaderUI
@Override
protected void rolloverColumnUpdated( int oldColumn, int newColumn ) {
header.repaint( header.getHeaderRect( oldColumn ) );
header.repaint( header.getHeaderRect( newColumn ) );
HiDPIUtils.repaint( header, header.getHeaderRect( oldColumn ) );
HiDPIUtils.repaint( header, header.getHeaderRect( newColumn ) );
}
@Override

View File

@@ -66,6 +66,7 @@ import com.formdev.flatlaf.icons.FlatCheckBoxIcon;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.Graphics2DProxy;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale;
@@ -297,7 +298,7 @@ public class FlatTableUI
case FlatClientProperties.STYLE_CLASS:
installStyle();
table.revalidate();
table.repaint();
HiDPIUtils.repaint( table );
break;
}
};
@@ -912,7 +913,7 @@ public class FlatTableUI
public void componentHidden( ComponentEvent e ) {
Container viewport = SwingUtilities.getUnwrappedParent( table );
if( viewport instanceof JViewport )
viewport.repaint();
HiDPIUtils.repaint( viewport );
}
@Override
@@ -931,7 +932,7 @@ public class FlatTableUI
int viewportHeight = viewport.getHeight();
int tableHeight = table.getHeight();
if( tableHeight < viewportHeight )
viewport.repaint( 0, tableHeight, viewport.getWidth(), viewportHeight - tableHeight );
HiDPIUtils.repaint( viewport, 0, tableHeight, viewport.getWidth(), viewportHeight - tableHeight );
}
}
}

View File

@@ -239,7 +239,7 @@ public class FlatTextFieldUI
case COMPONENT_ROUND_RECT:
case OUTLINE:
case TEXT_FIELD_PADDING:
c.repaint();
HiDPIUtils.repaint( c );
break;
case MINIMUM_WIDTH:
@@ -250,38 +250,38 @@ public class FlatTextFieldUI
case STYLE_CLASS:
installStyle();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_LEADING_ICON:
leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_TRAILING_ICON:
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_LEADING_COMPONENT:
uninstallLeadingComponent();
installLeadingComponent();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_TRAILING_COMPONENT:
uninstallTrailingComponent();
installTrailingComponent();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
uninstallClearButton();
installClearButton();
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
break;
case "enabled":
@@ -815,7 +815,7 @@ debug*/
if( visible != clearButton.isVisible() ) {
clearButton.setVisible( visible );
c.revalidate();
c.repaint();
HiDPIUtils.repaint( c );
}
}

View File

@@ -26,6 +26,7 @@ import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.UnknownStyleException;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -159,14 +160,14 @@ public class FlatToggleButtonUI
b.revalidate();
}
b.repaint();
HiDPIUtils.repaint( b );
break;
case TAB_BUTTON_UNDERLINE_PLACEMENT:
case TAB_BUTTON_UNDERLINE_HEIGHT:
case TAB_BUTTON_UNDERLINE_COLOR:
case TAB_BUTTON_SELECTED_BACKGROUND:
b.repaint();
HiDPIUtils.repaint( b );
break;
}
}

View File

@@ -36,6 +36,7 @@ import javax.swing.plaf.basic.BasicToolBarSeparatorUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/**
@@ -131,7 +132,7 @@ public class FlatToolBarSeparatorUI
} else
installStyle( s );
s.revalidate();
s.repaint();
HiDPIUtils.repaint( s );
break;
}
}

View File

@@ -47,6 +47,7 @@ import javax.swing.plaf.basic.BasicToolBarUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -443,7 +444,7 @@ public class FlatToolBarUI
// repaint button group
if( gr != null )
toolBar.repaint( gr );
HiDPIUtils.repaint(toolBar, gr );
}
private ButtonGroup getButtonGroup( AbstractButton b ) {

View File

@@ -47,6 +47,7 @@ import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
import com.formdev.flatlaf.util.HiDPIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
import com.formdev.flatlaf.util.UIScale;
@@ -310,7 +311,7 @@ public class FlatTreeUI
switch( e.getPropertyName() ) {
case TREE_WIDE_SELECTION:
case TREE_PAINT_SELECTION:
tree.repaint();
HiDPIUtils.repaint( tree );
break;
case "dropLocation":
@@ -325,7 +326,7 @@ public class FlatTreeUI
case STYLE_CLASS:
installStyle();
tree.revalidate();
tree.repaint();
HiDPIUtils.repaint( tree );
break;
case "enabled":
@@ -353,7 +354,7 @@ public class FlatTreeUI
Rectangle r = tree.getPathBounds( loc.getPath() );
if( r != null )
tree.repaint( 0, r.y, tree.getWidth(), r.height );
HiDPIUtils.repaint( tree, 0, r.y, tree.getWidth(), r.height );
}
@Override
@@ -370,14 +371,14 @@ public class FlatTreeUI
{
if( changedPaths.length > 4 ) {
// same is done in BasicTreeUI.Handler.valueChanged()
tree.repaint();
HiDPIUtils.repaint( tree );
} else {
int arc = (int) Math.ceil( UIScale.scale( selectionArc / 2f ) );
for( TreePath path : changedPaths ) {
Rectangle r = getPathBounds( tree, path );
if( r != null )
tree.repaint( r.x, r.y - arc, r.width, r.height + (arc * 2) );
HiDPIUtils.repaint( tree, r.x, r.y - arc, r.width, r.height + (arc * 2) );
}
}
}

View File

@@ -1347,13 +1347,13 @@ debug*/
@Override
public void focusGained( FocusEvent e ) {
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
repaintComponent.repaint();
HiDPIUtils.repaint( repaintComponent );
}
@Override
public void focusLost( FocusEvent e ) {
if( repaintCondition == null || repaintCondition.test( repaintComponent ) )
repaintComponent.repaint();
HiDPIUtils.repaint( repaintComponent );
}
}

View File

@@ -16,14 +16,17 @@
package com.formdev.flatlaf.util;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import javax.swing.JComponent;
import javax.swing.RepaintManager;
import com.formdev.flatlaf.FlatSystemProperties;
/**
@@ -322,4 +325,243 @@ public class HiDPIUtils
}
};
}
/**
* Repaints the given component.
* <p>
* See {@link #repaint(Component, int, int, int, int)} for more details.
*
* @since 3.5
*/
public static void repaint( Component c ) {
repaint( c, 0, 0, c.getWidth(), c.getHeight() );
}
/**
* Repaints the given component area.
* <p>
* See {@link #repaint(Component, int, int, int, int)} for more details.
*
* @since 3.5
*/
public static void repaint( Component c, Rectangle r ) {
repaint( c, r.x, r.y, r.width, r.height );
}
/**
* Repaints the given component area.
* <p>
* Invokes {@link Component#repaint(int, int, int, int)} on the given component,
* <p>
* Use this method, instead of {@code Component.repaint(...)},
* to fix a problem in Swing when using scale factors that end on .25 or .75
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
* repaint right and/or bottom 1px edge of component.
* <p>
* The problem may occur under following conditions:
* <ul>
* <li>using Java 9 or later
* <li>system scale factor is 125%, 175%, 225%, ...
* (Windows only; Java on macOS and Linux does not support fractional scale factors)
* <li>repaint whole component or right/bottom area of component
* <li>component is opaque; or component is contained in a opaque container
* that has same right/bottom bounds as component
* <li>component has bounds that Java/Swing scales different when repainting components
* </ul>
*
* @since 3.5
*/
public static void repaint( Component c, int x, int y, int width, int height ) {
// repaint given component area
// Always invoke repaint() on given component, even if also invoked (below)
// on one of its ancestors, for the case that component overrides that method.
// Also RepaintManager "merges" the two repaints into one.
c.repaint( x, y, width, height );
if( RepaintManager.currentManager( c ) instanceof HiDPIRepaintManager )
return;
// if necessary, also repaint given area in first ancestor that is larger than component
// to avoid clipping issue (see needsSpecialRepaint())
if( needsSpecialRepaint( c, x, y, width, height ) ) {
int x2 = x + c.getX();
int y2 = y + c.getY();
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
x2 += p.getX();
y2 += p.getY();
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() ) {
p.repaint( x2, y2, width, height );
break;
}
}
}
}
/**
* There is a problem in Swing, when using scale factors that end on .25 or .75
* (e.g. 1.25, 1.75, 2.25, etc) and repainting single components, which may not
* repaint right and/or bottom 1px edge of component.
* <p>
* The component is first painted to an in-memory image,
* and then that image is copied to the screen.
* See {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
* <p>
* There are two clipping rectangles involved when copying the image to the screen:
* {@code sun.java2d.SunGraphics2D#devClip} and
* {@code sun.java2d.SunGraphics2D#usrClip}.
* <p>
* {@code devClip} is the device clipping in physical pixels.
* It gets the bounds of the painting component, which is either the passed component,
* or if it is non-opaque, then the first opaque ancestor of the passed component.
* It is calculated in {@code sun.java2d.SunGraphics2D#constrain()} while
* getting a graphics context via {@link JComponent#getGraphics()}.
* <p>
* {@code usrClip} is the user clipping, which is set via {@link Graphics} clipping methods.
* This is done in {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}.
* <p>
* The intersection of {@code devClip} and {@code usrClip}
* (computed in {@code sun.java2d.SunGraphics2D#validateCompClip()})
* is used to copy the image to the screen.
* <p>
* Unfortunately different scaling/rounding strategies are used to calculate
* the two clipping rectangles, which is the reason of the issue.
* <p>
* {@code devClip} (see {@code sun.java2d.SunGraphics2D#constrain()}):
* <pre>{@code
* int devX = (int) (x * scale);
* int devWidth = Math.round( width * scale )
* }</pre>
* {@code usrClip} (see {@code javax.swing.RepaintManager.PaintManager#paintDoubleBufferedFPScales()}):
* <pre>{@code
* int usrX = (int) Math.ceil( (x * scale) - 0.5 );
* int usrWidth = ((int) Math.ceil( ((x + width) * scale) - 0.5 )) - usrX;
* }</pre>
* X/Y coordinates are always rounded down for {@code devClip}, but rounded up for {@code usrClip}.
* Width/height calculation is also different.
*/
private static boolean needsSpecialRepaint( Component c, int x, int y, int width, int height ) {
// no special repaint necessary for Java 8 or for macOS and Linux
// (Java on those platforms does not support fractional scale factors)
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
return false;
// check whether repaint area is empty or no component given
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
if( width <= 0 || height <= 0 || c == null )
return false;
// check whether component has zero size
// (same checks as in javax.swing.RepaintManager.addDirtyRegion0())
int compWidth = c.getWidth();
int compHeight = c.getHeight();
if( compWidth <= 0 || compHeight <= 0 )
return false;
// check whether repaint area does span to right or bottom component edges
// (in this case, {@code devClip} is always larger than {@code usrClip})
if( x + width < compWidth && y + height < compHeight )
return false;
// if component is not opaque, Swing uses the first opaque ancestor for painting
if( !c.isOpaque() ) {
int x2 = x;
int y2 = y;
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
x2 += p.getX();
y2 += p.getY();
if( p.isOpaque() ) {
// check whether repaint area does span to right or bottom edges
// of the opaque ancestor component
// (in this case, {@code devClip} is always larger than {@code usrClip})
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() )
return false;
break;
}
}
}
// check whether Special repaint is necessary for current scale factor
// (doing this check late because it temporary allocates some memory)
double scaleFactor = UIScale.getSystemScaleFactor( c.getGraphicsConfiguration() );
double fraction = scaleFactor - (int) scaleFactor;
if( fraction == 0 || fraction == 0.5 )
return false;
return true;
}
/**
* Installs a {@link HiDPIRepaintManager} on Windows when running in Java 9+,
* but only if default repaint manager is currently installed.
* <p>
* Invoke once on application startup.
* Compatible with all/other L&Fs.
*
* @since 3.5
*/
public static void installHiDPIRepaintManager() {
if( !SystemInfo.isJava_9_orLater || !SystemInfo.isWindows )
return;
RepaintManager manager = RepaintManager.currentManager( (Component) null );
if( manager.getClass() == RepaintManager.class )
RepaintManager.setCurrentManager( new HiDPIRepaintManager() );
}
/**
* Similar to {@link #repaint(Component, int, int, int, int)},
* but invokes callback instead of invoking {@link Component#repaint(int, int, int, int)}.
* <p>
* For use in custom repaint managers.
*
* @since 3.5
*/
public static void addDirtyRegion( JComponent c, int x, int y, int width, int height, DirtyRegionCallback callback ) {
if( needsSpecialRepaint( c, x, y, width, height ) ) {
int x2 = x + c.getX();
int y2 = y + c.getY();
for( Component p = c.getParent(); p != null; p = p.getParent() ) {
x2 += p.getX();
y2 += p.getY();
if( x2 + width < p.getWidth() && y2 + height < p.getHeight() && p instanceof JComponent ) {
callback.addDirtyRegion( (JComponent) p, x2, y2, width, height );
return;
}
}
}
callback.addDirtyRegion( c, x, y, width, height );
}
//---- interface DirtyRegionCallback --------------------------------------
/**
* For {@link HiDPIUtils#addDirtyRegion(JComponent, int, int, int, int, DirtyRegionCallback)}.
*
* @since 3.5
*/
public interface DirtyRegionCallback {
void addDirtyRegion( JComponent c, int x, int y, int w, int h );
}
//---- class HiDPIRepaintManager ------------------------------------------
/**
* A repaint manager that fixes a problem in Swing when repainting components
* at some scale factors (e.g. 125%, 175%, etc) on Windows.
* <p>
* Use {@link HiDPIUtils#installHiDPIRepaintManager()} to install it.
* <p>
* See {@link HiDPIUtils#repaint(Component, int, int, int, int)} for details.
*
* @since 3.5
*/
public static class HiDPIRepaintManager
extends RepaintManager
{
@Override
public void addDirtyRegion( JComponent c, int x, int y, int w, int h ) {
HiDPIUtils.addDirtyRegion( c, x, y, w, h, super::addDirtyRegion );
}
}
}