4040import com .cleanroommc .modularui .widgets .RichTextWidget ;
4141import com .cleanroommc .modularui .widgets .SlotGroupWidget ;
4242import com .cleanroommc .modularui .widgets .layout .Flow ;
43+ import it .unimi .dsi .fastutil .ints .Int2ObjectArrayMap ;
44+ import it .unimi .dsi .fastutil .ints .Int2ObjectMap ;
45+ import it .unimi .dsi .fastutil .objects .Object2IntArrayMap ;
46+ import it .unimi .dsi .fastutil .objects .Object2IntMap ;
4347import org .jetbrains .annotations .NotNull ;
4448import org .jetbrains .annotations .Nullable ;
4549
@@ -57,6 +61,11 @@ public class MultiblockUIFactory {
5761 private int screenHeight = 109 ;
5862 private Consumer <List <IWidget >> childrenConsumer ;
5963
64+ static {
65+ // register operations
66+ Operation .init ();
67+ }
68+
6069 public MultiblockUIFactory (@ NotNull MultiblockWithDisplayBase mte ) {
6170 this .mte = mte ;
6271 configureErrorText (builder -> {
@@ -409,9 +418,11 @@ public static Builder builder() {
409418 }
410419
411420 @ SuppressWarnings ({ "UnusedReturnValue" , "unused" })
412- public static class Builder {
421+ public static class Builder implements KeyManager {
413422
414423 private final List <IDrawable > textList = new ArrayList <>();
424+ private final List <Operation > operations = new ArrayList <>();
425+
415426 private Consumer <Builder > action ;
416427 private final SyncHandler syncHandler = makeSyncHandler ();
417428
@@ -811,13 +822,13 @@ public Builder addFuelNeededLine(String fuelName, int previousRecipeDuration) {
811822
812823 /** Insert an empty line into the text list. */
813824 public Builder addEmptyLine () {
814- this . textList . add (IKey .LINE_FEED );
825+ addKey (IKey .LINE_FEED );
815826 return this ;
816827 }
817828
818829 /** Add custom text dynamically, allowing for custom application logic. */
819- public Builder addCustom (Consumer <List < IDrawable > > customConsumer ) {
820- customConsumer .accept (this . textList );
830+ public Builder addCustom (Consumer <KeyManager > customConsumer ) {
831+ customConsumer .accept (this );
821832 return this ;
822833 }
823834
@@ -827,12 +838,12 @@ public boolean isEmpty() {
827838
828839 public void clear () {
829840 this .textList .clear ();
841+ this .operations .clear ();
830842 }
831843
832844 protected boolean hasChanged () {
833845 if (this .action == null ) return false ;
834- List <String > old = new ArrayList <>();
835- for (var drawable : this .textList ) old .add (JsonUtils .toJsonString (drawable ));
846+ List <String > old = toString (this .textList );
836847 build ();
837848 if (textList .size () != old .size ()) return true ;
838849 for (int i = 0 ; i < textList .size (); i ++) {
@@ -842,6 +853,14 @@ protected boolean hasChanged() {
842853 return false ;
843854 }
844855
856+ private static List <String > toString (List <? extends IDrawable > drawables ) {
857+ List <String > strings = new ArrayList <>();
858+ for (IDrawable drawable : drawables ) {
859+ strings .add (JsonUtils .toJsonString (drawable ));
860+ }
861+ return strings ;
862+ }
863+
845864 protected void sync (String key , PanelSyncManager syncManager ) {
846865 syncManager .syncValue (key , this .syncHandler );
847866 }
@@ -863,8 +882,9 @@ public void detectAndSendChanges(boolean init) {
863882
864883 private void syncText (PacketBuffer buffer ) {
865884 buffer .writeVarInt (textList .size ());
866- for (IDrawable drawable : textList ) {
867- var jsonString = JsonUtils .toJsonString (drawable );
885+ for (int i = 0 ; i < textList .size (); i ++) {
886+ buffer .writeByte (Operation .getId (operations .get (i )));
887+ var jsonString = JsonUtils .toJsonString (textList .get (i ));
868888 NetworkUtils .writeStringSafe (buffer , jsonString );
869889 }
870890 }
@@ -874,8 +894,9 @@ public void readOnClient(int id, PacketBuffer buf) {
874894 if (id == 0 ) {
875895 clear ();
876896 for (int i = buf .readVarInt (); i > 0 ; i --) {
897+ int op = buf .readByte ();
877898 String jsonString = NetworkUtils .readStringSafe (buf );
878- addKey (JsonUtils .fromJsonString (jsonString ));
899+ addKey (JsonUtils .fromJsonString (jsonString ), Operation . getById ( op ) );
879900 }
880901 }
881902 }
@@ -891,8 +912,8 @@ public void build(IRichTextBuilder<?> richText) {
891912 build ();
892913 dirty = false ;
893914 }
894- for (IDrawable drawable : textList ) {
895- richText . addLine ( drawable ). spaceLine ( 2 );
915+ for (int i = 0 ; i < operations . size (); i ++ ) {
916+ operations . get ( i ). apply ( textList . get ( i ), richText );
896917 }
897918 }
898919
@@ -919,12 +940,95 @@ public void onRebuild(Runnable onRebuild) {
919940 this .onRebuild = onRebuild ;
920941 }
921942
943+ private void addKey (IKey key , IDrawable ... hover ) {
944+ addKey (KeyUtil .setHover (key , hover ));
945+ }
946+
922947 private void addKey (IDrawable key ) {
948+ addKey (key , Operation .NEW_LINE_SPACE );
949+ }
950+
951+ private void addKey (@ NotNull IDrawable key , @ NotNull Operation op ) {
952+ if (textList .size () != operations .size ()) {
953+ throw new IllegalStateException ("textList and operations must be the same size!" );
954+ }
923955 this .textList .add (key );
956+ Operation .checkOp (op );
957+ this .operations .add (op );
924958 }
925959
926- private void addKey (IKey key , IDrawable ... hover ) {
927- addKey (KeyUtil .setHover (key , hover ));
960+ @ Override
961+ public void add (IDrawable drawable , Operation op ) {
962+ addKey (drawable , op );
963+ }
964+ }
965+
966+ @ FunctionalInterface
967+ public interface Operation {
968+
969+ Int2ObjectMap <Operation > ID_MAP = new Int2ObjectArrayMap <>();
970+ Object2IntMap <Operation > REVERSE_MAP = new Object2IntArrayMap <>();
971+
972+ Operation NO_OP = (drawable , richText ) -> {};
973+ Operation NEW_LINE = (drawable , richText ) -> richText .addLine (drawable );
974+ Operation ADD = (drawable , richText ) -> richText .add (drawable );
975+ Operation NEW_LINE_SPACE = NEW_LINE .andThen (richText -> richText .spaceLine (2 ));
976+
977+ static void init () {
978+ registerOp (NO_OP );
979+ registerOp (NEW_LINE );
980+ registerOp (ADD );
981+ registerOp (NEW_LINE_SPACE );
982+ }
983+
984+ static Operation getById (int id ) {
985+ return ID_MAP .get (id );
986+ }
987+
988+ static int getId (Operation op ) {
989+ return REVERSE_MAP .get (op );
990+ }
991+
992+ static void registerOp (Operation op ) {
993+ int nextId = ID_MAP .size ();
994+ ID_MAP .put (nextId , op );
995+ REVERSE_MAP .put (op , nextId );
996+ }
997+
998+ static void checkOp (Operation op ) {
999+ if (!REVERSE_MAP .containsKey (op ))
1000+ throw new IllegalStateException ("Operation is not registered!" );
1001+ Operation check = ID_MAP .get (REVERSE_MAP .getInt (op ));
1002+ if (check != op ) throw new IllegalStateException ("Operation is not identical!" );
1003+ }
1004+
1005+ void apply (IDrawable drawable , IRichTextBuilder <?> richText );
1006+
1007+ default Operation andThen (Operation after ) {
1008+ return (drawable , richText ) -> {
1009+ this .apply (drawable , richText );
1010+ after .apply (drawable , richText );
1011+ };
1012+ }
1013+
1014+ default Operation andThen (Consumer <IRichTextBuilder <?>> after ) {
1015+ return (drawable , richText ) -> {
1016+ this .apply (drawable , richText );
1017+ after .accept (richText );
1018+ };
1019+ }
1020+ }
1021+
1022+ public interface KeyManager {
1023+
1024+ default void add (IDrawable drawable ) {
1025+ add (drawable , Operation .NEW_LINE_SPACE );
1026+ }
1027+
1028+ void add (IDrawable drawable , Operation op );
1029+
1030+ default void addAll (Iterable <? extends IDrawable > drawables ) {
1031+ drawables .forEach (this ::add );
9281032 }
9291033 }
9301034}
0 commit comments