Refactor and javadoc pandalib-chat
This commit is contained in:
parent
62f5034ca0
commit
4e1a674c49
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,55 @@
|
|||||||
|
package fr.pandacube.lib.chat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom gradient with a least 2 colors in it.
|
||||||
|
*/
|
||||||
|
public class ChatColorGradient {
|
||||||
|
private record GradientColor(float location, TextColor color) { }
|
||||||
|
|
||||||
|
private final List<GradientColor> colors = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put a specific color at a specific location in the gradient.
|
||||||
|
* @param gradientLocation the location in the gradient.
|
||||||
|
* @param gradientColor the color to put at this location.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public synchronized ChatColorGradient add(float gradientLocation, TextColor gradientColor) {
|
||||||
|
colors.add(new GradientColor(gradientLocation, gradientColor));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute a color by interpolating between the 2 colors surrounding the provided location.
|
||||||
|
* @param gradientLocation the location at which to pick the gradient color.
|
||||||
|
* @return the computed color.
|
||||||
|
*/
|
||||||
|
public synchronized TextColor pickColorAt(float gradientLocation) {
|
||||||
|
if (colors.isEmpty())
|
||||||
|
throw new IllegalStateException("Must define at least one color in this ChatValueGradient instance.");
|
||||||
|
if (colors.size() == 1)
|
||||||
|
return colors.get(0).color();
|
||||||
|
|
||||||
|
colors.sort((p1, p2) -> Float.compare(p1.location(), p2.location()));
|
||||||
|
|
||||||
|
if (gradientLocation <= colors.get(0).location())
|
||||||
|
return colors.get(0).color();
|
||||||
|
if (gradientLocation >= colors.get(colors.size() - 1).location())
|
||||||
|
return colors.get(colors.size() - 1).color();
|
||||||
|
|
||||||
|
int p1 = 1;
|
||||||
|
for (; p1 < colors.size(); p1++) {
|
||||||
|
if (colors.get(p1).location() >= gradientLocation)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int p0 = p1 - 1;
|
||||||
|
float v0 = colors.get(p0).location(), v1 = colors.get(p1).location();
|
||||||
|
TextColor cc0 = colors.get(p0).color(), cc1 = colors.get(p1).color();
|
||||||
|
return ChatColorUtil.interpolateColor(v0, v1, gradientLocation, cc0, cc1);
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +1,37 @@
|
|||||||
package fr.pandacube.lib.chat;
|
package fr.pandacube.lib.chat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import net.kyori.adventure.util.RGBLike;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides methods to manipulate legacy colors and {@link ChatColor} class.
|
||||||
|
*/
|
||||||
public class ChatColorUtil {
|
public class ChatColorUtil {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All characters that represent a colorcode.
|
||||||
|
*/
|
||||||
public static final String ALL_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoPpRr";
|
|
||||||
public static final String ALL_COLORS = "0123456789AaBbCcDdEeFf";
|
public static final String ALL_COLORS = "0123456789AaBbCcDdEeFf";
|
||||||
|
/**
|
||||||
|
* All characters that represent a color or format code.
|
||||||
|
*/
|
||||||
|
public static final String ALL_CODES = ALL_COLORS + "KkLlMmNnOoPpRr";
|
||||||
|
|
||||||
|
|
||||||
private static final Pattern HEX_COLOR_PATTERN = Pattern.compile("§x(?>§[\\da-f]){6}", Pattern.CASE_INSENSITIVE);
|
private static final Pattern HEX_COLOR_PATTERN = Pattern.compile("§x(?>§[\\da-f]){6}", Pattern.CASE_INSENSITIVE);
|
||||||
private static final Pattern ESS_COLOR_PATTERN = Pattern.compile("§#[\\da-f]{6}", Pattern.CASE_INSENSITIVE);
|
private static final Pattern ESS_COLOR_PATTERN = Pattern.compile("§#[\\da-f]{6}", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the legacy format needed to reproduce the format at the end of the provided legacy text.
|
* Returns the legacy format needed to reproduce the format at the end of the provided legacy text.
|
||||||
* Supports standard chat colors and formats, BungeeCord Chat rgb format and EssentialsX rgb format.
|
* Supports standard chat colors and formats, BungeeCord Chat rgb format and EssentialsX rgb format.
|
||||||
* The RGB value from EssentialsX format is converted to BungeeCord Chat when included in the returned value.
|
* The RGB value from EssentialsX format is converted to BungeeCord Chat when included in the returned value.
|
||||||
|
* @param legacyText the legacy formated text.
|
||||||
|
* @return the active format at the end of the provided text.
|
||||||
*/
|
*/
|
||||||
public static String getLastColors(String legacyText) {
|
public static String getLastColors(String legacyText) {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
@ -76,6 +83,11 @@ public class ChatColorUtil {
|
|||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link ChatColor} associated with the provided char, case insensitive.
|
||||||
|
* @param code the case insensitive char code.
|
||||||
|
* @return the corresponding {@link ChatColor}.
|
||||||
|
*/
|
||||||
public static ChatColor getChatColorByChar(char code) {
|
public static ChatColor getChatColorByChar(char code) {
|
||||||
return ChatColor.getByChar(Character.toLowerCase(code));
|
return ChatColor.getByChar(Character.toLowerCase(code));
|
||||||
}
|
}
|
||||||
@ -84,8 +96,8 @@ public class ChatColorUtil {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate the color code of the provided string, that uses the the color char, to
|
* Translate the color code of the provided string, that uses the alt color char, to the {@code §} color code
|
||||||
* the {@code §} color code format.
|
* format.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is the improved version of {@link ChatColor#translateAlternateColorCodes(char, String)},
|
* This method is the improved version of {@link ChatColor#translateAlternateColorCodes(char, String)},
|
||||||
* because it takes into account essentials RGB color code, and {@code altColorChar} escaping (by doubling it).
|
* because it takes into account essentials RGB color code, and {@code altColorChar} escaping (by doubling it).
|
||||||
@ -94,6 +106,9 @@ public class ChatColorUtil {
|
|||||||
* <p>
|
* <p>
|
||||||
* This method should be used for user input (no permission check) or string configuration, but not string
|
* This method should be used for user input (no permission check) or string configuration, but not string
|
||||||
* from another API or containing URLs.
|
* from another API or containing URLs.
|
||||||
|
* @param altColorChar the alternative character to prefix color codes (usually {@code '&'}).
|
||||||
|
* @param textToTranslate the text to translate.
|
||||||
|
* @return the string translated to proper legacy text.
|
||||||
*/
|
*/
|
||||||
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate)
|
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate)
|
||||||
{
|
{
|
||||||
@ -161,8 +176,8 @@ public class ChatColorUtil {
|
|||||||
/**
|
/**
|
||||||
* Force a text to be italic, while keeping other formatting and colors.
|
* Force a text to be italic, while keeping other formatting and colors.
|
||||||
* The text is prefixed with the ITALIC tag, but is not reset at the end.
|
* The text is prefixed with the ITALIC tag, but is not reset at the end.
|
||||||
* @param legacyText the original text
|
* @param legacyText the original text.
|
||||||
* @return the text fully italic
|
* @return the text fully italic.
|
||||||
*/
|
*/
|
||||||
public static String forceItalic(String legacyText) {
|
public static String forceItalic(String legacyText) {
|
||||||
return forceFormat(legacyText, ChatColor.ITALIC);
|
return forceFormat(legacyText, ChatColor.ITALIC);
|
||||||
@ -171,8 +186,8 @@ public class ChatColorUtil {
|
|||||||
/**
|
/**
|
||||||
* Force a text to be bold, while keeping other formatting and colors.
|
* Force a text to be bold, while keeping other formatting and colors.
|
||||||
* The text is prefixed with the BOLD tag, but is not reset at the end.
|
* The text is prefixed with the BOLD tag, but is not reset at the end.
|
||||||
* @param legacyText the original text
|
* @param legacyText the original text.
|
||||||
* @return the text fully bold
|
* @return the text fully bold.
|
||||||
*/
|
*/
|
||||||
public static String forceBold(String legacyText) {
|
public static String forceBold(String legacyText) {
|
||||||
return forceFormat(legacyText, ChatColor.BOLD);
|
return forceFormat(legacyText, ChatColor.BOLD);
|
||||||
@ -181,8 +196,8 @@ public class ChatColorUtil {
|
|||||||
/**
|
/**
|
||||||
* Force a text to be underlined, while keeping other formatting and colors.
|
* Force a text to be underlined, while keeping other formatting and colors.
|
||||||
* The text is prefixed with the UNDERLINE tag, but is not reset at the end.
|
* The text is prefixed with the UNDERLINE tag, but is not reset at the end.
|
||||||
* @param legacyText the original text
|
* @param legacyText the original text.
|
||||||
* @return the text fully underlined
|
* @return the text fully underlined.
|
||||||
*/
|
*/
|
||||||
public static String forceUnderline(String legacyText) {
|
public static String forceUnderline(String legacyText) {
|
||||||
return forceFormat(legacyText, ChatColor.UNDERLINE);
|
return forceFormat(legacyText, ChatColor.UNDERLINE);
|
||||||
@ -191,8 +206,8 @@ public class ChatColorUtil {
|
|||||||
/**
|
/**
|
||||||
* Force a text to be stroked through, while keeping other formatting and colors.
|
* Force a text to be stroked through, while keeping other formatting and colors.
|
||||||
* The text is prefixed with the STRIKETHROUGH tag, but is not reset at the end.
|
* The text is prefixed with the STRIKETHROUGH tag, but is not reset at the end.
|
||||||
* @param legacyText the original text
|
* @param legacyText the original text.
|
||||||
* @return the text fully stroked through
|
* @return the text fully stroked through.
|
||||||
*/
|
*/
|
||||||
public static String forceStrikethrough(String legacyText) {
|
public static String forceStrikethrough(String legacyText) {
|
||||||
return forceFormat(legacyText, ChatColor.STRIKETHROUGH);
|
return forceFormat(legacyText, ChatColor.STRIKETHROUGH);
|
||||||
@ -201,8 +216,8 @@ public class ChatColorUtil {
|
|||||||
/**
|
/**
|
||||||
* Force a text to be obfuscated, while keeping other formatting and colors.
|
* Force a text to be obfuscated, while keeping other formatting and colors.
|
||||||
* The text is prefixed with the MAGIC tag, but is not reset at the end.
|
* The text is prefixed with the MAGIC tag, but is not reset at the end.
|
||||||
* @param legacyText the original text
|
* @param legacyText the original text.
|
||||||
* @return the text fully obfuscated
|
* @return the text fully obfuscated.
|
||||||
*/
|
*/
|
||||||
public static String forceObfuscated(String legacyText) {
|
public static String forceObfuscated(String legacyText) {
|
||||||
return forceFormat(legacyText, ChatColor.MAGIC);
|
return forceFormat(legacyText, ChatColor.MAGIC);
|
||||||
@ -224,8 +239,8 @@ public class ChatColorUtil {
|
|||||||
* Replace the RESET tag of the input string to the specified color tag.
|
* Replace the RESET tag of the input string to the specified color tag.
|
||||||
* @param legacyText the original text
|
* @param legacyText the original text
|
||||||
* @param color the color to used to replace the RESET tag
|
* @param color the color to used to replace the RESET tag
|
||||||
* (can be a combination of a color tag followed by multiple format tag)
|
* (can be a combination of a color tag followed by multiple format tag).
|
||||||
* @return the resulting text
|
* @return the resulting text.
|
||||||
*/
|
*/
|
||||||
public static String resetToColor(String legacyText, String color) {
|
public static String resetToColor(String legacyText, String color) {
|
||||||
return legacyText.replace(ChatColor.RESET.toString(), color);
|
return legacyText.replace(ChatColor.RESET.toString(), color);
|
||||||
@ -234,6 +249,11 @@ public class ChatColorUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the provided {@link ChatColor} to its Adventure counterpart.
|
||||||
|
* @param bungee a BungeeCord {@link ChatColor} instance.
|
||||||
|
* @return the {@link TextColor} equivalent to the provided {@link ChatColor}.
|
||||||
|
*/
|
||||||
public static TextColor toAdventure(ChatColor bungee) {
|
public static TextColor toAdventure(ChatColor bungee) {
|
||||||
if (bungee == null)
|
if (bungee == null)
|
||||||
return null;
|
return null;
|
||||||
@ -242,6 +262,11 @@ public class ChatColorUtil {
|
|||||||
return TextColor.color(bungee.getColor().getRGB());
|
return TextColor.color(bungee.getColor().getRGB());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the provided {@link TextColor} to its BungeeCord counterpart.
|
||||||
|
* @param col a Adventure {@link TextColor} instance.
|
||||||
|
* @return the {@link ChatColor} equivalent to the provided {@link TextColor}.
|
||||||
|
*/
|
||||||
public static ChatColor toBungee(TextColor col) {
|
public static ChatColor toBungee(TextColor col) {
|
||||||
if (col == null)
|
if (col == null)
|
||||||
return null;
|
return null;
|
||||||
@ -252,52 +277,20 @@ public class ChatColorUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a color, interpolating between 2 colors.
|
||||||
|
* @param v0 the value corresponding to color {@code cc0}.
|
||||||
|
* @param v1 the value corresponding to color {@code cc1}.
|
||||||
|
* @param v the value between {@code v0} and {@code v1} to interpolate.
|
||||||
|
* @param cc0 the first color.
|
||||||
|
* @param cc1 the second color.
|
||||||
|
* @return the interpolated color.
|
||||||
|
* @see TextColor#lerp(float, RGBLike, RGBLike)
|
||||||
|
*/
|
||||||
public static TextColor interpolateColor(float v0, float v1, float v, TextColor cc0, TextColor cc1) {
|
public static TextColor interpolateColor(float v0, float v1, float v, TextColor cc0, TextColor cc1) {
|
||||||
float normV = (v - v0) / (v1 - v0);
|
float normV = (v - v0) / (v1 - v0);
|
||||||
return TextColor.lerp(normV, cc0, cc1);
|
return TextColor.lerp(normV, cc0, cc1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class ChatValueGradient {
|
|
||||||
private record GradientValueColor(float value, TextColor color) { } // Java 16
|
|
||||||
|
|
||||||
final List<GradientValueColor> colors = new ArrayList<>();
|
|
||||||
|
|
||||||
public synchronized ChatValueGradient add(float v, TextColor col) {
|
|
||||||
colors.add(new GradientValueColor(v, col));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized TextColor pickColorAt(float v) {
|
|
||||||
if (colors.isEmpty())
|
|
||||||
throw new IllegalStateException("Must define at least one color in this ChatValueGradient instance.");
|
|
||||||
if (colors.size() == 1)
|
|
||||||
return colors.get(0).color();
|
|
||||||
|
|
||||||
colors.sort((p1, p2) -> Float.compare(p1.value(), p2.value()));
|
|
||||||
|
|
||||||
if (v <= colors.get(0).value())
|
|
||||||
return colors.get(0).color();
|
|
||||||
if (v >= colors.get(colors.size() - 1).value())
|
|
||||||
return colors.get(colors.size() - 1).color();
|
|
||||||
|
|
||||||
int p1 = 1;
|
|
||||||
for (; p1 < colors.size(); p1++) {
|
|
||||||
if (colors.get(p1).value() >= v)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int p0 = p1 - 1;
|
|
||||||
float v0 = colors.get(p0).value(), v1 = colors.get(p1).value();
|
|
||||||
TextColor cc0 = colors.get(p0).color(), cc1 = colors.get(p1).color();
|
|
||||||
return interpolateColor(v0, v1, v, cc0, cc1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package fr.pandacube.lib.chat;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class holding static configuration values for chat component rendering.
|
||||||
|
*/
|
||||||
|
public class ChatConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for decoration.
|
||||||
|
*/
|
||||||
|
public static TextColor decorationColor = NamedTextColor.YELLOW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The character used as a pattern for decoration.
|
||||||
|
*/
|
||||||
|
public static char decorationChar = '-';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default margin for left and right aligned text.
|
||||||
|
*/
|
||||||
|
public static int nbCharMargin = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for successful messages.
|
||||||
|
*/
|
||||||
|
public static TextColor successColor = NamedTextColor.GREEN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for error/failure messages.
|
||||||
|
*/
|
||||||
|
public static TextColor failureColor = NamedTextColor.RED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the color used for informational messages.
|
||||||
|
*/
|
||||||
|
public static TextColor infoColor = NamedTextColor.GOLD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for warning messages.
|
||||||
|
*/
|
||||||
|
public static TextColor warningColor = NamedTextColor.GOLD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used to display data in a message.
|
||||||
|
*/
|
||||||
|
public static TextColor dataColor = NamedTextColor.GRAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for displayed URLs and clickable URLs.
|
||||||
|
*/
|
||||||
|
public static TextColor urlColor = NamedTextColor.GREEN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for displayed commands and clickable commands.
|
||||||
|
*/
|
||||||
|
public static TextColor commandColor = NamedTextColor.GRAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color sued to display a command that is highlighted. For example, the current page in a pagination.
|
||||||
|
*/
|
||||||
|
public static TextColor highlightedCommandColor = NamedTextColor.WHITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for broadcasted messages.
|
||||||
|
* It is often used in combination with {@link #prefix}.
|
||||||
|
*/
|
||||||
|
public static TextColor broadcastColor = NamedTextColor.YELLOW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix used for prefixed messages.
|
||||||
|
* It can be a sylized name of the server, like {@code "[Pandacube] "}.
|
||||||
|
* It is often used in combination with {@link #broadcastColor}.
|
||||||
|
*/
|
||||||
|
public static Supplier<Chat> prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the width of the configured {@link #prefix}.
|
||||||
|
* @param console if the width has to be calculated for the console or not.
|
||||||
|
* @return the width of the configured {@link #prefix}.
|
||||||
|
*/
|
||||||
|
public static int getPrefixWidth(boolean console) {
|
||||||
|
Chat c;
|
||||||
|
return prefix == null ? 0 : (c = prefix.get()) == null ? 0 : ChatUtil.componentWidth(c.getAdv(), console);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
package fr.pandacube.lib.chat;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.ComponentLike;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import fr.pandacube.lib.chat.Chat.FormatableChat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for a {@link Chat} component for filling a chat line, with decoration and eventual aligned text.
|
||||||
|
*/
|
||||||
|
public class ChatFilledLine implements ComponentLike {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for a filled line with the provided left-aligned text.
|
||||||
|
* @param text the text to align ont the left.
|
||||||
|
* @return a new {@link ChatFilledLine} builder.
|
||||||
|
*/
|
||||||
|
public static ChatFilledLine leftText(ComponentLike text) {
|
||||||
|
return new ChatFilledLine(text, Alignment.LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for a filled line with the provided right-aligned text.
|
||||||
|
* @param text the text to align ont the right.
|
||||||
|
* @return a new {@link ChatFilledLine} builder.
|
||||||
|
*/
|
||||||
|
public static ChatFilledLine rightText(ComponentLike text) {
|
||||||
|
return new ChatFilledLine(text, Alignment.RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for a filled line with the provided centered text.
|
||||||
|
* @param text the text to center.
|
||||||
|
* @return a new {@link ChatFilledLine} builder.
|
||||||
|
*/
|
||||||
|
public static ChatFilledLine centerText(ComponentLike text) {
|
||||||
|
return new ChatFilledLine(text, Alignment.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for a filled line with no text.
|
||||||
|
* @return a new {@link ChatFilledLine} builder.
|
||||||
|
*/
|
||||||
|
public static ChatFilledLine filled() {
|
||||||
|
return new ChatFilledLine(null, Alignment.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private final ComponentLike text;
|
||||||
|
private final Alignment alignment;
|
||||||
|
private char decorationChar = ChatConfig.decorationChar;
|
||||||
|
private TextColor decorationColor = ChatConfig.decorationColor;
|
||||||
|
private boolean decorationBold = false;
|
||||||
|
private int nbSide = ChatConfig.nbCharMargin;
|
||||||
|
private boolean spacesAroundText = false;
|
||||||
|
private boolean console = false;
|
||||||
|
private Integer maxWidth = null;
|
||||||
|
|
||||||
|
private ChatFilledLine(ComponentLike text, Alignment alignment) {
|
||||||
|
this.text = text;
|
||||||
|
this.alignment = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the decoration char.
|
||||||
|
* @param decoChar the character that will fill the line.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatFilledLine decoChar(char decoChar) {
|
||||||
|
decorationChar = decoChar;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the decoration color.
|
||||||
|
* @param decoColor the color of the characters filling the line.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatFilledLine decoColor(TextColor decoColor) {
|
||||||
|
decorationColor = decoColor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the decoration in bold.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatFilledLine decoBold() {
|
||||||
|
decorationBold = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of side character when the text is aligned left or right.
|
||||||
|
* @param nbSide the number of character that will separate the border from the side of the text.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatFilledLine nbSide(int nbSide) {
|
||||||
|
this.nbSide = nbSide;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds spaces around the text.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatFilledLine spacesAroundText() {
|
||||||
|
spacesAroundText = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure if the line will be rendered on console or not.
|
||||||
|
* @param console true for console, false for game UI.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatFilledLine console(boolean console) {
|
||||||
|
this.console = console;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the width of the line.
|
||||||
|
* @param maxWidth the width to consider when rendering the line. In pixel for game UI rendering, n character for
|
||||||
|
* console rendering.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatFilledLine maxWidth(int maxWidth) {
|
||||||
|
this.maxWidth = maxWidth;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders this line to a {@link FormatableChat}.
|
||||||
|
* @return a new {@link FormatableChat} builded by this {@link ChatFilledLine}.
|
||||||
|
*/
|
||||||
|
public FormatableChat toChat() {
|
||||||
|
int maxWidth = (this.maxWidth != null)
|
||||||
|
? this.maxWidth
|
||||||
|
: console ? ChatUtil.CONSOLE_NB_CHAR_DEFAULT : ChatUtil.DEFAULT_CHAT_WIDTH;
|
||||||
|
|
||||||
|
if (alignment == Alignment.NONE) {
|
||||||
|
int count = maxWidth / ChatUtil.charW(decorationChar, console, decorationBold);
|
||||||
|
return Chat.text(ChatUtil.repeatedChar(decorationChar, count)).color(decorationColor).bold(decorationBold);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentLike text = spacesAroundText
|
||||||
|
? Chat.text(" ").then(this.text).thenText(" ")
|
||||||
|
: this.text;
|
||||||
|
|
||||||
|
int textWidth = ChatUtil.componentWidth(text.asComponent(), console);
|
||||||
|
|
||||||
|
if (textWidth > maxWidth)
|
||||||
|
return (FormatableChat) text;
|
||||||
|
|
||||||
|
int repeatedCharWidth = ChatUtil.charW(decorationChar, console, decorationBold);
|
||||||
|
int nbCharLeft = 0, nbCharRight = 0;
|
||||||
|
|
||||||
|
switch (alignment) {
|
||||||
|
case CENTER -> {
|
||||||
|
nbCharLeft = nbCharRight = (maxWidth - textWidth) / 2 / repeatedCharWidth;
|
||||||
|
if (nbCharLeft == 0)
|
||||||
|
return (FormatableChat) text;
|
||||||
|
}
|
||||||
|
case LEFT, RIGHT -> {
|
||||||
|
int remWidth = textWidth + nbSide * repeatedCharWidth;
|
||||||
|
if (remWidth > maxWidth)
|
||||||
|
return (FormatableChat) text;
|
||||||
|
boolean left = alignment == Alignment.LEFT;
|
||||||
|
int nbOtherSide = (maxWidth - remWidth) / repeatedCharWidth;
|
||||||
|
nbCharLeft = left ? nbSide : nbOtherSide;
|
||||||
|
nbCharRight = left ? nbOtherSide : nbSide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Chat d = Chat.chat()
|
||||||
|
.then(Chat.text(ChatUtil.repeatedChar(decorationChar, nbCharLeft)).color(decorationColor).bold(decorationBold))
|
||||||
|
.then(text);
|
||||||
|
if (decorationChar != ' ')
|
||||||
|
d.then(Chat.text(ChatUtil.repeatedChar(decorationChar, nbCharRight)).color(decorationColor).bold(decorationBold));
|
||||||
|
return (FormatableChat) d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Component asComponent() {
|
||||||
|
return toChat().asComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private enum Alignment {
|
||||||
|
LEFT,
|
||||||
|
CENTER,
|
||||||
|
RIGHT,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
}
|
@ -2,44 +2,82 @@ package fr.pandacube.lib.chat;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.BlockNBTComponent;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.ComponentBuilder;
|
||||||
import net.kyori.adventure.text.ComponentLike;
|
import net.kyori.adventure.text.ComponentLike;
|
||||||
|
import net.kyori.adventure.text.EntityNBTComponent;
|
||||||
|
import net.kyori.adventure.text.KeybindComponent;
|
||||||
|
import net.kyori.adventure.text.ScoreComponent;
|
||||||
|
import net.kyori.adventure.text.SelectorComponent;
|
||||||
|
import net.kyori.adventure.text.StorageNBTComponent;
|
||||||
|
import net.kyori.adventure.text.TextComponent;
|
||||||
|
import net.kyori.adventure.text.TranslatableComponent;
|
||||||
|
import net.kyori.adventure.text.event.HoverEventSource;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
|
||||||
import fr.pandacube.lib.chat.Chat.FormatableChat;
|
import fr.pandacube.lib.chat.Chat.FormatableChat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class holding the publicly accessible methods to create an instance of {@link Chat} component.
|
||||||
|
*/
|
||||||
public abstract class ChatStatic {
|
public abstract class ChatStatic {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static FormatableChat chatComponent(Component c) {
|
private static FormatableChat chatComponent(Component c) {
|
||||||
return new FormatableChat(Chat.componentToBuilder(c));
|
return new FormatableChat(componentToBuilder(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent}.
|
||||||
|
* @param c the {@link BaseComponent}.
|
||||||
|
* @return a new {@link FormatableChat}.
|
||||||
|
*/
|
||||||
public static FormatableChat chatComponent(BaseComponent c) {
|
public static FormatableChat chatComponent(BaseComponent c) {
|
||||||
return new FormatableChat(Chat.componentToBuilder(Chat.toAdventure(c)));
|
return new FormatableChat(componentToBuilder(Chat.toAdventure(c)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} from the provided {@link ComponentLike}.
|
||||||
|
* If the provided component is an instance of {@link Chat}, its content will be duplicated, and the provided one
|
||||||
|
* will be untouched.
|
||||||
|
* @param c the {@link ComponentLike}.
|
||||||
|
* @return a new {@link FormatableChat}.
|
||||||
|
*/
|
||||||
public static FormatableChat chatComponent(ComponentLike c) {
|
public static FormatableChat chatComponent(ComponentLike c) {
|
||||||
return chatComponent(c.asComponent());
|
return chatComponent(c.asComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with an empty main text content.
|
||||||
|
* @return a new empty {@link FormatableChat}.
|
||||||
|
*/
|
||||||
public static FormatableChat chat() {
|
public static FormatableChat chat() {
|
||||||
return new FormatableChat(Component.text());
|
return new FormatableChat(Component.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} from the provided Bungee {@link BaseComponent BaseComponent[]}.
|
||||||
|
* @param c the array of {@link BaseComponent}.
|
||||||
|
* @return a new {@link FormatableChat}.
|
||||||
|
*/
|
||||||
public static FormatableChat chatComponent(BaseComponent[] c) {
|
public static FormatableChat chatComponent(BaseComponent[] c) {
|
||||||
return chatComponent(Chat.toAdventure(c));
|
return chatComponent(Chat.toAdventure(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Chat instance with the provided plain text as its main text content.
|
* Creates a {@link FormatableChat} with the provided plain text as its main text content.
|
||||||
*
|
* @param plainText the text to use as the content.
|
||||||
* @param plainText the text to use as he content of the new Chat instance.
|
* @return a new {@link FormatableChat} with the provided text as its main text content.
|
||||||
* @return a Chat instance with the provided text as its main text content.
|
|
||||||
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
|
||||||
* instead.
|
* instead.
|
||||||
@ -53,10 +91,9 @@ public abstract class ChatStatic {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Chat instance with the provided legacy text as its main text content.
|
* Creates a {@link FormatableChat} with the provided legacy text as its content.
|
||||||
*
|
* @param legacyText the legacy text to use as the content.
|
||||||
* @param legacyText the text to use as he content of the new Chat instance.
|
* @return a new {@link FormatableChat} with the provided text as its content.
|
||||||
* @return a Chat instance with the provided text as its main text content.
|
|
||||||
* @throws IllegalArgumentException If the {@code plainText} parameter is instance of {@link Chat} or
|
* @throws IllegalArgumentException If the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)}
|
||||||
* instead.
|
* instead.
|
||||||
@ -68,55 +105,159 @@ public abstract class ChatStatic {
|
|||||||
return chatComponent(LegacyComponentSerializer.legacySection().deserialize(Objects.toString(legacyText)));
|
return chatComponent(LegacyComponentSerializer.legacySection().deserialize(Objects.toString(legacyText)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
|
||||||
|
* {@link ChatConfig#infoColor configured info color}.
|
||||||
|
* @param plainText the text to use as the content.
|
||||||
|
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
|
||||||
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
|
||||||
|
* {@link FormatableChat#infoColor()} instead.
|
||||||
|
*/
|
||||||
public static FormatableChat infoText(Object plainText) {
|
public static FormatableChat infoText(Object plainText) {
|
||||||
return text(plainText).infoColor();
|
return text(plainText).infoColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
|
||||||
|
* {@link ChatConfig#warningColor configured warning color}.
|
||||||
|
* @param plainText the text to use as the content.
|
||||||
|
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
|
||||||
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
|
||||||
|
* {@link FormatableChat#warningColor()} instead.
|
||||||
|
*/
|
||||||
public static FormatableChat warningText(Object plainText) {
|
public static FormatableChat warningText(Object plainText) {
|
||||||
return text(plainText).warningColor();
|
return text(plainText).warningColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
|
||||||
|
* {@link ChatConfig#dataColor configured data color}.
|
||||||
|
* @param plainText the text to use as the content.
|
||||||
|
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
|
||||||
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
|
||||||
|
* {@link FormatableChat#dataColor()} instead.
|
||||||
|
*/
|
||||||
public static FormatableChat dataText(Object plainText) {
|
public static FormatableChat dataText(Object plainText) {
|
||||||
return text(plainText).dataColor();
|
return text(plainText).dataColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
|
||||||
|
* {@link ChatConfig#decorationColor configured decorationColor color}.
|
||||||
|
* @param plainText the text to use as the content.
|
||||||
|
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
|
||||||
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
|
||||||
|
* {@link FormatableChat#decorationColor()} instead.
|
||||||
|
*/
|
||||||
public static FormatableChat decorationText(Object plainText) {
|
public static FormatableChat decorationText(Object plainText) {
|
||||||
return text(plainText).decorationColor();
|
return text(plainText).decorationColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
|
||||||
|
* {@link ChatConfig#successColor configured success color}.
|
||||||
|
* @param plainText the text to use as the content.
|
||||||
|
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
|
||||||
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
|
||||||
|
* {@link FormatableChat#successColor()} instead.
|
||||||
|
*/
|
||||||
public static FormatableChat successText(Object plainText) {
|
public static FormatableChat successText(Object plainText) {
|
||||||
return text(plainText).successColor();
|
return text(plainText).successColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided plain text as its main text content, and colored using the
|
||||||
|
* {@link ChatConfig#failureColor configured failure color}.
|
||||||
|
* @param plainText the text to use as the content.
|
||||||
|
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
|
||||||
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
|
||||||
|
* {@link FormatableChat#failureColor()} instead.
|
||||||
|
*/
|
||||||
public static FormatableChat failureText(Object plainText) {
|
public static FormatableChat failureText(Object plainText) {
|
||||||
return text(plainText).failureColor();
|
return text(plainText).failureColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided legacy text as its main text content, and colored in white in
|
||||||
|
* case there is no color on the generated parent component.
|
||||||
|
* @param legacyText the legacy text to use as the content.
|
||||||
|
* @return a new {@link FormatableChat} with the provided text as its main text content, and the configured color.
|
||||||
|
* @throws IllegalArgumentException if the {@code plainText} parameter is instance of {@link Chat} or
|
||||||
|
* {@link Component}. The caller should use {@link #chatComponent(ComponentLike)} and
|
||||||
|
* {@link FormatableChat#failureColor()} instead.
|
||||||
|
*/
|
||||||
public static FormatableChat playerNameText(String legacyText) {
|
public static FormatableChat playerNameText(String legacyText) {
|
||||||
FormatableChat fc = legacyText(legacyText);
|
FormatableChat fc = legacyText(legacyText);
|
||||||
fc.builder.colorIfAbsent(NamedTextColor.WHITE);
|
fc.builder.colorIfAbsent(NamedTextColor.WHITE);
|
||||||
return fc;
|
return fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} from the provided {@link Component}, coloring in white the generated parent
|
||||||
|
* component in case there is no color defined.
|
||||||
|
* If the provided component is an instance of {@link Chat}, its content will be duplicated, and the provided one
|
||||||
|
* will be untouched.
|
||||||
|
* @param c the {@link Component}.
|
||||||
|
* @return a new {@link FormatableChat}.
|
||||||
|
*/
|
||||||
public static FormatableChat playerNameComponent(Component c) {
|
public static FormatableChat playerNameComponent(Component c) {
|
||||||
FormatableChat fc = chatComponent(c);
|
FormatableChat fc = chatComponent(c);
|
||||||
fc.builder.colorIfAbsent(NamedTextColor.WHITE);
|
fc.builder.colorIfAbsent(NamedTextColor.WHITE);
|
||||||
return fc;
|
return fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided translation key and parameters.
|
||||||
|
* @param key the translation key.
|
||||||
|
* @param with the translation parameters.
|
||||||
|
* @return a new {@link FormatableChat} with the provided translation key and parameters.
|
||||||
|
*/
|
||||||
public static FormatableChat translation(String key, Object... with) {
|
public static FormatableChat translation(String key, Object... with) {
|
||||||
return new FormatableChat(Component.translatable().key(key).args(Chat.filterObjToComponentLike(with)));
|
return new FormatableChat(Component.translatable().key(key).args(Chat.filterObjToComponentLike(with)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided keybind.
|
||||||
|
* @param key the keybind to display.
|
||||||
|
* @return a new {@link FormatableChat} with the provided keybind.
|
||||||
|
*/
|
||||||
public static FormatableChat keybind(String key) {
|
public static FormatableChat keybind(String key) {
|
||||||
return new FormatableChat(Component.keybind().keybind(key));
|
return new FormatableChat(Component.keybind().keybind(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} with the provided score name and objective.
|
||||||
|
* @param name the score name.
|
||||||
|
* @param objective the score objective.
|
||||||
|
* @return a new {@link FormatableChat} with the provided score name and objective.
|
||||||
|
*/
|
||||||
public static FormatableChat score(String name, String objective) {
|
public static FormatableChat score(String name, String objective) {
|
||||||
return new FormatableChat(Component.score().name(name).objective(objective));
|
return new FormatableChat(Component.score().name(name).objective(objective));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableURL(Chat inner, String url, Chat hover) {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
* @param inner the component to make clickable.
|
||||||
|
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
|
||||||
|
* @param hover the content to display when hovering the component.
|
||||||
|
* @return a new {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableURL(ComponentLike inner, String url, HoverEventSource<?> hover) {
|
||||||
Objects.requireNonNull(url, "url");
|
Objects.requireNonNull(url, "url");
|
||||||
if (inner == null)
|
if (inner == null)
|
||||||
inner = text(url);
|
inner = text(url);
|
||||||
@ -125,19 +266,61 @@ public abstract class ChatStatic {
|
|||||||
return (FormatableChat) chat().clickURL(url).urlColor().hover(hover).then(inner);
|
return (FormatableChat) chat().clickURL(url).urlColor().hover(hover).then(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableURL(Chat inner, String url) {
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
* <p>
|
||||||
|
* When hovered, the component will display the url. To customize the hover content, use
|
||||||
|
* {@link #clickableURL(ComponentLike, String, HoverEventSource)}.
|
||||||
|
* @param inner the component to make clickable.
|
||||||
|
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
|
||||||
|
* @return a new {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableURL(ComponentLike inner, String url) {
|
||||||
return clickableURL(inner, url, null);
|
return clickableURL(inner, url, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableURL(String url, Chat hover) {
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
* <p>
|
||||||
|
* The text on which to click will be the URL itself. To configure the clicked text, use
|
||||||
|
* {@link #clickableURL(ComponentLike, String, HoverEventSource)}.
|
||||||
|
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
|
||||||
|
* @param hover the content to display when hovering the component.
|
||||||
|
* @return a new {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableURL(String url, HoverEventSource<?> hover) {
|
||||||
return clickableURL(null, url, hover);
|
return clickableURL(null, url, hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
* <p>
|
||||||
|
* The text on which to click will be the URL itself. To configure the clicked text, use
|
||||||
|
* {@link #clickableURL(ComponentLike, String)}.
|
||||||
|
* <p>
|
||||||
|
* When hovered, the component will display the url. To customize the hover content, use
|
||||||
|
* {@link #clickableURL(String, HoverEventSource)}.
|
||||||
|
* @param url the target url. Must start with {@code "http://"} or {@code "https://"}.
|
||||||
|
* @return a new {@link FormatableChat} that leads to a URL when clicked.
|
||||||
|
*/
|
||||||
public static FormatableChat clickableURL(String url) {
|
public static FormatableChat clickableURL(String url) {
|
||||||
return clickableURL(null, url, null);
|
return clickableURL(null, url, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableCommand(Chat inner, String commandWithSlash, Chat hover) {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* @param inner the component to make clickable.
|
||||||
|
* @param commandWithSlash the command to run. Must start with {@code "/"}.
|
||||||
|
* @param hover the content to display when hovering the component.
|
||||||
|
* @return a new {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableCommand(ComponentLike inner, String commandWithSlash, HoverEventSource<?> hover) {
|
||||||
Objects.requireNonNull(commandWithSlash, "commandWithSlash");
|
Objects.requireNonNull(commandWithSlash, "commandWithSlash");
|
||||||
if (!commandWithSlash.startsWith("/"))
|
if (!commandWithSlash.startsWith("/"))
|
||||||
throw new IllegalArgumentException("commandWithSlash must start with a '/' character.");
|
throw new IllegalArgumentException("commandWithSlash must start with a '/' character.");
|
||||||
@ -148,19 +331,64 @@ public abstract class ChatStatic {
|
|||||||
return (FormatableChat) chat().clickCommand(commandWithSlash).commandColor().hover(hover).then(inner);
|
return (FormatableChat) chat().clickCommand(commandWithSlash).commandColor().hover(hover).then(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableCommand(Chat inner, String commandWithSlash) {
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* <p>
|
||||||
|
* When hovered, the component will display the command itself. To customize the hover content, use
|
||||||
|
* {@link #clickableCommand(ComponentLike, String, HoverEventSource)}.
|
||||||
|
* @param inner the component to make clickable.
|
||||||
|
* @param commandWithSlash the command to run. Must start with {@code "/"}.
|
||||||
|
* @return a new {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableCommand(ComponentLike inner, String commandWithSlash) {
|
||||||
return clickableCommand(inner, commandWithSlash, null);
|
return clickableCommand(inner, commandWithSlash, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableCommand(String commandWithSlash, Chat hover) {
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* <p>
|
||||||
|
* The text on which to click will be the command itself. To configure the clicked text, use
|
||||||
|
* {@link #clickableCommand(ComponentLike, String, HoverEventSource)}.
|
||||||
|
* @param commandWithSlash the command to run. Must start with {@code "/"}.
|
||||||
|
* @param hover the content to display when hovering the component.
|
||||||
|
* @return a new {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableCommand(String commandWithSlash, HoverEventSource<?> hover) {
|
||||||
return clickableCommand(null, commandWithSlash, hover);
|
return clickableCommand(null, commandWithSlash, hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* <p>
|
||||||
|
* The text on which to click will be the command itself. To configure the clicked text, use
|
||||||
|
* {@link #clickableCommand(ComponentLike, String)}.
|
||||||
|
* <p>
|
||||||
|
* When hovered, the component will display the command itself. To customize the hover content, use
|
||||||
|
* {@link #clickableCommand(String, HoverEventSource)}.
|
||||||
|
* @param commandWithSlash the command to run. Must start with {@code "/"}.
|
||||||
|
* @return a new {@link FormatableChat} that runs a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
public static FormatableChat clickableCommand(String commandWithSlash) {
|
public static FormatableChat clickableCommand(String commandWithSlash) {
|
||||||
return clickableCommand(null, commandWithSlash, null);
|
return clickableCommand(null, commandWithSlash, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableSuggest(Chat inner, String commandWithSlash, Chat hover) {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* @param inner the component to make clickable.
|
||||||
|
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
|
||||||
|
* @param hover the content to display when hovering the component.
|
||||||
|
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableSuggest(ComponentLike inner, String commandWithSlash, HoverEventSource<?> hover) {
|
||||||
Objects.requireNonNull(commandWithSlash, "commandWithSlash");
|
Objects.requireNonNull(commandWithSlash, "commandWithSlash");
|
||||||
if (!commandWithSlash.startsWith("/"))
|
if (!commandWithSlash.startsWith("/"))
|
||||||
throw new IllegalArgumentException("commandWithSlash must start with a '/' character.");
|
throw new IllegalArgumentException("commandWithSlash must start with a '/' character.");
|
||||||
@ -171,14 +399,46 @@ public abstract class ChatStatic {
|
|||||||
return (FormatableChat) chat().clickSuggest(commandWithSlash).commandColor().hover(hover).then(inner);
|
return (FormatableChat) chat().clickSuggest(commandWithSlash).commandColor().hover(hover).then(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableSuggest(Chat inner, String commandWithSlash) {
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* <p>
|
||||||
|
* When hovered, the component will display the command itself. To customize the hover content, use
|
||||||
|
* {@link #clickableSuggest(ComponentLike, String, HoverEventSource)}.
|
||||||
|
* @param inner the component to make clickable.
|
||||||
|
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
|
||||||
|
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableSuggest(ComponentLike inner, String commandWithSlash) {
|
||||||
return clickableSuggest(inner, commandWithSlash, null);
|
return clickableSuggest(inner, commandWithSlash, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatableChat clickableSuggest(String commandWithSlash, Chat hover) {
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* <p>
|
||||||
|
* The text on which to click will be the command itself. To configure the clicked text, use
|
||||||
|
* {@link #clickableSuggest(ComponentLike, String, HoverEventSource)}.
|
||||||
|
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
|
||||||
|
* @param hover the content to display when hovering the component.
|
||||||
|
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
|
public static FormatableChat clickableSuggest(String commandWithSlash, HoverEventSource<?> hover) {
|
||||||
return clickableSuggest(null, commandWithSlash, hover);
|
return clickableSuggest(null, commandWithSlash, hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* <p>
|
||||||
|
* The text on which to click will be the command itself. To configure the clicked text, use
|
||||||
|
* {@link #clickableSuggest(ComponentLike, String)}.
|
||||||
|
* <p>
|
||||||
|
* When hovered, the component will display the command itself. To customize the hover content, use
|
||||||
|
* {@link #clickableSuggest(String, HoverEventSource)}.
|
||||||
|
* @param commandWithSlash the command to suggest. Must start with {@code "/"}.
|
||||||
|
* @return a new {@link FormatableChat} that pre-fill the chat box with a command when clicked.
|
||||||
|
* @throws IllegalArgumentException if {@code commandWithSlash} does not start with a {@code "/"}.
|
||||||
|
*/
|
||||||
public static FormatableChat clickableSuggest(String commandWithSlash) {
|
public static FormatableChat clickableSuggest(String commandWithSlash) {
|
||||||
return clickableSuggest(null, commandWithSlash, null);
|
return clickableSuggest(null, commandWithSlash, null);
|
||||||
}
|
}
|
||||||
@ -188,13 +448,195 @@ public abstract class ChatStatic {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with decoration and a left-aligned text.
|
||||||
|
* @param text the text aligned to the left.
|
||||||
|
* @param decorationChar the character used for decoration around the text.
|
||||||
|
* @param decorationColor the color used for the decoration characters.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with decoration and a left-aligned text.
|
||||||
|
* @see ChatFilledLine#leftText(ComponentLike)
|
||||||
|
*/
|
||||||
|
public static FormatableChat leftText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
|
||||||
|
return ChatFilledLine.leftText(text).decoChar(decorationChar).decoColor(decorationColor).spacesAroundText().console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with the configured decoration character and
|
||||||
|
* color and a left-aligned text.
|
||||||
|
* @param text the text aligned to the left.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with the configured decoration character
|
||||||
|
* and color and a left-aligned text.
|
||||||
|
* @see ChatFilledLine#leftText(ComponentLike)
|
||||||
|
* @see ChatConfig#decorationChar
|
||||||
|
* @see ChatConfig#decorationColor
|
||||||
|
*/
|
||||||
|
public static FormatableChat leftText(ComponentLike text, boolean console) {
|
||||||
|
return ChatFilledLine.leftText(text).spacesAroundText().console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with decoration and a right-aligned text.
|
||||||
|
* @param text the text aligned to the right.
|
||||||
|
* @param decorationChar the character used for decoration around the text.
|
||||||
|
* @param decorationColor the color used for the decoration characters.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with decoration and a right-aligned
|
||||||
|
* text.
|
||||||
|
* @see ChatFilledLine#rightText(ComponentLike)
|
||||||
|
*/
|
||||||
|
public static FormatableChat rightText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
|
||||||
|
return ChatFilledLine.rightText(text).decoChar(decorationChar).decoColor(decorationColor).spacesAroundText().console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with the configured decoration character and
|
||||||
|
* color and a right-aligned text.
|
||||||
|
* @param text the text aligned to the right.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with the configured decoration character
|
||||||
|
* and color and a right-aligned text.
|
||||||
|
* @see ChatFilledLine#rightText(ComponentLike)
|
||||||
|
* @see ChatConfig#decorationChar
|
||||||
|
* @see ChatConfig#decorationColor
|
||||||
|
*/
|
||||||
|
public static FormatableChat rightText(ComponentLike text, boolean console) {
|
||||||
|
return ChatFilledLine.rightText(text).spacesAroundText().console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with decoration and a centered text.
|
||||||
|
* @param text the text aligned to the center.
|
||||||
|
* @param decorationChar the character used for decoration around the text.
|
||||||
|
* @param decorationColor the color used for the decoration characters.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with decoration and a centered text.
|
||||||
|
* @see ChatFilledLine#centerText(ComponentLike)
|
||||||
|
*/
|
||||||
|
public static FormatableChat centerText(ComponentLike text, char decorationChar, TextColor decorationColor, boolean console) {
|
||||||
|
return ChatFilledLine.centerText(text).decoChar(decorationChar).decoColor(decorationColor).spacesAroundText().console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with the configured decoration character and
|
||||||
|
* color and a centered text.
|
||||||
|
* @param text the text aligned to the center.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with the configured decoration character
|
||||||
|
* and color and a centered text.
|
||||||
|
* @see ChatFilledLine#centerText(ComponentLike)
|
||||||
|
* @see ChatConfig#decorationChar
|
||||||
|
* @see ChatConfig#decorationColor
|
||||||
|
*/
|
||||||
|
public static FormatableChat centerText(ComponentLike text, boolean console) {
|
||||||
|
return ChatFilledLine.centerText(text).spacesAroundText().console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with a decoration character and color.
|
||||||
|
* @param decorationChar the character used for decoration.
|
||||||
|
* @param decorationColor the color used for the decoration characters.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with a decoration character and color.
|
||||||
|
* @see ChatFilledLine#filled()
|
||||||
|
*/
|
||||||
|
public static FormatableChat filledLine(char decorationChar, TextColor decorationColor, boolean console) {
|
||||||
|
return ChatFilledLine.filled().decoChar(decorationChar).decoColor(decorationColor).console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link FormatableChat} filling a line of chat (or console) with the configured decoration character and
|
||||||
|
* color.
|
||||||
|
* @param console if the line is rendered on console (true) or IG (false).
|
||||||
|
* @return a new {@link FormatableChat} filling a line of chat (or console) with a decoration character and color.
|
||||||
|
* @see ChatFilledLine#filled()
|
||||||
|
* @see ChatConfig#decorationChar
|
||||||
|
* @see ChatConfig#decorationColor
|
||||||
|
*/
|
||||||
|
public static FormatableChat filledLine(boolean console) {
|
||||||
|
return ChatFilledLine.filled().console(console).toChat();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the configured prefix and broadcast color to the provided message.
|
||||||
|
* @param message the message to decorate.
|
||||||
|
* @return the decorated message.
|
||||||
|
*/
|
||||||
public static Chat prefixedAndColored(ComponentLike message) {
|
public static Chat prefixedAndColored(ComponentLike message) {
|
||||||
return Chat.chat()
|
return Chat.chat()
|
||||||
.broadcastColor()
|
.broadcastColor()
|
||||||
.then(Chat.getConfig().prefix.get())
|
.then(ChatConfig.prefix.get())
|
||||||
.then(message);
|
.then(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static ComponentBuilder<?, ?> componentToBuilder(Component c) {
|
||||||
|
ComponentBuilder<?, ?> builder;
|
||||||
|
if (c instanceof TextComponent) {
|
||||||
|
builder = Component.text()
|
||||||
|
.content(((TextComponent) c).content());
|
||||||
|
}
|
||||||
|
else if (c instanceof TranslatableComponent) {
|
||||||
|
builder = Component.translatable()
|
||||||
|
.key(((TranslatableComponent) c).key())
|
||||||
|
.args(((TranslatableComponent) c).args());
|
||||||
|
}
|
||||||
|
else if (c instanceof SelectorComponent) {
|
||||||
|
builder = Component.selector()
|
||||||
|
.pattern(((SelectorComponent) c).pattern());
|
||||||
|
}
|
||||||
|
else if (c instanceof ScoreComponent) {
|
||||||
|
builder = Component.score()
|
||||||
|
.name(((ScoreComponent) c).name())
|
||||||
|
.objective(((ScoreComponent) c).objective());
|
||||||
|
}
|
||||||
|
else if (c instanceof KeybindComponent) {
|
||||||
|
builder = Component.keybind()
|
||||||
|
.keybind(((KeybindComponent) c).keybind());
|
||||||
|
}
|
||||||
|
else if (c instanceof BlockNBTComponent) {
|
||||||
|
builder = Component.blockNBT()
|
||||||
|
.interpret(((BlockNBTComponent) c).interpret())
|
||||||
|
.nbtPath(((BlockNBTComponent) c).nbtPath())
|
||||||
|
.pos(((BlockNBTComponent) c).pos());
|
||||||
|
}
|
||||||
|
else if (c instanceof EntityNBTComponent) {
|
||||||
|
builder = Component.entityNBT()
|
||||||
|
.interpret(((EntityNBTComponent) c).interpret())
|
||||||
|
.nbtPath(((EntityNBTComponent) c).nbtPath())
|
||||||
|
.selector(((EntityNBTComponent) c).selector());
|
||||||
|
}
|
||||||
|
else if (c instanceof StorageNBTComponent) {
|
||||||
|
builder = Component.storageNBT()
|
||||||
|
.interpret(((StorageNBTComponent) c).interpret())
|
||||||
|
.nbtPath(((StorageNBTComponent) c).nbtPath())
|
||||||
|
.storage(((StorageNBTComponent) c).storage());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Unknows component type " + c.getClass());
|
||||||
|
}
|
||||||
|
return builder.style(c.style()).append(c.children());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
package fr.pandacube.lib.chat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tree structure of {@link Chat} component intended to be rendered using {@link #render(boolean)}.
|
||||||
|
*/
|
||||||
|
public class ChatTreeNode {
|
||||||
|
|
||||||
|
private static final String TREE_MIDDLE_CONNECTED = "├";
|
||||||
|
private static final String TREE_END_CONNECTED = "└";
|
||||||
|
private static final String TREE_MIDDLE_OPEN = "│§0`§r";
|
||||||
|
private static final String TREE_END_OPEN = "§0```§r";
|
||||||
|
private static final String TREE_MIDDLE_OPEN_CONSOLE = "│";
|
||||||
|
private static final String TREE_END_OPEN_CONSOLE = " "; // nbsp
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The component for the current node.
|
||||||
|
*/
|
||||||
|
public final Chat component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Children nodes.
|
||||||
|
*/
|
||||||
|
public final List<ChatTreeNode> children = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an new {@link ChatTreeNode}.
|
||||||
|
* @param cmp the component for the current node.
|
||||||
|
*/
|
||||||
|
public ChatTreeNode(Chat cmp) {
|
||||||
|
component = cmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a child to the current node.
|
||||||
|
* @param child the child to add.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public ChatTreeNode addChild(ChatTreeNode child) {
|
||||||
|
children.add(child);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a tree view based on this tree structure.
|
||||||
|
* <p>
|
||||||
|
* Each element in the returned list represent 1 line of this tree view.
|
||||||
|
* Thus, the caller may send each line separately or at once depending of the quantity of data.
|
||||||
|
* @param console true to render for console, false otherwise.
|
||||||
|
* @return an array of component, each element being a single line.
|
||||||
|
*/
|
||||||
|
public List<Chat> render(boolean console) {
|
||||||
|
List<Chat> ret = new ArrayList<>();
|
||||||
|
|
||||||
|
ret.add(ChatStatic.chat()
|
||||||
|
.then(component));
|
||||||
|
|
||||||
|
for (int i = 0; i < children.size(); i++) {
|
||||||
|
List<Chat> childComponents = children.get(i).render(console);
|
||||||
|
boolean last = i == children.size() - 1;
|
||||||
|
for (int j = 0; j < childComponents.size(); j++) {
|
||||||
|
|
||||||
|
String prefix = last ? (j == 0 ? TREE_END_CONNECTED : (console ? TREE_END_OPEN_CONSOLE : TREE_END_OPEN))
|
||||||
|
: (j == 0 ? TREE_MIDDLE_CONNECTED : (console ? TREE_MIDDLE_OPEN_CONSOLE : TREE_MIDDLE_OPEN));
|
||||||
|
|
||||||
|
ret.add(ChatStatic.text(prefix)
|
||||||
|
.then(childComponents.get(j)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -98,91 +98,6 @@ public class ChatUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link Chat} that is a cliquable URL link.
|
|
||||||
* It is equivalent to the HTML {@code <a>} tag pointing to another page.
|
|
||||||
* @param text the link text.
|
|
||||||
* @param url the destination url. must starts with {@code http} or {@code https}.
|
|
||||||
* @return a {@link Chat} that is a cliquable URL link.
|
|
||||||
* @deprecated it uses String for displayed text. Use {@link Chat#clickableURL(Chat, String)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "2022-07-27")
|
|
||||||
public static FormatableChat createURLLink(String text, String url) {
|
|
||||||
return Chat.clickableURL(text == null ? null : Chat.legacyText(text), url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link Chat} that is a cliquable URL link.
|
|
||||||
* It is equivalent to the HTML {@code <a>} tag pointing to another page.
|
|
||||||
* @param text the link text.
|
|
||||||
* @param url the destination url. must starts with {@code http} or {@code https}.
|
|
||||||
* @param hoverText the text displayed when hovering the link.
|
|
||||||
* @return a {@link Chat} that is a cliquable URL link.
|
|
||||||
* @deprecated it uses String for displayed text. Use {@link Chat#clickableURL(Chat, String, Chat)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "2022-07-27")
|
|
||||||
public static FormatableChat createURLLink(String text, String url, String hoverText) {
|
|
||||||
return Chat.clickableURL(text == null ? null : Chat.legacyText(text), url, hoverText == null ? null : Chat.legacyText(hoverText));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link Chat} that is a cliquable command link.
|
|
||||||
* When the players clicks on it, they will execute the command.
|
|
||||||
* @param text the link text.
|
|
||||||
* @param commandWithSlash the command to execute when clicked.
|
|
||||||
* @param hoverText the text displayed when hovering the link.
|
|
||||||
* @return a {@link Chat} that is a cliquable command link.
|
|
||||||
* @deprecated it uses String for displayed text. Use {@link Chat#clickableCommand(Chat, String, Chat)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "2022-07-27")
|
|
||||||
public static FormatableChat createCommandLink(String text, String commandWithSlash, String hoverText) {
|
|
||||||
return Chat.clickableCommand(text == null ? null : Chat.legacyText(text), commandWithSlash, hoverText == null ? null : Chat.legacyText(hoverText));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link Chat} that is a cliquable command link.
|
|
||||||
* When the players clicks on it, they will execute the command.
|
|
||||||
* @param text the link text.
|
|
||||||
* @param commandWithSlash the command to execute when clicked.
|
|
||||||
* @param hoverText the text displayed when hovering the link.
|
|
||||||
* @return a {@link Chat} that is a cliquable command link.
|
|
||||||
* @deprecated it uses String for displayed text. Use {@link Chat#clickableCommand(Chat, String, Chat)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "2022-07-27")
|
|
||||||
public static FormatableChat createCommandLink(String text, String commandWithSlash, Chat hoverText) {
|
|
||||||
return Chat.clickableCommand(text == null ? null : Chat.legacyText(text), commandWithSlash, hoverText);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link Chat} that is a cliquable command suggestion.
|
|
||||||
* When the players clicks on it, they will execute the command.
|
|
||||||
* @param inner the link text.
|
|
||||||
* @param commandWithSlash the command to put in the chat box when clicked.
|
|
||||||
* @param hover the text displayed when hovering the link.
|
|
||||||
* @return a {@link Chat} that is a cliquable command suggestion.
|
|
||||||
* @deprecated it uses String for displayed text. Use {@link Chat#clickableSuggest(Chat, String, Chat)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "2022-07-27")
|
|
||||||
public static FormatableChat createCommandSuggest(String inner, String commandWithSlash, String hover) {
|
|
||||||
return Chat.clickableSuggest(inner == null ? null : Chat.legacyText(inner), commandWithSlash, hover == null ? null : Chat.legacyText(hover));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a {@link Chat} that is a cliquable command suggestion.
|
|
||||||
* When the players clicks on it, they will execute the command.
|
|
||||||
* @param inner the link text.
|
|
||||||
* @param commandWithSlash the command to put in the chat box when clicked.
|
|
||||||
* @param hover the text displayed when hovering the link.
|
|
||||||
* @return a {@link Chat} that is a cliquable command suggestion.
|
|
||||||
* @deprecated it uses String for displayed text. Use {@link Chat#clickableSuggest(Chat, String, Chat)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "2022-07-27")
|
|
||||||
public static FormatableChat createCommandSuggest(String inner, String commandWithSlash, Chat hover) {
|
|
||||||
return Chat.clickableSuggest(inner == null ? null : Chat.legacyText(inner), commandWithSlash, hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -224,7 +139,7 @@ public class ChatUtil {
|
|||||||
else {
|
else {
|
||||||
if (cmdFormat.endsWith("%d")) {
|
if (cmdFormat.endsWith("%d")) {
|
||||||
d.thenText(" ");
|
d.thenText(" ");
|
||||||
d.then(Chat.clickableSuggest(Chat.text("..."), cmdFormat.substring(0, cmdFormat.length() - 2), Chat.text("Choisir la page")));
|
d.thenCommandSuggest(Chat.text("..."), cmdFormat.substring(0, cmdFormat.length() - 2), Chat.text("Choisir la page"));
|
||||||
d.thenText(" ");
|
d.thenText(" ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -254,133 +169,34 @@ public class ChatUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param decorationColor support null values
|
|
||||||
*/
|
|
||||||
public static Chat centerText(Chat text, char repeatedChar, TextColor decorationColor, boolean console) {
|
|
||||||
return centerText(text, repeatedChar, decorationColor, console, console ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH);
|
|
||||||
}
|
|
||||||
public static Chat centerText(Chat text, char repeatedChar, TextColor decorationColor, boolean console, int maxWidth) {
|
|
||||||
return centerText(text, repeatedChar, decorationColor, false, console, maxWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param decorationColor support null values
|
|
||||||
*/
|
|
||||||
public static Chat centerText(Chat text, char repeatedChar, TextColor decorationColor, boolean decorationBold, boolean console, int maxWidth) {
|
|
||||||
|
|
||||||
int textWidth = componentWidth(text.getAdv(), console);
|
/* package */ static String repeatedChar(char repeatedChar, int count) {
|
||||||
|
|
||||||
if (textWidth > maxWidth)
|
|
||||||
return text;
|
|
||||||
|
|
||||||
int repeatedCharWidth = charW(repeatedChar, console, decorationBold);
|
|
||||||
int sideWidth = (maxWidth - textWidth) / 2;
|
|
||||||
int sideNbChar = sideWidth / repeatedCharWidth;
|
|
||||||
|
|
||||||
if (sideNbChar == 0)
|
|
||||||
return text;
|
|
||||||
|
|
||||||
String sideChars = repeatedChar(repeatedChar, sideNbChar);
|
|
||||||
FormatableChat side = ChatStatic.text(sideChars).color(decorationColor);
|
|
||||||
if (decorationBold)
|
|
||||||
side.bold();
|
|
||||||
|
|
||||||
Chat d = Chat.chat()
|
|
||||||
.then(side)
|
|
||||||
.then(text);
|
|
||||||
if (repeatedChar != ' ')
|
|
||||||
d.then(side);
|
|
||||||
|
|
||||||
return d;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chat leftText(Chat text, char repeatedChar, TextColor decorationColor, int nbLeft, boolean console) {
|
|
||||||
return leftText(text, repeatedChar, decorationColor, nbLeft, console, console ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chat leftText(Chat text, char repeatedChar, TextColor decorationColor, int nbLeft, boolean console, int maxWidth) {
|
|
||||||
|
|
||||||
int textWidth = componentWidth(text.getAdv(), console);
|
|
||||||
int repeatedCharWidth = charW(repeatedChar, console, false);
|
|
||||||
int leftWidth = nbLeft * repeatedCharWidth;
|
|
||||||
|
|
||||||
if (textWidth + leftWidth > maxWidth)
|
|
||||||
return text;
|
|
||||||
|
|
||||||
int rightNbChar = (maxWidth - (textWidth + leftWidth)) / repeatedCharWidth;
|
|
||||||
|
|
||||||
Chat d = ChatStatic.chat()
|
|
||||||
.then(ChatStatic.text(repeatedChar(repeatedChar, nbLeft)).color(decorationColor))
|
|
||||||
.then(text);
|
|
||||||
if (repeatedChar != ' ') {
|
|
||||||
d.then(ChatStatic.text(repeatedChar(repeatedChar, rightNbChar)).color(decorationColor));
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chat rightText(Chat text, char repeatedChar, TextColor decorationColor, int nbRight, boolean console) {
|
|
||||||
return rightText(text, repeatedChar, decorationColor, nbRight, console, console ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chat rightText(Chat text, char repeatedChar, TextColor decorationColor, int nbRight,
|
|
||||||
boolean console, int maxWidth) {
|
|
||||||
|
|
||||||
int textWidth = componentWidth(text.getAdv(), console);
|
|
||||||
int repeatedCharWidth = charW(repeatedChar, console, false);
|
|
||||||
int rightWidth = nbRight * repeatedCharWidth;
|
|
||||||
|
|
||||||
if (textWidth + rightWidth > maxWidth)
|
|
||||||
return text;
|
|
||||||
|
|
||||||
int leftNbChar = (maxWidth - (textWidth + rightWidth)) / repeatedCharWidth;
|
|
||||||
|
|
||||||
Chat d = ChatStatic.chat()
|
|
||||||
.then(ChatStatic.text(repeatedChar(repeatedChar, leftNbChar)).color(decorationColor))
|
|
||||||
.then(text);
|
|
||||||
if (repeatedChar != ' ') {
|
|
||||||
d.then(ChatStatic.text(repeatedChar(repeatedChar, nbRight)).color(decorationColor));
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chat emptyLine(char repeatedChar, TextColor decorationColor, boolean console) {
|
|
||||||
return emptyLine(repeatedChar, decorationColor, false, console);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chat emptyLine(char repeatedChar, TextColor decorationColor, boolean decorationBold, boolean console) {
|
|
||||||
return emptyLine(repeatedChar, decorationColor, decorationBold, console, (console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Chat emptyLine(char repeatedChar, TextColor decorationColor, boolean decorationBold, boolean console, int maxWidth) {
|
|
||||||
int count = maxWidth / charW(repeatedChar, console, decorationBold);
|
|
||||||
FormatableChat line = ChatStatic.text(repeatedChar(repeatedChar, count)).color(decorationColor);
|
|
||||||
if (decorationBold)
|
|
||||||
line.bold();
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String repeatedChar(char repeatedChar, int count) {
|
|
||||||
char[] c = new char[count];
|
char[] c = new char[count];
|
||||||
Arrays.fill(c, repeatedChar);
|
Arrays.fill(c, repeatedChar);
|
||||||
return new String(c);
|
return new String(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the width of the provided component.
|
||||||
|
* @param component the component to compute the width.
|
||||||
|
* @param console true to compute the width when displayed on console (so it will count the characters),
|
||||||
|
* false to compute the width when displayed in game (so it will count the pixels).
|
||||||
|
* @return the width of the provided component.
|
||||||
|
*/
|
||||||
public static int componentWidth(Component component, boolean console) {
|
public static int componentWidth(Component component, boolean console) {
|
||||||
return componentWidth(component, console, false);
|
return componentWidth(component, console, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the width of the provided component, with extra information about the parent component.
|
||||||
|
* @param component the component to compute the width.
|
||||||
|
* @param console true to compute the width when displayed on console (so it will count the characters),
|
||||||
|
* false to compute the width when displayed in game (so it will count the pixels).
|
||||||
|
* @param parentBold if the component inherits a bold styling from an eventual parent component.
|
||||||
|
* @return the width of the provided component.
|
||||||
|
*/
|
||||||
public static int componentWidth(Component component, boolean console, boolean parentBold) {
|
public static int componentWidth(Component component, boolean console, boolean parentBold) {
|
||||||
if (component == null)
|
if (component == null)
|
||||||
return 0;
|
return 0;
|
||||||
@ -408,6 +224,14 @@ public class ChatUtil {
|
|||||||
return (parent && child != State.FALSE) || (!parent && child == State.TRUE);
|
return (parent && child != State.FALSE) || (!parent && child == State.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the width of the provided text.
|
||||||
|
* @param str the text to compute the width.
|
||||||
|
* @param console true to compute the width when displayed on console (so it will count the characters),
|
||||||
|
* false to compute the width when displayed in game (so it will count the pixels).
|
||||||
|
* @param bold if the text is bold (may change its width).
|
||||||
|
* @return the width of the provided text.
|
||||||
|
*/
|
||||||
public static int strWidth(String str, boolean console, boolean bold) {
|
public static int strWidth(String str, boolean console, boolean bold) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (char c : str.toCharArray())
|
for (char c : str.toCharArray())
|
||||||
@ -415,6 +239,17 @@ public class ChatUtil {
|
|||||||
return Math.max(count, 0);
|
return Math.max(count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the width of the provided character.
|
||||||
|
* <p>
|
||||||
|
* It uses the mapping in {@link #CHAR_SIZES} for in-game display. For console, every character is size 1.
|
||||||
|
* The {@code §} character is treated has a negative value, to make legacy codes take 0 width.
|
||||||
|
* @param c the character to compute the width.
|
||||||
|
* @param console true to compute the width when displayed on console (so it will count the characters),
|
||||||
|
* false to compute the width when displayed in game (so it will count the pixels).
|
||||||
|
* @param bold if the character is bold (may change its width).
|
||||||
|
* @return the width of the provided character.
|
||||||
|
*/
|
||||||
public static int charW(char c, boolean console, boolean bold) {
|
public static int charW(char c, boolean console, boolean bold) {
|
||||||
if (console)
|
if (console)
|
||||||
return (c == '§') ? -1 : 1;
|
return (c == '§') ? -1 : 1;
|
||||||
@ -422,18 +257,28 @@ public class ChatUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the provided text in multiple lines, taking into account the legacy formating.
|
||||||
|
* <p>
|
||||||
|
* This method only takes into account IG text width. Use a regular text-wrapper for console instead.
|
||||||
|
* @param legacyText the text to wrap.
|
||||||
|
* @param pixelWidth the width in which the text must fit.
|
||||||
|
* @return the wrapped text in a {@link List} of {@link Chat} components.
|
||||||
|
*/
|
||||||
public static List<Chat> wrapInLimitedPixelsToChat(String legacyText, int pixelWidth) {
|
public static List<Chat> wrapInLimitedPixelsToChat(String legacyText, int pixelWidth) {
|
||||||
return wrapInLimitedPixels(legacyText, pixelWidth).stream()
|
return wrapInLimitedPixels(legacyText, pixelWidth).stream()
|
||||||
.map(ChatStatic::legacyText)
|
.map(ChatStatic::legacyText)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the provided text in multiple lines, taking into account the legacy formating.
|
||||||
|
* <p>
|
||||||
|
* This method only takes into account IG text width. Use a regular text-wrapper for console instead.
|
||||||
|
* @param legacyText the text to wrap.
|
||||||
|
* @param pixelWidth the width in which the text must fit.
|
||||||
|
* @return the wrapped text in a {@link List} of line.
|
||||||
|
*/
|
||||||
public static List<String> wrapInLimitedPixels(String legacyText, int pixelWidth) {
|
public static List<String> wrapInLimitedPixels(String legacyText, int pixelWidth) {
|
||||||
List<String> lines = new ArrayList<>();
|
List<String> lines = new ArrayList<>();
|
||||||
|
|
||||||
@ -522,12 +367,19 @@ public class ChatUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to render a matrix of {@link Chat} components into a table in the chat or console.
|
||||||
|
* @param data the component, in the form of {@link List} of {@link List} of {@link Chat}. The englobing list holds
|
||||||
public static List<Component> renderTable(List<List<Chat>> rows, String space, boolean console) {
|
* the table lines (line 0 being the top line). Each sublist holds the cells content (element 0 is the
|
||||||
List<List<Component>> compRows = new ArrayList<>(rows.size());
|
* leftText one). The row lengths can be different.
|
||||||
for (List<Chat> row : rows) {
|
* @param space a spacer to put between columns.
|
||||||
|
* @param console true to display the table on the console (character alignement), false in game chat (pixel
|
||||||
|
* alignment, much harder).
|
||||||
|
* @return a List containing each rendered line of the table.
|
||||||
|
*/
|
||||||
|
public static List<Component> renderTable(List<List<Chat>> data, String space, boolean console) {
|
||||||
|
List<List<Component>> compRows = new ArrayList<>(data.size());
|
||||||
|
for (List<Chat> row : data) {
|
||||||
List<Component> compRow = new ArrayList<>(row.size());
|
List<Component> compRow = new ArrayList<>(row.size());
|
||||||
for (Chat c : row) {
|
for (Chat c : row) {
|
||||||
compRow.add(c.getAdv());
|
compRow.add(c.getAdv());
|
||||||
@ -538,10 +390,20 @@ public class ChatUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Component> renderTableComp(List<List<Component>> rows, String space, boolean console) {
|
/**
|
||||||
|
* Try to render a matrix of {@link Component} components into a table in the chat or console.
|
||||||
|
* @param data the component, in the form of {@link List} of {@link List} of {@link Component}. The englobing list holds
|
||||||
|
* the table lines (line 0 being the top line). Each sublist holds the cells content (element 0 is the
|
||||||
|
* leftText one). The row lengths can be different.
|
||||||
|
* @param space a spacer to put between columns.
|
||||||
|
* @param console true to display the table on the console (character alignement), false in game chat (pixel
|
||||||
|
* alignment, much harder).
|
||||||
|
* @return a List containing each rendered line of the table.
|
||||||
|
*/
|
||||||
|
public static List<Component> renderTableComp(List<List<Component>> data, String space, boolean console) {
|
||||||
// determine columns width
|
// determine columns width
|
||||||
List<Integer> nbPixelPerColumn = new ArrayList<>();
|
List<Integer> nbPixelPerColumn = new ArrayList<>();
|
||||||
for (List<Component> row : rows) {
|
for (List<Component> row : data) {
|
||||||
for (int i = 0; i < row.size(); i++) {
|
for (int i = 0; i < row.size(); i++) {
|
||||||
int w = componentWidth(row.get(i), console);
|
int w = componentWidth(row.get(i), console);
|
||||||
if (nbPixelPerColumn.size() <= i)
|
if (nbPixelPerColumn.size() <= i)
|
||||||
@ -552,8 +414,8 @@ public class ChatUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the lines with appropriate spacing
|
// create the lines with appropriate spacing
|
||||||
List<Component> spacedRows = new ArrayList<>(rows.size());
|
List<Component> spacedRows = new ArrayList<>(data.size());
|
||||||
for (List<Component> row : rows) {
|
for (List<Component> row : data) {
|
||||||
Chat spacedRow = Chat.chat();
|
Chat spacedRow = Chat.chat();
|
||||||
for (int i = 0; i < row.size() - 1; i++) {
|
for (int i = 0; i < row.size() - 1; i++) {
|
||||||
int w = componentWidth(row.get(i), console);
|
int w = componentWidth(row.get(i), console);
|
||||||
@ -571,8 +433,19 @@ public class ChatUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a component acting as a spacer of a specific width.
|
||||||
|
* <p>
|
||||||
|
* The returned component contains mostly spaces. If it has visible characters, the component color will be set to
|
||||||
|
* black to be the least visible as possible.
|
||||||
|
* <p>
|
||||||
|
* For console, the method returns a {@link Component} with a regular space repeated {@code width} times.
|
||||||
|
* For IG, the methods returns a {@link Component} with a combination of spaces and some small characters, with part
|
||||||
|
* of them bold. For some specific width, the returned {@link Component} may not have the intended width.
|
||||||
|
* @param width the width of the space to produce.
|
||||||
|
* @param console true if the spacer is intended to be displayed on the console, false if it’s in game chat.
|
||||||
|
* @return a component acting as a spacer of a specific width.
|
||||||
|
*/
|
||||||
public static Component customWidthSpace(int width, boolean console) {
|
public static Component customWidthSpace(int width, boolean console) {
|
||||||
if (console)
|
if (console)
|
||||||
return Chat.text(" ".repeat(width)).getAdv();
|
return Chat.text(" ".repeat(width)).getAdv();
|
||||||
@ -631,10 +504,19 @@ public class ChatUtil {
|
|||||||
private static final char PROGRESS_BAR_EMPTY_CHAR = '.';
|
private static final char PROGRESS_BAR_EMPTY_CHAR = '.';
|
||||||
private static final char PROGRESS_BAR_FULL_CHAR = '|';
|
private static final char PROGRESS_BAR_FULL_CHAR = '|';
|
||||||
|
|
||||||
public static Chat progressBar(double[] values, TextColor[] colors, double total, int pixelWidth, boolean console) {
|
/**
|
||||||
|
* Generate a (eventually multi-part) progress bar using text.
|
||||||
|
* @param values the values to render in the progress bar.
|
||||||
|
* @param colors the colors attributed to each values.
|
||||||
|
* @param total the total value of the progress bar.
|
||||||
|
* @param width the width in which the progress bar should fit (in pixel for IG, in character count for console)
|
||||||
|
* @param console true if the progress bar is intended to be displayed on the console, false if it’s in game chat.
|
||||||
|
* @return a progress bar using text.
|
||||||
|
*/
|
||||||
|
public static Chat progressBar(double[] values, TextColor[] colors, double total, int width, boolean console) {
|
||||||
|
|
||||||
// 1. Compute char size for each values
|
// 1. Compute char size for each values
|
||||||
int progressPixelWidth = pixelWidth - strWidth(PROGRESS_BAR_START + PROGRESS_BAR_END, console, false);
|
int progressPixelWidth = width - strWidth(PROGRESS_BAR_START + PROGRESS_BAR_END, console, false);
|
||||||
int charPixelWidth = charW(PROGRESS_BAR_EMPTY_CHAR, console, false);
|
int charPixelWidth = charW(PROGRESS_BAR_EMPTY_CHAR, console, false);
|
||||||
|
|
||||||
assert charPixelWidth == charW(PROGRESS_BAR_FULL_CHAR, console, false) : "PROGRESS_BAR_EMPTY_CHAR and PROGRESS_BAR_FULL_CHAR should have the same pixel width according to #charW(...)";
|
assert charPixelWidth == charW(PROGRESS_BAR_FULL_CHAR, console, false) : "PROGRESS_BAR_EMPTY_CHAR and PROGRESS_BAR_FULL_CHAR should have the same pixel width according to #charW(...)";
|
||||||
@ -673,23 +555,27 @@ public class ChatUtil {
|
|||||||
.thenText(PROGRESS_BAR_END);
|
.thenText(PROGRESS_BAR_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Chat progressBar(double value, TextColor color, double total, int pixelWidth, boolean console) {
|
/**
|
||||||
return progressBar(new double[] { value }, new TextColor[] { color }, total, pixelWidth, console);
|
* Generate a progress bar using text.
|
||||||
|
* @param value the value to render in the progress bar.
|
||||||
|
* @param color the color of the filled part of the bar.
|
||||||
|
* @param total the total value of the progress bar.
|
||||||
|
* @param width the width in which the progress bar should fit (in pixel for IG, in character count for console)
|
||||||
|
* @param console true if the progress bar is intended to be displayed on the console, false if it’s in game chat.
|
||||||
|
* @return a progress bar using text.
|
||||||
|
*/
|
||||||
|
public static Chat progressBar(double value, TextColor color, double total, int width, boolean console) {
|
||||||
|
return progressBar(new double[] { value }, new TextColor[] { color }, total, width, console);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate an eventually too long prefix (like team prefix or permission group prefix), keep the last color and
|
||||||
|
* format.
|
||||||
|
* @param prefix the prefix that eventually needs truncation.
|
||||||
|
* @param maxLength the maximum length of the prefix.
|
||||||
|
* @return a truncated prefix, with the last color kept.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static String truncatePrefix(String prefix, int maxLength) {
|
public static String truncatePrefix(String prefix, int maxLength) {
|
||||||
if (prefix.length() > maxLength) {
|
if (prefix.length() > maxLength) {
|
||||||
String lastColor = ChatColorUtil.getLastColors(prefix);
|
String lastColor = ChatColorUtil.getLastColors(prefix);
|
||||||
@ -700,83 +586,21 @@ public class ChatUtil {
|
|||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String truncateAtLengthWithoutReset(String prefix, int l) {
|
|
||||||
if (prefix.length() > l) {
|
|
||||||
prefix = prefix.substring(0, l);
|
|
||||||
if (prefix.endsWith("§"))
|
|
||||||
prefix = prefix.substring(0, prefix.length()-1);
|
|
||||||
}
|
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static final String TREE_MIDDLE_CONNECTED = "├";
|
|
||||||
private static final String TREE_END_CONNECTED = "└";
|
|
||||||
private static final String TREE_MIDDLE_OPEN = "│§0`§r";
|
|
||||||
private static final String TREE_END_OPEN = "§0```§r";
|
|
||||||
private static final String TREE_MIDDLE_OPEN_CONSOLE = "│";
|
|
||||||
private static final String TREE_END_OPEN_CONSOLE = " "; // nbsp
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a tree view based on the tree structure {@code node}.
|
* Truncate an eventually too long string, also taking care of removing an eventual {@code §} character leftText alone
|
||||||
*
|
* at the end.
|
||||||
* Each element in the returned list represent 1 line of the tree view.
|
* @param str the string to eventually truncate.
|
||||||
* Thus, the caller may send each line separately or at once depending of the quantity of data.
|
* @param maxLength the maximum length of the string.
|
||||||
* @return A array of component, each element being a single line.
|
* @return a truncated string.
|
||||||
*/
|
*/
|
||||||
public static List<Chat> treeView(DisplayTreeNode node, boolean console) {
|
public static String truncateAtLengthWithoutReset(String str, int maxLength) {
|
||||||
List<Chat> ret = new ArrayList<>();
|
if (str.length() > maxLength) {
|
||||||
|
str = str.substring(0, maxLength);
|
||||||
ret.add(ChatStatic.chat()
|
if (str.endsWith("§"))
|
||||||
.then(node.component));
|
str = str.substring(0, str.length()-1);
|
||||||
|
|
||||||
for (int i = 0; i < node.children.size(); i++) {
|
|
||||||
List<Chat> childComponents = treeView(node.children.get(i), console);
|
|
||||||
boolean last = i == node.children.size() - 1;
|
|
||||||
for (int j = 0; j < childComponents.size(); j++) {
|
|
||||||
|
|
||||||
String prefix = last ? (j == 0 ? TREE_END_CONNECTED : (console ? TREE_END_OPEN_CONSOLE : TREE_END_OPEN))
|
|
||||||
: (j == 0 ? TREE_MIDDLE_CONNECTED : (console ? TREE_MIDDLE_OPEN_CONSOLE : TREE_MIDDLE_OPEN));
|
|
||||||
|
|
||||||
ret.add(ChatStatic.text(prefix)
|
|
||||||
.then(childComponents.get(j)));
|
|
||||||
}
|
}
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class DisplayTreeNode {
|
|
||||||
public final Chat component;
|
|
||||||
public final List<DisplayTreeNode> children = new ArrayList<>();
|
|
||||||
|
|
||||||
public DisplayTreeNode(Chat cmp) {
|
|
||||||
component = cmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayTreeNode addChild(DisplayTreeNode child) {
|
|
||||||
children.add(child);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import java.util.OptionalLong;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.LongStream;
|
import java.util.stream.LongStream;
|
||||||
|
|
||||||
import fr.pandacube.lib.chat.ChatUtil.DisplayTreeNode;
|
import fr.pandacube.lib.chat.ChatTreeNode;
|
||||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedEntity;
|
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedEntity;
|
||||||
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
||||||
import fr.pandacube.lib.util.Log;
|
import fr.pandacube.lib.util.Log;
|
||||||
@ -50,7 +50,7 @@ public abstract class PermEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public DisplayTreeNode debugPrefix() {
|
public ChatTreeNode debugPrefix() {
|
||||||
return Permissions.resolver.debugPrefix(name, type);
|
return Permissions.resolver.debugPrefix(name, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ public abstract class PermEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public DisplayTreeNode debugSuffix() {
|
public ChatTreeNode debugSuffix() {
|
||||||
return Permissions.resolver.debugSuffix(name, type);
|
return Permissions.resolver.debugSuffix(name, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,15 +159,15 @@ public abstract class PermEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public DisplayTreeNode debugPermission(String permission) {
|
public ChatTreeNode debugPermission(String permission) {
|
||||||
return debugPermission(permission, null, null);
|
return debugPermission(permission, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayTreeNode debugPermission(String permission, String server) {
|
public ChatTreeNode debugPermission(String permission, String server) {
|
||||||
return debugPermission(permission, server, null);
|
return debugPermission(permission, server, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayTreeNode debugPermission(String permission, String server, String world) {
|
public ChatTreeNode debugPermission(String permission, String server, String world) {
|
||||||
return Permissions.resolver.debugPermission(name, type, permission, server, world);
|
return Permissions.resolver.debugPermission(name, type, permission, server, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,7 @@ import com.google.common.cache.CacheBuilder;
|
|||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
|
||||||
import fr.pandacube.lib.chat.Chat;
|
import fr.pandacube.lib.chat.Chat;
|
||||||
import fr.pandacube.lib.chat.ChatUtil;
|
import fr.pandacube.lib.chat.ChatTreeNode;
|
||||||
import fr.pandacube.lib.chat.ChatUtil.DisplayTreeNode;
|
|
||||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedEntity;
|
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedEntity;
|
||||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedGroup;
|
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedGroup;
|
||||||
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedPlayer;
|
import fr.pandacube.lib.permissions.PermissionsCachedBackendReader.CachedPlayer;
|
||||||
@ -66,10 +65,10 @@ public class PermissionsResolver {
|
|||||||
return getEffectiveData(name, type, DataType.SUFFIX);
|
return getEffectiveData(name, type, DataType.SUFFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ DisplayTreeNode debugPrefix(String name, EntityType type) {
|
/* package */ ChatTreeNode debugPrefix(String name, EntityType type) {
|
||||||
return debugData(name, type, DataType.PREFIX);
|
return debugData(name, type, DataType.PREFIX);
|
||||||
}
|
}
|
||||||
/* package */ DisplayTreeNode debugSuffix(String name, EntityType type) {
|
/* package */ ChatTreeNode debugSuffix(String name, EntityType type) {
|
||||||
return debugData(name, type, DataType.SUFFIX);
|
return debugData(name, type, DataType.SUFFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +88,7 @@ public class PermissionsResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DisplayTreeNode debugData(String name, EntityType type, DataType dataType) {
|
private ChatTreeNode debugData(String name, EntityType type, DataType dataType) {
|
||||||
CachedEntity entity = (type == EntityType.User)
|
CachedEntity entity = (type == EntityType.User)
|
||||||
? backendReader.getCachedPlayer(UUID.fromString(name))
|
? backendReader.getCachedPlayer(UUID.fromString(name))
|
||||||
: backendReader.getCachedGroup(name);
|
: backendReader.getCachedGroup(name);
|
||||||
@ -104,7 +103,7 @@ public class PermissionsResolver {
|
|||||||
|
|
||||||
if (resolutionResult.conflict) {
|
if (resolutionResult.conflict) {
|
||||||
Log.warning("For data " + dataType + ":\n"
|
Log.warning("For data " + dataType + ":\n"
|
||||||
+ ChatUtil.treeView(resolutionResult.toDisplayTreeNode(), true).stream()
|
+ resolutionResult.toDisplayTreeNode().render(true).stream()
|
||||||
.map(Chat::getLegacyText)
|
.map(Chat::getLegacyText)
|
||||||
.collect(Collectors.joining(ChatColor.RESET + "\n")));
|
.collect(Collectors.joining(ChatColor.RESET + "\n")));
|
||||||
}
|
}
|
||||||
@ -163,7 +162,7 @@ public class PermissionsResolver {
|
|||||||
conflict = c != null;
|
conflict = c != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayTreeNode toDisplayTreeNode() {
|
public ChatTreeNode toDisplayTreeNode() {
|
||||||
Chat c = Chat.text(entity.name);
|
Chat c = Chat.text(entity.name);
|
||||||
if (result == null)
|
if (result == null)
|
||||||
c.then(Chat.text(" (non défini)").gray());
|
c.then(Chat.text(" (non défini)").gray());
|
||||||
@ -171,11 +170,11 @@ public class PermissionsResolver {
|
|||||||
c.thenLegacyText(" \"" + ChatColor.RESET + result + ChatColor.RESET + "\"");
|
c.thenLegacyText(" \"" + ChatColor.RESET + result + ChatColor.RESET + "\"");
|
||||||
if (conflictMessage != null)
|
if (conflictMessage != null)
|
||||||
c.thenFailure(" " + conflictMessage);
|
c.thenFailure(" " + conflictMessage);
|
||||||
DisplayTreeNode node = new DisplayTreeNode(c);
|
ChatTreeNode node = new ChatTreeNode(c);
|
||||||
|
|
||||||
if (result == null && !conflict && !inheritances.isEmpty()) {
|
if (result == null && !conflict && !inheritances.isEmpty()) {
|
||||||
// there is nothing interesting to show on current or subnode
|
// there is nothing interesting to show on current or subnode
|
||||||
node.children.add(new DisplayTreeNode(Chat.text("(Inheritances hidden for brevety)").darkGray().italic()));
|
node.children.add(new ChatTreeNode(Chat.text("(Inheritances hidden for brevety)").darkGray().italic()));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +289,7 @@ public class PermissionsResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ DisplayTreeNode debugPermission(String name, EntityType type, String permission, String server, String world) {
|
/* package */ ChatTreeNode debugPermission(String name, EntityType type, String permission, String server, String world) {
|
||||||
CachedEntity entity = (type == EntityType.User)
|
CachedEntity entity = (type == EntityType.User)
|
||||||
? backendReader.getCachedPlayer(UUID.fromString(name))
|
? backendReader.getCachedPlayer(UUID.fromString(name))
|
||||||
: backendReader.getCachedGroup(name);
|
: backendReader.getCachedGroup(name);
|
||||||
@ -306,7 +305,7 @@ public class PermissionsResolver {
|
|||||||
|
|
||||||
if (resolutionResult.conflict) {
|
if (resolutionResult.conflict) {
|
||||||
Log.warning("For permission " + permission + ":\n"
|
Log.warning("For permission " + permission + ":\n"
|
||||||
+ ChatUtil.treeView(resolutionResult.toDisplayTreeNode(), true).stream()
|
+ resolutionResult.toDisplayTreeNode().render(true).stream()
|
||||||
.map(Chat::getLegacyText)
|
.map(Chat::getLegacyText)
|
||||||
.collect(Collectors.joining(ChatColor.RESET + "\n")));
|
.collect(Collectors.joining(ChatColor.RESET + "\n")));
|
||||||
}
|
}
|
||||||
@ -484,7 +483,7 @@ public class PermissionsResolver {
|
|||||||
conflict = c != null;
|
conflict = c != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayTreeNode toDisplayTreeNode() {
|
public ChatTreeNode toDisplayTreeNode() {
|
||||||
Chat c = Chat.chat()
|
Chat c = Chat.chat()
|
||||||
.then(result == PermState.UNDEFINED ? Chat.dataText("■") : result == PermState.GRANTED ? Chat.successText("✔") : Chat.failureText("✘"))
|
.then(result == PermState.UNDEFINED ? Chat.dataText("■") : result == PermState.GRANTED ? Chat.successText("✔") : Chat.failureText("✘"))
|
||||||
.then(Chat.text(entity instanceof CachedPlayer cp ? Permissions.playerNameGetter.apply(cp.playerId) : entity.name)
|
.then(Chat.text(entity instanceof CachedPlayer cp ? Permissions.playerNameGetter.apply(cp.playerId) : entity.name)
|
||||||
@ -496,13 +495,13 @@ public class PermissionsResolver {
|
|||||||
c.thenData(" w=" + world);
|
c.thenData(" w=" + world);
|
||||||
if (conflictMessage != null)
|
if (conflictMessage != null)
|
||||||
c.then(Chat.failureText(" " + conflictMessage));
|
c.then(Chat.failureText(" " + conflictMessage));
|
||||||
DisplayTreeNode node = new DisplayTreeNode(c);
|
ChatTreeNode node = new ChatTreeNode(c);
|
||||||
|
|
||||||
selfPermissions.forEach(p -> node.children.add(p.toDisplayTreeNode()));
|
selfPermissions.forEach(p -> node.children.add(p.toDisplayTreeNode()));
|
||||||
|
|
||||||
if (result == PermState.UNDEFINED && !conflict && !inheritances.isEmpty()) {
|
if (result == PermState.UNDEFINED && !conflict && !inheritances.isEmpty()) {
|
||||||
// there is nothing interesting to show on current or subnode
|
// there is nothing interesting to show on current or subnode
|
||||||
node.children.add(new DisplayTreeNode(Chat.text("(Inheritances hidden for brevety)").darkGray().italic()));
|
node.children.add(new ChatTreeNode(Chat.text("(Inheritances hidden for brevety)").darkGray().italic()));
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,8 +520,8 @@ public class PermissionsResolver {
|
|||||||
result = r;
|
result = r;
|
||||||
type = t;
|
type = t;
|
||||||
}
|
}
|
||||||
public DisplayTreeNode toDisplayTreeNode() {
|
public ChatTreeNode toDisplayTreeNode() {
|
||||||
return new DisplayTreeNode(Chat.chat()
|
return new ChatTreeNode(Chat.chat()
|
||||||
.then(result ? Chat.successText("✔") : Chat.failureText("✘"))
|
.then(result ? Chat.successText("✔") : Chat.failureText("✘"))
|
||||||
.then(Chat.text(permission).color(type == PermType.WILDCARD ? ChatColor.YELLOW : type == PermType.SPECIAL ? ChatColor.LIGHT_PURPLE : ChatColor.WHITE)));
|
.then(Chat.text(permission).color(type == PermType.WILDCARD ? ChatColor.YELLOW : type == PermType.SPECIAL ? ChatColor.LIGHT_PURPLE : ChatColor.WHITE)));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user