BorderConf.java
package swingtree.style;
import com.google.errorprone.annotations.Immutable;
import swingtree.UI;
import java.awt.Color;
import java.util.Objects;
import java.util.Optional;
/**
* An immutable config container for border styles that is part of
* a {@link StyleConf} configuration object.
* The state of this object is updated through with-methods that return
* a new instance of this class with the updated state.
*/
@Immutable
final class BorderConf
{
private static final BorderConf _NONE = new BorderConf(
Arc.none(),
Arc.none(),
Arc.none(),
Arc.none(),
Outline.none(),
Outline.none(),
Outline.none(),
BorderColorsConf.none()
);
public static BorderConf none() { return _NONE; }
static BorderConf of(
Arc topLeftArc,
Arc topRightArc,
Arc bottomLeftArc,
Arc bottomRightArc,
Outline borderWidths,
Outline margin,
Outline padding,
BorderColorsConf borderColor
) {
if ( topLeftArc .equals( Arc.none() ) &&
topRightArc .equals( Arc.none() ) &&
bottomLeftArc .equals( Arc.none() ) &&
bottomRightArc.equals( Arc.none() ) &&
borderWidths .equals( Outline.none() ) &&
margin .equals( Outline.none() ) &&
padding .equals( Outline.none() ) &&
borderColor .equals( BorderColorsConf.none() )
)
return _NONE;
else
return new BorderConf(topLeftArc, topRightArc, bottomLeftArc, bottomRightArc, borderWidths, margin, padding, borderColor);
}
private final Arc _topLeftArc;
private final Arc _topRightArc;
private final Arc _bottomLeftArc;
private final Arc _bottomRightArc;
private final Outline _borderWidths;
private final Outline _margin;
private final Outline _padding;
private final BorderColorsConf _borderColors;
private BorderConf(
Arc topLeftArc,
Arc topRightArc,
Arc bottomLeftArc,
Arc bottomRightArc,
Outline borderWidths,
Outline margin,
Outline padding,
BorderColorsConf borderColors
) {
_topLeftArc = topLeftArc;
_topRightArc = topRightArc;
_bottomLeftArc = bottomLeftArc;
_bottomRightArc = bottomRightArc;
_borderWidths = Objects.requireNonNull(borderWidths);
_margin = Objects.requireNonNull(margin);
_padding = Objects.requireNonNull(padding);
_borderColors = Objects.requireNonNull(borderColors);
}
public Optional<Arc> topLeftArc() { return _topLeftArc.equals(Arc.none()) ? Optional.empty() : Optional.of(_topLeftArc); }
public Optional<Arc> topRightArc() { return _topRightArc.equals(Arc.none()) ? Optional.empty() : Optional.of(_topRightArc); }
public Optional<Arc> bottomLeftArc() { return _bottomLeftArc.equals(Arc.none()) ? Optional.empty() : Optional.of(_bottomLeftArc); }
public Optional<Arc> bottomRightArc() { return _bottomRightArc.equals(Arc.none()) ? Optional.empty() : Optional.of(_bottomRightArc); }
public boolean hasAnyNonZeroArcs() {
return ( !_topLeftArc .equals( Arc.none() ) && _topLeftArc.width() > 0 && _topLeftArc.height() > 0 ) ||
( !_topRightArc .equals( Arc.none() ) && _topRightArc.width() > 0 && _topRightArc.height() > 0 ) ||
( !_bottomLeftArc .equals( Arc.none() ) && _bottomLeftArc.width() > 0 && _bottomLeftArc.height() > 0 ) ||
( !_bottomRightArc.equals( Arc.none() ) && _bottomRightArc.width() > 0 && _bottomRightArc.height() > 0 );
}
public float topLeftRadius() { return !_topLeftArc.equals(Arc.none()) ? (_topLeftArc.width() + _topLeftArc.height()) / 2 : 0; }
public float topRightRadius() { return !_topRightArc.equals(Arc.none()) ? (_topRightArc.width() + _topRightArc.height()) / 2 : 0; }
public float bottomLeftRadius() { return !_bottomLeftArc.equals(Arc.none()) ? (_bottomLeftArc.width() + _bottomLeftArc.height()) / 2 : 0; }
public float bottomRightRadius() { return !_bottomRightArc.equals(Arc.none()) ? (_bottomRightArc.width() + _bottomRightArc.height()) / 2 : 0; }
public Outline widths() { return _borderWidths; }
public Outline margin() { return _margin; }
public Outline padding() { return _padding; }
public BorderColorsConf colors() {
return _borderColors;
}
BorderConf withWidths( Outline borderWidths ) {
if ( borderWidths.equals(_borderWidths) )
return this;
return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, borderWidths, _margin, _padding, _borderColors);
}
BorderConf withMargin( Outline margin ) {
if ( margin.equals(_margin) )
return this;
return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, margin, _padding, _borderColors);
}
BorderConf withPadding( Outline padding ) {
if ( padding.equals(_padding) )
return this;
return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, padding, _borderColors);
}
BorderConf withArcWidthAt( UI.Corner corner, double borderArcWidth ) {
if ( corner == UI.Corner.EVERY )
return this.withArcWidth(borderArcWidth);
float arcHeight;
switch ( corner ) {
case TOP_LEFT:
arcHeight = !_topLeftArc.equals(Arc.none()) ? _topLeftArc.height() : 0;
return BorderConf.of(Arc.of(borderArcWidth, arcHeight), _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors);
case TOP_RIGHT:
arcHeight = !_topRightArc.equals(Arc.none()) ? _topRightArc.height() : 0;
return BorderConf.of(_topLeftArc, Arc.of(borderArcWidth, arcHeight), _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors);
case BOTTOM_LEFT:
arcHeight = !_bottomLeftArc.equals(Arc.none()) ? _bottomLeftArc.height() : 0;
return BorderConf.of(_topLeftArc, _topRightArc, Arc.of(borderArcWidth, arcHeight), _bottomRightArc, _borderWidths, _margin, _padding, _borderColors);
case BOTTOM_RIGHT:
arcHeight = !_bottomRightArc.equals(Arc.none()) ? _bottomRightArc.height() : 0;
return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, Arc.of(borderArcWidth, arcHeight), _borderWidths, _margin, _padding, _borderColors);
default:
throw new IllegalArgumentException("Unknown corner: " + corner);
}
}
BorderConf withArcWidth( double borderArcWidth ) {
return this.withArcWidthAt(UI.Corner.TOP_LEFT, borderArcWidth)
.withArcWidthAt(UI.Corner.TOP_RIGHT, borderArcWidth)
.withArcWidthAt(UI.Corner.BOTTOM_LEFT, borderArcWidth)
.withArcWidthAt(UI.Corner.BOTTOM_RIGHT, borderArcWidth);
}
BorderConf withArcHeightAt( UI.Corner corner, double borderArcHeight ) {
if ( corner == UI.Corner.EVERY )
return this.withArcHeight(borderArcHeight);
float arcWidth;
switch ( corner ) {
case TOP_LEFT:
arcWidth = !_topLeftArc.equals(Arc.none()) ? _topLeftArc.width() : 0;
return BorderConf.of(Arc.of(arcWidth, borderArcHeight), _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors);
case TOP_RIGHT:
arcWidth = !_topRightArc.equals(Arc.none()) ? _topRightArc.width() : 0;
return BorderConf.of(_topLeftArc, Arc.of(arcWidth, borderArcHeight), _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors);
case BOTTOM_LEFT:
arcWidth = !_bottomLeftArc.equals(Arc.none()) ? _bottomLeftArc.width() : 0;
return BorderConf.of(_topLeftArc, _topRightArc, Arc.of(arcWidth, borderArcHeight), _bottomRightArc, _borderWidths, _margin, _padding, _borderColors);
case BOTTOM_RIGHT:
arcWidth = !_bottomRightArc.equals(Arc.none()) ? _bottomRightArc.width() : 0;
return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, Arc.of(arcWidth, borderArcHeight), _borderWidths, _margin, _padding, _borderColors);
default:
throw new IllegalArgumentException("Unknown corner: " + corner);
}
}
BorderConf withArcHeight( double borderArcHeight ) {
return this.withArcHeightAt(UI.Corner.TOP_LEFT, borderArcHeight)
.withArcHeightAt(UI.Corner.TOP_RIGHT, borderArcHeight)
.withArcHeightAt(UI.Corner.BOTTOM_LEFT, borderArcHeight)
.withArcHeightAt(UI.Corner.BOTTOM_RIGHT, borderArcHeight);
}
BorderConf withWidthAt( UI.Edge edge, float borderWidth ) {
if ( edge == UI.Edge.EVERY )
return this.withWidth(borderWidth);
switch (edge) {
case TOP: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths.withTop(borderWidth), _margin, _padding, _borderColors);
case RIGHT: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths.withRight(borderWidth), _margin, _padding, _borderColors);
case BOTTOM: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths.withBottom(borderWidth), _margin, _padding, _borderColors);
case LEFT: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths.withLeft(borderWidth), _margin, _padding, _borderColors);
default:
throw new IllegalArgumentException("Unknown side: " + edge);
}
}
BorderConf withWidth( double borderWidth ) {
return this.withWidthAt(UI.Edge.TOP, (float) borderWidth)
.withWidthAt(UI.Edge.RIGHT, (float) borderWidth)
.withWidthAt(UI.Edge.BOTTOM, (float) borderWidth)
.withWidthAt(UI.Edge.LEFT, (float) borderWidth);
}
BorderConf withColor( Color borderColor ) {
if ( StyleUtil.isUndefinedColor(borderColor) )
return this;
return BorderConf.of(
_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc,
_borderWidths, _margin, _padding,
BorderColorsConf.of(borderColor)
);
}
BorderConf withColors( Color top, Color right, Color bottom, Color left ) {
return BorderConf.of(
_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc,
_borderWidths, _margin, _padding,
BorderColorsConf.of(top, right, bottom, left)
);
}
BorderConf withColorAt( UI.Edge edge, Color borderColor ) {
if ( edge == UI.Edge.EVERY )
return this.withColor(borderColor);
switch (edge) {
case TOP: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors.withTop(borderColor));
case RIGHT: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors.withRight(borderColor));
case BOTTOM: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors.withBottom(borderColor));
case LEFT: return BorderConf.of(_topLeftArc, _topRightArc, _bottomLeftArc, _bottomRightArc, _borderWidths, _margin, _padding, _borderColors.withLeft(borderColor));
default:
throw new IllegalArgumentException("Unknown side: " + edge);
}
}
boolean allCornersShareTheSameArc() {
return _topLeftArc.equals(_topRightArc) &&
_topLeftArc.equals(_bottomLeftArc) &&
_topLeftArc.equals(_bottomRightArc);
}
boolean allSidesShareTheSameWidth() {
return Objects.equals(_borderWidths.top().orElse(null), _borderWidths.right().orElse(null)) &&
Objects.equals(_borderWidths.top().orElse(null), _borderWidths.bottom().orElse(null)) &&
Objects.equals(_borderWidths.top().orElse(null), _borderWidths.left().orElse(null));
}
boolean isVisible() {
boolean hasAnyNonZeroArcs = hasAnyNonZeroArcs();
boolean hasAnyNonZeroWidths = _borderWidths.isPositive();
boolean hasAVisibleColor = _borderColors.isAnyVisible();
return hasAnyNonZeroArcs || hasAnyNonZeroWidths || hasAVisibleColor;
}
BorderConf _scale( double scale ) {
if ( scale == 1.0 )
return this;
else if ( this.equals(_NONE) )
return _NONE;
else
return BorderConf.of(
_topLeftArc.scale(scale),
_topRightArc.scale(scale),
_bottomLeftArc.scale(scale),
_bottomRightArc.scale(scale),
_borderWidths.scale(scale),
_margin.scale(scale),
_padding.scale(scale),
_borderColors
);
}
BorderConf simplified() {
if ( this.equals(_NONE) )
return _NONE;
Arc simplifiedTopLeftArc = _topLeftArc.simplified();
Arc simplifiedTopRightArc = _topRightArc.simplified();
Arc simplifiedBottomLeftArc = _bottomLeftArc.simplified();
Arc simplifiedBottomRightArc = _bottomRightArc.simplified();
Outline simplifiedBorderWidths = _borderWidths.simplified();
Outline simplifiedMargin = _margin.simplified();
Outline simplifiedPadding = _padding; // Allowing the user to set an all 0 padding is needed for overriding the default insets (from former border!)
BorderColorsConf simplifiedBorderColor = _borderColors.isAnyVisible() ? _borderColors : BorderColorsConf.none();
boolean hasNoBorderWidths = simplifiedBorderWidths.equals(Outline.none());
if ( hasNoBorderWidths ) {
simplifiedBorderColor = BorderColorsConf.none();
}
if (
simplifiedTopLeftArc .equals(_topLeftArc ) &&
simplifiedTopRightArc .equals(_topRightArc ) &&
simplifiedBottomLeftArc .equals(_bottomLeftArc ) &&
simplifiedBottomRightArc.equals(_bottomRightArc) &&
simplifiedBorderWidths .equals(_borderWidths ) &&
simplifiedMargin .equals(_margin ) &&
simplifiedPadding .equals(_padding ) &&
Objects.equals(simplifiedBorderColor, _borderColors)
)
return this;
else
return BorderConf.of(
simplifiedTopLeftArc,
simplifiedTopRightArc,
simplifiedBottomLeftArc,
simplifiedBottomRightArc,
simplifiedBorderWidths,
simplifiedMargin,
simplifiedPadding,
simplifiedBorderColor
);
}
BorderConf correctedForRounding() {
Outline correction = _borderWidths.plus(_padding).plus(_margin)
.map( v -> v % 1 )
.map( v -> v > 0f ? 1f - v : 0f )
.map( v -> v == 0f ? null : v )
.simplified();
return this.withMargin(_margin.plus(correction));
}
@Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + _topLeftArc.hashCode();
hash = 97 * hash + _topRightArc.hashCode();
hash = 97 * hash + _bottomLeftArc.hashCode();
hash = 97 * hash + _bottomRightArc.hashCode();
hash = 97 * hash + _borderWidths.hashCode();
hash = 97 * hash + _margin.hashCode();
hash = 97 * hash + _padding.hashCode();
hash = 97 * hash + ( _borderColors != null ? _borderColors.hashCode() : 0 );
return hash;
}
@Override
public boolean equals( Object obj ) {
if ( obj == null ) return false;
if ( obj == this ) return true;
if ( obj.getClass() != getClass() ) return false;
BorderConf rhs = (BorderConf) obj;
return
_topLeftArc .equals(rhs._topLeftArc) &&
_topRightArc .equals(rhs._topRightArc) &&
_bottomLeftArc .equals(rhs._bottomLeftArc) &&
_bottomRightArc.equals(rhs._bottomRightArc) &&
_borderWidths .equals(rhs._borderWidths) &&
_margin .equals(rhs._margin) &&
_padding .equals(rhs._padding) &&
Objects.equals(_borderColors, rhs._borderColors);
}
@Override
public String toString()
{
if ( this.equals(_NONE) )
return this.getClass().getSimpleName() + "[NONE]";
String arcsString;
if ( allCornersShareTheSameArc() ) {
boolean arcWidthEqualsHeight = _topLeftArc.equals(Arc.none()) || _topLeftArc.width() == _topLeftArc.height();
arcsString = (
arcWidthEqualsHeight
? "radius=" + ( _topLeftArc.equals(Arc.none()) ? "?" : Arc._toString(_topLeftArc.width()) )
: "arcWidth=" + Arc._toString(_topLeftArc.width()) + ", arcHeight=" + Arc._toString(_topLeftArc.height())
);
} else {
arcsString =
"topLeftArc=" + StyleUtil.toString(_topLeftArc) +
", topRightArc=" + StyleUtil.toString(_topRightArc) +
", bottomLeftArc=" + StyleUtil.toString(_bottomLeftArc) +
", bottomRightArc=" + StyleUtil.toString(_bottomRightArc);
}
String borderWidthsString;
if ( allSidesShareTheSameWidth() ) {
borderWidthsString = "width=" + _borderWidths.top().map(this::_toString).orElse("?");
} else {
borderWidthsString =
"topWidth=" + _borderWidths.top().map(this::_toString).orElse("?") +
", rightWidth=" + _borderWidths.right().map(this::_toString).orElse("?") +
", bottomWidth=" + _borderWidths.bottom().map(this::_toString).orElse("?") +
", leftWidth=" + _borderWidths.left().map(this::_toString).orElse("?");
}
String colors;
if ( _borderColors.equals(BorderColorsConf.none()) )
colors = "color=?";
else if ( _borderColors.isHomogeneous() )
colors = "color=" + _borderColors.top().map(StyleUtil::toString).orElse("?");
else
colors = "colors=" + _borderColors;
return this.getClass().getSimpleName() + "[" +
arcsString + ", " +
borderWidthsString + ", " +
"margin=" + _margin + ", " +
"padding=" + _padding + ", " +
colors +
"]";
}
private String _toString( Float value ) {
return String.valueOf(value).replace(".0", "");
}
}