StyleConf.java

  1. package swingtree.style;

  2. import com.google.errorprone.annotations.Immutable;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import swingtree.UI;
  6. import swingtree.api.Configurator;
  7. import swingtree.api.Painter;

  8. import java.awt.*;
  9. import java.util.List;
  10. import java.util.*;
  11. import java.util.stream.Collectors;

  12. /**
  13.  *  An immutable config container with cloning based update methods designed
  14.  *  for functional {@link javax.swing.JComponent} styling.
  15.  *  The styling in SwingTree is completely functional, meaning that changing a property
  16.  *  of a {@link StyleConf} instance will always return a new {@link StyleConf} instance with the
  17.  *  updated property.
  18.  *  <p>
  19.  *  Consider the following example demonstrating how a {@link javax.swing.JPanel} is styled through the SwingTree
  20.  *  style API, which consists of a functional {@link swingtree.api.Styler} lambda that processes a
  21.  *  {@link ComponentStyleDelegate} instance that internally assembles a {@link StyleConf} object:
  22.  *  <pre>{@code
  23.  *  panel(FILL)
  24.  *  .withStyle( it -> it
  25.  *      .foundationColor(new Color(26,191,230))
  26.  *      .backgroundColor(new Color(255,255,255))
  27.  *      .paddingTop(30)
  28.  *      .paddingLeft(35)
  29.  *      .paddingRight(35)
  30.  *      .paddingBottom(30)
  31.  *      .borderRadius(25, 25)
  32.  *      .borderWidth(3)
  33.  *      .borderColor(new Color(0,102,255))
  34.  *      .shadowColor(new Color(64,64,64))
  35.  *      .shadowBlurRadius(6)
  36.  *      .shadowSpreadRadius(5)
  37.  *      .shadowInset(false)
  38.  *  )
  39.  *  }</pre>
  40.  *
  41.  *  This design is inspired by the CSS styling language and the use of immutable objects
  42.  *  is a key feature of the SwingTree API which makes it possible to safely compose
  43.  *  {@link swingtree.api.Styler} lambdas into a complex style pipeline
  44.  *  without having to worry about side effects.
  45.  *  See {@link swingtree.style.StyleSheet} for more information about
  46.  *  how this composition of styles is achieved in practice.
  47.  */
  48. @Immutable
  49. @SuppressWarnings("ReferenceEquality")
  50. public final class StyleConf
  51. {
  52.     private static final StyleConf _NONE = new StyleConf(
  53.                                             LayoutConf.none(),
  54.                                             BorderConf.none(),
  55.                                             BaseConf.none(),
  56.                                             FontConf.none(),
  57.                                             DimensionalityConf.none(),
  58.                                             StyleConfLayers.empty(),
  59.                                             NamedConfigs.empty()
  60.                                         );
  61.     private static final Logger log = LoggerFactory.getLogger(StyleConf.class);

  62.     /**
  63.      *  Exposes the "null object" pattern for {@link StyleConf} instances.
  64.      *  So the constant returned by this method is the default instance
  65.      *  that represents the absence of a style.
  66.      *
  67.      * @return The default style instance, representing the absence of a style.
  68.      */
  69.     public static StyleConf none() { return _NONE; }

  70.     static StyleConf of(
  71.         LayoutConf layout,
  72.         BorderConf border,
  73.         BaseConf base,
  74.         FontConf font,
  75.         DimensionalityConf dimensionality,
  76.         StyleConfLayers layers,
  77.         NamedConfigs<String> properties
  78.     ) {
  79.         if (
  80.             layout         == _NONE._layout &&
  81.             border         == _NONE._border &&
  82.             base           == _NONE._base &&
  83.             font           == _NONE._font &&
  84.             dimensionality == _NONE._dimensionality &&
  85.             layers         == _NONE._layers &&
  86.             properties     == _NONE._properties
  87.         )
  88.             return _NONE;
  89.         else
  90.             return new StyleConf(layout, border, base, font, dimensionality, layers, properties);
  91.     }


  92.     private final LayoutConf           _layout;
  93.     private final BorderConf           _border;
  94.     private final BaseConf             _base;
  95.     private final FontConf             _font;
  96.     private final DimensionalityConf   _dimensionality;
  97.     private final StyleConfLayers      _layers;
  98.     private final NamedConfigs<String> _properties;


  99.     private StyleConf(
  100.         LayoutConf           layout,
  101.         BorderConf           border,
  102.         BaseConf             base,
  103.         FontConf             font,
  104.         DimensionalityConf   dimensionality,
  105.         StyleConfLayers      layers,
  106.         NamedConfigs<String> properties
  107.     ) {
  108.         _layout         = Objects.requireNonNull(layout);
  109.         _border         = Objects.requireNonNull(border);
  110.         _base           = Objects.requireNonNull(base);
  111.         _font           = Objects.requireNonNull(font);
  112.         _dimensionality = Objects.requireNonNull(dimensionality);
  113.         _layers         = Objects.requireNonNull(layers);
  114.         _properties     = Objects.requireNonNull(properties);
  115.     }

  116.     public Optional<Object> layoutConstraint() { return _layout.constraint(); }

  117.     public LayoutConf layout() { return _layout; }

  118.     Outline padding() { return _border.padding(); }

  119.     Outline margin() { return _border.margin(); }

  120.     BorderConf border() { return _border; }

  121.     BaseConf base() { return _base; }

  122.     DimensionalityConf dimensionality() { return _dimensionality; }

  123.     StyleConfLayers layers() { return _layers; }

  124.     StyleConfLayer layer(UI.Layer layer ) { return _layers.get(layer); }

  125.     /**
  126.      *  Exposes the default shadow style configuration object.
  127.      * @return The default shadow style.
  128.      */
  129.     public ShadowConf shadow() {
  130.         ShadowConf found =
  131.                         _layers.get(ShadowConf.DEFAULT_LAYER)
  132.                         .shadows()
  133.                         .get(StyleUtil.DEFAULT_KEY);

  134.         return found != null ? found : ShadowConf.none();
  135.     }

  136.     /**
  137.      *  Internally, a style configuration consists of a set of layers defined by the {@link UI.Layer} enum.
  138.      *  Using this method you can retrieve all shadow styles for a particular layer
  139.      *  and with the provided name.
  140.      *
  141.      * @param layer The layer to retrieve the shadow style from.
  142.      * @param shadowName The name of the shadow style to retrieve.
  143.      * @return The shadow style with the provided name.
  144.      */
  145.     public ShadowConf shadow( UI.Layer layer, String shadowName ) {
  146.         Objects.requireNonNull(shadowName);
  147.         StyleConfLayer layerConf = _layers.get(layer);
  148.         NamedConfigs<ShadowConf> shadows = layerConf.shadows();
  149.         ShadowConf found = shadows.get(shadowName);
  150.         return found != null ? found : ShadowConf.none();
  151.     }

  152.     /**
  153.      *  Internally, a style configuration consists of a set of layers defined by the {@link UI.Layer} enum.
  154.      *  You can retrieve all shadow styles for a specific layer by calling this method.
  155.      *
  156.      * @return An unmodifiable list of all shadow styles sorted by their names in ascending alphabetical order.
  157.      */
  158.     List<ShadowConf> shadows( UI.Layer layer ) {
  159.         return Collections.unmodifiableList(
  160.                         _layers.get(layer)
  161.                         .shadows()
  162.                         .namedStyles()
  163.                         .stream()
  164.                         .sorted(Comparator.comparing(NamedConf::name))
  165.                         .map(NamedConf::style)
  166.                         .collect(Collectors.toList())
  167.                     );
  168.     }

  169.     NamedConfigs<ShadowConf> shadowsMap(UI.Layer layer) {
  170.         return _layers.get(layer).shadows();
  171.     }

  172.     boolean hasVisibleShadows(UI.Layer layer) {
  173.         return _layers.get(layer)
  174.                 .shadows()
  175.                 .stylesStream()
  176.                 .anyMatch(s -> s.color().isPresent() && s.color().get().getAlpha() > 0 );
  177.     }

  178.     public FontConf font() { return _font; }

  179.     /**
  180.      *  Returns a new {@link StyleConf} instance with the given layout constraint.
  181.      * @return An unmodifiable list of painters sorted by their names in ascending alphabetical order.
  182.      */
  183.     List<PainterConf> painters( UI.Layer layer ) {
  184.         return Collections.unmodifiableList(
  185.                             new ArrayList<>(_layers.get(layer)
  186.                                 .painters()
  187.                                 .sortedByNames()
  188.                             )
  189.                         );
  190.     }

  191.     StyleConf painter(UI.Layer layer, UI.ComponentArea area, String painterName, Painter painter ) {
  192.         Objects.requireNonNull(painterName);
  193.         Objects.requireNonNull(painter);
  194.         // We clone the painter map:
  195.         NamedConfigs<PainterConf> newPainters = _layers.get(layer)
  196.                                                     .painters()
  197.                                                     .withNamedStyle(
  198.                                                         painterName, // Existing painters are overwritten if they have the same name.
  199.                                                         PainterConf.of(painter, area)
  200.                                                     );
  201.         return _withPainters(layer, newPainters);
  202.     }

  203.     boolean hasPaintersOnLayer(UI.Layer layer ) {
  204.         return _layers.get(layer).painters().stylesStream().anyMatch(p -> !Painter.none().equals(p.painter()));
  205.     }

  206.     boolean hasImagesOnLayer(UI.Layer layer ) {
  207.         return _layers.get(layer).images().stylesStream().anyMatch(i -> i.image().isPresent() || i.primer().isPresent());
  208.     }

  209.     List<GradientConf> gradients( UI.Layer layer ) {
  210.         return _layers.get(layer).gradients().sortedByNames();
  211.     }

  212.     boolean hasCustomGradients() {
  213.         boolean hasCustomGradients = false;
  214.         for ( UI.Layer layer : UI.Layer.values() ) {
  215.             if ( hasCustomGradients(layer) ) {
  216.                 hasCustomGradients = true;
  217.                 break;
  218.             }
  219.         }
  220.         return hasCustomGradients;
  221.     }

  222.     boolean hasCustomGradients( UI.Layer layer ) {
  223.         NamedConfigs<GradientConf> gradients = _layers.get(layer).gradients();
  224.         return !( gradients.size() == 1 && GradientConf.none().equals(gradients.get(StyleUtil.DEFAULT_KEY)) );
  225.     }

  226.     boolean hasVisibleGradientsOnLayer( UI.Layer layer ) {
  227.         List<GradientConf> gradients = gradients(layer);
  228.         if ( gradients.isEmpty() ) return false;
  229.         return gradients.stream().anyMatch( s -> s.colors().length > 0 );
  230.     }

  231.     List<NoiseConf> noises( UI.Layer layer ) {
  232.         return _layers.get(layer).noises().sortedByNames();
  233.     }

  234.     boolean hasVisibleNoisesOnLayer( UI.Layer layer ) {
  235.         List<NoiseConf> noises = noises(layer);
  236.         if ( noises.isEmpty() ) return false;
  237.         return noises.stream().anyMatch( s -> !s.equals(NoiseConf.none()) );
  238.     }

  239.     List<UI.ComponentArea> gradientCoveredAreas() {
  240.         return gradientCoveredAreas(UI.Layer.values());
  241.     }

  242.     List<UI.ComponentArea> gradientCoveredAreas( UI.Layer... layers ) {
  243.         return Arrays.stream(layers)
  244.                 .map(_layers::get)
  245.                 .map(StyleConfLayer::gradients)
  246.                 .flatMap( g -> g
  247.                     .stylesStream()
  248.                     .map( grad -> grad.isOpaque() ? grad.area() : null )
  249.                     .filter(Objects::nonNull)
  250.                 )
  251.                 .distinct()
  252.                 .collect(Collectors.toList());
  253.     }

  254.     List<UI.ComponentArea> noiseCoveredAreas() {
  255.         return noiseCoveredAreas(UI.Layer.values());
  256.     }

  257.     List<UI.ComponentArea> noiseCoveredAreas( UI.Layer... layers ) {
  258.         return Arrays.stream(layers)
  259.                 .map(_layers::get)
  260.                 .map(StyleConfLayer::noises)
  261.                 .flatMap( n -> n
  262.                     .stylesStream()
  263.                     .map( noise -> noise.isOpaque() ? noise.area() : null )
  264.                     .filter(Objects::nonNull)
  265.                 )
  266.                 .distinct()
  267.                 .collect(Collectors.toList());
  268.     }

  269.     List<UI.ComponentArea> noiseAndGradientCoveredAreas() {
  270.         List<UI.ComponentArea> areas = new ArrayList<>(gradientCoveredAreas());
  271.         areas.addAll(noiseCoveredAreas());
  272.         return areas;
  273.     }

  274.     public StyleConf foundationColor( Color color ) { return _withBase(base().foundationColor(color)); }

  275.     public StyleConf backgroundColor( Color color ) { return _withBase(base().backgroundColor(color)); }

  276.     StyleConf _withLayout( LayoutConf layout ) {
  277.         if ( layout == _layout )
  278.             return this;

  279.         return StyleConf.of(layout, _border, _base, _font, _dimensionality, _layers, _properties);
  280.     }

  281.     StyleConf _withBorder( BorderConf border ) {
  282.         if ( border == _border )
  283.             return this;

  284.         return StyleConf.of(_layout, border, _base, _font, _dimensionality, _layers, _properties);
  285.     }

  286.     StyleConf _withBase( BaseConf background ) {
  287.         if ( background == _base )
  288.             return this;

  289.         return StyleConf.of(_layout, _border, background, _font, _dimensionality, _layers, _properties);
  290.     }

  291.     StyleConf _withFont( FontConf font ) {
  292.         if ( font == _font )
  293.             return this;

  294.         return StyleConf.of(_layout, _border, _base, font, _dimensionality, _layers, _properties);
  295.     }

  296.     StyleConf _withDimensionality( DimensionalityConf dimensionality ) {
  297.         if ( dimensionality == _dimensionality )
  298.             return this;

  299.         return StyleConf.of(_layout, _border, _base, _font, dimensionality, _layers, _properties);
  300.     }

  301.     StyleConf _withShadow( UI.Layer layer, NamedConfigs<ShadowConf> shadows ) {
  302.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, _layers.with(layer, _layers.get(layer).withShadows(shadows)), _properties);
  303.     }

  304.     StyleConf _withProperties( NamedConfigs<String> properties ) {
  305.         if ( properties == _properties )
  306.             return this;

  307.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, _layers, properties);
  308.     }

  309.     StyleConf _withShadow( UI.Layer layer, Configurator<ShadowConf> styler ) {
  310.         // A new map is created where all the styler is applied to all the values:
  311.         NamedConfigs<ShadowConf> styledShadows = _layers.get(layer).shadows().mapStyles(styler);
  312.         return _withShadow(layer, styledShadows);
  313.     }

  314.     StyleConf _withShadow( Configurator<ShadowConf> styler ) {
  315.         return _withLayers(_layers.map( layer -> layer.withShadows(layer.shadows().mapStyles(styler)) ));
  316.     }

  317.     StyleConf _withGradients( UI.Layer layer, NamedConfigs<GradientConf> shades ) {
  318.         Objects.requireNonNull(shades);
  319.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, _layers.with(layer, _layers.get(layer).withGradients(shades)), _properties);
  320.     }

  321.     StyleConf _withNoises( UI.Layer layer, NamedConfigs<NoiseConf> noises ) {
  322.         Objects.requireNonNull(noises);
  323.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, _layers.with(layer, _layers.get(layer).withNoises(noises)), _properties);
  324.     }

  325.     StyleConf _withImages( UI.Layer layer, NamedConfigs<ImageConf> images ) {
  326.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, _layers.with(layer, _layers.get(layer).withImages(images)), _properties);
  327.     }

  328.     StyleConf _withTexts( UI.Layer layer, NamedConfigs<TextConf> texts ) {
  329.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, _layers.with(layer, _layers.get(layer).withTexts(texts)), _properties);
  330.     }

  331.     StyleConf _withLayers( StyleConfLayers layers ) {
  332.         if ( layers == _layers )
  333.             return this;

  334.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, layers, _properties);
  335.     }

  336.     StyleConf _withPainters( UI.Layer layer, NamedConfigs<PainterConf> painters ) {
  337.         Objects.requireNonNull(painters);
  338.         return StyleConf.of(_layout, _border, _base, _font, _dimensionality, _layers.with(layer, _layers.get(layer).withPainters(painters)), _properties);
  339.     }

  340.     StyleConf property( String key, String value ) {
  341.         Objects.requireNonNull(key);
  342.         Objects.requireNonNull(value);
  343.         return _withProperties(_properties.withNamedStyle(key, value));
  344.     }

  345.     List<NamedConf<String>> properties() {
  346.         return _properties.namedStyles()
  347.                             .stream()
  348.                             .sorted(Comparator.comparing(NamedConf::name))
  349.                             .collect(Collectors.toList());
  350.     }

  351.     StyleConf gradient( UI.Layer layer, String shadeName, Configurator<GradientConf> styler ) {
  352.         Objects.requireNonNull(shadeName);
  353.         Objects.requireNonNull(styler);
  354.         GradientConf gradConf = _layers.get(layer).gradients().find(shadeName).orElse(GradientConf.none());
  355.         // We clone the shadow map:
  356.         try {
  357.             gradConf = styler.configure(gradConf);
  358.         } catch (Exception e) {
  359.             log.error("Failed to configure gradient '{}' for layer '{}'", shadeName, layer, e);
  360.         }
  361.         NamedConfigs<GradientConf> newShadows = _layers.get(layer).gradients().withNamedStyle(shadeName, gradConf);
  362.         return _withGradients(layer, newShadows);
  363.     }

  364.     GradientConf gradient( UI.Layer layer, String gradName ) {
  365.         Objects.requireNonNull(gradName);
  366.         StyleConfLayer layerConf = _layers.get(layer);
  367.         NamedConfigs<GradientConf> gradients = layerConf.gradients();
  368.         GradientConf found = gradients.get(gradName);
  369.         return found != null ? found : GradientConf.none();
  370.     }

  371.     StyleConf noise( UI.Layer layer, String noiseName, Configurator<NoiseConf> styler ) {
  372.         Objects.requireNonNull(noiseName);
  373.         Objects.requireNonNull(styler);
  374.         NoiseConf noise = _layers.get(layer).noises().find(noiseName).orElse(NoiseConf.none());
  375.         try {
  376.             noise = styler.configure(noise);
  377.         } catch (Exception e) {
  378.             log.error("Failed to configure noise '{}' for layer '{}'", noiseName, layer, e);
  379.         }
  380.         // We clone the noise map:
  381.         NamedConfigs<NoiseConf> newNoises = _layers.get(layer).noises().withNamedStyle(noiseName, noise);
  382.         return _withNoises(layer, newNoises);
  383.     }

  384.     StyleConf images(UI.Layer layer, String imageName, Configurator<ImageConf> styler ) {
  385.         Objects.requireNonNull(imageName);
  386.         Objects.requireNonNull(styler);
  387.         ImageConf ground = _layers.get(layer).images().find(imageName).orElse(ImageConf.none());
  388.         try {
  389.             ground = styler.configure(ground);
  390.         } catch (Exception e) {
  391.             log.error("Failed to configure image '{}' for layer '{}'", imageName, layer, e);
  392.         }
  393.         // We clone the ground map:
  394.         NamedConfigs<ImageConf> newImages = _layers.get(layer).images().withNamedStyle(imageName, ground);
  395.         return _withImages( layer, newImages );
  396.     }

  397.     List<ImageConf> images( UI.Layer layer ) {
  398.         return _layers.get(layer).images().sortedByNames();
  399.     }

  400.     StyleConf text(UI.Layer layer, String textName, Configurator<TextConf> styler ) {
  401.         Objects.requireNonNull(textName);
  402.         Objects.requireNonNull(styler);
  403.         TextConf text = _layers.get(layer).texts().find(textName).orElse(TextConf.none());
  404.         try {
  405.             text = styler.configure(text);
  406.         } catch (Exception e) {
  407.             log.error("Failed to configure text '{}' for layer '{}'", textName, layer, e);
  408.         }
  409.         // We clone the text map:
  410.         NamedConfigs<TextConf> newTexts = _layers.get(layer).texts().withNamedStyle(textName, text);
  411.         return _withTexts( layer, newTexts );
  412.     }

  413.     StyleConf text( Configurator<TextConf> styler ) {
  414.         return _withLayers(_layers.map( layer -> layer.withTexts(layer.texts().mapStyles(styler)) ));
  415.     }

  416.     List<TextConf> texts( UI.Layer layer ) {
  417.         return _layers.get(layer).texts().sortedByNames();
  418.     }

  419.     StyleConf scale( double scale ) {
  420.         return StyleConf.of(
  421.                     _layout,
  422.                     _border._scale(scale),
  423.                     _base, // Just colors and the cursor
  424.                     _font._scale(scale),
  425.                     _dimensionality._scale(scale),
  426.                     _layers._scale(scale),
  427.                     _properties
  428.                 );
  429.     }

  430.     StyleConf simplified() {
  431.         return _withBase(_base.simplified())
  432.                ._withBorder(_border.simplified())
  433.                ._withDimensionality(_dimensionality.simplified())
  434.                ._withLayers(_layers.simplified());
  435.     }

  436.     StyleConf correctedForRounding() {
  437.         return _withBorder(_border.correctedForRounding());
  438.     }

  439.     boolean hasEqualLayoutAs( StyleConf otherStyle ) {
  440.         return Objects.equals(_layout, otherStyle._layout);
  441.     }

  442.     boolean hasEqualMarginAndPaddingAs( StyleConf otherStyle ) {
  443.         return Objects.equals(_border.margin(), otherStyle._border.margin()) &&
  444.                Objects.equals(_border.padding(), otherStyle._border.padding());
  445.     }

  446.     boolean hasEqualBorderAs( StyleConf otherStyle ) {
  447.         return Objects.equals(_border, otherStyle._border);
  448.     }

  449.     boolean hasEqualBaseAs( StyleConf otherStyle ) {
  450.         return Objects.equals(_base, otherStyle._base);
  451.     }

  452.     boolean hasEqualFontAs( StyleConf otherStyle ) {
  453.         return Objects.equals(_font, otherStyle._font);
  454.     }

  455.     boolean hasEqualDimensionalityAs( StyleConf otherStyle ) {
  456.         return Objects.equals(_dimensionality, otherStyle._dimensionality);
  457.     }

  458.     boolean hasEqualFilterAs( StyleConf otherStyle ) {
  459.         return _layers.filter().equals(otherStyle._layers.filter());
  460.     }

  461.     boolean hasEqualShadowsAs( StyleConf otherStyle ) {
  462.         boolean allLayersAreEqual = true;
  463.         for ( UI.Layer layer : UI.Layer.values() ) {
  464.             if ( !hasEqualShadowsAs(layer, otherStyle) ) {
  465.                 allLayersAreEqual = false;
  466.                 break;
  467.             }
  468.         }
  469.         return allLayersAreEqual;
  470.     }

  471.     boolean hasEqualShadowsAs( UI.Layer layer, StyleConf otherStyle ) {
  472.         StyleConfLayer thisLayer = _layers.get(layer);
  473.         StyleConfLayer otherLayer = otherStyle._layers.get(layer);
  474.         if ( thisLayer == null && otherLayer == null )
  475.             return true;
  476.         if ( thisLayer == null || otherLayer == null )
  477.             return false;
  478.         return thisLayer.hasEqualShadowsAs(otherLayer);
  479.     }

  480.     boolean hasEqualPaintersAs( StyleConf otherStyle ) {
  481.         boolean allLayersAreEqual = true;
  482.         for ( UI.Layer layer : UI.Layer.values() ) {
  483.             if ( !hasEqualPaintersAs(layer, otherStyle) ) {
  484.                 allLayersAreEqual = false;
  485.                 break;
  486.             }
  487.         }
  488.         return allLayersAreEqual;
  489.     }

  490.     boolean hasEqualPaintersAs( UI.Layer layer, StyleConf otherStyle ) {
  491.         StyleConfLayer thisLayer = _layers.get(layer);
  492.         StyleConfLayer otherLayer = otherStyle._layers.get(layer);
  493.         if ( thisLayer == null && otherLayer == null )
  494.             return true;
  495.         if ( thisLayer == null || otherLayer == null )
  496.             return false;
  497.         return thisLayer.hasEqualPaintersAs(otherLayer);
  498.     }

  499.     boolean hasEqualGradientsAs( StyleConf otherStyle ) {
  500.         boolean allLayersAreEqual = true;
  501.         for ( UI.Layer layer : UI.Layer.values() ) {
  502.             if ( !hasEqualGradientsAs(layer, otherStyle) ) {
  503.                 allLayersAreEqual = false;
  504.                 break;
  505.             }
  506.         }
  507.         return allLayersAreEqual;
  508.     }

  509.     boolean hasEqualGradientsAs( UI.Layer layer, StyleConf otherStyle ) {
  510.         StyleConfLayer thisLayer = _layers.get(layer);
  511.         StyleConfLayer otherLayer = otherStyle._layers.get(layer);
  512.         if ( thisLayer == null && otherLayer == null )
  513.             return true;
  514.         if ( thisLayer == null || otherLayer == null )
  515.             return false;
  516.         return thisLayer.hasEqualGradientsAs(otherLayer);
  517.     }

  518.     boolean hasEqualNoisesAs( StyleConf otherStyle ) {
  519.         boolean allLayersAreEqual = true;
  520.         for ( UI.Layer layer : UI.Layer.values() ) {
  521.             if ( !hasEqualNoisesAs(layer, otherStyle) ) {
  522.                 allLayersAreEqual = false;
  523.                 break;
  524.             }
  525.         }
  526.         return allLayersAreEqual;
  527.     }

  528.     boolean hasEqualNoisesAs( UI.Layer layer, StyleConf otherStyle ) {
  529.         StyleConfLayer thisLayer = _layers.get(layer);
  530.         StyleConfLayer otherLayer = otherStyle._layers.get(layer);
  531.         if ( thisLayer == null && otherLayer == null )
  532.             return true;
  533.         if ( thisLayer == null || otherLayer == null )
  534.             return false;
  535.         return thisLayer.hasEqualNoisesAs(otherLayer);
  536.     }

  537.     boolean hasEqualImagesAs(StyleConf otherStyle ) {
  538.         boolean allLayersAreEqual = true;
  539.         for ( UI.Layer layer : UI.Layer.values() ) {
  540.             if ( !hasEqualImagesAs(layer, otherStyle) ) {
  541.                 allLayersAreEqual = false;
  542.                 break;
  543.             }
  544.         }
  545.         return allLayersAreEqual;
  546.     }

  547.     boolean hasEqualImagesAs( UI.Layer layer, StyleConf otherStyle ) {
  548.         StyleConfLayer thisLayer = _layers.get(layer);
  549.         StyleConfLayer otherLayer = otherStyle._layers.get(layer);
  550.         if ( thisLayer == null && otherLayer == null )
  551.             return true;
  552.         if ( thisLayer == null || otherLayer == null )
  553.             return false;
  554.         return thisLayer.hasEqualImagesAs(otherLayer);
  555.     }

  556.     boolean hasEqualTextsAs( StyleConf otherStyle ) {
  557.         boolean allLayersAreEqual = true;
  558.         for ( UI.Layer layer : UI.Layer.values() ) {
  559.             if ( !hasEqualTextsAs(layer, otherStyle) ) {
  560.                 allLayersAreEqual = false;
  561.                 break;
  562.             }
  563.         }
  564.         return allLayersAreEqual;
  565.     }

  566.     boolean hasEqualTextsAs( UI.Layer layer, StyleConf otherStyle ) {
  567.         StyleConfLayer thisLayer = _layers.get(layer);
  568.         StyleConfLayer otherLayer = otherStyle._layers.get(layer);
  569.         if ( thisLayer == null && otherLayer == null )
  570.             return true;
  571.         if ( thisLayer == null || otherLayer == null )
  572.             return false;
  573.         return thisLayer.hasEqualTextsAs(otherLayer);
  574.     }

  575.     boolean hasEqualPropertiesAs( StyleConf otherStyle ) {
  576.         return Objects.equals(_properties, otherStyle._properties);
  577.     }

  578.     @Override
  579.     public int hashCode() {
  580.         return Objects.hash(
  581.                     _layout, _border, _base, _font, _dimensionality, _layers, _properties
  582.                 );
  583.     }

  584.     @Override
  585.     public boolean equals( Object obj ) {
  586.         if ( obj == this ) return true;
  587.         if ( obj == null ) return false;
  588.         if ( !(obj instanceof StyleConf) ) return false;
  589.         StyleConf other = (StyleConf) obj;
  590.         return hasEqualLayoutAs(other)         &&
  591.                hasEqualBorderAs(other)         &&
  592.                hasEqualBaseAs(other)           &&
  593.                hasEqualFontAs(other)           &&
  594.                hasEqualDimensionalityAs(other) &&
  595.                hasEqualFilterAs(other)         &&
  596.                hasEqualShadowsAs(other)        &&
  597.                hasEqualPaintersAs(other)       &&
  598.                hasEqualGradientsAs(other)      &&
  599.                hasEqualNoisesAs(other)         &&
  600.                hasEqualImagesAs(other)         &&
  601.                hasEqualTextsAs(other)          &&
  602.                hasEqualPropertiesAs(other);
  603.     }

  604.     @Override
  605.     public String toString() {
  606.         String propertiesString = _properties.toString(StyleUtil.DEFAULT_KEY, "properties");

  607.         return this.getClass().getSimpleName() + "[" +
  608.                     _layout          + ", " +
  609.                     _border          + ", " +
  610.                     _base            + ", " +
  611.                     _font            + ", " +
  612.                     _dimensionality  + ", " +
  613.                     _layers          + ", " +
  614.                     propertiesString +
  615.                 "]";
  616.     }

  617. }