Size.java
package swingtree.layout;
import com.google.errorprone.annotations.Immutable;
import org.jspecify.annotations.Nullable;
import swingtree.api.Styler;
import swingtree.style.ComponentStyleDelegate;
import swingtree.style.ImageConf;
import java.awt.Dimension;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
/**
* An immutable value object that represents a size
* in the form of a width and height or lack thereof.
* This is used to represent the size of icons
* as part of {@link swingtree.api.IconDeclaration}s
* and the SwingTree style API, see {@link swingtree.UIForAnySwing#withStyle(Styler)},
* {@link ComponentStyleDelegate#image(swingtree.api.Configurator)} and
* {@link ImageConf#size(int, int)}.
*/
@Immutable
public final class Size
{
private static final Size UNKNOWN = new Size(-1, -1);
/**
* Exposes the {@link #UNKNOWN} size instance, which is a null object
* that represents an unknown size. It uses -1 for both width and height
* and will return {@link Optional#empty()} for both width and height.
*
* @return A {@link Size} instance that represents an unknown size.
*/
public static Size unknown() {
return UNKNOWN;
}
/**
* A factory method that creates a {@link Size} instance from a width and height.
* If the width or height is negative, the returned size will have be the {@link #UNKNOWN} size with
* a width or height of -1.
*
* @param width The width of the size.
* @param height The height of the size.
* @return A {@link Size} instance that represents the given width and height.
*/
public static Size of( float width, float height ) {
if ( width < 0 && height < 0 )
return UNKNOWN;
return new Size( width, height );
}
/**
* A factory method that creates a {@link Size} instance from a {@link Dimension}.
* @param dimension The dimension to convert to a {@link Size} instance.
* @return A {@link Size} instance that represents the given dimension.
*/
public static Size of( @Nullable Dimension dimension ) {
if ( dimension == null )
return UNKNOWN;
return of(dimension.width, dimension.height);
}
final float _width;
final float _height;
private Size( float width, float height ) {
_width = width < 0 ? -1 : width;
_height = height < 0 ? -1 : height;
}
/**
* The width of this {@link Size} instance may not be specified,
* in which case this method returns {@link Optional#empty()}
* and the thing that this configuration is applied to should
* resort to its default width.
*
* @return The width of this {@link Size} instance or {@link Optional#empty()} if unknown.
*/
public Optional<Float> width() {
return ( _width < 0 ? Optional.empty() : Optional.of(_width) );
}
/**
* The height of this {@link Size} instance may not be specified,
* in which case this method returns {@link Optional#empty()}
* and the thing that this configuration is applied to should
* resort to its default height.
*
* @return The height of this {@link Size} instance or {@link Optional#empty()} if unknown.
*/
public Optional<Float> height() {
return ( _height < 0 ? Optional.empty() : Optional.of(_height) );
}
public boolean hasPositiveWidth() {
return _width > 0;
}
public boolean hasPositiveHeight() {
return _height > 0;
}
/**
* Creates an updated {@link Size} instance with the given width.
* If the width is negative, the width of the returned size will be -1.
* @param width The width of the size to create.
* @return A new {@link Size} instance with the given width.
*/
public Size withWidth( int width ) {
return new Size(width, _height);
}
/**
* Creates an updated {@link Size} instance with the given height.
* If the height is negative, the height of the returned size will be -1.
* @param height The height of the size to create.
* @return A new {@link Size} instance with the given height.
*/
public Size withHeight( int height ) {
return new Size(_width, height);
}
public Dimension toDimension() {
return new Dimension((int) _width, (int) _height);
}
public Size scale( double scaleFactor ) {
float width = _width < 0 ? -1 : Math.round(_width * scaleFactor);
float height = _height < 0 ? -1 : Math.round(_height * scaleFactor);
return of(width, height);
}
@Override
public String toString() {
return this.getClass().getSimpleName()+"[" +
"width=" + _toString( _width ) + ", "+
"height=" + _toString( _height ) +
"]";
}
private static String _toString( float value ) {
return value < 0 ? "?" : String.valueOf(value).replace(".0", "");
}
@Override
public boolean equals( Object o ) {
if ( o == this ) return true;
if ( o == null ) return false;
if ( o.getClass() != this.getClass() ) return false;
Size that = (Size)o;
return _width == that._width &&
_height == that._height;
}
@Override
public int hashCode() {
return Objects.hash(_width, _height);
}
}