2022-07-20 13:18:57 +02:00
package fr.pandacube.lib.chat ;
2017-07-06 04:02:03 +02:00
2017-07-06 20:53:25 +02:00
import java.util.ArrayList ;
2017-07-06 04:02:03 +02:00
import java.util.Arrays ;
2022-07-28 01:11:28 +02:00
import java.util.Collections ;
import java.util.HashMap ;
2017-07-06 20:53:25 +02:00
import java.util.List ;
2017-07-06 04:02:03 +02:00
import java.util.Map ;
2020-06-08 16:16:18 +02:00
import java.util.Set ;
import java.util.TreeSet ;
2021-07-11 16:14:47 +02:00
import java.util.stream.Collectors ;
2017-07-06 04:02:03 +02:00
2021-07-20 01:08:29 +02:00
import net.kyori.adventure.text.Component ;
import net.kyori.adventure.text.TextComponent ;
import net.kyori.adventure.text.TranslatableComponent ;
2021-07-24 19:37:04 +02:00
import net.kyori.adventure.text.format.NamedTextColor ;
import net.kyori.adventure.text.format.TextColor ;
2021-07-20 01:08:29 +02:00
import net.kyori.adventure.text.format.TextDecoration ;
import net.kyori.adventure.text.format.TextDecoration.State ;
2017-07-06 04:02:03 +02:00
import net.md_5.bungee.api.ChatColor ;
2022-07-20 13:18:57 +02:00
import fr.pandacube.lib.chat.Chat.FormatableChat ;
2022-07-10 00:55:56 +02:00
2022-07-28 01:11:28 +02:00
/ * *
* Provides various methods and properties to manipulate text displayed in chat an other parts of the game .
* /
2020-11-02 23:23:41 +01:00
public class ChatUtil {
2017-07-06 04:02:03 +02:00
2022-07-28 01:11:28 +02:00
/ *
* Note : this field is for easy listing of all characters with special sizes . It will all be reported to
* # CHAR_SIZES on class initialization for optimization .
* /
private static final Map < Integer , String > SIZE_CHARS_MAPPING = Map . ofEntries (
2022-07-20 13:18:57 +02:00
Map . entry ( - 6 , " § " ) ,
Map . entry ( 2 , " !.,:;i|¡' " ) ,
Map . entry ( 3 , " `lìí’ ‘ " ) ,
Map . entry ( 4 , " I[]tï× " ) ,
Map . entry ( 5 , " \" ()*<>fk{} " ) ,
Map . entry ( 7 , " @~®©«» " ) ,
Map . entry ( 9 , " ├└ " )
) ;
2017-07-06 04:02:03 +02:00
2022-07-28 01:11:28 +02:00
/ * *
* The default text pixel width for a character in the default Minecraft font .
* If a character has another width , it should be found in { @link # CHAR_SIZES } .
* /
public static final int DEFAULT_CHAR_SIZE = 6 ;
/ * *
* Mapping indicating the text pixel with for specific characters in the default Minecraft font .
* If a character doesn ’ t have a mapping in this map , then its width is { @link # DEFAULT_CHAR_SIZE } .
* /
public static final Map < Character , Integer > CHAR_SIZES ;
static {
Map < Character , Integer > charSizes = new HashMap < > ( ) ;
for ( var e : SIZE_CHARS_MAPPING . entrySet ( ) ) {
int size = e . getKey ( ) ;
for ( char c : e . getValue ( ) . toCharArray ( ) ) {
charSizes . put ( c , size ) ;
}
}
CHAR_SIZES = Collections . unmodifiableMap ( charSizes ) ;
}
/ * *
* The default width of the Minecraft Java Edition chat window , in text pixels .
* /
2017-07-06 04:02:03 +02:00
public static final int DEFAULT_CHAT_WIDTH = 320 ;
2022-07-28 01:11:28 +02:00
/ * *
* The width of a Minecraft sign , in text pixels .
* /
2017-07-06 04:02:03 +02:00
public static final int SIGN_WIDTH = 90 ;
2022-07-28 01:11:28 +02:00
/ * *
* The width of a Minecraft book content , in text pixels .
* /
2017-07-06 04:02:03 +02:00
public static final int BOOK_WIDTH = 116 ;
2022-07-28 01:11:28 +02:00
/ * *
* The width of a Minecraft server MOTD message , in text pixels .
* /
2021-08-29 00:56:57 +02:00
public static final int MOTD_WIDTH = 270 ;
2022-07-28 01:11:28 +02:00
/ * *
* The width of a Minecraft Bedrock Edition form button , in text pixels .
* /
2021-11-25 11:55:55 +01:00
public static final int BEDROCK_FORM_WIDE_BUTTON = 178 ;
2017-07-06 04:02:03 +02:00
2022-07-28 01:11:28 +02:00
/ * *
* The default number of character per lines for the console .
* /
2017-07-06 04:02:03 +02:00
public static final int CONSOLE_NB_CHAR_DEFAULT = 50 ;
2020-11-02 23:23:41 +01:00
2022-07-28 01:11:28 +02:00
/ * *
* 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 " )
2021-07-11 16:14:47 +02:00
public static FormatableChat createURLLink ( String text , String url ) {
2022-07-28 01:11:28 +02:00
return Chat . clickableURL ( text = = null ? null : Chat . legacyText ( text ) , url ) ;
2020-11-02 23:23:41 +01:00
}
2022-07-28 01:11:28 +02:00
/ * *
* 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 " )
2021-07-11 16:14:47 +02:00
public static FormatableChat createURLLink ( String text , String url , String hoverText ) {
2022-07-28 01:11:28 +02:00
return Chat . clickableURL ( text = = null ? null : Chat . legacyText ( text ) , url , hoverText = = null ? null : Chat . legacyText ( hoverText ) ) ;
2017-07-06 04:02:03 +02:00
}
2022-07-28 01:11:28 +02:00
/ * *
* 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 " )
2021-07-11 16:14:47 +02:00
public static FormatableChat createCommandLink ( String text , String commandWithSlash , String hoverText ) {
2022-07-28 01:11:28 +02:00
return Chat . clickableCommand ( text = = null ? null : Chat . legacyText ( text ) , commandWithSlash , hoverText = = null ? null : Chat . legacyText ( hoverText ) ) ;
2017-07-06 04:02:03 +02:00
}
2022-07-28 01:11:28 +02:00
/ * *
* 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 " )
2021-07-11 16:14:47 +02:00
public static FormatableChat createCommandLink ( String text , String commandWithSlash , Chat hoverText ) {
2022-07-28 01:11:28 +02:00
return Chat . clickableCommand ( text = = null ? null : Chat . legacyText ( text ) , commandWithSlash , hoverText ) ;
2017-07-06 04:02:03 +02:00
}
2022-07-28 01:11:28 +02:00
/ * *
* 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 ) ) ;
2017-07-06 04:02:03 +02:00
}
2022-07-28 01:11:28 +02:00
/ * *
* 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 ) ;
2017-07-06 04:02:03 +02:00
}
2020-06-08 16:16:18 +02:00
/ * *
2022-07-28 01:11:28 +02:00
* Create a page navigator with clickable page numbers for the chat .
* @param prefix the text to put before the
2020-06-08 16:16:18 +02:00
* @param cmdFormat the command with % d inside to be replaced with the page number ( must start with slash )
2022-07-28 01:11:28 +02:00
* @param currentPage the current page number ( it is highlighted , and the pages around are displayed , according to
* { @code nbPagesToDisplay } ) .
* @param nbPages the number of pages .
* @param nbPagesToDisplay the number of pages to display around the first page , the last page and the
* { @code currentPage } .
* @return a { @link Chat } containging the created page navigator .
2020-06-08 16:16:18 +02:00
* /
2021-07-11 16:14:47 +02:00
public static Chat createPagination ( String prefix , String cmdFormat , int currentPage , int nbPages , int nbPagesToDisplay ) {
2020-06-08 16:16:18 +02:00
Set < Integer > pagesToDisplay = new TreeSet < > ( ) ;
for ( int i = 0 ; i < nbPagesToDisplay & & i < nbPages & & nbPages - i > 0 ; i + + ) {
pagesToDisplay . add ( i + 1 ) ;
pagesToDisplay . add ( nbPages - i ) ;
}
for ( int i = currentPage - nbPagesToDisplay + 1 ; i < currentPage + nbPagesToDisplay ; i + + ) {
if ( i > 0 & & i < = nbPages )
pagesToDisplay . add ( i ) ;
}
2022-07-20 13:18:57 +02:00
Chat d = ChatStatic . chat ( ) . thenLegacyText ( prefix ) ;
2020-06-08 16:16:18 +02:00
boolean first = true ;
int previous = 0 ;
for ( int page : pagesToDisplay ) {
if ( ! first ) {
if ( page = = previous + 1 ) {
2020-11-02 23:23:41 +01:00
d . thenText ( " " ) ;
2020-06-08 16:16:18 +02:00
}
else {
if ( cmdFormat . endsWith ( " %d " ) ) {
2020-11-02 23:23:41 +01:00
d . thenText ( " " ) ;
2022-07-28 01:11:28 +02:00
d . then ( Chat . clickableSuggest ( Chat . text ( " ... " ) , cmdFormat . substring ( 0 , cmdFormat . length ( ) - 2 ) , Chat . text ( " Choisir la page " ) ) ) ;
2020-11-02 23:23:41 +01:00
d . thenText ( " " ) ;
2020-06-08 16:16:18 +02:00
}
else
2020-11-02 23:23:41 +01:00
d . thenText ( " ... " ) ;
2020-06-08 16:16:18 +02:00
}
}
else
first = false ;
2022-07-28 01:11:28 +02:00
FormatableChat pDisp = Chat . clickableCommand ( Chat . text ( page ) , String . format ( cmdFormat , page ) , Chat . text ( " Aller à la page " + page ) ) ;
2020-06-08 16:16:18 +02:00
if ( page = = currentPage ) {
2022-01-21 18:56:21 +01:00
pDisp . highlightedCommandColor ( ) ;
2020-06-08 16:16:18 +02:00
}
2020-11-02 23:23:41 +01:00
d . then ( pDisp ) ;
2020-06-08 16:16:18 +02:00
previous = page ;
}
2021-07-11 16:14:47 +02:00
return d ;
2020-06-08 16:16:18 +02:00
}
2021-08-29 00:56:57 +02:00
2021-09-21 23:54:32 +02:00
/ * *
* @param decorationColor support null values
* /
public static Chat centerText ( Chat text , char repeatedChar , TextColor decorationColor , boolean console ) {
2021-08-29 00:56:57 +02:00
return centerText ( text , repeatedChar , decorationColor , console , console ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH ) ;
}
2021-09-21 23:54:32 +02:00
public static Chat centerText ( Chat text , char repeatedChar , TextColor decorationColor , boolean console , int maxWidth ) {
2021-10-03 16:10:18 +02:00
return centerText ( text , repeatedChar , decorationColor , false , console , maxWidth ) ;
2021-09-21 23:54:32 +02:00
}
2021-08-29 00:56:57 +02:00
/ * *
* @param decorationColor support null values
* /
2021-09-21 23:54:32 +02:00
public static Chat centerText ( Chat text , char repeatedChar , TextColor decorationColor , boolean decorationBold , boolean console , int maxWidth ) {
2017-07-06 04:02:03 +02:00
2021-07-20 01:08:29 +02:00
int textWidth = componentWidth ( text . getAdv ( ) , console ) ;
2020-11-02 23:23:41 +01:00
if ( textWidth > maxWidth )
return text ;
2021-09-21 23:54:32 +02:00
int repeatedCharWidth = charW ( repeatedChar , console , decorationBold ) ;
2020-11-02 23:23:41 +01:00
int sideWidth = ( maxWidth - textWidth ) / 2 ;
int sideNbChar = sideWidth / repeatedCharWidth ;
if ( sideNbChar = = 0 )
return text ;
String sideChars = repeatedChar ( repeatedChar , sideNbChar ) ;
2022-07-20 13:18:57 +02:00
FormatableChat side = ChatStatic . text ( sideChars ) . color ( decorationColor ) ;
2021-09-21 23:54:32 +02:00
if ( decorationBold )
side . bold ( ) ;
2017-07-06 04:02:03 +02:00
2020-11-02 23:23:41 +01:00
Chat d = Chat . chat ( )
2021-09-21 23:54:32 +02:00
. then ( side )
2020-11-02 23:23:41 +01:00
. then ( text ) ;
if ( repeatedChar ! = ' ' )
2021-09-21 23:54:32 +02:00
d . then ( side ) ;
2017-07-06 04:02:03 +02:00
2021-07-11 16:14:47 +02:00
return d ;
2017-07-06 04:02:03 +02:00
}
2021-09-21 23:54:32 +02:00
public static Chat leftText ( Chat text , char repeatedChar , TextColor decorationColor , int nbLeft , boolean console ) {
2021-08-29 00:56:57 +02:00
return leftText ( text , repeatedChar , decorationColor , nbLeft , console , console ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH ) ;
}
2021-09-21 23:54:32 +02:00
public static Chat leftText ( Chat text , char repeatedChar , TextColor decorationColor , int nbLeft , boolean console , int maxWidth ) {
2020-11-02 23:23:41 +01:00
2021-07-20 01:08:29 +02:00
int textWidth = componentWidth ( text . getAdv ( ) , console ) ;
2020-11-02 23:23:41 +01:00
int repeatedCharWidth = charW ( repeatedChar , console , false ) ;
int leftWidth = nbLeft * repeatedCharWidth ;
if ( textWidth + leftWidth > maxWidth )
2017-07-06 04:02:03 +02:00
return text ;
2020-11-02 23:23:41 +01:00
int rightNbChar = ( maxWidth - ( textWidth + leftWidth ) ) / repeatedCharWidth ;
2022-07-20 13:18:57 +02:00
Chat d = ChatStatic . chat ( )
. then ( ChatStatic . text ( repeatedChar ( repeatedChar , nbLeft ) ) . color ( decorationColor ) )
2020-11-02 23:23:41 +01:00
. then ( text ) ;
2017-07-06 04:02:03 +02:00
if ( repeatedChar ! = ' ' ) {
2022-07-20 13:18:57 +02:00
d . then ( ChatStatic . text ( repeatedChar ( repeatedChar , rightNbChar ) ) . color ( decorationColor ) ) ;
2017-07-06 04:02:03 +02:00
}
2021-07-11 16:14:47 +02:00
return d ;
2020-11-02 23:23:41 +01:00
2017-07-06 04:02:03 +02:00
}
2021-10-03 16:10:18 +02:00
public static Chat rightText ( Chat text , char repeatedChar , TextColor decorationColor , int nbRight , boolean console ) {
2021-08-29 00:56:57 +02:00
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 ) {
2020-11-02 23:23:41 +01:00
2021-07-20 01:08:29 +02:00
int textWidth = componentWidth ( text . getAdv ( ) , console ) ;
2020-11-02 23:23:41 +01:00
int repeatedCharWidth = charW ( repeatedChar , console , false ) ;
int rightWidth = nbRight * repeatedCharWidth ;
if ( textWidth + rightWidth > maxWidth )
2017-07-06 04:02:03 +02:00
return text ;
2020-11-02 23:23:41 +01:00
int leftNbChar = ( maxWidth - ( textWidth + rightWidth ) ) / repeatedCharWidth ;
2022-07-20 13:18:57 +02:00
Chat d = ChatStatic . chat ( )
. then ( ChatStatic . text ( repeatedChar ( repeatedChar , leftNbChar ) ) . color ( decorationColor ) )
2020-11-02 23:23:41 +01:00
. then ( text ) ;
2017-07-06 04:02:03 +02:00
if ( repeatedChar ! = ' ' ) {
2022-07-20 13:18:57 +02:00
d . then ( ChatStatic . text ( repeatedChar ( repeatedChar , nbRight ) ) . color ( decorationColor ) ) ;
2017-07-06 04:02:03 +02:00
}
2021-07-11 16:14:47 +02:00
return d ;
2017-07-06 04:02:03 +02:00
}
2021-07-24 19:37:04 +02:00
public static Chat emptyLine ( char repeatedChar , TextColor decorationColor , boolean console ) {
2021-09-21 23:54:32 +02:00
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 ) ;
2022-07-20 13:18:57 +02:00
FormatableChat line = ChatStatic . text ( repeatedChar ( repeatedChar , count ) ) . color ( decorationColor ) ;
2021-09-21 23:54:32 +02:00
if ( decorationBold )
line . bold ( ) ;
return line ;
2017-07-06 04:02:03 +02:00
}
2020-11-02 23:23:41 +01:00
private static String repeatedChar ( char repeatedChar , int count ) {
char [ ] c = new char [ count ] ;
Arrays . fill ( c , repeatedChar ) ;
return new String ( c ) ;
}
2017-07-06 04:02:03 +02:00
2021-07-20 01:08:29 +02:00
2017-07-06 04:02:03 +02:00
2021-07-20 01:08:29 +02:00
public static int componentWidth ( Component component , boolean console ) {
return componentWidth ( component , console , false ) ;
2017-07-06 04:02:03 +02:00
}
2021-07-20 01:08:29 +02:00
public static int componentWidth ( Component component , boolean console , boolean parentBold ) {
2020-11-02 23:23:41 +01:00
if ( component = = null )
return 0 ;
2017-07-06 04:02:03 +02:00
int count = 0 ;
2020-11-02 23:23:41 +01:00
2021-07-20 01:08:29 +02:00
State currentBold = component . style ( ) . decoration ( TextDecoration . BOLD ) ;
boolean actuallyBold = childBold ( parentBold , currentBold ) ;
2017-07-06 04:02:03 +02:00
if ( component instanceof TextComponent ) {
2021-07-20 01:08:29 +02:00
count + = strWidth ( ( ( TextComponent ) component ) . content ( ) , console , actuallyBold ) ;
2017-07-06 04:02:03 +02:00
}
else if ( component instanceof TranslatableComponent ) {
2021-07-20 01:08:29 +02:00
for ( Component c : ( ( TranslatableComponent ) component ) . args ( ) )
count + = componentWidth ( c , console , actuallyBold ) ;
2017-07-06 04:02:03 +02:00
}
2022-07-10 00:55:56 +02:00
for ( Component c : component . children ( ) )
count + = componentWidth ( c , console , actuallyBold ) ;
2017-07-06 04:02:03 +02:00
return count ;
}
2021-07-20 01:08:29 +02:00
private static boolean childBold ( boolean parent , TextDecoration . State child ) {
return ( parent & & child ! = State . FALSE ) | | ( ! parent & & child = = State . TRUE ) ;
}
2017-07-06 04:02:03 +02:00
public static int strWidth ( String str , boolean console , boolean bold ) {
int count = 0 ;
for ( char c : str . toCharArray ( ) )
count + = charW ( c , console , bold ) ;
2022-07-10 00:55:56 +02:00
return Math . max ( count , 0 ) ;
2017-07-06 04:02:03 +02:00
}
2021-09-21 23:54:32 +02:00
public static int charW ( char c , boolean console , boolean bold ) {
2022-07-28 01:11:28 +02:00
if ( console )
return ( c = = '§' ) ? - 1 : 1 ;
return CHAR_SIZES . getOrDefault ( c , DEFAULT_CHAR_SIZE ) ;
2017-07-06 04:02:03 +02:00
}
2017-07-06 20:53:25 +02:00
2021-07-11 16:14:47 +02:00
public static List < Chat > wrapInLimitedPixelsToChat ( String legacyText , int pixelWidth ) {
return wrapInLimitedPixels ( legacyText , pixelWidth ) . stream ( )
2022-07-10 00:55:56 +02:00
. map ( ChatStatic : : legacyText )
2021-07-11 16:14:47 +02:00
. collect ( Collectors . toList ( ) ) ;
}
2017-07-06 20:53:25 +02:00
public static List < String > wrapInLimitedPixels ( String legacyText , int pixelWidth ) {
List < String > lines = new ArrayList < > ( ) ;
legacyText + = " \ n " ; // workaround to force algorithm to compute last lines;
String currentLine = " " ;
int currentLineSize = 0 ;
int index = 0 ;
2022-07-10 00:55:56 +02:00
StringBuilder currentWord = new StringBuilder ( ) ;
2017-07-06 20:53:25 +02:00
int currentWordSize = 0 ;
boolean bold = false ;
2021-07-11 16:14:47 +02:00
boolean firstCharCurrentWordBold = false ;
2017-07-06 20:53:25 +02:00
do {
char c = legacyText . charAt ( index ) ;
if ( c = = ChatColor . COLOR_CHAR & & index < legacyText . length ( ) - 1 ) {
2022-07-10 00:55:56 +02:00
currentWord . append ( c ) ;
2017-07-06 20:53:25 +02:00
c = legacyText . charAt ( + + index ) ;
2022-07-10 00:55:56 +02:00
currentWord . append ( c ) ;
2017-07-06 20:53:25 +02:00
if ( c = = 'l' | | c = = 'L' ) // bold
bold = true ;
if ( ( c > = '0' & & c < = '9' ) // reset bold
| | ( c > = 'a' & & c < = 'f' )
| | ( c > = 'A' & & c < = 'F' )
2020-11-02 23:23:41 +01:00
| | c = = 'r' | | c = = 'R'
| | c = = 'x' | | c = = 'X' )
2017-07-06 20:53:25 +02:00
bold = false ;
}
else if ( c = = ' ' ) {
if ( currentLineSize + currentWordSize > pixelWidth & & currentLineSize > 0 ) { // wrap before word
lines . add ( currentLine ) ;
2020-11-02 23:23:41 +01:00
String lastStyle = ChatColorUtil . getLastColors ( currentLine ) ;
2017-07-06 20:53:25 +02:00
if ( currentWord . charAt ( 0 ) = = ' ' ) {
2022-07-10 00:55:56 +02:00
currentWord = new StringBuilder ( currentWord . substring ( 1 ) ) ;
2021-07-11 16:14:47 +02:00
currentWordSize - = charW ( ' ' , false , firstCharCurrentWordBold ) ;
2017-07-06 20:53:25 +02:00
}
currentLine = ( lastStyle . equals ( " §r " ) ? " " : lastStyle ) + currentWord ;
currentLineSize = currentWordSize ;
}
else {
currentLine + = currentWord ;
currentLineSize + = currentWordSize ;
}
2022-07-10 00:55:56 +02:00
currentWord = new StringBuilder ( " " + c ) ;
2017-07-06 20:53:25 +02:00
currentWordSize = charW ( c , false , bold ) ;
2021-07-11 16:14:47 +02:00
firstCharCurrentWordBold = bold ;
2017-07-06 20:53:25 +02:00
}
else if ( c = = '\n' ) {
if ( currentLineSize + currentWordSize > pixelWidth & & currentLineSize > 0 ) { // wrap before word
lines . add ( currentLine ) ;
2020-11-02 23:23:41 +01:00
String lastStyle = ChatColorUtil . getLastColors ( currentLine ) ;
2017-07-06 20:53:25 +02:00
if ( currentWord . charAt ( 0 ) = = ' ' ) {
2022-07-10 00:55:56 +02:00
currentWord = new StringBuilder ( currentWord . substring ( 1 ) ) ;
2017-07-06 20:53:25 +02:00
}
currentLine = ( lastStyle . equals ( " §r " ) ? " " : lastStyle ) + currentWord ;
}
else {
currentLine + = currentWord ;
}
// wrap after
lines . add ( currentLine ) ;
2020-11-02 23:23:41 +01:00
String lastStyle = ChatColorUtil . getLastColors ( currentLine ) ;
2017-07-06 20:53:25 +02:00
currentLine = lastStyle . equals ( " §r " ) ? " " : lastStyle ;
currentLineSize = 0 ;
2022-07-10 00:55:56 +02:00
currentWord = new StringBuilder ( ) ;
2017-07-06 20:53:25 +02:00
currentWordSize = 0 ;
2021-07-11 16:14:47 +02:00
firstCharCurrentWordBold = bold ;
2017-07-06 20:53:25 +02:00
}
else {
2022-07-10 00:55:56 +02:00
currentWord . append ( c ) ;
2017-07-06 20:53:25 +02:00
currentWordSize + = charW ( c , false , bold ) ;
}
} while ( + + index < legacyText . length ( ) ) ;
return lines ;
}
2021-12-20 13:43:43 +01:00
public static List < Component > renderTable ( List < List < Chat > > rows , String space , boolean console ) {
List < List < Component > > compRows = new ArrayList < > ( rows . size ( ) ) ;
for ( List < Chat > row : rows ) {
List < Component > compRow = new ArrayList < > ( row . size ( ) ) ;
for ( Chat c : row ) {
compRow . add ( c . getAdv ( ) ) ;
}
compRows . add ( compRow ) ;
}
return renderTableComp ( compRows , space , console ) ;
}
public static List < Component > renderTableComp ( List < List < Component > > rows , String space , boolean console ) {
// determine columns width
List < Integer > nbPixelPerColumn = new ArrayList < > ( ) ;
for ( List < Component > row : rows ) {
for ( int i = 0 ; i < row . size ( ) ; i + + ) {
int w = componentWidth ( row . get ( i ) , console ) ;
if ( nbPixelPerColumn . size ( ) < = i )
nbPixelPerColumn . add ( w ) ;
else if ( nbPixelPerColumn . get ( i ) < w )
nbPixelPerColumn . set ( i , w ) ;
}
}
// create the lines with appropriate spacing
List < Component > spacedRows = new ArrayList < > ( rows . size ( ) ) ;
for ( List < Component > row : rows ) {
Chat spacedRow = Chat . chat ( ) ;
for ( int i = 0 ; i < row . size ( ) - 1 ; i + + ) {
int w = componentWidth ( row . get ( i ) , console ) ;
int padding = nbPixelPerColumn . get ( i ) - w ;
spacedRow . then ( row . get ( i ) ) ;
spacedRow . then ( customWidthSpace ( padding , console ) ) ;
spacedRow . thenText ( space ) ;
}
if ( ! row . isEmpty ( ) )
spacedRow . then ( row . get ( row . size ( ) - 1 ) ) ;
spacedRows . add ( spacedRow . getAdv ( ) ) ;
}
return spacedRows ;
}
public static Component customWidthSpace ( int width , boolean console ) {
if ( console )
return Chat . text ( " " . repeat ( width ) ) . getAdv ( ) ;
return switch ( width ) {
case 0 , 1 - > Component . empty ( ) ;
case 2 - > Chat . text ( " . " ) . black ( ) . getAdv ( ) ;
case 3 - > Chat . text ( " ` " ) . black ( ) . getAdv ( ) ;
case 6 - > Chat . text ( " . " ) . black ( ) . getAdv ( ) ;
case 7 - > Chat . text ( " ` " ) . black ( ) . getAdv ( ) ;
case 11 - > Chat . text ( " ` " ) . black ( ) . getAdv ( ) ;
default - > {
int nbSpace = width / 4 ;
int nbBold = width % 4 ;
int nbNotBold = nbSpace - nbBold ;
if ( nbNotBold > 0 ) {
if ( nbBold > 0 ) {
yield Chat . text ( " " . repeat ( nbNotBold ) ) . bold ( false )
. then ( Chat . text ( " " . repeat ( nbBold ) ) . bold ( true ) )
. getAdv ( ) ;
}
else
yield Chat . text ( " " . repeat ( nbNotBold ) ) . bold ( false ) . getAdv ( ) ;
}
else if ( nbBold > 0 ) {
yield Chat . text ( " " . repeat ( nbBold ) ) . bold ( true ) . getAdv ( ) ;
}
throw new IllegalStateException ( " Should not be here (width= " + width + " ; nbSpace= " + nbSpace + " ; nbBold= " + nbBold + " ; nbNotBold= " + nbNotBold + " ) " ) ;
}
} ;
// "." is 2 px
// "`" is 3 px
// " " is 4 px
// 0 ""
// 1 ""
// 2 "."
// 3 "`"
// 4 " "
// 5 "§l "
// 6 ". "
// 7 "` "
// 8 " "
// 9 " §l "
// 10 "§l "
// 11 "` "
// 12 " "
}
2017-07-06 20:53:25 +02:00
2021-07-24 19:37:04 +02:00
private static final String PROGRESS_BAR_START = " [ " ;
private static final String PROGRESS_BAR_END = " ] " ;
private static final TextColor PROGRESS_BAR_EMPTY_COLOR = NamedTextColor . DARK_GRAY ;
private static final char PROGRESS_BAR_EMPTY_CHAR = '.' ;
private static final char PROGRESS_BAR_FULL_CHAR = '|' ;
public static Chat progressBar ( double [ ] values , TextColor [ ] colors , double total , int pixelWidth , boolean console ) {
// 1. Compute char size for each values
int progressPixelWidth = pixelWidth - strWidth ( PROGRESS_BAR_START + PROGRESS_BAR_END , 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(...) " ;
int progressCharWidth = progressPixelWidth / charPixelWidth ;
int [ ] sizes = new int [ values . length ] ;
double sumValuesBefore = 0 ;
int sumSizesBefore = 0 ;
for ( int i = 0 ; i < values . length ; i + + ) {
sumValuesBefore + = values [ i ] ;
int charPosition = Math . min ( ( int ) Math . round ( progressCharWidth * sumValuesBefore / total ) , progressCharWidth ) ;
sizes [ i ] = charPosition - sumSizesBefore ;
sumSizesBefore + = sizes [ i ] ;
}
// 2. Generate rendered text
2022-07-20 13:18:57 +02:00
Chat c = ChatStatic . text ( PROGRESS_BAR_START ) ;
2021-07-24 19:37:04 +02:00
int sumSizes = 0 ;
for ( int i = 0 ; i < sizes . length ; i + + ) {
sumSizes + = sizes [ i ] ;
2022-07-20 13:18:57 +02:00
FormatableChat subC = ChatStatic . text ( repeatedChar ( PROGRESS_BAR_FULL_CHAR , sizes [ i ] ) ) ;
2021-07-24 19:37:04 +02:00
if ( colors ! = null & & i < colors . length & & colors [ i ] ! = null )
subC . color ( colors [ i ] ) ;
c . then ( subC ) ;
}
return c
2022-07-20 13:18:57 +02:00
. then ( ChatStatic . text ( repeatedChar ( PROGRESS_BAR_EMPTY_CHAR , progressCharWidth - sumSizes ) )
2021-07-24 19:37:04 +02:00
. color ( PROGRESS_BAR_EMPTY_COLOR ) )
. 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 ) ;
}
2017-07-06 20:53:25 +02:00
2020-06-08 16:16:18 +02:00
2020-11-02 23:23:41 +01:00
public static String truncatePrefix ( String prefix , int maxLength ) {
if ( prefix . length ( ) > maxLength ) {
String lastColor = ChatColorUtil . getLastColors ( prefix ) ;
prefix = truncateAtLengthWithoutReset ( prefix , maxLength ) ;
if ( ! ChatColorUtil . getLastColors ( prefix ) . equals ( lastColor ) )
prefix = truncateAtLengthWithoutReset ( prefix , maxLength - lastColor . length ( ) ) + lastColor ;
}
return prefix ;
2020-06-08 16:16:18 +02:00
}
2020-11-02 23:23:41 +01:00
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 ;
2020-06-08 16:16:18 +02:00
}
2020-11-02 23:23:41 +01:00
2020-06-08 16:16:18 +02:00
2017-07-06 04:02:03 +02:00
2020-06-08 16:16:18 +02:00
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
2021-07-20 01:08:29 +02:00
/ * *
* Generate a tree view based on the tree structure { @code node } .
*
* Each element in the returned list represent 1 line of the tree view .
* Thus , the caller may send each line separately or at once depending of the quantity of data .
* @return A array of component , each element being a single line .
* /
public static List < Chat > treeView ( DisplayTreeNode node , boolean console ) {
List < Chat > ret = new ArrayList < > ( ) ;
2020-06-08 16:16:18 +02:00
2022-07-20 13:18:57 +02:00
ret . add ( ChatStatic . chat ( )
2021-07-20 01:08:29 +02:00
. then ( node . component ) ) ;
2020-06-08 16:16:18 +02:00
for ( int i = 0 ; i < node . children . size ( ) ; i + + ) {
2021-07-20 01:08:29 +02:00
List < Chat > childComponents = treeView ( node . children . get ( i ) , console ) ;
2020-06-08 16:16:18 +02:00
boolean last = i = = node . children . size ( ) - 1 ;
for ( int j = 0 ; j < childComponents . size ( ) ; j + + ) {
2021-07-20 01:08:29 +02:00
2020-06-08 16:16:18 +02:00
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 ) ) ;
2021-07-20 01:08:29 +02:00
2022-07-20 13:18:57 +02:00
ret . add ( ChatStatic . text ( prefix )
2021-07-20 01:08:29 +02:00
. then ( childComponents . get ( j ) ) ) ;
2020-06-08 16:16:18 +02:00
}
}
return ret ;
}
public static class DisplayTreeNode {
2021-07-20 01:08:29 +02:00
public final Chat component ;
2020-06-08 16:16:18 +02:00
public final List < DisplayTreeNode > children = new ArrayList < > ( ) ;
2021-07-20 01:08:29 +02:00
public DisplayTreeNode ( Chat cmp ) {
2020-06-08 16:16:18 +02:00
component = cmp ;
}
public DisplayTreeNode addChild ( DisplayTreeNode child ) {
children . add ( child ) ;
return this ;
}
}
2017-07-06 04:02:03 +02:00
}