diff --git a/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java b/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java index 45dd62c6f..ed4c3ce25 100644 --- a/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/AbstractComponent.java @@ -73,6 +73,10 @@ static List addOne(final List oldList, final T newElement) { return newList; } + static IllegalStateException nothingComponentLike() { + return new IllegalStateException("Cannot compose a component from the arguments - nothing component-like was given"); + } + /** * The list of children. */ diff --git a/api/src/main/java/net/kyori/adventure/text/Component.java b/api/src/main/java/net/kyori/adventure/text/Component.java index 959e8a40d..849e8db2a 100644 --- a/api/src/main/java/net/kyori/adventure/text/Component.java +++ b/api/src/main/java/net/kyori/adventure/text/Component.java @@ -38,6 +38,7 @@ import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEventSource; import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.StyleBuilderApplicable; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.serializer.ComponentSerializer; @@ -157,6 +158,58 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex return builder.build(); } + /** + * Compose a series of elements (styles and contents) into a component. + * + *

Styles apply to all components after them until a conflicting style is discovered

+ *
+   *     Component message = Component.compose(NamedTextColor.RED, translatable("welcome.message"), TextDecoration.BOLD, text(" SERVER"));
+   *   
+ * In this example all the text is red, but only the last word is bold. + *
+   *     Component message = Component.compose(NamedTextColor.GREEN, text("I am green. "), NamedTextColor.GRAY, text("I am gray."));
+   *   
+ * In this example, the first text is green and the second is gray. + * + * @param applicables the things used to make the component + * @return a component + * @since 4.4.0 + */ + static @NonNull Component compose(final @NonNull ComponentBuilderApplicable@NonNull... applicables) { + final int length = applicables.length; + if(length == 0) return Component.empty(); + if(length == 1) { + final ComponentBuilderApplicable ap0 = applicables[0]; + if(ap0 instanceof ComponentLike) { + return ((ComponentLike) ap0).asComponent(); + } + throw AbstractComponent.nothingComponentLike(); + } + final TextComponentImpl.BuilderImpl builder = new TextComponentImpl.BuilderImpl(); + Style.Builder style = null; + for(int i = 0; i < length; i++) { + final ComponentBuilderApplicable applicable = applicables[i]; + if(applicable instanceof StyleBuilderApplicable) { + if(style == null) { + style = Style.style(); + } + style.apply((StyleBuilderApplicable) applicable); + } else if(style != null && applicable instanceof ComponentLike) { + builder.applicableApply(((ComponentLike) applicable).asComponent().style(style)); + } else { + builder.applicableApply(applicable); + } + } + final int size = builder.children.size(); + if(size == 0) { + throw AbstractComponent.nothingComponentLike(); + } else if(size == 1) { + return builder.children.get(0); + } else { + return builder.build(); + } + } + /* * --------------------------- * ---- BlockNBTComponent ---- diff --git a/api/src/main/java/net/kyori/adventure/text/LinearComponents.java b/api/src/main/java/net/kyori/adventure/text/LinearComponents.java index aa43bc6b1..b7d0f5906 100644 --- a/api/src/main/java/net/kyori/adventure/text/LinearComponents.java +++ b/api/src/main/java/net/kyori/adventure/text/LinearComponents.java @@ -24,7 +24,6 @@ package net.kyori.adventure.text; import net.kyori.adventure.text.format.Style; -import net.kyori.adventure.text.format.StyleBuilderApplicable; import org.checkerframework.checker.nullness.qual.NonNull; /** @@ -50,43 +49,11 @@ private LinearComponents() { * @param applicables the things used to make the component * @return a component * @since 4.0.0 + * @deprecated for removal since 4.4.0. Use {@link Component#compose(ComponentBuilderApplicable...)} instead */ + @Deprecated public static @NonNull Component linear(final @NonNull ComponentBuilderApplicable@NonNull... applicables) { - final int length = applicables.length; - if(length == 0) return Component.empty(); - if(length == 1) { - final ComponentBuilderApplicable ap0 = applicables[0]; - if(ap0 instanceof ComponentLike) { - return ((ComponentLike) ap0).asComponent(); - } - throw nothingComponentLike(); - } - final TextComponentImpl.BuilderImpl builder = new TextComponentImpl.BuilderImpl(); - Style.Builder style = null; - for(int i = 0; i < length; i++) { - final ComponentBuilderApplicable applicable = applicables[i]; - if(applicable instanceof StyleBuilderApplicable) { - if(style == null) { - style = Style.style(); - } - style.apply((StyleBuilderApplicable) applicable); - } else if(style != null && applicable instanceof ComponentLike) { - builder.applicableApply(((ComponentLike) applicable).asComponent().style(style)); - } else { - builder.applicableApply(applicable); - } - } - final int size = builder.children.size(); - if(size == 0) { - throw nothingComponentLike(); - } else if(size == 1) { - return builder.children.get(0); - } else { - return builder.build(); - } + return Component.compose(applicables); } - private static IllegalStateException nothingComponentLike() { - return new IllegalStateException("Cannot build component linearly - nothing component-like was given"); - } } diff --git a/api/src/test/java/net/kyori/adventure/text/LinearComponentsTest.java b/api/src/test/java/net/kyori/adventure/text/ComponentComposeTest.java similarity index 82% rename from api/src/test/java/net/kyori/adventure/text/LinearComponentsTest.java rename to api/src/test/java/net/kyori/adventure/text/ComponentComposeTest.java index 50187b8c5..582d0e289 100644 --- a/api/src/test/java/net/kyori/adventure/text/LinearComponentsTest.java +++ b/api/src/test/java/net/kyori/adventure/text/ComponentComposeTest.java @@ -34,28 +34,28 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; -class LinearComponentsTest { +class ComponentComposeTest { @Test void testEmpty() { - assertSame(Component.empty(), LinearComponents.linear()); + assertSame(Component.empty(), Component.compose()); } @Test void testNothingComponentLike() { - assertThrows(IllegalStateException.class, () -> LinearComponents.linear(TextDecoration.BOLD)); - assertThrows(IllegalStateException.class, () -> LinearComponents.linear(TextDecoration.BOLD, TextColor.color(0xaa0000))); + assertThrows(IllegalStateException.class, () -> Component.compose(TextDecoration.BOLD)); + assertThrows(IllegalStateException.class, () -> Component.compose(TextDecoration.BOLD, TextColor.color(0xaa0000))); } @Test void testSingleComponentLike() { final Component c0 = Component.text("kittens"); - assertSame(c0, LinearComponents.linear(c0)); + assertSame(c0, Component.compose(c0)); } @Test void testSimpleText() { final Component c0 = Component.text("kittens", NamedTextColor.DARK_PURPLE); - assertEquals(c0, LinearComponents.linear(NamedTextColor.DARK_PURPLE, Component.text().content("kittens"))); + assertEquals(c0, Component.compose(NamedTextColor.DARK_PURPLE, Component.text().content("kittens"))); } @Test @@ -64,7 +64,7 @@ void testAdvancedText() { .append(Component.text("kittens", NamedTextColor.DARK_PURPLE)) .append(Component.text("cats", Style.style(NamedTextColor.DARK_AQUA, TextDecoration.BOLD, HoverEvent.showText(Component.text("are adorable!"))))) .build(); - assertEquals(c0, LinearComponents.linear( + assertEquals(c0, Component.compose( NamedTextColor.DARK_PURPLE, Component.text().content("kittens"), NamedTextColor.DARK_AQUA, TextDecoration.BOLD, HoverEvent.showText(Component.text("are adorable!")), Component.text().content("cats") ));