Compare commits

...

7 Commits
3.5 ... 3.5.1

9 changed files with 179 additions and 34 deletions

View File

@@ -1,6 +1,23 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 3.5.1
#### Fixed bugs
- HTML: Fixed occasional cutoff wrapped text when using multi-line text in HTML
tags `<h1>`...`<h6>`, `<code>`, `<kbd>`, `<big>`, `<small>` or `<samp>`.
(issue #873; regression in 3.5)
- Popup: Fixed `UnsupportedOperationException: PERPIXEL_TRANSLUCENT translucency
is not supported` exception on Haiku OS when showing popup (partly) outside of
window. (issue #869)
- HiDPI: Fixed occasional wrong repaint areas when using
`HiDPIUtils.installHiDPIRepaintManager()`. (see PR #864)
- Added system property `flatlaf.useSubMenuSafeTriangle` to allow disabling
submenu safe triangle (PR #490) for
[SWTSwing](https://github.com/Chrriis/SWTSwing). (issue #870)
## 3.5 ## 3.5
#### New features and improvements #### New features and improvements
@@ -16,6 +33,13 @@ FlatLaf Change Log
#### Fixed bugs #### Fixed bugs
- macOS: Disabled rounded popup border (see PR #772) on macOS 14.4+ because it
may freeze the application and crash the macOS WindowServer process (reports
vary from Finder restarts to OS restarts). This is a temporary change until a
solution is found. See NetBeans issues
[apache/netbeans#7560](https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215)
and
[apache/netbeans#6647](https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442).
- FlatLaf window decorations: Window top border on Windows 10 in "full window - FlatLaf window decorations: Window top border on Windows 10 in "full window
content" mode was not fully repainted when activating or deactivating window. content" mode was not fully repainted when activating or deactivating window.
(issue #809) (issue #809)
@@ -70,13 +94,6 @@ FlatLaf Change Log
(some security software allows loading native library but blocks method (some security software allows loading native library but blocks method
invocation). invocation).
- macOS: Fixed crash when running in WebSwing. (issue #826; regression in 3.4) - macOS: Fixed crash when running in WebSwing. (issue #826; regression in 3.4)
- macOS: Disabled rounded popup border (see PR #772) on macOS 14.4+ because it
may freeze the application and crash the macOS WindowServer process (reports
vary from Finder restarts to OS restarts). This is a temporary change until a
solution is found. See NetBeans issues
[apache/netbeans#7560](https://github.com/apache/netbeans/issues/7560#issuecomment-2226439215)
and
[apache/netbeans#6647](https://github.com/apache/netbeans/issues/6647#issuecomment-2070124442).
#### Incompatibilities #### Incompatibilities

View File

@@ -1,5 +1,5 @@
#Signature file v4.1 #Signature file v4.1
#Version 3.5 #Version 3.5.1
CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties CLSS public abstract interface com.formdev.flatlaf.FlatClientProperties
fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType" fld public final static java.lang.String BUTTON_TYPE = "JButton.buttonType"
@@ -296,6 +296,7 @@ fld public final static java.lang.String UPDATE_UI_ON_SYSTEM_FONT_CHANGE = "flat
fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations" fld public final static java.lang.String USE_JETBRAINS_CUSTOM_DECORATIONS = "flatlaf.useJetBrainsCustomDecorations"
anno 0 java.lang.Deprecated() anno 0 java.lang.Deprecated()
fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary" fld public final static java.lang.String USE_NATIVE_LIBRARY = "flatlaf.useNativeLibrary"
fld public final static java.lang.String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle"
fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection" fld public final static java.lang.String USE_TEXT_Y_CORRECTION = "flatlaf.useTextYCorrection"
fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont" fld public final static java.lang.String USE_UBUNTU_FONT = "flatlaf.useUbuntuFont"
fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations" fld public final static java.lang.String USE_WINDOW_DECORATIONS = "flatlaf.useWindowDecorations"

View File

@@ -204,6 +204,15 @@ public interface FlatSystemProperties
*/ */
String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath"; String NATIVE_LIBRARY_PATH = "flatlaf.nativeLibraryPath";
/**
* Specifies whether safe triangle is used to improve usability of submenus.
* <p>
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
* <strong>Default</strong> {@code true}
* @since 3.5.1
*/
String USE_SUB_MENU_SAFE_TRIANGLE = "flatlaf.useSubMenuSafeTriangle";
/** /**
* Checks whether a system property is set and returns {@code true} if its value * Checks whether a system property is set and returns {@code true} if its value
* is {@code "true"} (case-insensitive), otherwise it returns {@code false}. * is {@code "true"} (case-insensitive), otherwise it returns {@code false}.

View File

@@ -45,6 +45,7 @@ import javax.swing.UIManager;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.LoggingFacade;
/** /**
* Improves usability of submenus by using a * Improves usability of submenus by using a
@@ -64,6 +65,7 @@ class SubMenuUsabilityHelper
// https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607 // https://github.com/apache/netbeans/issues/4231#issuecomment-1179616607
private static SubMenuUsabilityHelper instance; private static SubMenuUsabilityHelper instance;
private boolean eventQueuePushNotSupported;
private SubMenuEventQueue subMenuEventQueue; private SubMenuEventQueue subMenuEventQueue;
private SafeTrianglePainter safeTrianglePainter; private SafeTrianglePainter safeTrianglePainter;
private boolean changePending; private boolean changePending;
@@ -83,6 +85,9 @@ class SubMenuUsabilityHelper
if( instance != null ) if( instance != null )
return false; return false;
if( !FlatSystemProperties.getBoolean( FlatSystemProperties.USE_SUB_MENU_SAFE_TRIANGLE, true ) )
return false;
instance = new SubMenuUsabilityHelper(); instance = new SubMenuUsabilityHelper();
MenuSelectionManager.defaultManager().addChangeListener( instance ); MenuSelectionManager.defaultManager().addChangeListener( instance );
return true; return true;
@@ -99,7 +104,7 @@ class SubMenuUsabilityHelper
@Override @Override
public void stateChanged( ChangeEvent e ) { public void stateChanged( ChangeEvent e ) {
if( !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true )) if( eventQueuePushNotSupported || !FlatUIUtils.getUIBoolean( KEY_USE_SAFE_TRIANGLE, true ))
return; return;
// handle menu selection change later, but only once in case of temporary changes // handle menu selection change later, but only once in case of temporary changes
@@ -173,8 +178,29 @@ debug*/
targetBottomY = popupLocation.y + popupSize.height; targetBottomY = popupLocation.y + popupSize.height;
// install own event queue to suppress mouse events when mouse is moved within safe triangle // install own event queue to suppress mouse events when mouse is moved within safe triangle
if( subMenuEventQueue == null ) if( subMenuEventQueue == null ) {
subMenuEventQueue = new SubMenuEventQueue(); SubMenuEventQueue queue = new SubMenuEventQueue();
try {
Toolkit toolkit = Toolkit.getDefaultToolkit();
toolkit.getSystemEventQueue().push( queue );
// check whether push() worked
// (e.g. SWTSwing uses own event queue that does not support push())
if( toolkit.getSystemEventQueue() != queue ) {
eventQueuePushNotSupported = true;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", null );
return;
}
subMenuEventQueue = queue;
} catch( RuntimeException ex ) {
// catch runtime exception from EventQueue.push()
eventQueuePushNotSupported = true;
LoggingFacade.INSTANCE.logSevere( "FlatLaf: Failed to push submenu event queue. Disabling submenu safe triangle.", ex );
return;
}
}
// create safe triangle painter // create safe triangle painter
if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) ) if( safeTrianglePainter == null && UIManager.getBoolean( KEY_SHOW_SAFE_TRIANGLE ) )
@@ -247,8 +273,6 @@ debug*/
} }
} ); } );
timeoutTimer.setRepeats( false ); timeoutTimer.setRepeats( false );
Toolkit.getDefaultToolkit().getSystemEventQueue().push( this );
} }
void uninstall() { void uninstall() {

View File

@@ -19,13 +19,22 @@ package com.formdev.flatlaf.ui;
import java.awt.Color; import java.awt.Color;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.swing.AbstractButton;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JToolTip;
import javax.swing.plaf.basic.BasicHTML; import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document; import javax.swing.text.Document;
import javax.swing.text.LabelView; import javax.swing.text.LabelView;
import javax.swing.text.Style; import javax.swing.text.Style;
import javax.swing.text.StyleConstants; import javax.swing.text.StyleConstants;
import javax.swing.text.View; import javax.swing.text.View;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.StyleSheet; import javax.swing.text.html.StyleSheet;
@@ -42,7 +51,7 @@ public class FlatHTML
* which re-calculates font sizes based on current component font size. * which re-calculates font sizes based on current component font size.
* This is necessary for "absolute-size" keywords (e.g. "x-large") * This is necessary for "absolute-size" keywords (e.g. "x-large")
* for "font-size" attributes in default style sheet (see javax/swing/text/html/default.css). * for "font-size" attributes in default style sheet (see javax/swing/text/html/default.css).
* See also <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-size?retiredLocale=de#values">CSS font-size</a>. * See also <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-size#values">CSS font-size</a>.
* <p> * <p>
* This method should be invoked after {@link BasicHTML#updateRenderer(JComponent, String)}. * This method should be invoked after {@link BasicHTML#updateRenderer(JComponent, String)}.
*/ */
@@ -61,16 +70,86 @@ public class FlatHTML
// - if point size at index 7 is not 36, then probably HTML text contains BASE_SIZE rule // - if point size at index 7 is not 36, then probably HTML text contains BASE_SIZE rule
// - if point size at index 4 is equal to given font size, then it is not necessary to add BASE_SIZE rule // - if point size at index 4 is equal to given font size, then it is not necessary to add BASE_SIZE rule
StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet(); StyleSheet styleSheet = ((HTMLDocument)doc).getStyleSheet();
/*debug
for( int i = 1; i <= 7; i++ )
System.out.println( i+": "+ styleSheet.getPointSize( i ) );
debug*/
int fontBaseSize = c.getFont().getSize(); int fontBaseSize = c.getFont().getSize();
if( styleSheet.getPointSize( 7 ) != 36f || if( styleSheet.getPointSize( 7 ) != 36f ||
styleSheet.getPointSize( 4 ) == fontBaseSize ) styleSheet.getPointSize( 4 ) == fontBaseSize )
return; return;
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule() // check whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
styleSheet.addRule( "BASE_SIZE " + fontBaseSize ); if( !usesAbsoluteSizeKeywordForFontSize( view ) )
clearViewCaches( view ); return;
// dumpViews( view, 0 ); // get HTML text from component
String text;
if( c instanceof JLabel )
text = ((JLabel)c).getText();
else if( c instanceof AbstractButton )
text = ((AbstractButton)c).getText();
else if( c instanceof JToolTip )
text = ((JToolTip)c).getTipText();
else
return;
if( text == null )
return;
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
String style = "<style>BASE_SIZE " + c.getFont().getSize() + "</style>";
String openTag = "";
String closeTag = "";
String lowerText = text.toLowerCase( Locale.ENGLISH );
int headIndex;
int styleIndex;
int insertIndex;
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
// there is a <head> tag --> insert after <head> tag
insertIndex = headIndex + "<head>".length();
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
// there is a <style> tag --> insert before <style> tag
insertIndex = styleIndex;
} else {
// no <head> or <style> tag --> insert <head> tag after <html> tag
insertIndex = "<html>".length();
openTag = "<head>";
closeTag = "</head>";
}
String newText = text.substring( 0, insertIndex )
+ openTag + style + closeTag
+ text.substring( insertIndex );
BasicHTML.updateRenderer( c, newText );
}
private static final Set<String> absoluteSizeKeywordsSet = new HashSet<>( Arrays.asList(
"xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large" ) );
/**
* Checks whether view uses "absolute-size" keywords (e.g. "x-large") for font-size
* (see javax/swing/text/html/default.css).
*/
private static boolean usesAbsoluteSizeKeywordForFontSize( View view ) {
AttributeSet attributes = view.getAttributes();
if( attributes != null ) {
Object fontSize = attributes.getAttribute( CSS.Attribute.FONT_SIZE );
if( fontSize != null ) {
if( absoluteSizeKeywordsSet.contains( fontSize.toString() ) )
return true;
}
}
int viewCount = view.getViewCount();
for( int i = 0; i < viewCount; i++ ) {
if( usesAbsoluteSizeKeywordForFontSize( view.getView( i ) ) )
return true;
}
return false;
} }
/** /**
@@ -128,8 +207,8 @@ public class FlatHTML
* updates the HTML view. * updates the HTML view.
*/ */
public static void propertyChange( PropertyChangeEvent e ) { public static void propertyChange( PropertyChangeEvent e ) {
if( BasicHTML.propertyKey.equals( e.getPropertyName() ) ) if( BasicHTML.propertyKey.equals( e.getPropertyName() ) && e.getNewValue() instanceof View )
FlatHTML.updateRendererCSSFontBaseSize( (JComponent) e.getSource() ); updateRendererCSSFontBaseSize( (JComponent) e.getSource() );
} }
/*debug /*debug
@@ -142,15 +221,27 @@ public class FlatHTML
public static void dumpViews( View view, int indent ) { public static void dumpViews( View view, int indent ) {
for( int i = 0; i < indent; i++ ) for( int i = 0; i < indent; i++ )
System.out.print( " " ); System.out.print( " " );
System.out.print( view.getClass().isAnonymousClass() ? view.getClass().getName() : view.getClass().getSimpleName() );
if( view instanceof LabelView ) { System.out.printf( "%s @%-8x %3d,%2d",
LabelView lview = ((LabelView)view); view.getClass().isAnonymousClass() ? view.getClass().getName() : view.getClass().getSimpleName(),
Font font = lview.getFont(); System.identityHashCode( view ),
Color foreground = lview.getForeground(); (int) view.getPreferredSpan( View.X_AXIS ),
System.out.printf( " %2d-%-2d %-14s %d #%06x", (int) view.getPreferredSpan( View.Y_AXIS ) );
lview.getStartOffset(), lview.getEndOffset() - 1,
font.getName(), font.getSize(), AttributeSet attrs = view.getAttributes();
foreground.getRGB() & 0xffffff ); if( attrs != null ) {
Object fontSize = attrs.getAttribute( CSS.Attribute.FONT_SIZE );
System.out.printf( " %-8s", fontSize );
}
if( view instanceof javax.swing.text.GlyphView ) {
javax.swing.text.GlyphView gview = ((javax.swing.text.GlyphView)view);
java.awt.Font font = gview.getFont();
System.out.printf( " %3d-%-3d %s %2d (@%x) #%06x '%s'",
gview.getStartOffset(), gview.getEndOffset() - 1,
font.getName(), font.getSize(), System.identityHashCode( font ),
gview.getForeground().getRGB() & 0xffffff,
gview.getText( gview.getStartOffset(), gview.getEndOffset() ) );
} }
System.out.println(); System.out.println();

View File

@@ -139,7 +139,10 @@ public class FlatPopupFactory
forceHeavyWeight = true; forceHeavyWeight = true;
// create drop shadow popup // create drop shadow popup
return new DropShadowPopup( getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight ), owner, contents ); Popup popupForScreenOfOwner = getPopupForScreenOfOwner( owner, contents, x, y, forceHeavyWeight );
return owner.getGraphicsConfiguration().isTranslucencyCapable()
? new DropShadowPopup( popupForScreenOfOwner, owner, contents )
: new NonFlashingPopup( popupForScreenOfOwner, owner, contents );
} }
/** /**

View File

@@ -1685,7 +1685,7 @@ debug*/
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false ); w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
// add gap for selected tab to path // add gap for selected tab to path
if( getTabType() == TAB_TYPE_CARD ) { if( getTabType() == TAB_TYPE_CARD && selectedIndex >= 0 ) {
float csh = scale( (float) contentSeparatorHeight ); float csh = scale( (float) contentSeparatorHeight );
Rectangle tabRect = getTabBounds( tabPane, selectedIndex ); Rectangle tabRect = getTabBounds( tabPane, selectedIndex );

View File

@@ -521,12 +521,12 @@ public class HiDPIUtils
int x2 = x + c.getX(); int x2 = x + c.getX();
int y2 = y + c.getY(); int y2 = y + c.getY();
for( Component p = c.getParent(); p != null; p = p.getParent() ) { 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 ) { if( x2 + width < p.getWidth() && y2 + height < p.getHeight() && p instanceof JComponent ) {
callback.addDirtyRegion( (JComponent) p, x2, y2, width, height ); callback.addDirtyRegion( (JComponent) p, x2, y2, width, height );
return; return;
} }
x2 += p.getX();
y2 += p.getY();
} }
} }

View File

@@ -14,7 +14,7 @@
# limitations under the License. # limitations under the License.
# #
flatlaf.releaseVersion = 3.5 flatlaf.releaseVersion = 3.5.1
flatlaf.developmentVersion = 3.6-SNAPSHOT flatlaf.developmentVersion = 3.6-SNAPSHOT
org.gradle.parallel = true org.gradle.parallel = true