All Downloads are FREE. Search and download functionalities are using the official Maven repository.

swingtree.style.ImageConf Maven / Gradle / Ivy

package swingtree.style;

import com.google.errorprone.annotations.Immutable;
import org.jspecify.annotations.Nullable;
import swingtree.UI;
import swingtree.api.IconDeclaration;
import swingtree.layout.Size;

import javax.swing.ImageIcon;
import java.awt.*;
import java.util.Objects;
import java.util.Optional;

/**
 *  This class represents the style of an image which can be drawn onto the inner
 *  area of a component.
 *  Note that the inner component area is the area enclosed by the border, which
 *  is itself not part of said area!
 *  

* The following properties with their respective purpose are available: *
*

    *
  1. Layer: * The layer onto which the image will be drawn. * Layers exist to determine the order in which something is drawn onto the component. * Here a list of available layers: *
      *
    • {@link swingtree.UI.Layer#BACKGROUND}
    • *
    • {@link swingtree.UI.Layer#CONTENT}
    • *
    • {@link swingtree.UI.Layer#BORDER}
    • *
    • {@link swingtree.UI.Layer#FOREGROUND}
    • *
    *
  2. *
  3. Primer: * The primer color of the image style which will * be used as a filler color for the image background. * The background is the inner component area of the component. *
  4. *
  5. Image: * The image which will be drawn onto the component, * which may be specified as an instance of {@link Image}, {@link ImageIcon} * or path to an image file (see {@link swingtree.UI#findIcon(String)}). *
  6. *
  7. Placement: * The placement type determines where the image will be drawn onto the component. * The following placement options are available: *
      *
    • {@link swingtree.UI.Placement#CENTER}
    • *
    • {@link swingtree.UI.Placement#TOP_LEFT}
    • *
    • {@link swingtree.UI.Placement#TOP_RIGHT}
    • *
    • {@link swingtree.UI.Placement#BOTTOM_LEFT}
    • *
    • {@link swingtree.UI.Placement#BOTTOM_RIGHT}
    • *
    • {@link swingtree.UI.Placement#TOP}
    • *
    • {@link swingtree.UI.Placement#BOTTOM}
    • *
    • {@link swingtree.UI.Placement#LEFT}
    • *
    • {@link swingtree.UI.Placement#RIGHT}
    • *
    *
  8. *
  9. Repeat: * If this flag is set to {@code true}, then the image may be painted * multiple times so that it fills up the entire inner component area. * There will not be a noticeable effect of this flag if the * image already fills out the inner component area (see {@link #autoFit(boolean)}, {@link #size(int, int)}). *
  10. *
  11. Fit-Mode: * If this enum determines how the image will be stretched or shrunk * to fill the inner component area dependent on the specified width and height, * meaning that if the width was not specified explicitly through {@link #width(Integer)} * then the image will be scaled to fit the inner component width, * and if a height was not specified through {@link #height(Integer)} then * the image will be scaled to fit the inner component height.
    * Note that the inner component area is the area enclosed by the border, which * is itself not part of said area! *
  12. *
  13. Width and Height: * These properties allow you to specify the width and height of the image. * If the width or height is not specified, then the image will be drawn * with its original width or height or it will be scaled to fit the inner component area * if {@link #autoFit(boolean)} is set to {@code true}. *
  14. *
  15. Opacity: * This property allows you to specify the opacity of the image. * The opacity must be between 0.0f and 1.0f, where 0.0f means that the image is completely transparent * and 1.0f means that the image is completely opaque. *
  16. *
  17. Padding: * This property allows you to specify the padding of the image. * The padding is the space between the image and the inner component area. * The padding can be specified for each side of the image individually * or for all sides at once. *
  18. *
  19. Offset: * The offset consists of two integers, one for the horizontal offset * and one for the vertical offset.
    * It allows you to specify the offset of the image from the placement position. * This means that after the image has been placed onto the component, * it will be moved by the specified offset in the horizontal and vertical direction. *
  20. *
  21. Clip Area: * The clip area determines onto which part of the component the image will be drawn. * The following clip areas are available: *
      *
    • {@link swingtree.UI.ComponentArea#ALL} - * The entire component, which is the union of all other clip * areas ({@code INTERIOR + EXTERIOR + BORDER + CONTENT}). *
    • *
    • {@link swingtree.UI.ComponentArea#INTERIOR} - * The inner component area, which is defined as {@code ALL - EXTERIOR - BORDER}. *
    • *
    • {@link swingtree.UI.ComponentArea#EXTERIOR} - * The outer component area, which can be expressed as {@code ALL - INTERIOR - BORDER}, * or {@code ALL - CONTENT}. *
    • *
    • {@link swingtree.UI.ComponentArea#BORDER} - * The border of the component, which is the area between the inner and outer component area * and which can be expressed as {@code ALL - INTERIOR - EXTERIOR}. *
    • *
    • {@link swingtree.UI.ComponentArea#BODY} - * The body of the component is the inner component area including the border area. * It can be expressed as {@code ALL - EXTERIOR}, or {@code INTERIOR + BORDER}. *
    • *
    * Note that the inner/interior component area is the area enclosed by (and excluding) the border, * whereas the exterior component area is the area surrounding the border. * The component body area is the interior/inner component area plus the border. *

    * The default clip area is {@link swingtree.UI.ComponentArea#BODY} * as this is the area which is most commonly used. *

  22. *
*

* Take a look at the following example: *

{@code
 *      of(component).withStyle( it -> it
 *          .image( image -> image
 *              .layer(Layer.BACKGROUND)
 *              .image(image)
 *              .placement(Placement.CENTER)
 *              .autoFit(false)
 *              .repeat(true)
 *              .primer(Color.CYAN)
 *              .padding(12)
 *          )
 *      );
 *  }
*

* This will draw the specified image onto the background layer of the component. * The image will be drawn at the center of the inner component area with a padding of 12, * without being scaled to fit the inner component area, instead the size of the image * will be used.
* If it does not fill the entire inner component area based on its size, then * it will be repeated across said area, and the primer color * will be used as a filler color for the parts of the image which * are transparent. *

**/ @Immutable @SuppressWarnings("Immutable") public final class ImageConf implements Simplifiable { static final UI.Layer DEFAULT_LAYER = UI.Layer.BACKGROUND; private static final ImageConf _NONE = new ImageConf( null, null, UI.Placement.UNDEFINED, false, UI.FitComponent.NO, Size.unknown(), 1.0f, Outline.none(), Offset.none(), UI.ComponentArea.BODY ); static ImageConf none() { return _NONE; } static ImageConf of( @Nullable Color primer, @Nullable ImageIcon image, UI.Placement placement, boolean repeat, UI.FitComponent fitMode, Size size, float opacity, Outline padding, Offset offset, UI.ComponentArea clipArea ) { if ( Objects.equals( primer, _NONE._primer ) && Objects.equals( image , _NONE._image ) && placement.equals( _NONE._placement ) && repeat == _NONE._repeat && fitMode .equals( _NONE._fitMode ) && size .equals( _NONE._size ) && opacity == _NONE._opacity && padding .equals( _NONE._padding ) && offset .equals( _NONE._offset ) && clipArea .equals( _NONE._clipArea ) ) return _NONE; else return new ImageConf(primer, image, placement, repeat, fitMode, size, opacity, padding, offset, clipArea); } private final @Nullable Color _primer; private final @Nullable ImageIcon _image; private final UI.Placement _placement; private final boolean _repeat; private final UI.FitComponent _fitMode; private final Size _size; private final float _opacity; private final Outline _padding; private final Offset _offset; private final UI.ComponentArea _clipArea; private ImageConf( @Nullable Color primer, @Nullable ImageIcon image, UI.Placement placement, boolean repeat, UI.FitComponent fitMode, Size size, float opacity, Outline padding, Offset offset, UI.ComponentArea clipArea ) { _primer = primer; _image = image; _placement = Objects.requireNonNull(placement); _repeat = repeat; _fitMode = Objects.requireNonNull(fitMode); _size = Objects.requireNonNull(size); _opacity = opacity; _padding = Objects.requireNonNull(padding); _offset = Objects.requireNonNull(offset); _clipArea = Objects.requireNonNull(clipArea); if ( _opacity < 0.0f || _opacity > 1.0f ) throw new IllegalArgumentException("transparency must be between 0.0f and 1.0f"); } Optional primer() { return Optional.ofNullable(_primer); } Optional image() { return Optional.ofNullable(_image); } UI.Placement placement() { if ( _placement == UI.Placement.UNDEFINED && _image instanceof SvgIcon ) return ((SvgIcon) _image).getPreferredPlacement(); return _placement; } boolean repeat() { return _repeat; } UI.FitComponent fitMode() { return _fitMode; } Optional width() { return _size.width().map(Number::intValue); } Optional height() { return _size.height().map(Number::intValue); } float opacity() { return _opacity; } Outline padding() { return _padding; } int horizontalOffset() { return (int) _offset.x(); } int verticalOffset() { return (int) _offset.y(); } UI.ComponentArea clipArea() { return _clipArea; } /** * Here you can specify the primer color of the image style which will be used * as a filler color for the image background.
* Note that the primer color will not be visible if the image is opaque and it fills the entire component. * * @param color The primer color of the image style. * @return A new {@link ImageConf} instance with the specified primer color. */ public ImageConf primer( Color color ) { Objects.requireNonNull(color, "Use UI.Color.UNDEFINED instead of null to represent the absence of a color."); if ( StyleUtil.isUndefinedColor(color) ) color = null; if ( Objects.equals(color, _primer) ) return this; return ImageConf.of(color, _image, _placement, _repeat, _fitMode, _size, _opacity, _padding, _offset, _clipArea); } /** * Here you can specify the image which will be drawn onto the component. * The supplied object must be an instance of {@link Image} implementation. * * @param image The image which will be drawn onto the component. * @return A new {@link ImageConf} instance with the specified image. */ public ImageConf image(Image image ) { return ImageConf.of(_primer, image == null ? null : new ImageIcon(image), _placement, _repeat, _fitMode, _size, _opacity, _padding, _offset, _clipArea); } /** * Here you can specify the image icon which will be drawn onto the component. * The supplied object must be an instance of {@link ImageIcon} implementation. * * @param image The image icon which will be drawn onto the component. * @return A new {@link ImageConf} instance with the specified image. */ public ImageConf image(ImageIcon image ) { return ImageConf.of(_primer, image, _placement, _repeat, _fitMode, _size, _opacity, _padding, _offset, _clipArea); } /** * Here you can specify the path to the image in the form of an {@link IconDeclaration} * for which the icon will be loaded and drawn onto the component. * If the icon could not be found, then the image will not be drawn. * The path is relative to the classpath or may be an absolute path. * * @param image The path to the (icon) image in the form of an {@link IconDeclaration}. * @return A new {@link ImageConf} instance with the specified image. * @throws NullPointerException If the specified {@code image} is null. */ public ImageConf image(IconDeclaration image ) { Objects.requireNonNull(image); return image.find().map(this::image).orElse(this); } /** * Here you can specify the path to the image for which the icon will be loaded, * cached and drawn onto the component. * If the icon could not be found, then the image will not be drawn. * The path is relative to the classpath or may be an absolute path. * (see {@link swingtree.UI#findIcon(String)}). * * @param path The path to the (icon) image. * @return A new {@link ImageConf} instance with the specified image. * @throws NullPointerException If the specified {@code path} is null. */ public ImageConf image(String path ) { Objects.requireNonNull(path); return image(() -> path); } /** * Here you can specify the placement of the image onto the component. * The default placement is {@link swingtree.UI.Placement#CENTER}.
* Here a list of available options and their effect: *
    *
  • {@link swingtree.UI.Placement#CENTER} - * The image will be drawn at the center of the component. * So the center of the image will be at the center of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#TOP_LEFT} - * The image will be drawn beginning at the top left corner of the inner component area. * So the top left corner of the image will be in the top left corner of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#TOP_RIGHT} - * The image will be placed in the top right corner of the inner component area. * So the top right corner of the image will be in the top right corner of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#BOTTOM_LEFT} - * The image will be drawn in the bottom left corner of the inner component area. * So the bottom left corner of the image will be in the bottom left corner of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#BOTTOM_RIGHT} - * The image will be drawn in the bottom right corner of the inner component area. * So the bottom right corner of the image will be in the bottom right corner of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#TOP} - * The image will be drawn in the top center of the inner component area. * So the top center of the image will be in the top center of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#BOTTOM} - * The image will be drawn in the bottom center of the inner component area. * So the bottom center of the image will be in the bottom center of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#LEFT} - * The image will be drawn in the left center of the inner component area. * So the left center of the image will be in the left center of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#RIGHT} - * The image will be drawn in the right center of the inner component area. * So the right center of the image will be in the right center of the inner component area. *
  • *
  • {@link swingtree.UI.Placement#UNDEFINED} - * The image will be drawn at a position which is determined by other * factors such as the image size and the component size. *
* * @param placement The placement of the image onto the component. * @return A new {@link ImageConf} instance with the specified placement. */ public ImageConf placement(UI.Placement placement ) { return ImageConf.of(_primer, _image, placement, _repeat, _fitMode, _size, _opacity, _padding, _offset, _clipArea); } /** * If this flag is set to {@code true}, then the image may be painted * multiple times so that it fills up the entire inner component area. * There will not be a noticeable effect of this flag if the * image already fills out the inner component area (see {@link #autoFit(boolean)}, {@link #size(int, int)}). * * @param repeat Weather the image should be painted repeatedly across the inner component area. * @return A new {@link ImageConf} instance with the specified {@code repeat} flag value. */ public ImageConf repeat(boolean repeat ) { return ImageConf.of(_primer, _image, _placement, repeat, _fitMode, _size, _opacity, _padding, _offset, _clipArea); } /** * If this flag is set to {@code true}, then the image will be stretched or shrunk * to fill the inner component area dependent on the specified width and height, * meaning that if the width was not specified explicitly through {@link #width(Integer)} * then the image will be scaled to fit the inner component width, * and if a height was not specified through {@link #height(Integer)} then * the image will be scaled to fit the inner component height.
* Note that the inner component area is the area enclosed by the border, which * is itself not part of said area! * * @param autoFit If true the image will be scaled to fit the inner component area for every * dimension which was not specified, * otherwise the image will not be scaled to fit the inner component area. * @return A new {@link ImageConf} instance with the specified {@code autoFit} flag value. */ public ImageConf autoFit(boolean autoFit ) { UI.FitComponent fit = autoFit ? UI.FitComponent.WIDTH_AND_HEIGHT : UI.FitComponent.NO; return ImageConf.of(_primer, _image, _placement, _repeat, fit, _size, _opacity, _padding, _offset, _clipArea); } /** * There are different kinds of strategies to fit the image onto the component. * These strategies are identified using the {@link UI.FitComponent} enum * which defines the following fit modes: *
    *
  • {@link UI.FitComponent#NO} - * The image will not be scaled to fit the inner component area. *
  • *
  • {@link UI.FitComponent#WIDTH} - * The image will be scaled to fit the inner component width. *
  • *
  • {@link UI.FitComponent#HEIGHT} - * The image will be scaled to fit the inner component height. *
  • *
  • {@link UI.FitComponent#WIDTH_AND_HEIGHT} - * The image will be scaled to fit both the component width and height. *
  • *
  • {@link UI.FitComponent#MAX_DIM} - * The image will be scaled to fit the smaller * of the two dimension of the inner component area. *
  • *
  • {@link UI.FitComponent#MIN_DIM} - * The image will be scaled to fit the larger * of the two dimension of the inner component area. *
  • *
* @param fit The fit mode of the image. * @return A new {@link ImageConf} instance with the specified {@code fit} mode. */ public ImageConf fitMode( UI.FitComponent fit ) { return ImageConf.of(_primer, _image, _placement, _repeat, fit, _size, _opacity, _padding, _offset, _clipArea); } /** * Ensures that the image has the specified width. * * @param width The width of the image. * @return A new {@link ImageConf} instance with the specified {@code width}. */ public ImageConf width(Integer width ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size.withWidth(width), _opacity, _padding, _offset, _clipArea); } /** * Ensures that the image has the specified height. * * @param height The height of the image. * @return A new {@link ImageConf} instance with the specified {@code height}. */ public ImageConf height( Integer height ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size.withHeight(height), _opacity, _padding, _offset, _clipArea); } /** * Ensures that the image has the specified width and height. * * @param width The width of the image. * @param height The height of the image. * @return A new {@link ImageConf} instance with the specified {@code width} and {@code height}. */ public ImageConf size(int width, int height ) { return size(Size.of(width, height)); } /** * Ensures that the image has the specified width and height. * * @param size The size of the image. * @return A new {@link ImageConf} instance with the specified {@code size}. */ public ImageConf size(Size size ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, size, _opacity, _padding, _offset, _clipArea); } /** * This method allows you to specify the opacity of the image. * The opacity must be between 0.0f and 1.0f, where 0.0f means that the image is completely transparent * and 1.0f means that the image is completely opaque. * * @param opacity The opacity of the image. * @return A new {@link ImageConf} instance with the specified opacity. */ public ImageConf opacity(float opacity ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size, opacity, _padding, _offset, _clipArea); } /** * This method allows you to specify the padding of the image. * The padding is the space between the image and the inner component area. * * @param padding The padding of the image. * @return A new {@link ImageConf} instance with the specified padding. */ ImageConf padding(Outline padding ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size, _opacity, padding, _offset, _clipArea); } /** * This method allows you to specify the padding of the image. * The padding is the space between the image and the inner component area. * * @param top The top padding of the image. * @param right The right padding of the image. * @param bottom The bottom padding of the image. * @param left The left padding of the image. * @return A new {@link ImageConf} instance with the specified padding. */ public ImageConf padding(int top, int right, int bottom, int left ) { return padding(Outline.of(top, right, bottom, left)); } /** * This method allows you to specify the padding of the image. * The padding is the space between the image and the inner component area. * * @param topBottom The top and bottom padding of the image. * @param leftRight The left and right padding of the image. * @return A new {@link ImageConf} instance with the specified padding. */ public ImageConf padding(int topBottom, int leftRight ) { return padding(Outline.of(topBottom, leftRight, topBottom, leftRight)); } /** * This method allows you to specify the padding for all sides of the image. * The padding is the space between the image and the inner component area. * * @param padding The padding of the image. * @return A new {@link ImageConf} instance with the specified padding. */ public ImageConf padding(int padding ) { return padding(Outline.of(padding, padding, padding, padding)); } /** * Use this to specify the vertical and horizontal offset by which the image will be moved * and drawn onto the component. * * @param x The horizontal offset. * @param y The vertical offset. * @return A new {@link ImageConf} instance with the specified offset. */ public ImageConf offset(int x, int y ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size, _opacity, _padding, Offset.of(x, y), _clipArea); } /** * Use this to specify the horizontal offset by which the image will be moved * and drawn onto the component. * * @param x The horizontal offset. * @return A new {@link ImageConf} instance with the specified offset. */ public ImageConf horizontalOffset(int x ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size, _opacity, _padding, _offset.withX(x), _clipArea); } /** * Use this to specify the vertical offset by which the image will be moved * and drawn onto the component. * * @param y The vertical offset. * @return A new {@link ImageConf} instance with the specified offset. */ public ImageConf verticalOffset(int y ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size, _opacity, _padding, _offset.withY(y), _clipArea); } /** * Use this to specify the clip area of the image, * which determines on which part of the component the image will be drawn. * The {@link swingtree.UI.ComponentArea} enum defines the following clip areas: *
    *
  • {@link swingtree.UI.ComponentArea#ALL} - * The image will be drawn onto the entire component, which * is the union of all other clip areas ({@code INTERIOR + EXTERIOR + BORDER + CONTENT}). *
  • *
  • {@link swingtree.UI.ComponentArea#INTERIOR} - * The image will be drawn onto the inner component area, * which is defined as {@code ALL - EXTERIOR - BORDER}. *
  • *
  • {@link swingtree.UI.ComponentArea#EXTERIOR} - * The image will be drawn onto the outer component area, * which can be expressed as {@code ALL - INTERIOR - BORDER}, * or {@code ALL - CONTENT}. *
  • *
  • {@link swingtree.UI.ComponentArea#BORDER} - * The image will be drawn onto the border of the component, * which is the area between the inner and outer component area * and which can be expressed as {@code ALL - INTERIOR - EXTERIOR}. *
  • *
  • {@link swingtree.UI.ComponentArea#BODY} - * The image will be drawn onto the component body, * which is the inner component area including the border area. * It can be expressed as {@code ALL - EXTERIOR}, or {@code INTERIOR + BORDER}. *
  • *
* The default clip area is {@link swingtree.UI.ComponentArea#INTERIOR}, * which means that the image will be drawn onto the inner component area. *

* Use {@link UI.ComponentArea#ALL} to draw the image without any additional clipping * onto the entire component, which may also cover the border and margin area of the component. * * @param clipArea The clip area of the image. * @return A new {@link ImageConf} instance with the specified clip area. */ public ImageConf clipTo(UI.ComponentArea clipArea ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size, _opacity, _padding, _offset, clipArea); } ImageConf _scale(double scaleFactor ) { return ImageConf.of(_primer, _image, _placement, _repeat, _fitMode, _size.scale(scaleFactor), _opacity, _padding.scale(scaleFactor), _offset.scale(scaleFactor), _clipArea); } @Override public ImageConf simplified() { if ( this.equals(_NONE) ) return _NONE; ImageIcon simplifiedImage = _opacity == 0.0f ? null : _image; Color simplifiedPrimer = _primer == null || _primer.getAlpha() == 0 ? null : _primer; if ( StyleUtil.isUndefinedColor(simplifiedPrimer) ) simplifiedPrimer = null; if ( simplifiedImage == null && simplifiedPrimer == null ) return none(); return ImageConf.of( simplifiedPrimer, simplifiedImage, _placement, _repeat, _fitMode, _size, _opacity, _padding.simplified(), _offset, _clipArea ); } @Override public int hashCode() { return Objects.hash(_primer, _image, _placement, _repeat, _fitMode, _size, _opacity, _padding, _offset, _clipArea); } @Override public boolean equals( Object obj ) { if ( obj == null ) return false; if ( obj == this ) return true; if ( obj.getClass() != getClass() ) return false; ImageConf rhs = (ImageConf) obj; return Objects.equals(_primer, rhs._primer) && Objects.equals(_image, rhs._image) && Objects.equals(_placement, rhs._placement) && _repeat == rhs._repeat && Objects.equals(_fitMode, rhs._fitMode) && Objects.equals(_size, rhs._size) && _opacity == rhs._opacity && Objects.equals(_padding, rhs._padding) && Objects.equals(_offset, rhs._offset) && Objects.equals(_clipArea, rhs._clipArea); } @Override public String toString() { if ( this.equals(_NONE) ) return this.getClass().getSimpleName()+"[NONE]"; return this.getClass().getSimpleName() + "[" + "primer=" + StyleUtil.toString(_primer) + ", " + "image=" + ( _image == null ? "?" : _image.toString() ) + ", " + "placement=" + _placement + ", " + "repeat=" + _repeat + ", " + "fitComponent=" + _fitMode + ", " + "width=" + _size.width().map(Objects::toString).orElse("?") + ", " + "height=" + _size.height().map(Objects::toString).orElse("?") + ", " + "opacity=" + _opacity + ", " + "padding=" + _padding + ", " + "offset=" + _offset + ", " + "clipArea=" + _clipArea + "]"; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy