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 extends ComponentLike> 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