diff --git a/api/src/main/java/net/kyori/adventure/text/JoinConfiguration.java b/api/src/main/java/net/kyori/adventure/text/JoinConfiguration.java index 4f7f11f839..c8e8620fae 100644 --- a/api/src/main/java/net/kyori/adventure/text/JoinConfiguration.java +++ b/api/src/main/java/net/kyori/adventure/text/JoinConfiguration.java @@ -25,6 +25,7 @@ import java.util.function.Function; import java.util.function.Predicate; +import net.kyori.adventure.text.format.Style; import net.kyori.adventure.util.Buildable; import net.kyori.examination.Examinable; import org.jetbrains.annotations.ApiStatus; @@ -61,6 +62,10 @@ * a predicate (required, defaults to {@code true}) *

a predicate that specifies if a given component should be included in the join process

* + *
  • + * a root {@link Style style} (required, defaults to {@link Style#empty()}) + *

    the style of the parent component that contains the joined components.

    + *
  • * * *

    Note that the last separator only acts as an override for the normal separator. @@ -227,6 +232,14 @@ public interface JoinConfiguration extends Buildable predicate(); + /** + * Gets the style of the parent component that contains the joined components. + * + * @return the style + * @since 4.10.0 + */ + @NotNull Style parentStyle(); + /** * A builder for join configurations. * @@ -299,7 +312,7 @@ interface Builder extends Buildable.Builder { @NotNull Builder convertor(final @NotNull Function convertor); /** - * Gets the predicate of this join configuration builder. + * Sets the predicate of this join configuration builder. * *

    This is used to determine if a component is to be included in the join process. It does not touch the prefix, suffix or any of the separators.

    * @@ -309,5 +322,15 @@ interface Builder extends Buildable.Builder { */ @Contract("_ -> this") @NotNull Builder predicate(final @NotNull Predicate predicate); + + /** + * Sets the style of the parent component that contains the joined components. + * + * @param parentStyle the style + * @return this builder + * @since 4.10.0 + */ + @Contract("_ -> this") + @NotNull Builder parentStyle(final @NotNull Style parentStyle); } } diff --git a/api/src/main/java/net/kyori/adventure/text/JoinConfigurationImpl.java b/api/src/main/java/net/kyori/adventure/text/JoinConfigurationImpl.java index f683503060..98a9d0129e 100644 --- a/api/src/main/java/net/kyori/adventure/text/JoinConfigurationImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/JoinConfigurationImpl.java @@ -28,6 +28,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; +import net.kyori.adventure.text.format.Style; import net.kyori.examination.ExaminableProperty; import net.kyori.examination.string.StringExaminer; import org.jetbrains.annotations.Contract; @@ -55,6 +56,7 @@ final class JoinConfigurationImpl implements JoinConfiguration { private final Component lastSeparatorIfSerial; private final Function convertor; private final Predicate predicate; + private final Style rootStyle; private JoinConfigurationImpl() { this.prefix = null; @@ -64,6 +66,7 @@ private JoinConfigurationImpl() { this.lastSeparatorIfSerial = null; this.convertor = DEFAULT_CONVERTOR; this.predicate = DEFAULT_PREDICATE; + this.rootStyle = Style.empty(); } private JoinConfigurationImpl(final @NotNull BuilderImpl builder) { @@ -74,6 +77,7 @@ private JoinConfigurationImpl(final @NotNull BuilderImpl builder) { this.lastSeparatorIfSerial = builder.lastSeparatorIfSerial == null ? null : builder.lastSeparatorIfSerial.asComponent(); this.convertor = builder.convertor; this.predicate = builder.predicate; + this.rootStyle = builder.rootStyle; } @Override @@ -111,6 +115,11 @@ private JoinConfigurationImpl(final @NotNull BuilderImpl builder) { return this.predicate; } + @Override + public @NotNull Style parentStyle() { + return this.rootStyle; + } + @Override public JoinConfiguration.@NotNull Builder toBuilder() { return new BuilderImpl(this); @@ -140,10 +149,6 @@ public String toString() { Objects.requireNonNull(components, "components"); final Iterator it = components.iterator(); - final Component prefix = config.prefix(); - final Component suffix = config.suffix(); - final Function convertor = config.convertor(); - final Predicate predicate = config.predicate(); if (!it.hasNext()) { return singleElementJoin(config, null); @@ -153,13 +158,26 @@ public String toString() { int componentsSeen = 0; if (!it.hasNext()) { - return singleElementJoin(config, component); + return singleElementJoin( + config, + component + ); } + final Component prefix = config.prefix(); + final Component suffix = config.suffix(); + final Function convertor = config.convertor(); + final Predicate predicate = config.predicate(); + final Style rootStyle = config.parentStyle(); + final boolean hasRootStyle = rootStyle != Style.empty(); + final Component separator = config.separator(); final boolean hasSeparator = separator != null; - final TextComponent.Builder builder = Component.text(); + final TextComponent.Builder builder = + hasRootStyle ? + Component.text().style(rootStyle) : + Component.text(); if (prefix != null) builder.append(prefix); while (component != null) { @@ -203,20 +221,24 @@ public String toString() { final Component suffix = config.suffix(); final Function convertor = config.convertor(); final Predicate predicate = config.predicate(); + final Style rootStyle = config.parentStyle(); + final boolean hasRootStyle = rootStyle != Style.empty(); if (prefix == null && suffix == null) { + final Component result; if (component == null || !predicate.test(component)) { - return Component.empty(); + result = Component.empty(); } else { - return convertor.apply(component); + result = convertor.apply(component); } + return hasRootStyle ? Component.text().style(rootStyle).append(result).build() : result; } final TextComponent.Builder builder = Component.text(); if (prefix != null) builder.append(prefix); if (component != null && predicate.test(component)) builder.append(convertor.apply(component)); if (suffix != null) builder.append(suffix); - return builder.build(); + return hasRootStyle ? Component.text().style(rootStyle).append(builder).build() : builder.build(); } static final class BuilderImpl implements JoinConfiguration.Builder { @@ -227,6 +249,7 @@ static final class BuilderImpl implements JoinConfiguration.Builder { private ComponentLike lastSeparatorIfSerial; private Function convertor; private Predicate predicate; + private Style rootStyle; BuilderImpl() { this(JoinConfigurationImpl.NULL); @@ -240,6 +263,7 @@ private BuilderImpl(final @NotNull JoinConfigurationImpl joinConfig) { this.convertor = joinConfig.convertor; this.lastSeparatorIfSerial = joinConfig.lastSeparatorIfSerial; this.predicate = joinConfig.predicate; + this.rootStyle = joinConfig.rootStyle; } @Override @@ -284,6 +308,12 @@ private BuilderImpl(final @NotNull JoinConfigurationImpl joinConfig) { return this; } + @Override + public @NotNull Builder parentStyle(final @NotNull Style parentStyle) { + this.rootStyle = Objects.requireNonNull(parentStyle, "rootStyle"); + return this; + } + @Override public @NotNull JoinConfiguration build() { return new JoinConfigurationImpl(this); diff --git a/api/src/test/java/net/kyori/adventure/text/JoinTest.java b/api/src/test/java/net/kyori/adventure/text/JoinTest.java index eece31b68b..72b728dea8 100644 --- a/api/src/test/java/net/kyori/adventure/text/JoinTest.java +++ b/api/src/test/java/net/kyori/adventure/text/JoinTest.java @@ -23,9 +23,13 @@ */ package net.kyori.adventure.text; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.stream.IntStream; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextDecoration; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; @@ -319,6 +323,54 @@ final void testStandardJoinConfigurationsArrayLike() { ); } + @Test + final void testJoinWithRootStyle() { + final Style style = Style.style(NamedTextColor.RED, TextDecoration.BOLD); + final Style epicStyle = Style.style(NamedTextColor.BLACK, TextDecoration.ITALIC); + final List componentsToJoin = new ArrayList<>(); + componentsToJoin.add(Component.text("FIRST")); + componentsToJoin.add(Component.text("SECOND", style)); + componentsToJoin.add(Component.text("THIRD")); + final Component result = Component.join(JoinConfiguration.builder().separator(Component.text(",")).parentStyle(epicStyle).build(), componentsToJoin); + + assertEquals( + Component.text().style(epicStyle) + .append(Component.text("FIRST")) + .append(Component.text(",")) + .append(Component.text("SECOND", style)) + .append(Component.text(",")) + .append(Component.text("THIRD")) + .build(), + result + ); + } + + @Test + final void testJoinWithRootStyleSingleComponent() { + final List componentsToJoin = new ArrayList<>(); + componentsToJoin.add(Component.text("FIRST")); + final Style epicStyle = Style.style(NamedTextColor.BLACK, TextDecoration.ITALIC); + final Component result = Component.join(JoinConfiguration.builder().separator(Component.text(",")).parentStyle(epicStyle).build(), componentsToJoin); + + assertEquals( + Component.text().style(epicStyle) + .append(Component.text("FIRST")) + .build(), + result + ); + } + + @Test + final void testJoinWithRootStyleNoComponents() { + final List componentsToJoin = new ArrayList<>(); + final Style epicStyle = Style.style(NamedTextColor.BLACK, TextDecoration.ITALIC); + final Component result = Component.join(JoinConfiguration.builder().separator(Component.text(", ")).parentStyle(epicStyle).build(), componentsToJoin); + + assertEquals( + Component.text().style(epicStyle).build(), + result); + } + private static final class TestComponentLike implements ComponentLike { @Override