IconDeclaration.java

  1. package swingtree.api;

  2. import com.google.errorprone.annotations.Immutable;
  3. import swingtree.UI;
  4. import swingtree.layout.Size;

  5. import javax.swing.Icon;
  6. import javax.swing.ImageIcon;
  7. import java.util.Objects;
  8. import java.util.Optional;

  9. /**
  10.  *  Primarily designed to be implemented by an {@link Enum} type
  11.  *  that declares a set of icon paths so that the enum instances
  12.  *  can be used to identify and load
  13.  *  (cached) icons across your application.
  14.  *  <p>
  15.  *  Here an example of how to use this interface:
  16.  *  <pre>{@code
  17.  * public enum Icons implements IconDeclaration
  18.  * {
  19.  *     ADD("icons/add.png"),
  20.  *     REMOVE("icons/remove.png"),
  21.  *     EDIT("icons/edit.png"),
  22.  *     SAVE("icons/save.png"),
  23.  *     CANCEL("icons/cancel.png"),
  24.  *     REFRESH("icons/refresh.png");
  25.  *     // ...
  26.  *
  27.  *     private final String path;
  28.  *
  29.  *     Icons(String path) { this.path = path; }
  30.  *
  31.  *     {@literal @}Override public String path() {
  32.  *         return path;
  33.  *     }
  34.  * }
  35.  * }</pre>
  36.  *
  37.  *  You may then use the enum instances
  38.  *  in the SwingTree API just like you would use the {@link ImageIcon} class:
  39.  *  <pre>{@code
  40.  *  UI.button(Icons.ADD)
  41.  *  .onClick( it -> vm.add() )
  42.  *  }</pre>
  43.  *
  44.  *  The reason why enums should be used instead of Strings is
  45.  *  so that you have some more compile time safety in your application!
  46.  *  When it comes to resource loading Strings are brittle because they
  47.  *  are susceptible to typos and refactoring mistakes.
  48.  *  <p>
  49.  *  Instances of this class are intended to be used as part of a view model
  50.  *  instead of using the {@link Icon} or {@link ImageIcon} classes directly.
  51.  *  <p>
  52.  *  The reason for this is the fact that traditional Swing icons
  53.  *  are often heavyweight objects whose loading may or may not succeed, and so they are
  54.  *  not suitable for direct use in a property as part of your view model.
  55.  *  Instead, you should use this {@link IconDeclaration} interface, which is a
  56.  *  lightweight value object that merely models the resource location of the icon
  57.  *  even if it is not yet loaded or even does not exist at all.
  58.  *  <p>
  59.  *  Not only does this make your view model more robust, but it also allows you
  60.  *  to write unit tests much more easily, because you can now create instances
  61.  *  where the icon may not be available at all, yet you can still test the
  62.  *  behavior of your view model.
  63.  */
  64. @Immutable
  65. public interface IconDeclaration
  66. {
  67.     /**
  68.      *  The most important part of the identity of an
  69.      *  icon declaration is the path to the icon resource.
  70.      *  This path may be relative to the classpath or may be an absolute path.
  71.      *
  72.      * @return The path to the icon resource, which is relative
  73.      *         to the classpath or may be an absolute path.
  74.      */
  75.     String path();

  76.     /**
  77.      *  The preferred size of the icon, which is not necessarily the actual size
  78.      *  of the icon that is being loaded but rather the size that the icon should
  79.      *  be scaled to when it is being loaded.
  80.      *
  81.      * @return The preferred size of the icon or {@link Size#unknown()} if the size is unspecified,
  82.      *          which means that the icon should be loaded in its original size.
  83.      */
  84.     default Size size() {
  85.         return Size.unknown();
  86.     }

  87.     /**
  88.      *  This method is used to find the icon resource
  89.      *  and load it as an {@link ImageIcon} instance
  90.      *  wrapped in an {@link Optional},
  91.      *  or return an empty {@link Optional} if the icon resource
  92.      *  could not be found.
  93.      *
  94.      * @return An {@link Optional} that contains the {@link ImageIcon}
  95.      *         if the icon resource was found, otherwise an empty {@link Optional}.
  96.      */
  97.     default Optional<ImageIcon> find() {
  98.         return UI.findIcon(this);
  99.     }

  100.     /**
  101.      *  Creates and returns an updated {@link IconDeclaration} instance
  102.      *  with a new preferred size for the icon.
  103.      *
  104.      * @param size The preferred size of the icon in the form of a {@link Size} instance.
  105.      * @return A new {@link IconDeclaration} instance with the same path
  106.      *        but with the given size.
  107.      */
  108.     default IconDeclaration withSize( Size size ) {
  109.         return IconDeclaration.of(size, path());
  110.     }

  111.     /**
  112.      *  Creates and returns an updated {@link IconDeclaration} instance
  113.      *  with a new preferred width and height for the icon.
  114.      *
  115.      * @param width The preferred width of the icon.
  116.      * @param height The preferred height of the icon.
  117.      * @return A new {@link IconDeclaration} instance with the same path
  118.      *        but with the specified width and height as preferred size.
  119.      */
  120.     default IconDeclaration withSize( int width, int height ) {
  121.         return IconDeclaration.of(Size.of(width, height), path());
  122.     }

  123.     /**
  124.      *  Creates and returns an updated {@link IconDeclaration} instance
  125.      *  with a new preferred width for the icon.
  126.      *
  127.      * @param width The preferred width of the icon.
  128.      * @return A new {@link IconDeclaration} instance with the same path
  129.      *        but with the specified width as preferred width.
  130.      */
  131.     default IconDeclaration withWidth( int width ) {
  132.         return IconDeclaration.of(size().withWidth(width), path());
  133.     }

  134.     /**
  135.      *  Allows you to create an updated {@link IconDeclaration} instance
  136.      *  with a new preferred height for the icon.
  137.      *
  138.      * @param height The preferred height of the icon.
  139.      * @return A new {@link IconDeclaration} instance with the same path
  140.      *        but with the specified height as preferred height.
  141.      */
  142.     default IconDeclaration withHeight( int height ) {
  143.         return IconDeclaration.of(size().withHeight(height), path());
  144.     }

  145.     /**
  146.      *  This method is used to find an {@link ImageIcon} of a specific type
  147.      *  and load and return it wrapped in an {@link Optional},
  148.      *  or return an empty {@link Optional} if the icon resource could not be found.
  149.      *
  150.      * @param type The type of icon to find.
  151.      * @return An {@link Optional} that contains the {@link ImageIcon} of the given type.
  152.      * @param <T> The type of icon to find.
  153.      */
  154.     default <T extends ImageIcon> Optional<T> find( Class<T> type ) {
  155.         return UI.findIcon(this).map(type::cast);
  156.     }

  157.     /**
  158.      *  A factory method for creating an {@link IconDeclaration} instance
  159.      *  from the provided path to the icon resource.
  160.      *
  161.      * @param path The path to the icon resource, which may be relative
  162.      *             to the classpath or may be an absolute path.
  163.      * @return A new {@link IconDeclaration} instance
  164.      *        that represents the given icon resource as a constant.
  165.      */
  166.     static IconDeclaration of( String path ) {
  167.         return of(Size.unknown(), path);
  168.     }

  169.     /**
  170.      *  A factory method for creating an {@link IconDeclaration} instance
  171.      *  from the provided path to the icon resource and the preferred size.
  172.      *
  173.      * @param size The preferred size of the icon.
  174.      * @param path The path to the icon resource, which may be relative
  175.      *             to the classpath or may be an absolute path.
  176.      * @return A new {@link IconDeclaration} instance
  177.      *        that represents the given icon resource as a constant.
  178.      */
  179.     static IconDeclaration of( Size size, String path ) {
  180.         Objects.requireNonNull(size);
  181.         Objects.requireNonNull(path);
  182.         return new BasicIconDeclaration(size, path);
  183.     }
  184. }