|
| 1 | +package dev.terminalmc.chatnotify.gui.widget.field; |
| 2 | + |
| 3 | +import dev.terminalmc.chatnotify.mixin.accessor.MultiLineEditBoxAccessor; |
| 4 | +import dev.terminalmc.chatnotify.mixin.accessor.MultilineTextFieldAccessor; |
| 5 | +import net.minecraft.ChatFormatting; |
| 6 | +import net.minecraft.client.gui.Font; |
| 7 | +import net.minecraft.client.gui.components.MultiLineEditBox; |
| 8 | +import net.minecraft.client.gui.components.Tooltip; |
| 9 | +import net.minecraft.network.chat.Component; |
| 10 | +import org.jetbrains.annotations.NotNull; |
| 11 | +import org.jetbrains.annotations.Nullable; |
| 12 | + |
| 13 | +import java.util.Optional; |
| 14 | +import java.util.function.Consumer; |
| 15 | +import java.util.function.Function; |
| 16 | +import java.util.regex.Pattern; |
| 17 | +import java.util.regex.PatternSyntaxException; |
| 18 | + |
| 19 | +/** |
| 20 | + * Requires a mixin to change the text render color. |
| 21 | + */ |
| 22 | +public class MultiLineTextField extends MultiLineEditBox { |
| 23 | + private TextField.Validator validator; |
| 24 | + public boolean lenient = false; |
| 25 | + private int defaultTextColor; |
| 26 | + private Tooltip defaultTooltip; |
| 27 | + private int textColor; |
| 28 | + private long lastClickTime; |
| 29 | + |
| 30 | + public MultiLineTextField(Font font, int x, int y, int width, int height, |
| 31 | + Component placeholder, Component message) { |
| 32 | + super(font, x, y, width, height, placeholder, message); |
| 33 | + this.validator = new Validator.Default(); |
| 34 | + this.defaultTextColor = 0xE0E0E0; |
| 35 | + } |
| 36 | + |
| 37 | + public MultiLineTextField regexValidator() { |
| 38 | + validator = new Validator.Regex(); |
| 39 | + return this; |
| 40 | + } |
| 41 | + |
| 42 | + @Override |
| 43 | + public void setValueListener(@NotNull Consumer<String> responder) { |
| 44 | + super.setValueListener((str) -> { |
| 45 | + if (valid(str) || lenient) responder.accept(str); |
| 46 | + }); |
| 47 | + } |
| 48 | + |
| 49 | + @Override |
| 50 | + public void setTooltip(@Nullable Tooltip tooltip) { |
| 51 | + defaultTooltip = tooltip; |
| 52 | + super.setTooltip(tooltip); |
| 53 | + } |
| 54 | + |
| 55 | + public int getTextColor() { |
| 56 | + return textColor; |
| 57 | + } |
| 58 | + |
| 59 | + public void setTextColor(int color) { |
| 60 | + if (textColor == defaultTextColor) textColor = color; |
| 61 | + defaultTextColor = color; |
| 62 | + } |
| 63 | + |
| 64 | + @Override |
| 65 | + public boolean mouseClicked(double mouseX, double mouseY, int button) { |
| 66 | + if (super.mouseClicked(mouseX, mouseY, button)) { |
| 67 | + // Double-click to select all |
| 68 | + long time = System.currentTimeMillis(); |
| 69 | + if (lastClickTime + 250L > time) { |
| 70 | + ((MultilineTextFieldAccessor)((MultiLineEditBoxAccessor)this) |
| 71 | + .getTextField()).setCursor(this.getValue().length()); |
| 72 | + ((MultilineTextFieldAccessor)((MultiLineEditBoxAccessor)this) |
| 73 | + .getTextField()).setSelectCursor(0); |
| 74 | + } |
| 75 | + lastClickTime = time; |
| 76 | + return true; |
| 77 | + } |
| 78 | + return false; |
| 79 | + } |
| 80 | + |
| 81 | + private boolean valid(String str) { |
| 82 | + Optional<Component> error = validator.validate(str); |
| 83 | + if (error.isPresent()) { |
| 84 | + super.setTooltip(Tooltip.create(error.get())); |
| 85 | + this.textColor = 0xFF5555; |
| 86 | + return false; |
| 87 | + } else { |
| 88 | + this.textColor = defaultTextColor; |
| 89 | + super.setTooltip(defaultTooltip); |
| 90 | + return true; |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + public interface Validator { |
| 95 | + Optional<Component> validate(String str); |
| 96 | + |
| 97 | + // Implementations |
| 98 | + |
| 99 | + class Custom implements TextField.Validator { |
| 100 | + private final Function<String, Optional<Component>> validator; |
| 101 | + |
| 102 | + public Custom(Function<String, Optional<Component>> validator) { |
| 103 | + this.validator = validator; |
| 104 | + } |
| 105 | + |
| 106 | + @Override |
| 107 | + public Optional<Component> validate(String str) { |
| 108 | + return validator.apply(str); |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + class Default implements TextField.Validator { |
| 113 | + @Override |
| 114 | + public Optional<Component> validate(String str) { |
| 115 | + return Optional.empty(); |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + class Regex implements TextField.Validator { |
| 120 | + @Override |
| 121 | + public Optional<Component> validate(String str) { |
| 122 | + try { |
| 123 | + Pattern.compile(str); |
| 124 | + return Optional.empty(); |
| 125 | + } catch (PatternSyntaxException e) { |
| 126 | + return Optional.of(Component.literal(TextField.fixRegexMessage(e.getMessage())) |
| 127 | + .withStyle(ChatFormatting.RED)); |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | +} |
0 commit comments