diff --git a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java index 16cae74d..6607d92a 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/BaseComponent.java @@ -81,6 +81,8 @@ public abstract class BaseComponent setUnderlined( old.isUnderlined() ); setStrikethrough( old.isStrikethroughRaw() ); setObfuscated( old.isObfuscatedRaw() ); + setClickEvent( old.getClickEvent() ); + setHoverEvent( old.getHoverEvent() ); } /** diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java index 23d83ee2..55c66f30 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/ClickEvent.java @@ -7,41 +7,39 @@ import lombok.NoArgsConstructor; import lombok.Setter; @Getter -@Setter @AllArgsConstructor -@NoArgsConstructor -public class ClickEvent +final public class ClickEvent { /** * The type of action to preform on click */ - private Action action; + private final Action action; /** * Depends on action * @see net.md_5.bungee.api.chat.ClickEvent.Action */ - private String value; + private final String value; public enum Action { /** * Open a url at the path given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} */ OPEN_URL, /** * Open a file at the path given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} */ OPEN_FILE, /** * Run the command given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} */ RUN_COMMAND, /** * Inserts the string given by - * {@link net.md_5.bungee.api.chat.ClickEvent#setValue(String)} + * {@link net.md_5.bungee.api.chat.ClickEvent#getValue()} * into the players text box */ SUGGEST_COMMAND diff --git a/api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java b/api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java new file mode 100644 index 00000000..038ae5c7 --- /dev/null +++ b/api/src/main/java/net/md_5/bungee/api/chat/ComponentBuilder.java @@ -0,0 +1,157 @@ +package net.md_5.bungee.api.chat; + +import lombok.NoArgsConstructor; +import net.md_5.bungee.api.ChatColor; + +import java.util.ArrayList; +import java.util.List; + +/** + * ComponentBuilder simplifies creating basic messages by allowing + * the use of a chainable builder. + *

+ *

+ *     new ComponentBuilder("Hello ").color(ChatColor.RED).
+ *         append("World").color(ChatColor.BLUE).
+ *         append("!").bold(true).create();
+ * 
+ *

+ * All methods (excluding {@link #append(String)} and {@link #create()} + * work on the last part appended to the builder, so in the example + * above "Hello " would be {@link net.md_5.bungee.api.ChatColor#RED} + * and "World" would be {@link net.md_5.bungee.api.ChatColor#BLUE} but + * "!" would be bold and {@link net.md_5.bungee.api.ChatColor#BLUE} + * because append copies the previous part's formatting + */ +public class ComponentBuilder +{ + private TextComponent current; + private List parts = new ArrayList<>(); + + /** + * Creates a componentBuilder with the given text as the + * first part. + * + * @param text the first text element + */ + public ComponentBuilder(String text) + { + current = new TextComponent( text ); + } + + /** + * Appends the text to the builder and makes it the current + * target for formatting. The text will have all the + * formatting from the previous part. + * + * @param text the text to append + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder append(String text) + { + parts.add( current ); + current = new TextComponent( current ); + current.setText( text ); + return this; + } + + /** + * Sets the color of the current part. + * + * @param color the new color + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder color(ChatColor color) + { + current.setColor( color ); + return this; + } + + /** + * Sets whether the current part is bold. + * + * @param bold whether this part is bold + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder bold(boolean bold) + { + current.setBold( bold ); + return this; + } + + /** + * Sets whether the current part is italic + * + * @param italic whether this part is italic + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder italic(boolean italic) + { + current.setItalic( italic ); + return this; + } + + /** + * Sets whether the current part is underlined + * + * @param underlined whether this part is underlined + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder underlined(boolean underlined) + { + current.setUnderlined( underlined ); + return this; + } + + /** + * Sets whether the current part is strikethrough + * + * @param strikethrough whether this part is strikethrough + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder strikethrough(boolean strikethrough) + { + current.setStrikethrough( strikethrough ); + return this; + } + + /** + * Sets whether the current pat is obfuscated + * + * @param obfuscated whether this part is obfuscated + * @return this ComponentBuilder for chaining + */ + public ComponentBuilder obfuscated(boolean obfuscated) + { + current.setObfuscated( obfuscated ); + return this; + } + + /** + * Sets the click event for the current part. + * @param clickEvent + * @return + */ + public ComponentBuilder event(ClickEvent clickEvent) + { + current.setClickEvent( clickEvent ); + return this; + } + + public ComponentBuilder event(HoverEvent hoverEvent) + { + current.setHoverEvent( hoverEvent ); + return this; + } + + /** + * Returns the components needed to display the message + * created by this builder + * @return the created components + */ + public BaseComponent[] create() + { + parts.add( current ); + return parts.toArray( new BaseComponent[parts.size()] ); + } +} diff --git a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java index a1516173..c2df8f9c 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/HoverEvent.java @@ -5,14 +5,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -@NoArgsConstructor @Getter -@Setter @AllArgsConstructor -public class HoverEvent +final public class HoverEvent { - private Action action; - private BaseComponent value; + private final Action action; + private final BaseComponent[] value; public enum Action { diff --git a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java index 839e355d..e1c68c07 100644 --- a/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java +++ b/api/src/main/java/net/md_5/bungee/api/chat/TextComponent.java @@ -94,19 +94,10 @@ public class TextComponent extends BaseComponent TextComponent old = component; component = new TextComponent( old ); - ClickEvent clickEvent = new ClickEvent(); - clickEvent.setAction( ClickEvent.Action.OPEN_URL ); String urlString = message.substring( i, pos ); - if ( urlString.startsWith( "http" ) ) - { - component.setText( urlString ); - clickEvent.setValue( urlString ); - } else - { - component.setText( urlString ); - clickEvent.setValue( "http://" + urlString ); - } - component.setClickEvent( clickEvent ); + component.setText( urlString ); + component.setClickEvent( new ClickEvent( ClickEvent.Action.OPEN_URL, + urlString.startsWith( "http" ) ? urlString : "http://" + urlString ) ); components.add( component ); i += pos - i - 1; component = old; diff --git a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java index 62689570..7128dab9 100644 --- a/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/chat/BaseComponentSerializer.java @@ -55,11 +55,13 @@ public class BaseComponentSerializer if ( object.has( "hoverEvent" ) ) { JsonObject event = object.getAsJsonObject( "hoverEvent" ); - HoverEvent hoverEvent = new HoverEvent(); - hoverEvent.setAction( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ) ); - BaseComponent res = context.deserialize( event.get( "value" ), BaseComponent.class ); - hoverEvent.setValue( res ); - component.setHoverEvent( hoverEvent ); + BaseComponent[] res; + if (event.get("value").isJsonArray()) { + res = context.deserialize( event.get( "value" ), BaseComponent[].class ); + } else { + res = new BaseComponent[]{context.deserialize( event.get( "value" ), BaseComponent.class )}; + } + component.setHoverEvent( new HoverEvent( HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase() ), res ) ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java index 6e5ab23f..39f12ba1 100644 --- a/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java +++ b/proxy/src/main/java/net/md_5/bungee/command/CommandAlertRaw.java @@ -4,6 +4,7 @@ import com.google.common.base.Joiner; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.plugin.Command; @@ -34,15 +35,12 @@ public class CommandAlertRaw extends Command ProxyServer.getInstance().broadcast( ComponentSerializer.parse( message ) ); } catch ( Exception e ) { - TextComponent error = new TextComponent( "An error occurred while parsing your message. (Hover for details)" ); - error.setColor( ChatColor.RED ); - error.setUnderlined( true ); - - TextComponent errorMessage = new TextComponent( e.getMessage() ); - errorMessage.setColor( ChatColor.RED ); - - error.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, errorMessage ) ); - sender.sendMessage( error ); + sender.sendMessage( + new ComponentBuilder( "An error occured while parsing your message. (Hover for details)" ). + color( ChatColor.RED ).underlined( true ). + event( new HoverEvent( HoverEvent.Action.SHOW_TEXT, + new ComponentBuilder( e.getMessage() ).color( ChatColor.RED ).create() ) ). + create() ); } } } diff --git a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java index 584310d1..898c2b5f 100644 --- a/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java +++ b/proxy/src/test/java/net/md_5/bungee/chat/ComponentsTest.java @@ -3,6 +3,7 @@ package net.md_5.bungee.chat; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TranslatableComponent; import org.junit.Assert; @@ -66,4 +67,16 @@ public class ComponentsTest Assert.assertEquals( ChatColor.WHITE + "Page " + ChatColor.WHITE + "5" + ChatColor.WHITE + " of " + ChatColor.WHITE + "50", positional.toLegacyText() ); } + @Test + public void testBuilder() + { + BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ). + append( "World" ).bold( true ).color( ChatColor.BLUE ). + append( "!" ).color( ChatColor.YELLOW ).create(); + + Assert.assertEquals( "Hello World!", BaseComponent.toPlainText( components ) ); + Assert.assertEquals( ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD + + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", BaseComponent.toLegacyText( components ) ); + } + }