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

swingtree.UIForLabel Maven / Gradle / Ivy

package swingtree;

import org.slf4j.Logger;
import sprouts.Val;
import swingtree.api.IconDeclaration;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;

/**
 *  A SwingTree builder node designed for configuring {@link JLabel} instances.
 * 	

* Take a look at the living swing-tree documentation * where you can browse a large collection of examples demonstrating how to use the API of this class. */ public final class UIForLabel extends UIForAnySwing, L> { private static final Logger log = org.slf4j.LoggerFactory.getLogger(UIForLabel.class); private final BuilderState _state; UIForLabel( BuilderState state ) { Objects.requireNonNull(state); _state = state; } @Override protected BuilderState _state() { return _state; } @Override protected UIForLabel _newBuilderWithState(BuilderState newState ) { return new UIForLabel<>(newState); } private void _makeBold( L thisComponent ) { Font f = thisComponent.getFont(); thisComponent.setFont(f.deriveFont(f.getStyle() | Font.BOLD)); } private void _makePlain( L thisComponent ) { Font f = thisComponent.getFont(); thisComponent.setFont(f.deriveFont(f.getStyle() & ~Font.BOLD)); } /** * Makes the wrapped {@link JLabel} font bold (!plain). * * @return This very builder to allow for method chaining. */ public UIForLabel makeBold() { return this.peek( label -> { _makeBold(label); }); } /** * Use this to make the underlying {@link JLabel} into a clickable link. * * @param href A string containing a valid URL used as link hyper reference. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code href} is {@code null}. */ public UIForLabel makeLinkTo( String href ) { NullUtil.nullArgCheck( href, "href", String.class ); return makeLinkTo( Val.of(href) ); } /** * Use this to make the underlying {@link JLabel} into a clickable link * based on the string provided property defining the link address. * When the link wrapped by the provided property changes, * then a click on the label will lead to the wrapped link. * * @param href A string property containing a valid URL used as link hyper reference. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code href} is {@code null}. */ public UIForLabel makeLinkTo( Val href ) { NullUtil.nullArgCheck( href, "href", Val.class ); NullUtil.nullPropertyCheck( href, "href", "Use an empty String instead of null to model a link going nowhere." ); return _with( thisComponent -> { LazyRef text = LazyRef.of(thisComponent::getText); thisComponent.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { try { String ref = href.orElseThrow().trim(); if ( ref.isEmpty() ) return; if ( !ref.startsWith("http") ) ref = "https://" + ref; Desktop.getDesktop().browse(new URI(ref)); } catch (IOException | URISyntaxException e1) { log.error("Failed to open link: " + href.orElseThrow(), e1); } } @Override public void mouseExited(MouseEvent e) { thisComponent.setText(text.get()); } @Override public void mouseEntered(MouseEvent e) { thisComponent.setText("" + text.get() + ""); } }); }) ._this(); } /** * Makes the wrapped {@link JLabel} font plain (!bold). * * @return This very builder to allow for method chaining. */ public UIForLabel makePlain() { return _with( label -> { _makePlain(label); }) ._this(); } /** * Makes the wrapped {@link JLabel} font bold if it is plain * and plain if it is bold... * * @return This very builder to allow for method chaining. */ public final UIForLabel toggleBold() { return _with( label -> { Font f = label.getFont(); label.setFont(f.deriveFont(f.getStyle() ^ Font.BOLD)); }) ._this(); } /** * Makes the wrapped {@link JLabel} font bold if the provided flag is true, * and plain if the flag is false. * See {@link #makeBold()} and {@link #makePlain()} for more information. * * @param isBold The flag determining if the font of this label should be bold or plain. * @return This very builder to allow for method chaining. */ public final UIForLabel isBoldIf( boolean isBold ) { if ( isBold ) return makeBold(); else return makePlain(); } /** * When the flag wrapped by the provided property changes, * then the font of this label will switch between being bold and plain. * * @param isBold The property which should be bound to the boldness of this label. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code isBold} is {@code null}. */ public final UIForLabel isBoldIf( Val isBold ) { NullUtil.nullArgCheck( isBold, "isBold", Val.class ); NullUtil.nullPropertyCheck( isBold, "isBold", "You can not use null to model if a label is bold or not." ); return _withOnShow( isBold, (thisComponent,v) -> { if ( v ) _makeBold(thisComponent); else _makePlain(thisComponent); }) ._with( thisComponent -> { if ( isBold.orElseThrow() ) _makeBold(thisComponent); else _makePlain(thisComponent); }) ._this(); } /** * Defines the single line of text this component will display. If * the value of text is null or empty string, nothing is displayed. *

* The default value of this property is null. *

* * @param text The new text to be set for the wrapped label. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code text} is {@code null}. */ public final UIForLabel withText( String text ) { NullUtil.nullArgCheck( text, "text", String.class ); return _with( thisComponent -> { thisComponent.setText(text); }) ._this(); } /** * Dynamically defines a single line of text displayed on this label. * If the value of text is null or an empty string, nothing is displayed. * When the text wrapped by the provided property changes, * then so does the text displayed on this label change. * * @param text The text property to be bound to the wrapped label. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code text} is {@code null}. */ public final UIForLabel withText( Val text ) { NullUtil.nullArgCheck( text, "text", Val.class ); NullUtil.nullPropertyCheck( text, "text", "Please use an empty String instead of null." ); return _withOnShow( text, (thisComponent,v) -> { thisComponent.setText(v); }) ._with( thisComponent -> { thisComponent.setText( text.orElseThrow() ); }) ._this(); } /** * A convenience method to avoid peeking into this builder like so: *

{@code
     *     UI.label("Something")
     *     .peek( label -> label.setHorizontalAlignment(...) );
     *  }
* This sets the horizontal alignment of the label's content (icon and text). * * @param horizontalAlign The horizontal alignment which should be applied to the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code horizontalAlign} is {@code null}. */ public UIForLabel withHorizontalAlignment( UI.HorizontalAlignment horizontalAlign ) { NullUtil.nullArgCheck( horizontalAlign, "horizontalAlign", UI.HorizontalAlignment.class ); return _with( thisComponent -> { horizontalAlign.forSwing().ifPresent(thisComponent::setHorizontalAlignment); }) ._this(); } /** * This binds to a property defining the horizontal alignment of the label's content (icon and text). * When the alignment enum wrapped by the provided property changes, * then so does the alignment of this label. * * @param horizontalAlign The horizontal alignment property which should be applied to the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code horizontalAlign} is {@code null}. */ public UIForLabel withHorizontalAlignment(Val horizontalAlign ) { NullUtil.nullArgCheck( horizontalAlign, "horizontalAlign", Val.class ); NullUtil.nullPropertyCheck( horizontalAlign, "horizontalAlign", "Null is not a valid alignment." ); return _withOnShow( horizontalAlign, (thisComponent,v) -> { v.forSwing().ifPresent(thisComponent::setHorizontalAlignment); }) ._with( thisComponent -> { horizontalAlign.orElseThrow().forSwing().ifPresent(thisComponent::setHorizontalAlignment); }) ._this(); } /** * Use this to set the vertical alignment of the label's content (icon and text). * This is a convenience method to avoid peeking into this builder like so: *
{@code
     *     UI.label("Something")
     *     .peek( label -> label.setVerticalAlignment(...) );
     *  }
* * @param verticalAlign The vertical alignment which should be applied to the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code verticalAlign} is {@code null}. */ public UIForLabel withVerticalAlignment( UI.VerticalAlignment verticalAlign ) { NullUtil.nullArgCheck( verticalAlign, "verticalAlign", UI.VerticalAlignment.class ); return _with( thisComponent -> { verticalAlign.forSwing().ifPresent(thisComponent::setVerticalAlignment); }) ._this(); } /** * This binds to a property defining the vertical alignment of the label's content (icon and text). * When the alignment enum wrapped by the provided property changes, * then so does the alignment of this label. * * @param verticalAlign The vertical alignment property which should be applied to the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code verticalAlign} is {@code null}. */ public UIForLabel withVerticalAlignment( Val verticalAlign ) { NullUtil.nullArgCheck( verticalAlign, "verticalAlign", Val.class ); NullUtil.nullPropertyCheck( verticalAlign, "verticalAlign", "Null is not a valid alignment." ); return _withOnShow( verticalAlign, (thisComponent,v) -> { v.forSwing().ifPresent(thisComponent::setVerticalAlignment); }) ._with( thisComponent -> { verticalAlign.orElseThrow().forSwing().ifPresent(thisComponent::setVerticalAlignment); }) ._this(); } /** * Use this to set the horizontal and vertical alignment of the label's content (icon and text). * This is a convenience method to avoid peeking into this builder like so: *
{@code
     *     UI.label("Something")
     *     .peek( label -> label.setHorizontalAlignment(...); label.setVerticalAlignment(...) );
     *  }
* * @param alignment The alignment which should be applied to the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code alignment} is {@code null}. */ public UIForLabel withAlignment( UI.Alignment alignment ) { NullUtil.nullArgCheck( alignment, "alignment", UI.Alignment.class ); return _with( thisComponent -> { alignment.getHorizontal().forSwing().ifPresent(thisComponent::setHorizontalAlignment); alignment.getVertical().forSwing().ifPresent(thisComponent::setVerticalAlignment); }) ._this(); } /** * This binds to a property defining the horizontal and vertical alignment of the label's content (icon and text). * When the alignment enum wrapped by the provided property changes, * then so does the alignment of this label. * * @param alignment The alignment property which should be applied to the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code alignment} is {@code null}. */ public UIForLabel withAlignment( Val alignment ) { NullUtil.nullArgCheck( alignment, "alignment", Val.class ); NullUtil.nullPropertyCheck( alignment, "alignment", "Null is not a valid alignment." ); return _withOnShow( alignment, (thisComponent,v) -> { v.getHorizontal().forSwing().ifPresent(thisComponent::setHorizontalAlignment); v.getVertical().forSwing().ifPresent(thisComponent::setVerticalAlignment); }) ._with( thisComponent -> { UI.Alignment a = alignment.orElseThrow(); a.getHorizontal().forSwing().ifPresent(thisComponent::setHorizontalAlignment); a.getVertical().forSwing().ifPresent(thisComponent::setVerticalAlignment); }) ._this(); } /** * Use this to set the horizontal position of the label's text, relative to its image. * A convenience method to avoid peeking into this builder like so: *
{@code
     *     UI.label("Something")
     *         .peek( label -> label.setHorizontalTextPosition(...) );
     *  }
* * @param horizontalAlign The horizontal alignment which should be applied to the text of the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code horizontalAlign} is {@code null}. */ public UIForLabel withHorizontalTextPosition( UI.HorizontalAlignment horizontalAlign ) { NullUtil.nullArgCheck( horizontalAlign, "horizontalAlign", UI.HorizontalAlignment.class ); return _with( thisComponent -> { horizontalAlign.forSwing().ifPresent(thisComponent::setHorizontalTextPosition); }) ._this(); } /** * Use this to bind to a property defining the horizontal position of the label's text, relative to its image. * When the alignment enum wrapped by the provided property changes, * then so does the alignment of this label. * * @param horizontalAlign The horizontal alignment property which should be applied to the text of the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code horizontalAlign} is {@code null}. */ public UIForLabel withHorizontalTextPosition( Val horizontalAlign ) { NullUtil.nullArgCheck( horizontalAlign, "horizontalAlign", Val.class ); NullUtil.nullPropertyCheck( horizontalAlign, "horizontalAlign", "Null is not a valid alignment." ); return _withOnShow( horizontalAlign, (thisComponent, v) -> { v.forSwing().ifPresent(thisComponent::setHorizontalTextPosition); }) ._with( thisComponent -> { horizontalAlign.orElseThrow().forSwing().ifPresent(thisComponent::setHorizontalTextPosition); }) ._this(); } /** * Use this to set the horizontal position of the label's text, relative to its image.
* This is a convenience method to avoid peeking into this builder like so: *
{@code
     *     UI.label("Something")
     *     .peek( label -> label.setVerticalTextPosition(...) );
     *  }
* * @param verticalAlign The vertical alignment which should be applied to the text of the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code verticalAlign} is {@code null}. */ public UIForLabel withVerticalTextPosition( UI.VerticalAlignment verticalAlign ) { NullUtil.nullArgCheck( verticalAlign, "verticalAlign", UI.VerticalAlignment.class ); return _with( thisComponent -> { verticalAlign.forSwing().ifPresent(thisComponent::setVerticalTextPosition); }) ._this(); } /** * Use this to bind to a property defining the vertical position of the label's text, relative to its image. * When the alignment enum wrapped by the provided property changes, * then so does the alignment of this label. * * @param verticalAlign The vertical alignment property which should be applied to the text of the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code verticalAlign} is {@code null}. */ public UIForLabel withVerticalTextPosition( Val verticalAlign ) { NullUtil.nullArgCheck( verticalAlign, "verticalAlign", Val.class ); NullUtil.nullPropertyCheck( verticalAlign, "verticalAlign", "Null is not a valid alignment." ); return _withOnShow( verticalAlign, (thisComponent,v) -> { v.forSwing().ifPresent(thisComponent::setVerticalTextPosition); }) ._with( thisComponent -> { verticalAlign.orElseThrow().forSwing().ifPresent(thisComponent::setVerticalTextPosition); }) ._this(); } /** * Use this to set the horizontal and vertical position of the label's text, relative to its image. * This is a convenience method to avoid peeking into this builder like so: *
{@code
     *     UI.label("Something")
     *         .peek( label -> label.setHorizontalTextPosition(...); label.setVerticalTextPosition(...) );
     *  }
* * @param alignment The alignment which should be applied to the text of the underlying component. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code alignment} is {@code null}. */ public UIForLabel withTextPosition( UI.Alignment alignment ) { NullUtil.nullArgCheck( alignment, "alignment", UI.Alignment.class ); return _with( thisComponent -> { alignment.getHorizontal().forSwing().ifPresent(thisComponent::setHorizontalTextPosition); alignment.getVertical().forSwing().ifPresent(thisComponent::setVerticalTextPosition); }) ._this(); } /** * Use this to set the icon for the wrapped {@link JLabel}. * This is in essence a convenience method to avoid peeking into this builder like so: *
{@code
     *     UI.label("Something")
     *         .peek( label -> label.setIcon(...) );
     *  }
* * * @param icon The {@link Icon} which should be displayed on the label. * @return This very builder to allow for method chaining. */ public UIForLabel withIcon( Icon icon ) { return _with( thisComponent -> { thisComponent.setIcon(icon); }) ._this(); } /** * Use this to set the icon for the wrapped {@link JLabel} * based on the provided {@link IconDeclaration}. *

* An {@link IconDeclaration} should be preferred over the {@link Icon} class * as part of a view model, because it is a lightweight value object that merely * models the resource location of the icon even if it is not yet loaded or even * does not exist at all. * * @param icon The {@link IconDeclaration} which should be displayed on the label. * @return This very builder to allow for method chaining. */ public UIForLabel withIcon( IconDeclaration icon ) { Objects.requireNonNull(icon,"icon"); return _with( thisComponent -> { icon.find().ifPresent( i -> thisComponent.setIcon(i) ); }) ._this(); } /** * Use this to dynamically set the icon property for the wrapped {@link JLabel}. * When the icon wrapped by the provided property changes, * then so does the icon of this label. *

* But note that you may not use the {@link Icon} or {@link ImageIcon} classes directly, * instead you must use implementations of the {@link IconDeclaration} interface, * which merely models the resource location of the icon, but does not load * the whole icon itself. *

* The reason for this distinction is the fact that traditional Swing icons * are heavy objects whose loading may or may not succeed, and so they are * not suitable for direct use in a property as part of your view model. * Instead, you should use the {@link IconDeclaration} interface, which is a * lightweight value object that merely models the resource location of the icon * even if it is not yet loaded or even does not exist at all. *

* This is especially useful in case of unit tests for you view model, * where the icon may not be available at all, but you still want to test * the behaviour of your view model. * * @param icon The {@link Icon} property which should be displayed on the label. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code icon} is {@code null}. */ public UIForLabel withIcon( Val icon ) { NullUtil.nullArgCheck(icon,"icon",Val.class); return _withOnShow( icon, (thisComponent,v) -> { v.find().ifPresent(thisComponent::setIcon); }) ._with( thisComponent -> { icon.orElseThrow().find().ifPresent(thisComponent::setIcon); }) ._this(); } /** * Use this to set the size of the font of the wrapped {@link JLabel}. * @param size The size of the font which should be displayed on the label. * @return This very builder to allow for method chaining. */ public UIForLabel withFontSize( int size ) { return _with( thisComponent -> { Font f = thisComponent.getFont(); thisComponent.setFont(f.deriveFont((float)size)); }) ._this(); } /** * Use this to dynamically set the size of the font of the wrapped {@link JLabel} * through the provided view model property. * When the integer wrapped by the provided property changes, * then so does the font size of this label. * * @param size The size property of the font which should be displayed on the label. * @return This very builder to allow for method chaining. * @throws IllegalArgumentException if {@code size} is {@code null}. */ public UIForLabel withFontSize( Val size ) { NullUtil.nullArgCheck( size, "size", Val.class ); NullUtil.nullPropertyCheck( size, "size", "Use the default font size of this component instead of null!" ); return _withOnShow( size, (thisComponent,v) -> { Font f = thisComponent.getFont(); thisComponent.setFont(f.deriveFont((float)v)); }) ._with( thisComponent -> { Font f = thisComponent.getFont(); thisComponent.setFont(f.deriveFont((float)size.orElseThrow())); }) ._this(); } /** * Use this to set the font of the wrapped {@link JLabel}. * @param font The font of the text which should be displayed on the label. * @return This builder instance, to allow for method chaining. * @throws IllegalArgumentException if {@code font} is {@code null}. */ public final UIForLabel withFont( Font font ) { NullUtil.nullArgCheck(font, "font", Font.class, "Use 'UI.FONT_UNDEFINED' instead of null!"); return _with( thisComponent -> { if ( _isUndefinedFont(font) ) thisComponent.setFont(null); else thisComponent.setFont(font); }) ._this(); } /** * Use this to dynamically set the font of the wrapped {@link JLabel} * through the provided view model property. * When the font wrapped by the provided property changes, * then so does the font of this label. * * @param font The font property of the text which should be displayed on the label. * @return This builder instance, to allow for method chaining. * @throws IllegalArgumentException if {@code font} is {@code null}. * @throws IllegalArgumentException if {@code font} is a property which can wrap {@code null}. */ public final UIForLabel withFont( Val font ) { NullUtil.nullArgCheck(font, "font", Val.class); NullUtil.nullPropertyCheck(font, "font", "Use the default font of this component instead of null!"); return _withOnShow( font, (thisComponent,v) -> { if ( _isUndefinedFont(v) ) thisComponent.setFont(null); else thisComponent.setFont(v); }) ._with( thisComponent -> { thisComponent.setFont(font.orElseThrow()); }) ._this(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy