com.alee.utils.LafUtils Maven / Gradle / Ivy
/*
* This file is part of WebLookAndFeel library.
*
* WebLookAndFeel library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebLookAndFeel library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebLookAndFeel library. If not, see .
*/
package com.alee.utils;
import com.alee.extended.painter.AdaptivePainter;
import com.alee.extended.painter.Painter;
import com.alee.extended.painter.TexturePainter;
import com.alee.global.StyleConstants;
import com.alee.laf.WebLookAndFeel;
import com.alee.laf.label.WebLabel;
import com.alee.laf.scroll.WebScrollBarUI;
import com.alee.laf.text.WebTextField;
import com.alee.utils.laf.FocusType;
import com.alee.utils.laf.ShapeProvider;
import com.alee.utils.laf.Styleable;
import com.alee.utils.laf.WeblafBorder;
import com.alee.utils.ninepatch.NinePatchIcon;
import com.alee.utils.swing.BorderMethods;
import com.alee.utils.xml.ResourceFile;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ScrollBarUI;
import javax.swing.plaf.basic.BasicButtonUI;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
/**
* This class provides a set of utilities for core WebLaF components.
* Created mostly for internal usage within WebLaF.
*
* @author Mikle Garin
*/
public final class LafUtils
{
/**
* Predefined transforms to move shapes or painting 1px l/r/u/d.
*/
public static final AffineTransform moveLeft = new AffineTransform ()
{
{
translate ( -1, 0 );
}
};
public static final AffineTransform moveRight = new AffineTransform ()
{
{
translate ( 1, 0 );
}
};
public static final AffineTransform moveUp = new AffineTransform ()
{
{
translate ( 0, -1 );
}
};
public static final AffineTransform moveDown = new AffineTransform ()
{
{
translate ( 0, 1 );
}
};
/**
* Creates border for web components.
*/
public static Border createWebBorder ( final Insets insets )
{
return new WeblafBorder ( insets.top, insets.left, insets.bottom, insets.right );
}
public static Border createWebBorder ( final int top, final int left, final int bottom, final int right )
{
return new WeblafBorder ( top, left, bottom, right );
}
public static Border createWebBorder ( final int margin )
{
return new WeblafBorder ( margin, margin, margin, margin );
}
/**
* Returns the specified painter if it can be assigned to proper painter type.
* Otherwise returns newly created adapter painter that wraps the specified painter.
* Used by component UIs to adapt general-type painters for their specific-type needs.
*
* @param painter processed painter
* @param properClass proper painter class
* @param adapterClass adapter painter class
* @param proper painter type
* @return specified painter if it can be assigned to proper painter type, new painter adapter if it cannot be assigned
*/
public static T getProperPainter ( final Painter painter, final Class properClass, final Class adapterClass )
{
return painter == null ? null : ( ReflectUtils.isAssignable ( properClass, painter.getClass () ) ? ( T ) painter :
( T ) ReflectUtils.createInstanceSafely ( adapterClass, painter ) );
}
/**
* Returns either the specified painter if it is not an adapted painter or the adapted painter.
* Used by component UIs to retrieve painters adapted for their specific needs.
*
* @param painter painter to process
* @param desired painter type
* @return either the specified painter if it is not an adapted painter or the adapted painter
*/
public static T getAdaptedPainter ( final Painter painter )
{
return ( T ) ( painter != null && painter instanceof AdaptivePainter ? ( ( AdaptivePainter ) painter ).getPainter () : painter );
}
/**
* Fires painter property change event.
* This is a workaround since {@code firePropertyChange()} method is protected and cannot be called w/o using reflection.
*
* @param component component to fire property change to
* @param oldPainter old painter
* @param newPainter new painter
*/
public static void firePainterChanged ( final JComponent component, final Painter oldPainter, final Painter newPainter )
{
try
{
ReflectUtils.callMethod ( component, "firePropertyChange", WebLookAndFeel.PAINTER_PROPERTY, oldPainter, newPainter );
}
catch ( final NoSuchMethodException e )
{
e.printStackTrace ();
}
catch ( final InvocationTargetException e )
{
e.printStackTrace ();
}
catch ( final IllegalAccessException e )
{
e.printStackTrace ();
}
}
/**
* Sets scroll pane bars style ID.
*
* @param scrollPane scroll pane to process
* @param styleId scroll pane bars style ID
*/
public static void setScrollBarStyleId ( final JScrollPane scrollPane, final String styleId )
{
final JScrollBar vsb = scrollPane.getVerticalScrollBar ();
if ( vsb != null )
{
final ScrollBarUI vui = vsb.getUI ();
if ( vui instanceof WebScrollBarUI )
{
final WebScrollBarUI ui = ( WebScrollBarUI ) vui;
ui.setStyleId ( styleId );
}
}
final JScrollBar hsb = scrollPane.getHorizontalScrollBar ();
if ( hsb != null )
{
final ScrollBarUI hui = hsb.getUI ();
if ( hui instanceof WebScrollBarUI )
{
final WebScrollBarUI ui = ( WebScrollBarUI ) hui;
ui.setStyleId ( styleId );
}
}
}
/**
* Returns component UI or null if UI cannot be retreived.
*
* @param component component to retrieve UI from
* @param UI class type
* @return component UI or null if UI cannot be retreived
*/
public static T getUI ( final Component component )
{
return ReflectUtils.callMethodSafely ( component, "getUI" );
}
/**
* Updates component border using the specified margin
*
* @param component component which border needs to be updated
* @param margin component margin, or null if it doesn't have one
* @param painter component painter, or null if it doesn't have one
*/
public static void updateBorder ( final JComponent component, final Insets margin, final Painter painter )
{
if ( component != null )
{
// Preserve old borders
if ( SwingUtils.isPreserveBorders ( component ) )
{
return;
}
final boolean ltr = component.getComponentOrientation ().isLeftToRight ();
final Insets m = new Insets ( 0, 0, 0, 0 );
// Calculating margin borders
if ( margin != null )
{
m.top += margin.top;
m.left += ltr ? margin.left : margin.right;
m.bottom += margin.bottom;
m.right += ltr ? margin.right : margin.left;
}
// Calculating additional borders
if ( painter != null )
{
// Painter borders
final Insets pi = painter.getMargin ( component );
if ( pi != null )
{
m.top += pi.top;
m.left += ltr ? pi.left : pi.right;
m.bottom += pi.bottom;
m.right += ltr ? pi.right : pi.left;
}
}
// Installing border
component.setBorder ( LafUtils.createWebBorder ( m ) );
}
}
/**
* Fills either clipped or visible rect with component background color if its opaque
*/
public static void fillVisibleBackground ( final Graphics g, final JComponent c )
{
if ( c.isOpaque () )
{
g.setColor ( c.getBackground () );
fillVisible ( g, c );
}
}
/**
* Fills either clipped or visible rect
*/
public static void fillVisible ( final Graphics g, final JComponent c )
{
final Shape clip = g.getClip ();
final Rectangle rect = clip != null ? clip.getBounds () : c.getVisibleRect ();
g.fillRect ( rect.x, rect.y, rect.width, rect.height );
}
/**
* Nullifies button styles
*/
public static void nullifyButtonUI ( final JButton button )
{
button.setUI ( new BasicButtonUI () );
button.setMargin ( new Insets ( 0, 0, 0, 0 ) );
button.setBorder ( null );
button.setBorderPainted ( false );
button.setContentAreaFilled ( false );
button.setFocusable ( false );
button.setOpaque ( false );
}
/**
* Creates rounded shape based on its corner points
*/
public static Shape createRoundedShape ( final int round, final int... points )
{
if ( points == null || points.length % 2 != 0 )
{
throw new RuntimeException ( "Incorrect x,y combinations amount" );
}
final Point[] fp = new Point[ points.length / 2 ];
for ( int i = 0; i < points.length; i += 2 )
{
fp[ i / 2 ] = new Point ( points[ i ], points[ i + 1 ] );
}
return createRoundedShape ( round, fp );
}
public static Shape createRoundedShape ( final int round, final Point... points )
{
return createRoundedShape ( round, points, null );
}
public static Shape createRoundedShape ( final int round, final Point[] points, final boolean[] rounded )
{
if ( points == null || points.length < 3 )
{
throw new RuntimeException ( "There should be atleast three points presented" );
}
if ( rounded != null && rounded.length != points.length )
{
throw new RuntimeException ( "Rouned marks array size should fit points array size" );
}
final GeneralPath gp = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );
for ( int i = 0; i < points.length; i++ )
{
final Point p = points[ i ];
if ( i == 0 )
{
// Start part
final Point beforePoint = points[ points.length - 1 ];
if ( round == 0 || rounded != null && !rounded[ points.length - 1 ] )
{
gp.moveTo ( beforePoint.x, beforePoint.y );
}
else
{
final Point actualBeforePoint = getRoundSidePoint ( round, beforePoint, p );
gp.moveTo ( actualBeforePoint.x, actualBeforePoint.y );
}
if ( round == 0 || rounded != null && !rounded[ i ] )
{
gp.lineTo ( p.x, p.y );
}
else
{
final Point before = getRoundSidePoint ( round, p, beforePoint );
final Point after = getRoundSidePoint ( round, p, points[ i + 1 ] );
gp.lineTo ( before.x, before.y );
gp.quadTo ( p.x, p.y, after.x, after.y );
}
}
else
{
// Proceeding to next point
if ( round == 0 || rounded != null && !rounded[ i ] )
{
gp.lineTo ( p.x, p.y );
}
else
{
final Point before = getRoundSidePoint ( round, p, points[ i - 1 ] );
final Point after = getRoundSidePoint ( round, p, points[ i < points.length - 1 ? i + 1 : 0 ] );
gp.lineTo ( before.x, before.y );
gp.quadTo ( p.x, p.y, after.x, after.y );
}
}
}
return gp;
}
private static Point getRoundSidePoint ( final int round, final Point from, final Point to )
{
if ( from.y == to.y )
{
if ( from.x < to.x )
{
return new Point ( from.x + Math.min ( round, ( to.x - from.x ) / 2 ), from.y );
}
else
{
return new Point ( from.x - Math.min ( round, ( from.x - to.x ) / 2 ), from.y );
}
}
else if ( from.x == to.x )
{
if ( from.y < to.y )
{
return new Point ( from.x, from.y + Math.min ( round, ( to.y - from.y ) / 2 ) );
}
else
{
return new Point ( from.x, from.y - Math.min ( round, ( from.y - to.y ) / 2 ) );
}
}
else
{
// todo do for non-90-degree angles
return null;
}
}
/**
* Draws alpha-background
*/
public static void drawAlphaLayer ( final Graphics2D g2d, final Rectangle rectangle )
{
drawAlphaLayer ( g2d, rectangle.x, rectangle.y, rectangle.width, rectangle.height );
}
public static void drawAlphaLayer ( final Graphics2D g2d, final int x, final int y, final int width, final int height )
{
drawAlphaLayer ( g2d, x, y, width, height, StyleConstants.ALPHA_RECT_SIZE );
}
public static void drawAlphaLayer ( final Graphics2D g2d, final Rectangle rectangle, final int size )
{
drawAlphaLayer ( g2d, rectangle.x, rectangle.y, rectangle.width, rectangle.height, size );
}
public static void drawAlphaLayer ( final Graphics2D g2d, final int x, final int y, final int width, final int height, final int size )
{
drawAlphaLayer ( g2d, x, y, width, height, size, StyleConstants.LIGHT_ALPHA, StyleConstants.DARK_ALPHA );
}
public static void drawAlphaLayer ( final Graphics2D g2d, final Rectangle rectangle, final int size, final Color light,
final Color dark )
{
drawAlphaLayer ( g2d, rectangle.x, rectangle.y, rectangle.width, rectangle.height, size, light, dark );
}
public static void drawAlphaLayer ( final Graphics2D g2d, final int x, final int y, final int width, final int height, final int size,
final Color light, final Color dark )
{
// todo Optimize paint by using generated texture image
final int xAmount = width / size + 1;
final int yAmount = height / size + 1;
boolean lightColor;
for ( int i = 0; i < xAmount; i++ )
{
for ( int j = 0; j < yAmount; j++ )
{
lightColor = ( i + j ) % 2 == 0;
final Color color = lightColor ? light : dark;
if ( color != null )
{
g2d.setPaint ( color );
final int w = ( x + i * size + size > x + width ) ? ( width - i * size ) : size;
final int h = ( y + j * size + size > y + height ) ? ( height - j * size ) : size;
g2d.fillRect ( x + i * size, y + j * size, w, h );
}
}
}
}
/**
* Determines real text size
*/
public static Rectangle getTextBounds ( final String text, final Graphics g, final Font font )
{
return getTextBounds ( text, ( Graphics2D ) g, font );
}
public static Rectangle getTextBounds ( final String text, final Graphics2D g2d, final Font font )
{
final FontRenderContext renderContext = g2d.getFontRenderContext ();
final GlyphVector glyphVector = font.createGlyphVector ( renderContext, text );
return glyphVector.getVisualBounds ().getBounds ();
}
/**
* Paints web styled border within the component with shadow and background if needed
*/
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component )
{
return drawWebStyle ( g2d, component, StyleConstants.shadeColor, StyleConstants.shadeWidth, StyleConstants.smallRound );
}
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component, final Color shadeColor, final int shadeWidth,
final int round )
{
return drawWebStyle ( g2d, component, shadeColor, shadeWidth, round, true );
}
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component, final Color shadeColor, final int shadeWidth,
final int round, final boolean fillBackground )
{
return drawWebStyle ( g2d, component, shadeColor, shadeWidth, round, fillBackground, false );
}
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component, final Color shadeColor, final int shadeWidth,
final int round, final boolean fillBackground, final boolean webColored )
{
return drawWebStyle ( g2d, component, shadeColor, shadeWidth, round, fillBackground, webColored, StyleConstants.darkBorderColor,
StyleConstants.disabledBorderColor );
}
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component, final Color shadeColor, final int shadeWidth,
final int round, final boolean fillBackground, final boolean webColored, final float opacity )
{
return drawWebStyle ( g2d, component, shadeColor, shadeWidth, round, fillBackground, webColored, StyleConstants.darkBorderColor,
StyleConstants.disabledBorderColor, opacity );
}
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component, final Color shadeColor, final int shadeWidth,
final int round, final boolean fillBackground, final boolean webColored, final Color border )
{
return drawWebStyle ( g2d, component, shadeColor, shadeWidth, round, fillBackground, webColored, border, border );
}
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component, final Color shadeColor, final int shadeWidth,
final int round, final boolean fillBackground, final boolean webColored, final Color border,
final Color disabledBorder )
{
return drawWebStyle ( g2d, component, shadeColor, shadeWidth, round, fillBackground, webColored, border, disabledBorder, 1f );
}
public static Shape drawWebStyle ( final Graphics2D g2d, final JComponent component, final Color shadeColor, final int shadeWidth,
final int round, final boolean fillBackground, final boolean webColored, final Color border,
final Color disabledBorder, final float opacity )
{
// todo Use simple drawRoundRect e.t.c. methods
// todo Add new class "ShapeInfo" that will contain a shape data and pass it instead of shapes
// Ignore incorrect or zero opacity
if ( opacity <= 0f || opacity > 1f )
{
return null;
}
// State settings
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Composite oc = GraphicsUtils.setupAlphaComposite ( g2d, opacity, opacity < 1f );
// Shapes
final Shape borderShape = getWebBorderShape ( component, shadeWidth, round );
// Outer shadow
if ( component.isEnabled () && shadeColor != null )
{
GraphicsUtils.drawShade ( g2d, borderShape, shadeColor, shadeWidth );
}
// Background
if ( fillBackground )
{
// Setup either cached gradient paint or single color paint
g2d.setPaint ( webColored ? getWebGradientPaint ( 0, shadeWidth, 0, component.getHeight () - shadeWidth ) :
component.getBackground () );
if ( round > 0 )
{
g2d.fillRoundRect ( shadeWidth, shadeWidth, component.getWidth () - shadeWidth * 2, component.getHeight () - shadeWidth * 2,
round * 2 + 2, round * 2 + 2 );
}
else
{
g2d.fillRect ( shadeWidth, shadeWidth, component.getWidth () - shadeWidth * 2, component.getHeight () - shadeWidth * 2 );
}
}
// Border
if ( border != null )
{
g2d.setPaint ( component.isEnabled () ? border : disabledBorder );
g2d.draw ( borderShape );
}
// Restoring old values
GraphicsUtils.restoreComposite ( g2d, oc, opacity < 1f );
GraphicsUtils.restoreAntialias ( g2d, aa );
return borderShape;
}
private static final Map gradientCache = new HashMap ();
public static GradientPaint getWebGradientPaint ( final Rectangle bounds )
{
return getWebGradientPaint ( bounds.x, bounds.y, bounds.x, bounds.y + bounds.height );
}
public static GradientPaint getWebGradientPaint ( final int x1, final int y1, final int x2, final int y2 )
{
final String key = x1 + ";" + y1 + ";" + x2 + ";" + y2;
if ( gradientCache.containsKey ( key ) )
{
return gradientCache.get ( key );
}
else
{
final GradientPaint gp = new GradientPaint ( x1, y1, StyleConstants.topBgColor, x2, y2, StyleConstants.bottomBgColor );
gradientCache.put ( key, gp );
return gp;
}
}
public static Shape getWebBorderShape ( final JComponent component, final int shadeWidth, final int round )
{
if ( round > 0 )
{
return new RoundRectangle2D.Double ( shadeWidth, shadeWidth, component.getWidth () - shadeWidth * 2 - 1,
component.getHeight () - shadeWidth * 2 - 1, round * 2, round * 2 );
}
else
{
return new Rectangle2D.Double ( shadeWidth, shadeWidth, component.getWidth () - shadeWidth * 2 - 1,
component.getHeight () - shadeWidth * 2 - 1 );
}
}
/**
* Paints custom shaped web styled border within the component with shadow and background
*/
public static void drawCustomWebBorder ( final Graphics2D g2d, final JComponent component, final Shape borderShape,
final Color shadeColor, final int shadeWidth, final boolean fillBackground,
final boolean webColored )
{
drawCustomWebBorder ( g2d, component, borderShape, shadeColor, shadeWidth, fillBackground, webColored, Color.GRAY,
Color.LIGHT_GRAY );
}
public static void drawCustomWebBorder ( final Graphics2D g2d, final JComponent component, final Shape borderShape,
final Color shadeColor, final int shadeWidth, final boolean fillBackground,
final boolean webColored, final Color border, final Color disabledBorder )
{
final Object aa = GraphicsUtils.setupAntialias ( g2d );
// Outer shadow
if ( component.isEnabled () )
{
GraphicsUtils.drawShade ( g2d, borderShape, shadeColor, shadeWidth );
}
// Background
if ( fillBackground )
{
if ( webColored )
{
final Rectangle shapeBounds = borderShape.getBounds ();
g2d.setPaint ( new GradientPaint ( 0, shapeBounds.y, StyleConstants.topBgColor, 0, shapeBounds.y + shapeBounds.height,
StyleConstants.bottomBgColor ) );
g2d.fill ( borderShape );
}
else
{
g2d.setPaint ( component.getBackground () );
g2d.fill ( borderShape );
}
}
// Border
if ( border != null )
{
g2d.setPaint ( component.isEnabled () ? border : disabledBorder );
g2d.draw ( borderShape );
}
GraphicsUtils.restoreAntialias ( g2d, aa );
}
/**
* Paints web styled focus within the component
*/
public static boolean drawWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final int shadeWidth,
final int round )
{
return drawWebFocus ( g2d, component, focusType, shadeWidth, round, null );
}
public static boolean drawWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final int shadeWidth,
final int round, final Boolean mouseover )
{
return drawWebFocus ( g2d, component, focusType, shadeWidth, round, mouseover, null );
}
public static boolean drawWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final int shadeWidth,
final int round, final Boolean mouseover, final Boolean hasFocus )
{
return drawWebFocus ( g2d, component, focusType, shadeWidth, round, mouseover, hasFocus,
focusType.equals ( FocusType.componentFocus ) ? StyleConstants.focusColor : StyleConstants.fieldFocusColor );
}
public static boolean drawWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final int shadeWidth,
final int round, final Boolean mouseover, final Boolean hasFocus, final Color color )
{
return drawWebFocus ( g2d, component, focusType, shadeWidth, round, mouseover, hasFocus, color,
focusType.equals ( FocusType.componentFocus ) ? StyleConstants.focusStroke : StyleConstants.fieldFocusStroke );
}
public static boolean drawWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final int shadeWidth,
final int round, final Boolean mouseover, Boolean hasFocus, final Color color,
final Stroke stroke )
{
hasFocus = hasFocus != null ? hasFocus : component.hasFocus () && component.isEnabled ();
if ( hasFocus && focusType.equals ( FocusType.componentFocus ) )
{
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Stroke os = GraphicsUtils.setupStroke ( g2d, stroke );
g2d.setPaint ( color );
g2d.draw ( getWebFocusShape ( component, focusType, shadeWidth, round ) );
GraphicsUtils.restoreStroke ( g2d, os );
GraphicsUtils.restoreAntialias ( g2d, aa );
return true;
}
else if ( focusType.equals ( FocusType.fieldFocus ) && ( hasFocus || mouseover != null && mouseover ) )
{
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Stroke os = GraphicsUtils.setupStroke ( g2d, stroke );
// g2d.setPaint ( hasFocus ? StyleConstants.fieldFocusColor :
// StyleConstants.transparentFieldFocusColor );
g2d.setPaint ( color );
g2d.draw ( getWebFocusShape ( component, focusType, shadeWidth, round ) );
GraphicsUtils.restoreStroke ( g2d, os );
GraphicsUtils.restoreAntialias ( g2d, aa );
return true;
}
else
{
return false;
}
}
public static Shape getWebFocusShape ( final JComponent component, final FocusType focusType, final int shadeWidth, int round )
{
// Focus side spacing
final int spacing = focusType.equals ( FocusType.componentFocus ) ? 2 : 0;
// Corners rounding
round = focusType.equals ( FocusType.componentFocus ) ? Math.max ( 0, round - 2 ) : round;
// Final focus shape
if ( round > 0 )
{
return new RoundRectangle2D.Double ( shadeWidth + spacing, shadeWidth + spacing,
component.getWidth () - shadeWidth * 2 - spacing * 2 - 1, component.getHeight () - shadeWidth * 2 - spacing * 2 - 1,
round * 2, round * 2 );
}
else
{
return new Rectangle2D.Double ( shadeWidth + spacing, shadeWidth + spacing,
component.getWidth () - shadeWidth * 2 - spacing * 2 - 1, component.getHeight () - shadeWidth * 2 - spacing * 2 - 1 );
}
}
/**
* Draws custom shaped web styled focus within the component
*/
public static void drawCustomWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final Shape shape )
{
drawCustomWebFocus ( g2d, component, focusType, shape, null );
}
public static void drawCustomWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final Shape shape,
final Boolean mouseover )
{
drawCustomWebFocus ( g2d, component, focusType, shape, mouseover, null );
}
public static void drawCustomWebFocus ( final Graphics2D g2d, final JComponent component, final FocusType focusType, final Shape shape,
final Boolean mouseover, Boolean hasFocus )
{
hasFocus = hasFocus != null ? hasFocus : component.hasFocus () && component.isEnabled ();
if ( hasFocus && focusType.equals ( FocusType.componentFocus ) )
{
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Stroke os = GraphicsUtils.setupStroke ( g2d, StyleConstants.focusStroke );
g2d.setPaint ( StyleConstants.focusColor );
g2d.draw ( shape );
GraphicsUtils.restoreStroke ( g2d, os );
GraphicsUtils.restoreAntialias ( g2d, aa );
}
else if ( focusType.equals ( FocusType.fieldFocus ) && ( hasFocus || mouseover != null && mouseover ) )
{
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Stroke os = GraphicsUtils.setupStroke ( g2d, StyleConstants.fieldFocusStroke );
g2d.setPaint ( hasFocus ? StyleConstants.fieldFocusColor : StyleConstants.transparentFieldFocusColor );
g2d.draw ( shape );
GraphicsUtils.restoreStroke ( g2d, os );
GraphicsUtils.restoreAntialias ( g2d, aa );
}
}
/**
* Draws web styled selection using shapes operations. This method is pretty slow and should not be used for multiply selections
* presentantion
*/
public static int halfButton = 4;
public static int halfSelector = 2;
public static int halfLine = 1;
public static int shadeWidth = 2;
public static void drawWebSelection ( final Graphics2D g2d, final Color color, final int x, final int y, final int width,
final int height, final boolean resizableLR, final boolean resizableUD,
final boolean drawConnectors )
{
drawWebSelection ( g2d, color, new Rectangle ( x, y, width, height ), resizableLR, resizableUD, drawConnectors );
}
public static void drawWebSelection ( final Graphics2D g2d, final Color color, final int x, final int y, final int width,
final int height, final boolean resizableLR, final boolean resizableUD,
final boolean drawConnectors, final boolean drawSideControls )
{
drawWebSelection ( g2d, color, new Rectangle ( x, y, width, height ), resizableLR, resizableUD, drawConnectors, drawSideControls );
}
public static void drawWebSelection ( final Graphics2D g2d, final Color color, final Rectangle selection, final boolean resizableLR,
final boolean resizableUD, final boolean drawConnectors )
{
drawWebSelection ( g2d, color, selection, resizableLR, resizableUD, drawConnectors, true );
}
public static void drawWebSelection ( final Graphics2D g2d, final Color color, Rectangle selection, final boolean resizableLR,
final boolean resizableUD, final boolean drawConnectors, final boolean drawSideControls )
{
selection = GeometryUtils.validateRect ( selection );
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Area buttonsShape = new Area ();
// Top
if ( resizableUD )
{
if ( resizableLR )
{
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x - halfButton, selection.y - halfButton, halfButton * 2, halfButton * 2 ) ) );
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x + selection.width - halfButton, selection.y - halfButton, halfButton * 2,
halfButton * 2 )
) );
}
if ( drawSideControls )
{
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x + selection.width / 2 - halfButton, selection.y - halfButton, halfButton * 2,
halfButton * 2 )
) );
}
}
// Middle
if ( resizableLR && drawSideControls )
{
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x - halfButton, selection.y + selection.height / 2 - halfButton, halfButton * 2,
halfButton * 2 )
) );
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x + selection.width - halfButton, selection.y + selection.height / 2 - halfButton,
halfButton * 2, halfButton * 2 )
) );
}
// Bottom
if ( resizableUD )
{
if ( resizableLR )
{
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x - halfButton, selection.y + selection.height - halfButton, halfButton * 2,
halfButton * 2 )
) );
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x + selection.width - halfButton, selection.y + selection.height - halfButton,
halfButton * 2, halfButton * 2 )
) );
}
if ( drawSideControls )
{
buttonsShape.add ( new Area (
new Ellipse2D.Double ( selection.x + selection.width / 2 - halfButton, selection.y + selection.height - halfButton,
halfButton * 2, halfButton * 2 )
) );
}
}
// Button connectors
if ( drawConnectors )
{
final Area selectionShape = new Area (
new RoundRectangle2D.Double ( selection.x - halfLine, selection.y - halfLine, selection.width + halfLine * 2,
selection.height + halfLine * 2, 5, 5 )
);
selectionShape.subtract ( new Area (
new RoundRectangle2D.Double ( selection.x + halfLine, selection.y + halfLine, selection.width - halfLine * 2,
selection.height - halfLine * 2, 3, 3 )
) );
buttonsShape.add ( selectionShape );
}
// Shade
GraphicsUtils.drawShade ( g2d, buttonsShape, Color.GRAY, shadeWidth );
// Border
g2d.setPaint ( Color.GRAY );
g2d.draw ( buttonsShape );
// Background
g2d.setPaint ( color );
g2d.fill ( buttonsShape );
GraphicsUtils.restoreAntialias ( g2d, aa );
}
public static void drawWebSelector ( final Graphics2D g2d, final Color color, Rectangle selection, final int selector )
{
selection = GeometryUtils.validateRect ( selection );
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Ellipse2D buttonsShape;
if ( selector == SwingConstants.NORTH_WEST )
{
buttonsShape =
new Ellipse2D.Double ( selection.x - halfSelector, selection.y - halfSelector, halfSelector * 2, halfSelector * 2 );
}
else if ( selector == SwingConstants.NORTH )
{
buttonsShape =
new Ellipse2D.Double ( selection.x + selection.width / 2 - halfSelector, selection.y - halfSelector, halfSelector * 2,
halfSelector * 2 );
}
else if ( selector == SwingConstants.NORTH_EAST )
{
buttonsShape =
new Ellipse2D.Double ( selection.x + selection.width - halfSelector, selection.y - halfSelector, halfSelector * 2,
halfSelector * 2 );
}
else if ( selector == SwingConstants.WEST )
{
buttonsShape =
new Ellipse2D.Double ( selection.x - halfSelector, selection.y + selection.height / 2 - halfSelector, halfSelector * 2,
halfSelector * 2 );
}
else if ( selector == SwingConstants.EAST )
{
buttonsShape =
new Ellipse2D.Double ( selection.x + selection.width - halfSelector, selection.y + selection.height / 2 - halfSelector,
halfSelector * 2, halfSelector * 2 );
}
else if ( selector == SwingConstants.SOUTH_WEST )
{
buttonsShape =
new Ellipse2D.Double ( selection.x - halfSelector, selection.y + selection.height - halfSelector, halfSelector * 2,
halfSelector * 2 );
}
else if ( selector == SwingConstants.SOUTH )
{
buttonsShape =
new Ellipse2D.Double ( selection.x + selection.width / 2 - halfSelector, selection.y + selection.height - halfSelector,
halfSelector * 2, halfSelector * 2 );
}
else if ( selector == SwingConstants.SOUTH_EAST )
{
buttonsShape =
new Ellipse2D.Double ( selection.x + selection.width - halfSelector, selection.y + selection.height - halfSelector,
halfSelector * 2, halfSelector * 2 );
}
else
{
buttonsShape = null;
}
// Background
g2d.setPaint ( color );
g2d.fill ( buttonsShape );
GraphicsUtils.restoreAntialias ( g2d, aa );
}
/**
* Draws web styled selection using predefined images set. This method is much faster than the one before but has less settings due to
* the predefined graphics
*/
private static final NinePatchIcon conn = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/conn.9.png" ) );
private static final NinePatchIcon lr_conn = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/lr_conn.9.png" ) );
private static final NinePatchIcon ud_conn = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/ud_conn.9.png" ) );
private static final NinePatchIcon corners_conn =
new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/corners_conn.9.png" ) );
private static final NinePatchIcon full_conn = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/full_conn.9.png" ) );
private static final NinePatchIcon lr = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/lr.9.png" ) );
private static final NinePatchIcon ud = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/ud.9.png" ) );
private static final NinePatchIcon corners = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/corners.9.png" ) );
private static final NinePatchIcon full = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/full.9.png" ) );
private static final ImageIcon gripper = new ImageIcon ( LafUtils.class.getResource ( "icons/selection/gripper.png" ) );
public static void drawWebIconedSelection ( final Graphics2D g2d, final Rectangle selection, final boolean resizableLR,
final boolean resizableUD, final boolean drawConnectors )
{
drawWebIconedSelection ( g2d, selection, resizableLR, resizableUD, drawConnectors, true );
}
public static void drawWebIconedSelection ( final Graphics2D g2d, Rectangle selection, final boolean resizableLR,
final boolean resizableUD, final boolean drawConnectors, final boolean drawSideControls )
{
selection = GeometryUtils.validateRect ( selection );
// Calculating selection rect
final Rectangle rect = calculateIconedRect ( selection );
// Drawing selection
if ( drawConnectors )
{
if ( !resizableLR && !resizableUD )
{
conn.paintIcon ( g2d, rect );
}
else if ( resizableLR && !resizableUD && drawSideControls )
{
lr_conn.paintIcon ( g2d, rect );
}
else if ( !resizableLR && resizableUD && drawSideControls )
{
ud_conn.paintIcon ( g2d, rect );
}
else if ( resizableLR && resizableUD )
{
if ( drawSideControls )
{
full_conn.paintIcon ( g2d, rect );
}
else
{
corners_conn.paintIcon ( g2d, rect );
}
}
}
else
{
if ( resizableLR && !resizableUD && drawSideControls )
{
lr.paintIcon ( g2d, rect );
}
else if ( !resizableLR && resizableUD && drawSideControls )
{
ud.paintIcon ( g2d, rect );
}
else if ( resizableLR && resizableUD )
{
if ( drawSideControls )
{
full.paintIcon ( g2d, rect );
}
else
{
corners.paintIcon ( g2d, rect );
}
}
}
}
public static void drawWebIconedSelector ( final Graphics2D g2d, Rectangle selection, final int selector )
{
selection = GeometryUtils.validateRect ( selection );
// Calculating selector rect
final Rectangle rect = calculateIconedRect ( selection );
// Drawing selector
getSelectorIcon ( selector ).paintIcon ( g2d, rect );
}
public static void drawWebIconedGripper ( final Graphics2D g2d, final Point point )
{
drawWebIconedGripper ( g2d, point.x, point.y );
}
public static void drawWebIconedGripper ( final Graphics2D g2d, final int x, final int y )
{
g2d.drawImage ( gripper.getImage (), x - gripper.getIconWidth () / 2, y - gripper.getIconHeight () / 2, null );
}
private static Rectangle calculateIconedRect ( final Rectangle selection )
{
// Recalculating coordinates to iconed view
return new Rectangle ( selection.x - halfButton - shadeWidth, selection.y - halfButton - shadeWidth,
selection.width + halfButton * 2 + shadeWidth * 2, selection.height + halfButton * 2 + shadeWidth * 2 );
}
private static final Map selectorCache = new HashMap ();
private static NinePatchIcon getSelectorIcon ( final int selector )
{
if ( selectorCache.containsKey ( selector ) )
{
return selectorCache.get ( selector );
}
else
{
final NinePatchIcon npi = new NinePatchIcon ( LafUtils.class.getResource ( "icons/selection/selector" + selector + ".9.png" ) );
selectorCache.put ( selector, npi );
return npi;
}
}
/**
* Draws etched shape with specified background colors
*/
public static void drawEtchedShape ( final Graphics2D g2d, final BufferedImage topBg, final BufferedImage bottomBg,
final Shape fullShape, final Shape bevelShape )
{
final Object aa = GraphicsUtils.setupAntialias ( g2d );
final Rectangle bounds = fullShape.getBounds ();
g2d.setPaint ( new TexturePaint ( topBg,
new Rectangle ( bounds.getLocation (), new Dimension ( topBg.getWidth (), topBg.getHeight () ) ) ) );
g2d.fill ( fullShape );
final Shape oldClip = g2d.getClip ();
final Area newClip = new Area ( oldClip );
newClip.intersect ( new Area ( bevelShape ) );
g2d.setClip ( newClip );
g2d.setPaint ( new TexturePaint ( bottomBg,
new Rectangle ( bounds.getLocation (), new Dimension ( bottomBg.getWidth (), bottomBg.getHeight () ) ) ) );
g2d.fill ( bevelShape );
GraphicsUtils.drawShade ( g2d, bevelShape, Color.BLACK, 4 );
g2d.setClip ( oldClip );
g2d.setPaint ( Color.DARK_GRAY );
g2d.draw ( bevelShape );
GraphicsUtils.restoreAntialias ( g2d, aa );
}
/**
* Draw a string with a drop shadow. The light angle is assumed to be 0 degrees, (i.e., window is illuminated from top) and the shadow
* size is 2, with a 1 pixel vertical displacement. The shadow is intended to be subtle to be usable in as many text components as
* possible. The shadow is generated with multiple calls to draw string. This method paints the text on coordinates 0, 1. If text
* should
* be painted elsewhere, a transform should be applied to the graphics before passing it.
*/
public static void paintTextShadow ( final Graphics2D g2d, final String s )
{
paintTextShadow ( g2d, s, Color.LIGHT_GRAY );
}
/**
* Draw a string with a drop shadow. The light angle is assumed to be 0 degrees, (i.e., window is illuminated from top) and the shadow
* size is 2, with a 1 pixel vertical displacement. The shadow is intended to be subtle to be usable in as many text components as
* possible. The shadow is generated with multiple calls to draw string. This method paints the text on coordinates 0, 1. If text
* should
* be painted elsewhere, a transform should be applied to the graphics before passing it.
*/
public static void paintTextShadow ( final Graphics2D g2d, final String s, final Color c )
{
paintTextEffect ( g2d, s, ColorUtils.removeAlpha ( c ), TEXT_SHADOW_SIZE, -TEXT_SHADOW_SIZE, 1 - TEXT_SHADOW_SIZE, true );
}
/**
* Draw a string with a glow effect. Glow differs from a drop shadow in that it isn't offset in any direction (i.e., not affected by
* "lighting conditions").
*/
public static void paintTextGlow ( final Graphics2D g2d, final String s, final Color glow )
{
paintTextEffect ( g2d, s, ColorUtils.removeAlpha ( glow ), TEXT_SHADOW_SIZE, -TEXT_SHADOW_SIZE, -TEXT_SHADOW_SIZE, false );
}
/**
* Draw a string with a blur or shadow effect. The light angle is assumed to be 0 degrees, (i.e., window is illuminated from top). The
* effect is intended to be subtle to be usable in as many text components as possible. The effect is generated with multiple calls to
* draw string. This method paints the text on coordinates {@code tx}, {@code ty}. If text should be painted elsewhere, a
* transform should be applied to the graphics before passing it.
*/
private static final int TEXT_SHADOW_SIZE = 2;
public static void paintTextEffect ( final Graphics2D g2d, final String s, final Color c, final int size, final double tx,
final double ty, final boolean isShadow )
{
// Effect "darkness"
final float opacity = 0.8f;
final Composite oldComposite = g2d.getComposite ();
final Color oldColor = g2d.getColor ();
// Use a alpha blend smaller than 1 to prevent the effect from becoming too dark when multiple paints occur on top of each other.
float preAlpha = 0.4f;
if ( oldComposite instanceof AlphaComposite && ( ( AlphaComposite ) oldComposite ).getRule () == AlphaComposite.SRC_OVER )
{
preAlpha = Math.min ( ( ( AlphaComposite ) oldComposite ).getAlpha (), preAlpha );
}
g2d.setColor ( c );
g2d.translate ( tx, ty );
// If the effect is a shadow it looks better to stop painting a bit earlier - shadow will look softer
final int maxSize = isShadow ? size - 1 : size;
for ( int i = -size; i <= maxSize; i++ )
{
for ( int j = -size; j <= maxSize; j++ )
{
final double distance = i * i + j * j;
float alpha = opacity;
if ( distance > 0.0d )
{
alpha = ( float ) ( 1.0f / ( ( distance * size ) * opacity ) );
}
alpha *= preAlpha;
if ( alpha > 1.0f )
{
alpha = 1.0f;
}
g2d.setComposite ( AlphaComposite.getInstance ( AlphaComposite.SRC_OVER, alpha ) );
g2d.drawString ( s, i + size, j + size );
}
}
// Restore graphics
g2d.translate ( -tx, -ty );
g2d.setComposite ( oldComposite );
g2d.setColor ( oldColor );
g2d.drawString ( s, 0, 0 );
// final Color oldColor = g2d.getColor ();
// g2d.setColor ( c );
// g2d.drawString ( s, 1, 1 );
//
// g2d.setColor ( oldColor );
// g2d.drawString ( s, 0, 0 );
}
/**
* Draws dashed rectangle
*/
public static void drawDashedRect ( final Graphics2D g2d, final int x1, final int y1, final int x2, final int y2,
final int stripeLength, final int spaceLength )
{
drawDashedRect ( g2d, x1, y1, x2, y2, stripeLength, spaceLength, 0.0f );
}
public static void drawDashedRect ( final Graphics2D g2d, final int x1, final int y1, final int x2, final int y2,
final int stripeLength, final int spaceLength, final float stripeStart )
{
if ( x2 < x1 || y2 < y1 )
{
return;
}
final float[] dash = { stripeLength, spaceLength };
final BasicStroke stroke = new BasicStroke ( 1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f, dash, stripeStart );
final Stroke oldStroke = GraphicsUtils.setupStroke ( g2d, stroke );
g2d.drawRect ( x1, y1, x2 - x1, y2 - y1 );
GraphicsUtils.restoreStroke ( g2d, oldStroke );
}
/**
* Returns custom rounded rectangle shape
*/
public static GeneralPath createRoundedRectShape ( final int x, final int y, final int w, final int h, final int arcW, final int arcH )
{
final GeneralPath gp = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );
gp.moveTo ( x, y + arcH );
gp.quadTo ( x, y, x + arcW, y );
gp.lineTo ( x + w - arcW, y );
gp.quadTo ( x + w, y, x + w, y + arcH );
gp.lineTo ( x + w, y + h - arcH );
gp.quadTo ( x + w, y + h, x + w - arcW, y + h );
gp.lineTo ( x + arcW, y + h );
gp.quadTo ( x, y + h, x, y + h - arcH );
gp.closePath ();
return gp;
}
/**
* Returns shear to center text
*/
public static Point getTextCenterShear ( final FontMetrics fm, final String text )
{
return new Point ( getTextCenterShearX ( fm, text ), getTextCenterShearY ( fm ) );
}
public static int getTextCenterShearX ( final FontMetrics fm, final String text )
{
return -fm.stringWidth ( text ) / 2;
}
public static int getTextCenterShearY ( final FontMetrics fm )
{
return ( fm.getAscent () - fm.getLeading () - fm.getDescent () ) / 2;
}
/**
* Attempts to update component border if border methods are available for it.
* Returns whether attempt has succeed or not.
*
* @param component component which border should be updated
* @return true if attempt has succeed, false otherwise
*/
public static boolean updateBorder ( final Component component )
{
final BorderMethods borderMethods = LafUtils.getBorderMethods ( component );
if ( borderMethods != null )
{
borderMethods.updateBorder ();
return true;
}
else
{
return false;
}
}
/**
* Returns BorderMethods for the specified component or null if custom WebLaF border is not supported.
*
* @param component component to process
* @return BorderMethods for the specified component or null if custom WebLaF border is not supported
*/
public static BorderMethods getBorderMethods ( final Component component )
{
if ( component instanceof BorderMethods )
{
return ( BorderMethods ) component;
}
else
{
final ComponentUI ui = getUI ( component );
if ( ui != null && ui instanceof BorderMethods )
{
return ( BorderMethods ) ui;
}
}
return null;
}
/**
* Returns shape provider for the specified component or null if shape provider is not supported.
* This might be used to provide dynamic component shape to other components.
*
* @param component component to process
* @return shape provider for the specified component or null if shape provider is not supported
*/
public static ShapeProvider getShapeProvider ( final Component component )
{
if ( component instanceof ShapeProvider )
{
return ( ShapeProvider ) component;
}
else
{
final ComponentUI ui = getUI ( component );
if ( ui != null && ui instanceof ShapeProvider )
{
return ( ShapeProvider ) ui;
}
}
return null;
}
/**
* Returns Styleable for the specified component or null if component is not styleable.
*
* @param component component to process
* @return Styleable for the specified component or null if component is not styleable
*/
public static Styleable getStyleable ( final Component component )
{
if ( component instanceof Styleable )
{
return ( Styleable ) component;
}
else
{
final ComponentUI ui = getUI ( component );
if ( ui != null && ui instanceof Styleable )
{
return ( Styleable ) ui;
}
}
return null;
}
/**
* Returns bounds for editor display atop of the label.
*
* @param label edited label
* @param editor label editor field
* @return bounds for editor display atop of the label
*/
public static Rectangle getLabelEditorBounds ( final WebLabel label, final WebTextField editor )
{
editor.setFieldMargin ( 0, label.getIcon () != null ? label.getIconTextGap () : 0, 0, 0 );
// Bounds
final Rectangle bounds = new Rectangle ( 0, 0, label.getWidth (), label.getHeight () );
// Label settings
final Insets lm = label.getInsets ();
bounds.x += lm.left;
bounds.y += lm.top;
bounds.width -= lm.left + lm.right;
bounds.height -= lm.top + lm.bottom;
// Field settings
final Insets fm = editor.getMargin ();
final int dm = 1 + editor.getShadeWidth ();
bounds.x -= fm.left + dm;
bounds.y -= fm.top + dm;
bounds.width += fm.left + fm.right + dm * 2;
bounds.height += fm.top + fm.bottom + dm * 2;
// Additional pixel for field size
bounds.width += 1;
return bounds;
}
/**
* Returns TexturePainter which is read from the source.
*
* @param source one of possible sources: URL, String, File, Reader, InputStream
* @return TexturePainter
*/
public static TexturePainter loadTexturePainter ( final Object source )
{
return loadTexturePainter ( XmlUtils.loadResourceFile ( source ) );
}
/**
* Returns TexturePainter which is read from specified ResourceFile.
*
* @param resource file description
* @return TexturePainter
*/
public static TexturePainter loadTexturePainter ( final ResourceFile resource )
{
return new TexturePainter ( XmlUtils.loadImageIcon ( resource ) );
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy