Skip to content

Commit 79459ff

Browse files
committed
Add custom multi-line text field for trigger editor
1 parent 74965ec commit 79459ff

15 files changed

Lines changed: 211 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Fixed a crash when adding a restyle string using the trigger editor
66
- Moved custom messages to advanced options screen
7+
- Switched to multi-line text field for trigger editor
78

89
## 2.3.0
910

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
}

common/src/main/java/dev/terminalmc/chatnotify/gui/widget/field/TextField.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public TextField(Font font, int x, int y, int width, int height, Component msg,
6060
super(font, x, y, width, height, msg);
6161
this.font = font;
6262
this.validator = new Validator.Custom(validator);
63-
this.defaultTextColor = 14737632;
63+
this.defaultTextColor = 0xE0E0E0;
6464
}
6565

6666
public TextField setValidator(Function<String, Optional<Component>> validator) {
@@ -157,7 +157,7 @@ private boolean valid(String str) {
157157
Optional<Component> error = validator.validate(str);
158158
if (error.isPresent()) {
159159
super.setTooltip(Tooltip.create(error.get()));
160-
super.setTextColor(16733525);
160+
super.setTextColor(0xFF5555);
161161
return false;
162162
} else {
163163
super.setTextColor(defaultTextColor);
@@ -254,7 +254,7 @@ public Optional<Component> validate(String str) {
254254
* <p>Also, messages contain carriage-return characters which don't play
255255
* well with Minecraft so this method removes them.</p>
256256
*/
257-
private static String fixRegexMessage(String str) {
257+
public static String fixRegexMessage(String str) {
258258
// Remove carriage returns
259259
str = str.replaceAll("\\u000D", "");
260260

common/src/main/java/dev/terminalmc/chatnotify/gui/widget/list/option/AdvancedOptionList.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ private static class ExclusionFieldEntry extends Entry {
393393
triggerField.setResponder((string) -> trigger.string = string.strip());
394394
triggerField.setValue(trigger.string);
395395
triggerField.setTooltip(Tooltip.create(
396-
localized("option", "notif.trigger.field.tooltip")));
396+
localized("option", "trigger.field.tooltip")));
397397
triggerField.setTooltipDelay(Duration.ofMillis(500));
398398
elements.add(triggerField);
399399

common/src/main/java/dev/terminalmc/chatnotify/gui/widget/list/option/KeyOptionList.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private static class TriggerFieldEntry extends Entry {
120120
triggerField.setResponder((str) -> trigger.string = str.strip());
121121
triggerField.setValue(trigger.string);
122122
triggerField.setTooltip(Tooltip.create(
123-
localized("option", "notif.trigger.field.tooltip")));
123+
localized("option", "trigger.field.tooltip")));
124124
triggerField.setTooltipDelay(Duration.ofMillis(500));
125125
elements.add(triggerField);
126126
}

common/src/main/java/dev/terminalmc/chatnotify/gui/widget/list/option/NotifOptionList.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ private static class TriggerFieldEntry extends Entry {
282282
triggerField.setResponder((str) -> trigger.string = str.strip());
283283
triggerField.setValue(trigger.string);
284284
triggerField.setTooltip(Tooltip.create(
285-
localized("option", "notif.trigger.field.tooltip")));
285+
localized("option", "trigger.field.tooltip")));
286286
triggerField.setTooltipDelay(Duration.ofMillis(500));
287287
elements.add(triggerField);
288288
movingX += triggerFieldWidth;

common/src/main/java/dev/terminalmc/chatnotify/gui/widget/list/option/TriggerOptionList.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import dev.terminalmc.chatnotify.config.Trigger;
2424
import dev.terminalmc.chatnotify.gui.screen.OptionsScreen;
2525
import dev.terminalmc.chatnotify.gui.widget.HsvColorPicker;
26+
import dev.terminalmc.chatnotify.gui.widget.field.MultiLineTextField;
2627
import dev.terminalmc.chatnotify.gui.widget.field.TextField;
2728
import dev.terminalmc.chatnotify.util.FormatUtil;
2829
import dev.terminalmc.chatnotify.util.MessageUtil;
@@ -61,24 +62,28 @@ public TriggerOptionList(Minecraft mc, int width, int height, int y, int itemHei
6162
this.filter = filter;
6263
this.restyle = restyle;
6364

64-
addEntry(new Entry.TriggerFieldEntry(dynEntryX, dynEntryWidth, entryHeight,
65-
this, trigger));
65+
Entry triggerFieldEntry = new Entry.TriggerFieldEntry(
66+
dynEntryX, dynEntryWidth, entryHeight + itemHeight, this, trigger);
67+
addEntry(triggerFieldEntry);
68+
addEntry(new SpaceEntry(triggerFieldEntry));
69+
6670
if (trigger.styleString != null) {
6771
addEntry(new Entry.StyleStringFieldEntry(dynEntryX, dynEntryWidth, entryHeight,
6872
this, trigger));
6973
}
7074

71-
textDisplayBox = new MultiLineEditBox(mc.font, dynEntryX, 0, dynEntryWidth, entryHeight,
75+
textDisplayBox = new MultiLineTextField(mc.font, dynEntryX, 0, dynEntryWidth, entryHeight,
7276
localized("option", "trigger.text.placeholder"), Component.empty());
7377
textDisplayBox.setValue(displayText);
7478
keyDisplayBox = new TextField(dynEntryX, 0, dynEntryWidth, entryHeight);
7579
keyDisplayBox.setMaxLength(256);
7680
keyDisplayBox.setValue(displayKey);
7781

78-
Entry e = new Entry.MessageFieldEntry(dynEntryX, dynEntryWidth, entryHeight + itemHeight,
82+
Entry messageFieldEntry = new Entry.MessageFieldEntry(
83+
dynEntryX, dynEntryWidth, entryHeight + itemHeight,
7984
textDisplayBox, localized("option", "trigger.message.text"));
80-
addEntry(e);
81-
addEntry(new SpaceEntry(e));
85+
addEntry(messageFieldEntry);
86+
addEntry(new SpaceEntry(messageFieldEntry));
8287
addEntry(new Entry.MessageFieldEntry(dynEntryX, dynEntryWidth, entryHeight, keyDisplayBox,
8388
localized("option", "trigger.message.key")));
8489

@@ -182,10 +187,11 @@ private static class TriggerFieldEntry extends Entry {
182187
movingX += list.tinyWidgetWidth;
183188

184189
// Trigger field
185-
TextField triggerField = new TextField(movingX, 0, triggerFieldWidth, height);
190+
MultiLineTextField triggerField = new MultiLineTextField(
191+
Minecraft.getInstance().font, movingX, 0, triggerFieldWidth, height,
192+
localized("option", "trigger.field.tooltip"), Component.empty());
186193
if (trigger.type == Trigger.Type.REGEX) triggerField.regexValidator();
187-
triggerField.setMaxLength(240);
188-
triggerField.setResponder((str) -> {
194+
triggerField.setValueListener((str) -> {
189195
trigger.string = str.strip();
190196
if (list.children().size() > 4) {
191197
list.children().removeIf((entry) -> entry instanceof MessageEntry
@@ -196,7 +202,7 @@ private static class TriggerFieldEntry extends Entry {
196202
});
197203
triggerField.setValue(trigger.string);
198204
triggerField.setTooltip(Tooltip.create(
199-
localized("option", "notif.trigger.field.tooltip")));
205+
localized("option", "trigger.field.tooltip")));
200206
triggerField.setTooltipDelay(Duration.ofMillis(500));
201207
elements.add(triggerField);
202208
movingX += triggerFieldWidth;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dev.terminalmc.chatnotify.mixin;
2+
3+
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
4+
import dev.terminalmc.chatnotify.gui.widget.field.MultiLineTextField;
5+
import net.minecraft.client.gui.components.MultiLineEditBox;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.injection.At;
8+
9+
@Mixin(MultiLineEditBox.class)
10+
public class MixinMultiLineEditBox{
11+
@ModifyExpressionValue(
12+
method = "renderContents",
13+
at = @At(
14+
value = "CONSTANT",
15+
args = "intValue=-2039584")
16+
)
17+
private int modifyColor(int original) {
18+
if ((Object)this instanceof MultiLineTextField multilineTextField) {
19+
return multilineTextField.getTextColor();
20+
}
21+
return original;
22+
}
23+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package dev.terminalmc.chatnotify.mixin.accessor;
2+
3+
import net.minecraft.client.gui.components.MultiLineEditBox;
4+
import net.minecraft.client.gui.components.MultilineTextField;
5+
import org.spongepowered.asm.mixin.Mixin;
6+
import org.spongepowered.asm.mixin.gen.Accessor;
7+
8+
@Mixin(MultiLineEditBox.class)
9+
public interface MultiLineEditBoxAccessor {
10+
@Accessor
11+
MultilineTextField getTextField();
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package dev.terminalmc.chatnotify.mixin.accessor;
2+
3+
import net.minecraft.client.gui.components.MultilineTextField;
4+
import org.spongepowered.asm.mixin.Mixin;
5+
import org.spongepowered.asm.mixin.gen.Accessor;
6+
7+
@Mixin(MultilineTextField.class)
8+
public interface MultilineTextFieldAccessor {
9+
@Accessor("cursor")
10+
void setCursor(int cursor);
11+
12+
@Accessor("selectCursor")
13+
void setSelectCursor(int selectCursor);
14+
}

0 commit comments

Comments
 (0)