Skip to content

Commit f8a06bf

Browse files
authored
Improved text handling and keyboard input functionality for UITextBox (#900)
* Refactor UITextBox for improved text handling and keyboard input functionality * use cut paste copy sounds
1 parent ec2a399 commit f8a06bf

1 file changed

Lines changed: 114 additions & 14 deletions

File tree

source/funkin/editors/ui/UITextBox.hx

Lines changed: 114 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,27 +60,27 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
6060

6161
var selected = selectable && focused;
6262
if (autoAlpha) {
63-
if(selectable) {
63+
if (selectable) {
6464
alpha = label.alpha = 1;
6565
} else {
6666
alpha = label.alpha = 0.4;
6767
}
6868
}
6969

7070
var off = multiline ? 4 : ((bHeight - label.height) / 2);
71-
label.follow(this, label.autoSize ? (bWidth-label.textField.width)/2 : 4, off);
71+
label.follow(this, label.autoSize ? (bWidth - label.textField.width) / 2 : 4, off);
7272
framesOffset = (selected ? 18 : (hovered ? 9 : 0));
7373
@:privateAccess {
7474
if (selected) {
7575
__wasFocused = true;
7676
caretSpr.alpha = (FlxG.game.ticks % 666) >= 333 ? 1 : 0;
7777

78-
var curPos = switch(position) {
78+
var curPos = switch (position) {
7979
case 0:
8080
FlxPoint.get(0, 0);
8181
default:
8282
if (position >= label.text.length) {
83-
label.textField.__getCharBoundaries(label.text.length-1, cacheRect);
83+
label.textField.__getCharBoundaries(label.text.length - 1, cacheRect);
8484
FlxPoint.get(cacheRect.x + cacheRect.width, cacheRect.y);
8585
} else {
8686
label.textField.__getCharBoundaries(position, cacheRect);
@@ -100,60 +100,160 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
100100
}
101101
}
102102

103+
private static var seperators:Array<String> = [
104+
" ", "\n", "\t", "\r", "-", "_", "=", "+", "/", "\\", "|", ",", ".", ";", ":", "!", "?", "@", "#", "$", "%", "^", "&", "*", "(", ")", "[", "]", "{",
105+
"}",
106+
];
107+
108+
public inline static function isSeperator(char:String):Bool
109+
return seperators.contains(char);
110+
111+
public inline static function findWholeWord(text:String, pos:Int, ?isDelete:Bool = false):Null<Array<Int>> {
112+
if (text.length == 0)
113+
return null;
114+
115+
var start = pos;
116+
var end = pos;
117+
118+
while (!isDelete && start > 0 && !isSeperator(text.charAt(start - 1)))
119+
start--;
120+
121+
while (end < text.length && !isSeperator(text.charAt(end)))
122+
end++;
123+
124+
if (end == pos && isSeperator(text.charAt(end - 1)))
125+
start--;
126+
127+
return [start, end];
128+
}
129+
103130
public function onKeyDown(e:KeyCode, modifier:KeyModifier) {
104-
switch(e) {
131+
switch (e) {
105132
case RETURN:
106133
focused = false;
107-
if (onChange != null) onChange(label.text);
134+
if (onChange != null)
135+
onChange(label.text);
108136
case LEFT:
137+
if (modifier.ctrlKey) {
138+
if (position == 0)
139+
return;
140+
141+
var wordBounds = findWholeWord(label.text, position);
142+
if (wordBounds != null) {
143+
position = position == wordBounds[0] ? wordBounds[0] - 1 : wordBounds[0];
144+
} else {
145+
position = 0;
146+
}
147+
148+
return;
149+
}
150+
109151
changeSelection(-1);
110152
case RIGHT:
153+
if (modifier.ctrlKey) {
154+
if (position == label.text.length)
155+
return;
156+
157+
var wordBounds = findWholeWord(label.text, position);
158+
if (wordBounds != null) {
159+
position = position == wordBounds[1] ? wordBounds[1] + 1 : wordBounds[1];
160+
} else {
161+
position = label.text.length;
162+
}
163+
164+
return;
165+
}
166+
111167
changeSelection(1);
112168
case BACKSPACE:
113169
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND));
170+
171+
if (modifier.ctrlKey) {
172+
var wordBounds = findWholeWord(label.text, position);
173+
if (wordBounds != null) {
174+
label.text = label.text.substr(0, wordBounds[0]) + label.text.substr(wordBounds[1]);
175+
position = wordBounds[0];
176+
}
177+
return;
178+
}
179+
114180
if (position > 0) {
115-
label.text = label.text.substr(0, position-1) + label.text.substr(position);
181+
label.text = label.text.substr(0, position - 1) + label.text.substr(position);
116182
changeSelection(-1);
117183
}
184+
case DELETE:
185+
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND));
186+
187+
if (modifier.ctrlKey) {
188+
var wordBounds = findWholeWord(label.text, position, true);
189+
if (wordBounds != null) {
190+
label.text = label.text.substr(0, wordBounds[0]) + label.text.substr(wordBounds[1]);
191+
position = wordBounds[0];
192+
}
193+
return;
194+
}
195+
196+
if (position < label.text.length) {
197+
label.text = label.text.substr(0, position) + label.text.substr(position + 1);
198+
}
118199
case HOME:
119200
position = 0;
120201
case END:
121202
position = label.text.length;
122203
case V:
123-
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
204+
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_PASTE_SOUND));
124205
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
125206
// but somehow it gave a int outside of the KeyModifier's range :sob:
126207
// apparently there is a boolean that just checks for you. yw :D
127208

128209
// if we are not holding ctrl, ignore
129-
if (!modifier.ctrlKey) return;
210+
if (!modifier.ctrlKey)
211+
return;
130212
// we pasting
131213
var data:String = Clipboard.generalClipboard.getData(TEXT_FORMAT);
132-
if (data != null) onTextInput(data);
214+
if (data != null)
215+
onTextInput(data);
133216
case C:
134-
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
217+
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND));
135218
// if we are not holding ctrl, ignore
136-
if (!modifier.ctrlKey) return;
219+
if (!modifier.ctrlKey)
220+
return;
137221

138222
// copying
139223
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
224+
case X:
225+
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CUT_SOUND));
226+
227+
// if we are not holding ctrl, ignore
228+
if (!modifier.ctrlKey)
229+
return;
230+
231+
// cutting
232+
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
233+
position = 0;
234+
label.text = "";
140235
default:
236+
if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey)
237+
return;
238+
141239
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
142240
}
143241
}
144242

145243
public function changeSelection(change:Int) {
146-
position = FlxMath.wrap(position + change, 0, label.text.length);
244+
position = Std.int(FlxMath.bound(position + change, 0, label.text.length));
147245
}
246+
148247
public function onKeyUp(e:KeyCode, modifier:KeyModifier) {}
149248

150249
public function onTextInput(text:String):Void {
151250
label.text = label.text.substr(0, position) + text + label.text.substr(position);
152251
position += text.length;
153252
}
253+
154254
// untested, but this should be a fix for if the text wont type
155255
public function onTextEdit(text:String, start:Int, end:Int):Void {
156256
label.text = label.text.substr(0, position) + text + label.text.substr(position);
157257
position += text.length;
158258
}
159-
}
259+
}

0 commit comments

Comments
 (0)