Compare commits

...

14 Commits
0.17 ... 0.18

Author SHA1 Message Date
Karl Tauber
6572198178 release 0.18 2019-11-10 10:06:59 +01:00
Karl Tauber
f69b3f56dd ToolTip: use anti-aliasing to render multi-line tooltips 2019-11-10 00:09:51 +01:00
Karl Tauber
c379f2f3b0 JIDE: added missing dependency to flatlaf-testing project 2019-11-09 18:51:52 +01:00
Karl Tauber
e7194e43b4 JIDE: README.md added and publishing added to build.gradle.kts 2019-11-09 18:44:31 +01:00
Karl Tauber
883b282cd8 JIDE: JideTabbedPane: hover tab event if mouse is over close button
unfortunately it is not possible to replace JIDEs arrow and close buttons with own implementations
2019-11-09 18:21:25 +01:00
Karl Tauber
7c2b2d7f26 JIDE: basic JideTabbedPane implementation 2019-11-09 17:23:55 +01:00
Karl Tauber
08f525de5f TabbedPane: content pane is no longer opaque and use antialiasing for painting separator and content border 2019-11-09 15:53:02 +01:00
Karl Tauber
433659a5df TabbedPane: no longer modify BasicTabbedPaneUI.contentBorderInsets in getContentBorderInsets() because this is useless and confusing 2019-11-09 13:57:39 +01:00
Karl Tauber
7f50a30b29 TabbedPane: reworked painting in scroll-tab-layout, so that the separator line now spans the whole width and is no longer interrupted by the scroll buttons 2019-11-09 10:58:31 +01:00
Karl Tauber
d5944779e8 TabbedPane: use FlatClientProperties for JTabbedPane.hasFullBorder client property 2019-11-08 23:01:33 +01:00
Karl Tauber
fdaea31475 JIDE: flatlaf-jide-oss subproject created 2019-11-08 15:51:28 +01:00
Karl Tauber
a66ebd29b4 update to Gradle 5.6.4
./gradlew wrapper --gradle-version=5.6.4
2019-11-07 18:24:33 +01:00
Karl Tauber
f3006467e9 TextField and TextArea: do not apply minimum width if columns property > 0 2019-10-30 15:14:23 +01:00
Karl Tauber
aa52af4c8f added FlatLaf.isDark() 2019-10-29 11:01:48 +01:00
31 changed files with 1340 additions and 77 deletions

View File

@@ -1,6 +1,18 @@
FlatLaf Change Log FlatLaf Change Log
================== ==================
## 0.18
- TextField and TextArea: Do not apply minimum width if `columns` property is
greater than zero.
- TabbedPane: In scroll-tab-layout, the separator line now spans the whole width
and is no longer interrupted by the scroll buttons.
- TabbedPane: Content pane is no longer opaque. Use antialiasing for painting
separator and content border.
- ToolTip: Use anti-aliasing to render multi-line tooltips.
- JIDE Common Layer: Support `JideTabbedPane`.
## 0.17 ## 0.17
- CheckBox: Support painting a third state (set client property - CheckBox: Support painting a third state (set client property

View File

@@ -35,7 +35,7 @@ build script:
groupId: com.formdev groupId: com.formdev
artifactId: flatlaf artifactId: flatlaf
version: 0.17 version: 0.18
Otherwise download `flatlaf-<version>.jar` here: Otherwise download `flatlaf-<version>.jar` here:
@@ -46,6 +46,7 @@ Addons
------ ------
- [SwingX](flatlaf-swingx) - [SwingX](flatlaf-swingx)
- [JIDE Common Layer](https://github.com/jidesoft/jide-oss)
Documentation Documentation

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
version = "0.17" version = "0.18"
allprojects { allprojects {
repositories { repositories {

View File

@@ -30,6 +30,8 @@ public interface FlatClientProperties
String SELECTED_STATE = "JButton.selectedState"; String SELECTED_STATE = "JButton.selectedState";
String SELECTED_STATE_INDETERMINATE = "indeterminate"; String SELECTED_STATE_INDETERMINATE = "indeterminate";
String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder";
/** /**
* Checks whether a client property of a component has the given value. * Checks whether a client property of a component has the given value.
*/ */

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a dark color scheme and looks like Darcula LaF. * A Flat LaF that has a dark color scheme and looks like Darcula LaF.
* *
* The UI defaults are loaded from FlatDarculaLaf.properties, FlatDarkLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatDarculaLaf public class FlatDarculaLaf

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a dark color scheme. * A Flat LaF that has a dark color scheme.
* *
* The UI defaults are loaded from FlatDarkLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatDarkLaf public class FlatDarkLaf
@@ -37,4 +39,9 @@ public class FlatDarkLaf
public String getDescription() { public String getDescription() {
return "Flat Dark Look and Feel"; return "Flat Dark Look and Feel";
} }
@Override
public boolean isDark() {
return true;
}
} }

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a light color scheme and looks like IntelliJ LaF. * A Flat LaF that has a light color scheme and looks like IntelliJ LaF.
* *
* The UI defaults are loaded from FlatIntelliJLaf.properties, FlatLightLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatIntelliJLaf public class FlatIntelliJLaf

View File

@@ -74,6 +74,8 @@ public abstract class FlatLaf
return getName(); return getName();
} }
public abstract boolean isDark();
@Override @Override
public boolean isNativeLookAndFeel() { public boolean isNativeLookAndFeel() {
return true; return true;

View File

@@ -19,6 +19,8 @@ package com.formdev.flatlaf;
/** /**
* A Flat LaF that has a light color scheme. * A Flat LaF that has a light color scheme.
* *
* The UI defaults are loaded from FlatLightLaf.properties and FlatLaf.properties
*
* @author Karl Tauber * @author Karl Tauber
*/ */
public class FlatLightLaf public class FlatLightLaf
@@ -37,4 +39,9 @@ public class FlatLightLaf
public String getDescription() { public String getDescription() {
return "Flat Light Look and Feel"; return "Flat Light Look and Feel";
} }
@Override
public boolean isDark() {
return false;
}
} }

View File

@@ -17,12 +17,19 @@
package com.formdev.flatlaf.ui; package com.formdev.flatlaf.ui;
import static com.formdev.flatlaf.util.UIScale.scale; import static com.formdev.flatlaf.util.UIScale.scale;
import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font; import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import javax.swing.JButton; import javax.swing.JButton;
@@ -108,7 +115,6 @@ public class FlatTabbedPaneUI
tabAreaInsets = scale( tabAreaInsets ); tabAreaInsets = scale( tabAreaInsets );
tabHeight = scale( tabHeight ); tabHeight = scale( tabHeight );
tabSelectionHeight = scale( tabSelectionHeight ); tabSelectionHeight = scale( tabSelectionHeight );
contentSeparatorHeight = scale( contentSeparatorHeight );
MigLayoutVisualPadding.install( tabPane, null ); MigLayoutVisualPadding.install( tabPane, null );
} }
@@ -135,7 +141,7 @@ public class FlatTabbedPaneUI
public void propertyChange( PropertyChangeEvent e ) { public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e ); super.propertyChange( e );
if( "JTabbedPane.hasFullBorder".equals( e.getPropertyName() ) ) { if( TABBED_PANE_HAS_FULL_BORDER.equals( e.getPropertyName() ) ) {
tabPane.revalidate(); tabPane.revalidate();
tabPane.repaint(); tabPane.repaint();
} }
@@ -176,70 +182,45 @@ public class FlatTabbedPaneUI
@Override @Override
protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) { protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) {
return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 // was added by superclass return super.calculateTabWidth( tabPlacement, tabIndex, metrics ) - 3 /* was added by superclass */;
+ (!isTopOrBottom( tabPlacement ) && isScrollTabLayout() ? contentSeparatorHeight : 0);
} }
@Override @Override
protected int calculateTabHeight( int tabPlacement, int tabIndex, int fontHeight ) { protected int calculateTabHeight( int tabPlacement, int tabIndex, int fontHeight ) {
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ ) return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, fontHeight ) - 2 /* was added by superclass */ );
+ (isTopOrBottom( tabPlacement ) && isScrollTabLayout() ? contentSeparatorHeight : 0);
} }
/** /**
* The content border insets are used to create a separator between tabs and content. * The content border insets are used to create a separator between tabs and content.
* Except in scroll tab policy, where the separator is painted in paintTabArea().
* If client property JTabbedPane.hasFullBorder is true, then the content border insets * If client property JTabbedPane.hasFullBorder is true, then the content border insets
* are also used for the border. * are also used for the border.
*/ */
@Override @Override
protected Insets getContentBorderInsets( int tabPlacement ) { protected Insets getContentBorderInsets( int tabPlacement ) {
boolean hasFullBorder = this.hasFullBorder || (tabPane.getClientProperty( "JTabbedPane.hasFullBorder" ) == Boolean.TRUE); boolean hasFullBorder = this.hasFullBorder || clientPropertyEquals( tabPane, TABBED_PANE_HAS_FULL_BORDER, true );
int sh = contentSeparatorHeight; int sh = scale( contentSeparatorHeight );
Insets insets = hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ); Insets insets = hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 );
if( isScrollTabLayout() ) Insets contentBorderInsets = new Insets( 0, 0, 0, 0 );
insets.top = 0;
rotateInsets( insets, contentBorderInsets, tabPlacement ); rotateInsets( insets, contentBorderInsets, tabPlacement );
return contentBorderInsets; return contentBorderInsets;
} }
@Override @Override
protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) { protected int getTabLabelShiftX( int tabPlacement, int tabIndex, boolean isSelected ) {
if( isScrollTabLayout() && !isTopOrBottom( tabPlacement ) ) {
float shift = contentSeparatorHeight / 2f;
return Math.round( tabPlacement == LEFT ? -shift : shift );
} else
return 0; return 0;
} }
@Override @Override
protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) { protected int getTabLabelShiftY( int tabPlacement, int tabIndex, boolean isSelected ) {
if( isScrollTabLayout() && isTopOrBottom( tabPlacement ) ) {
float shift = contentSeparatorHeight / 2f;
return Math.round( tabPlacement == TOP ? -shift : shift );
} else
return 0; return 0;
} }
@Override @Override
protected void paintTabArea( Graphics g, int tabPlacement, int selectedIndex ) { public void update( Graphics g, JComponent c ) {
if( isScrollTabLayout() ) { FlatUIUtils.setRenderingHints( (Graphics2D) g );
// paint separator between tabs and content
Rectangle bounds = g.getClipBounds();
g.setColor( contentAreaColor );
if( tabPlacement == TOP || tabPlacement == BOTTOM ) { super.update( g, c );
int y = (tabPlacement == TOP) ? bounds.y + bounds.height - contentSeparatorHeight : bounds.y;
g.fillRect( bounds.x, y, bounds.x + bounds.width, contentSeparatorHeight );
} else {
int x = (tabPlacement == LEFT) ? bounds.x + bounds.width - contentSeparatorHeight : bounds.x;
g.fillRect( x, bounds.y, contentSeparatorHeight, bounds.y + bounds.height );
}
}
super.paintTabArea( g, tabPlacement, selectedIndex );
} }
@Override @Override
@@ -275,19 +256,6 @@ public class FlatTabbedPaneUI
protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex, protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected ) int x, int y, int w, int h, boolean isSelected )
{ {
if( isScrollTabLayout() ) {
// make tab bounds smaller for separator between tabs and content
if( tabPlacement == TOP || tabPlacement == BOTTOM ) {
if( tabPlacement == BOTTOM )
y += contentSeparatorHeight;
h -= contentSeparatorHeight;
} else {
if( tabPlacement == RIGHT )
x += contentSeparatorHeight;
w -= contentSeparatorHeight;
}
}
// paint tab background // paint tab background
boolean enabled = tabPane.isEnabled(); boolean enabled = tabPane.isEnabled();
g.setColor( enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex g.setColor( enabled && tabPane.isEnabledAt( tabIndex ) && getRolloverTab() == tabIndex
@@ -302,8 +270,38 @@ public class FlatTabbedPaneUI
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected ) int x, int y, int w, int h, boolean isSelected )
{ {
if( !isSelected ) if( isSelected )
return; paintTabSelection( g, tabPlacement, x, y, w, h );
}
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
// increase clip bounds in scroll-tab-layout to paint over the separator line
Rectangle clipBounds = isScrollTabLayout() ? g.getClipBounds() : null;
if( clipBounds != null ) {
Rectangle newClipBounds = new Rectangle( clipBounds );
int contentSeparatorHeight = scale( this.contentSeparatorHeight );
switch( tabPlacement ) {
case TOP:
default:
newClipBounds.height += contentSeparatorHeight;
break;
case BOTTOM:
newClipBounds.y -= contentSeparatorHeight;
newClipBounds.height += contentSeparatorHeight;
break;
case LEFT:
newClipBounds.width += contentSeparatorHeight;
break;
case RIGHT:
newClipBounds.x -= contentSeparatorHeight;
newClipBounds.width += contentSeparatorHeight;
break;
}
g.setClip( newClipBounds );
}
g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor ); g.setColor( tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
@@ -330,13 +328,16 @@ public class FlatTabbedPaneUI
g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h ); g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h );
break; break;
} }
if( clipBounds != null )
g.setClip( clipBounds );
} }
/** /**
* Actually does the nearly the same as super.paintContentBorder() but * Actually does the nearly the same as super.paintContentBorder() but
* - content pane is always opaque
* - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly * - not using UIManager.getColor("TabbedPane.contentAreaColor") to be GUI builder friendly
* - not invoking paintContentBorder*Edge() methods * - not invoking paintContentBorder*Edge() methods
* - repaint selection
*/ */
@Override @Override
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) { protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
@@ -377,9 +378,34 @@ public class FlatTabbedPaneUI
h -= (y - insets.top); h -= (y - insets.top);
} }
// compute insets for separator or full border
boolean hasFullBorder = this.hasFullBorder || clientPropertyEquals( tabPane, TABBED_PANE_HAS_FULL_BORDER, true );
int sh = scale( contentSeparatorHeight * 100 ); // multiply by 100 because rotateInsets() does not use floats
Insets ci = new Insets( 0, 0, 0, 0 );
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
// paint content area // paint content area
g.setColor( contentAreaColor ); g.setColor( contentAreaColor );
g.fillRect( x, y, w, h ); Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Rectangle2D.Float( x, y, w, h ), false );
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
((Graphics2D)g).fill( path );
// repaint selection in scroll-tab-layout because it may be painted before
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
if( isScrollTabLayout() && selectedIndex >= 0 ) {
Component scrollableTabViewport = findComponentByClassName( tabPane,
BasicTabbedPaneUI.class.getName() + "$ScrollableTabViewport" );
if( scrollableTabViewport != null ) {
Rectangle tabRect = getTabBounds( tabPane, selectedIndex );
Shape oldClip = g.getClip();
g.setClip( scrollableTabViewport.getBounds() );
paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
g.setClip( oldClip );
}
}
} }
@Override @Override
@@ -392,7 +418,17 @@ public class FlatTabbedPaneUI
return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT; return tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
} }
private boolean isTopOrBottom( int tabPlacement ) { private Component findComponentByClassName( Container c, String className ) {
return tabPlacement == TOP || tabPlacement == BOTTOM; for( Component child : c.getComponents() ) {
if( className.equals( child.getClass().getName() ) )
return child;
if( child instanceof Container ) {
Component c2 = findComponentByClassName( (Container) child, className );
if( c2 != null )
return c2;
}
}
return null;
} }
} }

View File

@@ -21,6 +21,7 @@ import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics; import java.awt.Graphics;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JTextArea;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
@@ -81,15 +82,19 @@ public class FlatTextAreaUI
@Override @Override
public Dimension getPreferredSize( JComponent c ) { public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ) ); return applyMinimumWidth( super.getPreferredSize( c ), c );
} }
@Override @Override
public Dimension getMinimumSize( JComponent c ) { public Dimension getMinimumSize( JComponent c ) {
return applyMinimumWidth( super.getMinimumSize( c ) ); return applyMinimumWidth( super.getMinimumSize( c ), c );
} }
private Dimension applyMinimumWidth( Dimension size ) { private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
// do not apply minimum width if JTextArea.columns is set
if( c instanceof JTextArea && ((JTextArea)c).getColumns() > 0 )
return size;
// Assume that text area is in a scroll pane (that displays the border) // Assume that text area is in a scroll pane (that displays the border)
// and subtract 1px border line width. // and subtract 1px border line width.
// Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding // Using "(scale( 1 ) * 2)" instead of "scale( 2 )" to deal with rounding

View File

@@ -25,6 +25,7 @@ import java.awt.event.FocusListener;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JSpinner; import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.LookAndFeel; import javax.swing.LookAndFeel;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
@@ -137,6 +138,10 @@ public class FlatTextFieldUI
} }
private Dimension applyMinimumWidth( Dimension size, JComponent c ) { private Dimension applyMinimumWidth( Dimension size, JComponent c ) {
// do not apply minimum width if JTextField.columns is set
if( c instanceof JTextField && ((JTextField)c).getColumns() > 0 )
return size;
Container parent = c.getParent(); Container parent = c.getParent();
if( parent instanceof JComboBox || if( parent instanceof JComboBox ||
parent instanceof JSpinner || parent instanceof JSpinner ||

View File

@@ -81,7 +81,7 @@ public class FlatToolTipUI
boolean leftToRight = (comp != null ? comp : c).getComponentOrientation().isLeftToRight(); boolean leftToRight = (comp != null ? comp : c).getComponentOrientation().isLeftToRight();
for( String line : lines ) { for( String line : lines ) {
y += lineHeight; y += lineHeight;
g.drawString( line, leftToRight ? x : x2 - SwingUtilities.computeStringWidth( fm, line ), y ); FlatUIUtils.drawString( c, g, line, leftToRight ? x : x2 - SwingUtilities.computeStringWidth( fm, line ), y );
} }
} else } else
super.paint( g, c ); super.paint( g, c );

View File

@@ -73,6 +73,14 @@ public class FlatUIUtils
dim.height + insets.top + insets.bottom ); dim.height + insets.top + insets.bottom );
} }
public static Insets addInsets( Insets insets1, Insets insets2 ) {
return new Insets(
insets1.top + insets2.top,
insets1.left + insets2.left,
insets1.bottom + insets2.bottom,
insets1.right + insets2.right );
}
public static Color getUIColor( String key, int defaultColorRGB ) { public static Color getUIColor( String key, int defaultColorRGB ) {
Color color = UIManager.getColor( key ); Color color = UIManager.getColor( key );
return (color != null) ? color : new Color( defaultColorRGB ); return (color != null) ? color : new Color( defaultColorRGB );
@@ -248,6 +256,22 @@ public class FlatUIUtils
} }
/** /**
* Draws the given string at the specified location using text properties
* and anti-aliasing hints from the provided component.
*
* Use this method instead of Graphics.drawString() for correct anti-aliasing.
*
* Replacement for SwingUtilities2.drawString()
*/
public static void drawString( JComponent c, Graphics g, String text, int x, int y ) {
JavaCompatibility.drawStringUnderlineCharAt( c, g, text, -1, x, y );
}
/**
* Draws the given string at the specified location underlining the specified
* character. The provided component is used to query text properties and
* anti-aliasing hints.
*
* Replacement for SwingUtilities2.drawStringUnderlineCharAt() * Replacement for SwingUtilities2.drawStringUnderlineCharAt()
*/ */
public static void drawStringUnderlineCharAt( JComponent c, Graphics g, public static void drawStringUnderlineCharAt( JComponent c, Graphics g,

View File

@@ -24,8 +24,9 @@ import java.awt.image.BufferedImage;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.LookAndFeel;
import javax.swing.UIManager; import javax.swing.UIManager;
import com.formdev.flatlaf.FlatDarkLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.FlatUIUtils;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
import com.kitfox.svg.app.beans.SVGIcon; import com.kitfox.svg.app.beans.SVGIcon;
@@ -129,6 +130,7 @@ public class ScaledSVGIcon
} }
private static void lafChanged() { private static void lafChanged() {
darkLaf = (UIManager.getLookAndFeel() instanceof FlatDarkLaf); LookAndFeel lookAndFeel = UIManager.getLookAndFeel();
darkLaf = (lookAndFeel instanceof FlatLaf && ((FlatLaf)lookAndFeel).isDark());
} }
} }

View File

@@ -4,6 +4,7 @@
package com.formdev.flatlaf.demo; package com.formdev.flatlaf.demo;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
import java.awt.*; import java.awt.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
@@ -30,10 +31,10 @@ class TabsPanel
private void hasFullBorderChanged() { private void hasFullBorderChanged() {
Boolean hasFullBorder = hasFullBorderCheckBox.isSelected() ? true : null; Boolean hasFullBorder = hasFullBorderCheckBox.isSelected() ? true : null;
tabbedPane1.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane1.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane2.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane2.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane3.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane3.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane4.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane4.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
} }
private void moreTabsChanged() { private void moreTabsChanged() {

View File

@@ -0,0 +1,37 @@
FlatLaf addon for JIDE Common Layer
===================================
This addon for FlatLaf adds support for **some**
[JIDE Common Layer](https://github.com/jidesoft/jide-oss) components.
Following JIDE Common Layer components are currently supported by this addon:
- `JideTabbedPane`
**Note**: We have currently no plans to support additional components. If you're
a paid customer of JFormDesigner and need support for other components, email to
JFormDesigner support and ask for it. Otherwise, try to implement it yourself
and create a pull request.
Download
--------
FlatLaf for JIDE Common Layer binaries are available on **JCenter** and **Maven
Central**.
If you use Maven or Gradle, add a dependency with following coordinates to your
build script:
groupId: com.formdev
artifactId: flatlaf-jide-oss
version: 0.18
Otherwise download `flatlaf-jide-oss-<version>.jar` here:
[![Download](https://api.bintray.com/packages/jformdesigner/flatlaf/flatlaf-jide-oss/images/download.svg)](https://bintray.com/jformdesigner/flatlaf/flatlaf-jide-oss/_latestVersion)
You also need `flatlaf-<version>.jar`, which you can download here:
[![Download](https://api.bintray.com/packages/jformdesigner/flatlaf/flatlaf/images/download.svg)](https://bintray.com/jformdesigner/flatlaf/flatlaf/_latestVersion)

View File

@@ -0,0 +1,121 @@
/*
* 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.
*/
version = rootProject.version
plugins {
`java-library`
`maven-publish`
id( "com.jfrog.bintray" ) version "1.8.4"
}
dependencies {
implementation( project( ":flatlaf-core" ) )
implementation( "com.jidesoft:jide-oss:3.6.18" )
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks {
assemble {
dependsOn(
"sourcesJar",
"javadocJar"
)
}
javadoc {
options {
this as StandardJavadocDocletOptions
tags = listOf( "uiDefault", "clientProperty" )
}
isFailOnError = false
}
register( "sourcesJar", Jar::class ) {
archiveClassifier.set( "sources" )
from( sourceSets.main.get().allJava )
}
register( "javadocJar", Jar::class ) {
archiveClassifier.set( "javadoc" )
from( javadoc )
}
}
publishing {
publications {
create<MavenPublication>( "maven" ) {
artifactId = "flatlaf-jide-oss"
groupId = "com.formdev"
from( components["java"] )
artifact( tasks["sourcesJar"] )
artifact( tasks["javadocJar"] )
pom {
name.set( "FlatLaf addon for JIDE Common Layer" )
description.set( "Flat Look and Feel addon for JIDE Common Layer" )
url.set( "https://github.com/JFormDesigner/FlatLaf" )
licenses {
license {
name.set( "The Apache License, Version 2.0" )
url.set( "http://www.apache.org/licenses/LICENSE-2.0.txt" )
}
}
developers {
developer {
name.set( "Karl Tauber" )
organization.set( "FormDev Software GmbH" )
organizationUrl.set( "https://www.formdev.com/" )
}
}
scm {
url.set( "https://github.com/JFormDesigner/FlatLaf" )
}
}
}
}
}
bintray {
user = System.getenv( "BINTRAY_USER" ) ?: System.getProperty( "bintray.user" )
key = System.getenv( "BINTRAY_KEY" ) ?: System.getProperty( "bintray.key" )
setPublications( "maven" )
with( pkg ) {
repo = "flatlaf"
name = "flatlaf-jide-oss"
setLicenses( "Apache-2.0" )
vcsUrl = "https://github.com/JFormDesigner/FlatLaf"
with( version ) {
name = project.version.toString()
}
publish = true
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.jideoss;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.swing.UIDefaults;
import com.formdev.flatlaf.FlatDefaultsAddon;
import com.formdev.flatlaf.FlatLaf;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.LookAndFeelFactory.UIDefaultsCustomizer;
import com.jidesoft.plaf.LookAndFeelFactory.UIDefaultsInitializer;
/**
* JIDE Common Layer addon for FlatLaf.
*
* @author Karl Tauber
*/
public class FlatJideOssDefaultsAddon
extends FlatDefaultsAddon
{
/**
* Finds JIDE Common Layer addon .properties file for the given LaF class
* in the same package as this class.
*/
@Override
public InputStream getDefaults( Class<?> lafClass ) {
LookAndFeelFactory.registerDefaultInitializer( FlatLaf.class.getName(), FlatJideUIDefaultsCustomizer.class.getName() );
LookAndFeelFactory.registerDefaultCustomizer( FlatLaf.class.getName(), FlatJideUIDefaultsCustomizer.class.getName() );
Class<?> addonClass = this.getClass();
String propertiesName = "/" + addonClass.getPackage().getName().replace( '.', '/' )
+ '/' + lafClass.getSimpleName() + ".properties";
return addonClass.getResourceAsStream( propertiesName );
}
//---- class FlatJideUIDefaultsCustomizer ---------------------------------
/**
* Because JIDE overwrites our UI defaults (from properties files) with its
* own UI defaults, we have to first remember our UI defaults in the initializer
* (invoked before JIDE overwrites UI defaults) and then restore them in the customizer.
*/
public static class FlatJideUIDefaultsCustomizer
implements UIDefaultsInitializer, UIDefaultsCustomizer
{
private static HashMap<Object, Object> jideDefaults;
@Override
public void initialize( UIDefaults defaults ) {
jideDefaults = new HashMap<>();
for( Map.Entry<Object, Object> e : defaults.entrySet() ) {
Object key = e.getKey();
if( key instanceof String && ((String)key).startsWith( "Jide" ) )
jideDefaults.put( key, e.getValue() );
}
}
@Override
public void customize( UIDefaults defaults ) {
if( jideDefaults != null ) {
defaults.putAll( jideDefaults );
jideDefaults = null;
}
}
}
}

View File

@@ -0,0 +1,372 @@
/*
* 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.jideoss.ui;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
import static com.formdev.flatlaf.FlatClientProperties.clientPropertyEquals;
import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.ui.FlatUIUtils;
import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.plaf.basic.BasicJideTabbedPaneUI;
import com.jidesoft.swing.JideTabbedPane;
/**
* Provides the Flat LaF UI delegate for {@link com.jidesoft.swing.JideTabbedPane}.
*
* @author Karl Tauber
*/
public class FlatJideTabbedPaneUI
extends BasicJideTabbedPaneUI
{
protected Color underlineColor;
protected Color disabledUnderlineColor;
protected Color hoverColor;
protected Color focusColor;
protected Color contentAreaColor;
protected int tabHeight;
protected int tabSelectionHeight;
protected int contentSeparatorHeight;
protected boolean hasFullBorder;
protected boolean tabsOverlapBorder;
public static ComponentUI createUI( JComponent c ) {
return new FlatJideTabbedPaneUI();
}
@Override
protected void installDefaults() {
super.installDefaults();
_background = UIDefaultsLookup.getColor( "JideTabbedPane.background" );
underlineColor = UIManager.getColor( "TabbedPane.underlineColor" );
disabledUnderlineColor = UIManager.getColor( "TabbedPane.disabledUnderlineColor" );
hoverColor = UIManager.getColor( "TabbedPane.hoverColor" );
focusColor = UIManager.getColor( "TabbedPane.focusColor" );
contentAreaColor = UIManager.getColor( "TabbedPane.contentAreaColor" );
tabHeight = UIManager.getInt( "TabbedPane.tabHeight" );
tabSelectionHeight = UIManager.getInt( "TabbedPane.tabSelectionHeight" );
contentSeparatorHeight = UIManager.getInt( "TabbedPane.contentSeparatorHeight" );
hasFullBorder = UIManager.getBoolean( "TabbedPane.hasFullBorder" );
tabsOverlapBorder = UIManager.getBoolean( "TabbedPane.tabsOverlapBorder" );
// scale
_textIconGap = scale( _textIconGap );
tabHeight = scale( tabHeight );
tabSelectionHeight = scale( tabSelectionHeight );
}
@Override
protected void uninstallDefaults() {
super.uninstallDefaults();
underlineColor = null;
disabledUnderlineColor = null;
hoverColor = null;
focusColor = null;
contentAreaColor = null;
}
@Override
protected PropertyChangeListener createPropertyChangeListener() {
return new PropertyChangeHandler() {
@Override
public void propertyChange( PropertyChangeEvent e ) {
super.propertyChange( e );
String propertyName = e.getPropertyName();
if( JideTabbedPane.PROPERTY_SELECTED_INDEX.equals( propertyName ) ) {
repaintTab( (Integer) e.getOldValue() );
repaintTab( (Integer) e.getNewValue() );
} else if( FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER.equals( propertyName ) ) {
_tabPane.revalidate();
_tabPane.repaint();
}
}
};
}
private void repaintTab( int tabIndex ) {
if( tabIndex < 0 || tabIndex >= _tabPane.getTabCount() )
return;
Rectangle r = getTabBounds( _tabPane, tabIndex );
if( r != null )
_tabPane.repaint( r );
}
@Override
protected MouseListener createMouseListener() {
return new RolloverMouseHandler();
}
@Override
protected MouseMotionListener createMouseMotionListener() {
return new RolloverMouseMotionHandler();
}
@Override
protected int calculateTabHeight( int tabPlacement, int tabIndex, FontMetrics metrics ) {
return Math.max( tabHeight, super.calculateTabHeight( tabPlacement, tabIndex, metrics ) );
}
@Override
protected int calculateTabWidth( int tabPlacement, int tabIndex, FontMetrics metrics ) {
return Math.max( tabHeight, super.calculateTabWidth( tabPlacement, tabIndex, metrics ) );
}
@Override
protected Insets getTabInsets( int tabPlacement, int tabIndex ) {
return scale( super.getTabInsets( tabPlacement, tabIndex ) );
}
@Override
protected Insets getSelectedTabPadInsets( int tabPlacement ) {
return scale( super.getSelectedTabPadInsets( tabPlacement ) );
}
@Override
protected Insets getTabAreaInsets( int tabPlacement ) {
return scale( super.getTabAreaInsets( tabPlacement ) );
}
@Override
protected int getTabShape() {
return JideTabbedPane.SHAPE_BOX;
}
/**
* The content border insets are used to create a separator between tabs and content.
* If client property JTabbedPane.hasFullBorder is true, then the content border insets
* are also used for the border.
*/
@Override
protected Insets getContentBorderInsets( int tabPlacement ) {
return FlatUIUtils.addInsets( getContentBorderInsets0( tabPlacement ),
scale( super.getContentBorderInsets( tabPlacement ) ) );
}
private Insets getContentBorderInsets0( int tabPlacement ) {
boolean hasFullBorder = this.hasFullBorder || clientPropertyEquals( _tabPane, TABBED_PANE_HAS_FULL_BORDER, true );
int sh = scale( contentSeparatorHeight );
Insets insets = hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 );
Insets contentBorderInsets = new Insets( 0, 0, 0, 0 );
rotateInsets( insets, contentBorderInsets, tabPlacement );
return contentBorderInsets;
}
@Override
public void update( Graphics g, JComponent c ) {
FlatUIUtils.setRenderingHints( (Graphics2D) g );
super.update( g, c );
}
@Override
public void paint( Graphics g, JComponent c ) {
super.paint( g, c );
// must paint tab area after content border was painted
if( !scrollableTabLayoutEnabled() && _tabPane.getTabCount() > 0 )
paintTabArea( g, _tabPane.getTabPlacement(), _tabPane.getSelectedIndex(), c );
}
@Override
protected void paintTabBackground( Graphics g, int tabPlacement, int tabIndex,
int x, int y, int w, int h, boolean isSelected )
{
// paint tab background
boolean enabled = _tabPane.isEnabled();
g.setColor( enabled && _tabPane.isEnabledAt( tabIndex ) &&
(_indexMouseOver == tabIndex || (_closeButtons != null && ((JideTabbedPane.NoFocusButton)_closeButtons[tabIndex]).isMouseOver()))
? hoverColor
: (enabled && isSelected && _tabPane.hasFocus()
? focusColor
: _tabPane.getBackgroundAt( tabIndex )) );
g.fillRect( x, y, w, h );
}
@Override
protected void paintTabBorder( Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h,
boolean isSelected )
{
if( isSelected )
paintTabSelection( g, tabPlacement, x, y, w, h );
}
protected void paintTabSelection( Graphics g, int tabPlacement, int x, int y, int w, int h ) {
// increase clip bounds in scroll-tab-layout to paint over the separator line
Rectangle clipBounds = scrollableTabLayoutEnabled() ? g.getClipBounds() : null;
if( clipBounds != null ) {
Rectangle newClipBounds = new Rectangle( clipBounds );
int contentSeparatorHeight = scale( this.contentSeparatorHeight );
switch( tabPlacement ) {
case TOP:
default:
newClipBounds.height += contentSeparatorHeight;
break;
case BOTTOM:
newClipBounds.y -= contentSeparatorHeight;
newClipBounds.height += contentSeparatorHeight;
break;
case LEFT:
newClipBounds.width += contentSeparatorHeight;
break;
case RIGHT:
newClipBounds.x -= contentSeparatorHeight;
newClipBounds.width += contentSeparatorHeight;
break;
}
g.setClip( newClipBounds );
}
g.setColor( _tabPane.isEnabled() ? underlineColor : disabledUnderlineColor );
Insets contentInsets = getContentBorderInsets0( tabPlacement );
// paint underline selection
switch( tabPlacement ) {
case TOP:
default:
int sy = y + h + contentInsets.top - tabSelectionHeight;
g.fillRect( x, sy, w, tabSelectionHeight );
break;
case BOTTOM:
g.fillRect( x, y - contentInsets.bottom, w, tabSelectionHeight );
break;
case LEFT:
int sx = x + w + contentInsets.left - tabSelectionHeight;
g.fillRect( sx, y, tabSelectionHeight, h );
break;
case RIGHT:
g.fillRect( x - contentInsets.right, y, tabSelectionHeight, h );
break;
}
if( clipBounds != null )
g.setClip( clipBounds );
}
/**
* Actually does the nearly the same as super.paintContentBorder() but
* - not invoking paintContentBorder*Edge() methods
* - repaint selection
*/
@Override
protected void paintContentBorder( Graphics g, int tabPlacement, int selectedIndex ) {
if( _tabPane.getTabCount() <= 0 )
return;
Insets insets = _tabPane.getInsets();
Insets tabAreaInsets = getTabAreaInsets( tabPlacement );
int x = insets.left;
int y = insets.top;
int w = _tabPane.getWidth() - insets.right - insets.left;
int h = _tabPane.getHeight() - insets.top - insets.bottom;
Dimension lsize = isTabLeadingComponentVisible() ? _tabLeadingComponent.getPreferredSize() : new Dimension();
Dimension tsize = isTabTrailingComponentVisible() ? _tabTrailingComponent.getPreferredSize() : new Dimension();
// remove tabs from bounds
switch( tabPlacement ) {
case LEFT:
x += Math.max( calculateTabAreaWidth( tabPlacement, _runCount, _maxTabWidth ),
Math.max( lsize.width, tsize.width ) );
if( tabsOverlapBorder )
x -= tabAreaInsets.right;
w -= (x - insets.left);
break;
case RIGHT:
w -= calculateTabAreaWidth( tabPlacement, _runCount, _maxTabWidth );
if( tabsOverlapBorder )
w += tabAreaInsets.left;
break;
case BOTTOM:
h -= calculateTabAreaHeight( tabPlacement, _runCount, _maxTabHeight );
if( tabsOverlapBorder )
h += tabAreaInsets.top;
break;
case TOP:
default:
y += Math.max( calculateTabAreaHeight( tabPlacement, _runCount, _maxTabHeight ),
Math.max( lsize.height, tsize.height ) );
if( tabsOverlapBorder )
y -= tabAreaInsets.bottom;
h -= (y - insets.top);
}
// compute insets for separator or full border
boolean hasFullBorder = this.hasFullBorder || clientPropertyEquals( _tabPane, TABBED_PANE_HAS_FULL_BORDER, true );
int sh = scale( contentSeparatorHeight * 100 ); // multiply by 100 because rotateInsets() does not use floats
Insets ci = new Insets( 0, 0, 0, 0 );
rotateInsets( hasFullBorder ? new Insets( sh, sh, sh, sh ) : new Insets( sh, 0, 0, 0 ), ci, tabPlacement );
// paint content area
g.setColor( contentAreaColor );
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Rectangle2D.Float( x, y, w, h ), false );
path.append( new Rectangle2D.Float( x + (ci.left / 100f), y + (ci.top / 100f),
w - (ci.left / 100f) - (ci.right / 100f), h - (ci.top / 100f) - (ci.bottom / 100f) ), false );
((Graphics2D)g).fill( path );
// repaint selection in scroll-tab-layout because it may be painted before
// the content border was painted (from BasicTabbedPaneUI$ScrollableTabPanel)
if( scrollableTabLayoutEnabled() && selectedIndex >= 0 && _tabScroller != null && _tabScroller.viewport != null ) {
Rectangle tabRect = getTabBounds( _tabPane, selectedIndex );
Shape oldClip = g.getClip();
g.setClip( _tabScroller.viewport.getBounds() );
paintTabSelection( g, tabPlacement, tabRect.x, tabRect.y, tabRect.width, tabRect.height );
g.setClip( oldClip );
}
}
@Override
protected void paintFocusIndicator( Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex,
Rectangle iconRect, Rectangle textRect, boolean isSelected )
{
}
}

View File

@@ -0,0 +1 @@
com.formdev.flatlaf.jideoss.FlatJideOssDefaultsAddon

View File

@@ -0,0 +1,32 @@
#
# 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.
#
#---- UI delegates ----
JideTabbedPaneUI=com.formdev.flatlaf.jideoss.ui.FlatJideTabbedPaneUI
#---- JideTabbedPane ----
JideTabbedPane.background=@background
JideTabbedPane.foreground=@foreground
JideTabbedPane.tabAreaBackground=@background
JideTabbedPane.tabInsets=@@TabbedPane.tabInsets
JideTabbedPane.tabAreaInsets=@@TabbedPane.tabAreaInsets
JideTabbedPane.contentBorderInsets=0,0,0,0
JideTabbedPane.tabRunOverlay=@@TabbedPane.tabRunOverlay
JideTabbedPane.shadow=@@TabbedPane.shadow

View File

@@ -6,8 +6,8 @@ This addon for FlatLaf adds support for **some** widely used SwingX components.
Many SwingX components that do not use UI delegates (e.g. `JXButton`, `JXLabel`, Many SwingX components that do not use UI delegates (e.g. `JXButton`, `JXLabel`,
`JXList`, etc) work with FlatLaf without adaptation. `JXList`, etc) work with FlatLaf without adaptation.
Following SwingX components, which use UI delegates, are supported by this Following SwingX components, which use UI delegates, are currently supported by
addon: this addon:
- `JXBusyLabel` - `JXBusyLabel`
- `JXDatePicker` - `JXDatePicker`
@@ -32,7 +32,7 @@ build script:
groupId: com.formdev groupId: com.formdev
artifactId: flatlaf-swingx artifactId: flatlaf-swingx
version: 0.17 version: 0.18
Otherwise download `flatlaf-swingx-<version>.jar` here: Otherwise download `flatlaf-swingx-<version>.jar` here:

View File

@@ -24,11 +24,13 @@ dependencies {
implementation( project( ":flatlaf-core" ) ) implementation( project( ":flatlaf-core" ) )
implementation( project( ":flatlaf-extras" ) ) implementation( project( ":flatlaf-extras" ) )
implementation( project( ":flatlaf-swingx" ) ) implementation( project( ":flatlaf-swingx" ) )
implementation( project( ":flatlaf-jide-oss" ) )
implementation( "com.miglayout:miglayout-swing:5.2" ) implementation( "com.miglayout:miglayout-swing:5.2" )
implementation( "com.jgoodies:jgoodies-forms:1.9.0" ) implementation( "com.jgoodies:jgoodies-forms:1.9.0" )
implementation( "org.swinglabs.swingx:swingx-all:1.6.5-1" ) implementation( "org.swinglabs.swingx:swingx-all:1.6.5-1" )
implementation( "org.swinglabs.swingx:swingx-beaninfo:1.6.5-1" ) implementation( "org.swinglabs.swingx:swingx-beaninfo:1.6.5-1" )
implementation( "com.jidesoft:jide-oss:3.6.18" )
} }
java { java {

View File

@@ -4,6 +4,7 @@
package com.formdev.flatlaf.testing; package com.formdev.flatlaf.testing;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
import java.awt.*; import java.awt.*;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; import javax.swing.border.*;
@@ -37,10 +38,10 @@ public class FlatContainerTest
private void hasFullBorderChanged() { private void hasFullBorderChanged() {
Boolean hasFullBorder = hasFullBorderCheckBox.isSelected() ? true : null; Boolean hasFullBorder = hasFullBorderCheckBox.isSelected() ? true : null;
tabbedPane1.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane1.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane2.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane2.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane3.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane3.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane4.putClientProperty( "JTabbedPane.hasFullBorder", hasFullBorder ); tabbedPane4.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
} }
private void moreTabsChanged() { private void moreTabsChanged() {

View File

@@ -21,6 +21,8 @@ import com.formdev.flatlaf.FlatLaf;
/** /**
* A Flat LaF that has a test color scheme. * A Flat LaF that has a test color scheme.
* *
* The UI defaults are loaded from FlatTestLaf.properties and FlatLaf.properties
*
* Used to develop Flat LaF. * Used to develop Flat LaF.
* *
* @author Karl Tauber * @author Karl Tauber
@@ -37,4 +39,9 @@ public class FlatTestLaf
public String getDescription() { public String getDescription() {
return "Flat Test Look and Feel"; return "Flat Test Look and Feel";
} }
@Override
public boolean isDark() {
return false;
}
} }

View File

@@ -0,0 +1,294 @@
/*
* 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.testing.jideoss;
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_HAS_FULL_BORDER;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import com.formdev.flatlaf.testing.*;
import com.formdev.flatlaf.testing.FlatTestFrame;
import com.jgoodies.forms.layout.*;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.swing.*;
import net.miginfocom.swing.*;
/**
* @author Karl Tauber
*/
public class FlatJideOssTest
extends FlatTestPanel
{
public static void main( String[] args ) {
SwingUtilities.invokeLater( () -> {
FlatTestFrame frame = FlatTestFrame.create( args, "FlatJideOssTest" );
LookAndFeelFactory.installJideExtension();
frame.showFrame( FlatJideOssTest::new );
UIManager.addPropertyChangeListener( e -> {
if( "lookAndFeel".equals( e.getPropertyName() ) ) {
LookAndFeelFactory.installJideExtension();
}
} );
} );
}
FlatJideOssTest() {
initComponents();
}
private void tabScrollChanged() {
int tabLayoutPolicy = tabScrollCheckBox.isSelected() ? JTabbedPane.SCROLL_TAB_LAYOUT : JTabbedPane.WRAP_TAB_LAYOUT;
tabbedPane1.setTabLayoutPolicy( tabLayoutPolicy );
tabbedPane2.setTabLayoutPolicy( tabLayoutPolicy );
tabbedPane3.setTabLayoutPolicy( tabLayoutPolicy );
tabbedPane4.setTabLayoutPolicy( tabLayoutPolicy );
}
private void hasFullBorderChanged() {
Boolean hasFullBorder = hasFullBorderCheckBox.isSelected() ? true : null;
tabbedPane1.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane2.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane3.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
tabbedPane4.putClientProperty( TABBED_PANE_HAS_FULL_BORDER, hasFullBorder );
}
private void moreTabsChanged() {
boolean moreTabs = moreTabsCheckBox.isSelected();
addRemoveMoreTabs( tabbedPane1, moreTabs );
addRemoveMoreTabs( tabbedPane2, moreTabs );
addRemoveMoreTabs( tabbedPane3, moreTabs );
addRemoveMoreTabs( tabbedPane4, moreTabs );
}
private void addRemoveMoreTabs( JTabbedPane tabbedPane, boolean add ) {
if( add ) {
tabbedPane.addTab( "Tab 4", new JLabel( "tab 4" ) );
tabbedPane.addTab( "Tab 5", new JLabel( "tab 5" ) );
} else {
int tabCount = tabbedPane.getTabCount();
if( tabCount > 3 ) {
for( int i = 0; i < 2; i++ )
tabbedPane.removeTabAt( tabbedPane.getTabCount() - 1 );
}
}
}
private void initComponents() {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
JPanel panel9 = new JPanel();
JLabel tabbedPaneLabel = new JLabel();
tabbedPane1 = new JideTabbedPane();
JPanel panel1 = new JPanel();
JLabel label1 = new JLabel();
JPanel panel2 = new JPanel();
JLabel label2 = new JLabel();
tabbedPane3 = new JideTabbedPane();
JPanel panel5 = new JPanel();
JLabel label5 = new JLabel();
JPanel panel6 = new JPanel();
JLabel label6 = new JLabel();
tabbedPane2 = new JideTabbedPane();
JPanel panel3 = new JPanel();
JLabel label3 = new JLabel();
JPanel panel4 = new JPanel();
JLabel label4 = new JLabel();
tabbedPane4 = new JideTabbedPane();
JPanel panel7 = new JPanel();
JLabel label7 = new JLabel();
JPanel panel8 = new JPanel();
JLabel label8 = new JLabel();
JPanel panel14 = new JPanel();
moreTabsCheckBox = new JCheckBox();
tabScrollCheckBox = new JCheckBox();
hasFullBorderCheckBox = new JCheckBox();
CellConstraints cc = new CellConstraints();
//======== this ========
setLayout(new MigLayout(
"insets dialog,hidemode 3",
// columns
"[grow,fill]",
// rows
"[grow,fill]"));
//======== panel9 ========
{
panel9.setOpaque(false);
panel9.setLayout(new FormLayout(
"70dlu:grow, $lcgap, 70dlu:grow",
"pref, 2*($lgap, fill:70dlu:grow), $lgap, pref"));
//---- tabbedPaneLabel ----
tabbedPaneLabel.setText("JideTabbedPane:");
panel9.add(tabbedPaneLabel, cc.xy(1, 1));
//======== tabbedPane1 ========
{
//======== panel1 ========
{
panel1.setLayout(new FlowLayout());
//---- label1 ----
label1.setText("TOP");
panel1.add(label1);
}
tabbedPane1.addTab("Tab 1", panel1);
//======== panel2 ========
{
panel2.setBorder(new LineBorder(Color.magenta));
panel2.setLayout(new FlowLayout());
}
tabbedPane1.addTab("Tab 2", panel2);
//---- label2 ----
label2.setText("text");
tabbedPane1.addTab("Tab 3", label2);
}
panel9.add(tabbedPane1, cc.xy(1, 3));
//======== tabbedPane3 ========
{
tabbedPane3.setTabPlacement(SwingConstants.LEFT);
//======== panel5 ========
{
panel5.setLayout(new FlowLayout());
//---- label5 ----
label5.setText("LEFT");
panel5.add(label5);
}
tabbedPane3.addTab("Tab 1", panel5);
//======== panel6 ========
{
panel6.setBorder(new LineBorder(Color.magenta));
panel6.setLayout(new FlowLayout());
}
tabbedPane3.addTab("Tab 2", panel6);
//---- label6 ----
label6.setText("text");
tabbedPane3.addTab("Tab 3", label6);
}
panel9.add(tabbedPane3, cc.xy(3, 3));
//======== tabbedPane2 ========
{
tabbedPane2.setTabPlacement(SwingConstants.BOTTOM);
//======== panel3 ========
{
panel3.setLayout(new FlowLayout());
//---- label3 ----
label3.setText("BOTTOM");
panel3.add(label3);
}
tabbedPane2.addTab("Tab 1", panel3);
//======== panel4 ========
{
panel4.setBorder(new LineBorder(Color.magenta));
panel4.setLayout(new FlowLayout());
}
tabbedPane2.addTab("Tab 2", panel4);
tabbedPane2.setEnabledAt(1, false);
//---- label4 ----
label4.setText("text");
tabbedPane2.addTab("Tab 3", label4);
}
panel9.add(tabbedPane2, cc.xy(1, 5));
//======== tabbedPane4 ========
{
tabbedPane4.setTabPlacement(SwingConstants.RIGHT);
//======== panel7 ========
{
panel7.setLayout(new FlowLayout());
//---- label7 ----
label7.setText("RIGHT");
panel7.add(label7);
}
tabbedPane4.addTab("Tab 1", panel7);
//======== panel8 ========
{
panel8.setBorder(new LineBorder(Color.magenta));
panel8.setLayout(new FlowLayout());
}
tabbedPane4.addTab("Tab 2", panel8);
//---- label8 ----
label8.setText("text");
tabbedPane4.addTab("Tab 3", label8);
}
panel9.add(tabbedPane4, cc.xy(3, 5));
//======== panel14 ========
{
panel14.setOpaque(false);
panel14.setLayout(new MigLayout(
"insets 0,hidemode 3",
// columns
"[]" +
"[]" +
"[]",
// rows
"[center]"));
//---- moreTabsCheckBox ----
moreTabsCheckBox.setText("more tabs");
moreTabsCheckBox.setMnemonic('M');
moreTabsCheckBox.addActionListener(e -> moreTabsChanged());
panel14.add(moreTabsCheckBox, "cell 0 0");
//---- tabScrollCheckBox ----
tabScrollCheckBox.setText("tabLayoutPolicy = SCROLL");
tabScrollCheckBox.setMnemonic('S');
tabScrollCheckBox.setSelected(true);
tabScrollCheckBox.addActionListener(e -> tabScrollChanged());
panel14.add(tabScrollCheckBox, "cell 1 0,alignx left,growx 0");
//---- hasFullBorderCheckBox ----
hasFullBorderCheckBox.setText("JTabbedPane.hasFullBorder");
hasFullBorderCheckBox.setMnemonic('F');
hasFullBorderCheckBox.addActionListener(e -> hasFullBorderChanged());
panel14.add(hasFullBorderCheckBox, "cell 2 0,alignx left,growx 0");
}
panel9.add(panel14, cc.xywh(1, 7, 3, 1));
}
add(panel9, "cell 0 0");
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JideTabbedPane tabbedPane1;
private JideTabbedPane tabbedPane3;
private JideTabbedPane tabbedPane2;
private JideTabbedPane tabbedPane4;
private JCheckBox moreTabsCheckBox;
private JCheckBox tabScrollCheckBox;
private JCheckBox hasFullBorderCheckBox;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -0,0 +1,203 @@
JFDML JFormDesigner: "7.0.0.0.194" Java: "11.0.2" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
root: new FormRoot {
auxiliary() {
"JavaCodeGenerator.defaultVariableLocal": true
}
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets dialog,hidemode 3"
"$columnConstraints": "[grow,fill]"
"$rowConstraints": "[grow,fill]"
} ) {
name: "this"
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class com.jgoodies.forms.layout.FormLayout ) {
"$columnSpecs": "70dlu:grow, labelcompgap, 70dlu:grow"
"$rowSpecs": "pref, linegap, fill:70dlu:grow, linegap, fill:70dlu:grow, linegap, pref"
} ) {
name: "panel9"
"opaque": false
add( new FormComponent( "javax.swing.JLabel" ) {
name: "tabbedPaneLabel"
"text": "JideTabbedPane:"
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 1
} )
add( new FormContainer( "com.jidesoft.swing.JideTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane1"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel1"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
"text": "TOP"
} )
}, new FormLayoutConstraints( null ) {
"title": "Tab 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel2"
"border": &LineBorder0 new javax.swing.border.LineBorder( sfield java.awt.Color magenta, 1, false )
}, new FormLayoutConstraints( null ) {
"title": "Tab 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label2"
"text": "text"
}, new FormLayoutConstraints( null ) {
"title": "Tab 3"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 1
"gridY": 3
} )
add( new FormContainer( "com.jidesoft.swing.JideTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane3"
"tabPlacement": 2
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel5"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label5"
"text": "LEFT"
} )
}, new FormLayoutConstraints( null ) {
"title": "Tab 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel6"
"border": #LineBorder0
}, new FormLayoutConstraints( null ) {
"title": "Tab 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label6"
"text": "text"
}, new FormLayoutConstraints( null ) {
"title": "Tab 3"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 3
"gridY": 3
} )
add( new FormContainer( "com.jidesoft.swing.JideTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane2"
"tabPlacement": 3
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel3"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label3"
"text": "BOTTOM"
} )
}, new FormLayoutConstraints( null ) {
"title": "Tab 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel4"
"border": #LineBorder0
}, new FormLayoutConstraints( null ) {
"title": "Tab 2"
"enabled": false
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label4"
"text": "text"
}, new FormLayoutConstraints( null ) {
"title": "Tab 3"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 5
} )
add( new FormContainer( "com.jidesoft.swing.JideTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) {
name: "tabbedPane4"
"tabPlacement": 4
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel7"
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label7"
"text": "RIGHT"
} )
}, new FormLayoutConstraints( null ) {
"title": "Tab 1"
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.FlowLayout ) ) {
name: "panel8"
"border": #LineBorder0
}, new FormLayoutConstraints( null ) {
"title": "Tab 2"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label8"
"text": "text"
}, new FormLayoutConstraints( null ) {
"title": "Tab 3"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridX": 3
"gridY": 5
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "insets 0,hidemode 3"
"$columnConstraints": "[][][]"
"$rowConstraints": "[center]"
} ) {
name: "panel14"
"opaque": false
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "moreTabsCheckBox"
"text": "more tabs"
"mnemonic": 77
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "moreTabsChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "tabScrollCheckBox"
"text": "tabLayoutPolicy = SCROLL"
"mnemonic": 83
"selected": true
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "tabScrollChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "hasFullBorderCheckBox"
"text": "JTabbedPane.hasFullBorder"
"mnemonic": 70
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "hasFullBorderChanged", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 2 0,alignx left,growx 0"
} )
}, new FormLayoutConstraints( class com.jgoodies.forms.layout.CellConstraints ) {
"gridY": 7
"gridWidth": 3
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 0"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
"size": new java.awt.Dimension( 500, 500 )
} )
}
}

View File

@@ -17,6 +17,7 @@
#---- variables ---- #---- variables ----
@background=ccffcc @background=ccffcc
@foreground=ff0000
@selectionBackground=00aa00 @selectionBackground=00aa00
@selectionInactiveBackground=888888 @selectionInactiveBackground=888888
@selectionInactiveForeground=ffffff @selectionInactiveForeground=ffffff
@@ -29,7 +30,7 @@
#---- globals ---- #---- globals ----
*.background=@background *.background=@background
*.foreground=ff0000 *.foreground=@foreground
*.textBackground=ccffcc *.textBackground=ccffcc
*.textForeground=ff0000 *.textForeground=ff0000
*.caretForeground=0000ff *.caretForeground=0000ff

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -19,5 +19,6 @@ rootProject.name = "FlatLaf"
include( "flatlaf-core" ) include( "flatlaf-core" )
include( "flatlaf-extras" ) include( "flatlaf-extras" )
include( "flatlaf-swingx" ) include( "flatlaf-swingx" )
include( "flatlaf-jide-oss" )
include( "flatlaf-demo" ) include( "flatlaf-demo" )
include( "flatlaf-testing" ) include( "flatlaf-testing" )