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 ) );
+ }
+
}