瀏覽代碼

add multiline support for TextInput (#1089)

Casey Clyde 3 年之前
父節點
當前提交
720b8702a5
共有 1 個文件被更改,包括 133 次插入14 次删除
  1. 133 14
      h2d/TextInput.hx

+ 133 - 14
h2d/TextInput.hx

@@ -33,6 +33,10 @@ class TextInput extends Text {
 		Contrary to `Text.maxWidth` does not cause a word-wrap, but also masks out contents that are outside the max width.
 	**/
 	public var inputWidth : Null<Int>;
+	/**
+		Whether the text input allows multiple lines.
+	**/
+	public var multiline: Bool = false;
 	/**
 		If not null, represents current text selection range.
 	**/
@@ -51,6 +55,8 @@ class TextInput extends Text {
 	var cursorText : String;
 	var cursorX : Float;
 	var cursorXIndex : Int;
+	var cursorY : Float;
+	var cursorYIndex : Int;
 	var cursorBlink = 0.;
 	var cursorScroll = 0;
 	var scrollX = 0.;
@@ -193,10 +199,23 @@ class TextInput extends Text {
 				text = text.substr(0, cursorIndex) + text.substr(end);
 				onChange();
 			}
-		case K.ESCAPE, K.ENTER, K.NUMPAD_ENTER:
+		case K.ESCAPE:
 			cursorIndex = -1;
 			interactive.blur();
 			return;
+		case K.ENTER, K.NUMPAD_ENTER:
+			if(!multiline) {
+				cursorIndex = -1;
+				interactive.blur();
+				return;
+			} else {
+				beforeChange();
+				if( selectionRange != null )
+					cutSelection();
+				text = text.substr(0, cursorIndex) + '\n' + text.substr(cursorIndex);
+				cursorIndex++;
+				onChange();
+			}
 		case K.Z if( K.isDown(K.CTRL) ):
 			if( undo.length > 0 && canEdit ) {
 				redo.push(curHistoryState());
@@ -305,6 +324,7 @@ class TextInput extends Text {
 		while (ret < len && !charset.isSpace(StringTools.fastCodeAt(text, ret))) ret++;
 		return ret;
 	}
+
 	function getWordStart() {
 		if (cursorIndex <= 0) {
 			return cursorIndex;
@@ -340,6 +360,70 @@ class TextInput extends Text {
 		while( undo.length > maxHistorySize ) undo.shift();
 	}
 
+	function getAllLines() {
+		var lines = this.text.split('\n');
+		var finalLines : Array<String> = [];
+
+		for(l in lines) {
+			var splitText = splitText(l).split('\n');
+			finalLines = finalLines.concat(splitText);
+		}
+
+		for(i in 0...finalLines.length) {
+			finalLines[i] += '\n';
+		}
+
+		return finalLines;
+	}
+
+	function getCurrentLine() : String {
+		var lines = getAllLines();
+		var currIndex = 0;
+
+		for(i in 0...lines.length) {
+			currIndex += lines[i].length;
+			if(cursorIndex < currIndex) {
+				return lines[i];
+			}
+		}
+		return '';
+	}
+	
+	function getCursorXOffset() {
+		var lines = getAllLines();
+		var offset = cursorIndex;
+		var currLine = getCurrentLine();
+		var currIndex = 0;
+
+		for(i in 0...lines.length) {
+			currIndex += lines[i].length;
+			if(cursorIndex < currIndex) {
+				break;
+			} else {
+				offset -= lines[i].length;
+			}
+		}
+
+		return calcTextWidth(currLine.substr(0, offset));
+	}
+
+	function getCursorYOffset() {
+		// return 0.0;
+		var lines = getAllLines();
+		var currIndex = 0;
+		var lineNum = 0;
+
+		for(i in 0...lines.length) {
+			currIndex += lines[i].length;
+			if(cursorIndex < currIndex) {
+				lineNum = i;
+				break;
+			}
+		}
+
+		return lineNum * font.lineHeight;
+	}
+
 	/**
 		Returns a String representing currently selected text area or `null` if no text is selected.
 	**/
@@ -371,18 +455,30 @@ class TextInput extends Text {
 
 	function textPos( x : Float, y : Float ) {
 		x += scrollX;
+		var lineIndex = Math.floor(y / font.lineHeight);
+		var lines = getAllLines();
+		var selectedLine = lines[lineIndex];
 		var pos = 0;
-		while( pos < text.length ) {
-			if( calcTextWidth(text.substr(0,pos+1)) > x )
+		for(i in 0...lineIndex) {
+			pos += lines[i].length;
+		}
+
+		var linePos = 0;
+		while( linePos < selectedLine.length ) {
+			if( calcTextWidth(selectedLine.substr(0,linePos+1)) > x ) {
+				pos++;
 				break;
+			}
 			pos++;
+			linePos++;
 		}
-		return pos;
+		return pos - 1;
 	}
 
 	override function sync(ctx) {
+		var lines = getAllLines();
 		interactive.width = (inputWidth != null ? inputWidth : maxWidth != null ? Math.ceil(maxWidth) : textWidth);
-		interactive.height = font.lineHeight;
+		interactive.height = font.lineHeight * lines.length;
 		super.sync(ctx);
 	}
 
@@ -396,7 +492,8 @@ class TextInput extends Text {
 			if( cursorIndex > text.length ) cursorIndex = text.length;
 			cursorText = text;
 			cursorXIndex = cursorIndex;
-			cursorX = calcTextWidth(text.substr(0, cursorIndex));
+			cursorX = getCursorXOffset();
+			cursorY = getCursorYOffset();
 			if( inputWidth != null && cursorX - scrollX >= inputWidth )
 				scrollX = cursorX - inputWidth + 1;
 			else if( cursorX < scrollX && cursorIndex > 0 )
@@ -409,16 +506,35 @@ class TextInput extends Text {
 		absY -= scrollX * matC;
 
 		if( selectionRange != null ) {
-			if( selectionSize == 0 ) {
-				selectionPos = calcTextWidth(text.substr(0, selectionRange.start));
-				selectionSize = calcTextWidth(text.substr(selectionRange.start, selectionRange.length));
+			var lines = getAllLines();
+			var lineOffset = 0;
+
+			for(i in 0...lines.length) {
+				var line = lines[i];
+
+				var selEnd = line.length;
+
+				if(selectionRange.start > lineOffset + line.length || selectionRange.start + selectionRange.length < lineOffset) {
+					lineOffset += line.length;
+					continue;
+				}
+
+				var selStart = Math.floor(Math.max(0, selectionRange.start - lineOffset));
+				var selEnd = Math.floor(Math.min(line.length - selStart, selectionRange.length + selectionRange.start - lineOffset - selStart));
+				
+				selectionPos = calcTextWidth(line.substr(0, selStart));
+				selectionSize = calcTextWidth(line.substr(selStart, selEnd));
 				if( selectionRange.start + selectionRange.length == text.length ) selectionSize += cursorTile.width; // last pixel
+	
+				selectionTile.dx += selectionPos;
+				selectionTile.dy += i * font.lineHeight;
+				selectionTile.width += selectionSize;
+				emitTile(ctx, selectionTile);
+				selectionTile.dx -= selectionPos;
+				selectionTile.dy -= i * font.lineHeight;
+				selectionTile.width -= selectionSize;
+				lineOffset += line.length;
 			}
-			selectionTile.dx += selectionPos;
-			selectionTile.width += selectionSize;
-			emitTile(ctx, selectionTile);
-			selectionTile.dx -= selectionPos;
-			selectionTile.width -= selectionSize;
 		}
 
 		super.draw(ctx);
@@ -429,8 +545,11 @@ class TextInput extends Text {
 			cursorBlink += ctx.elapsedTime;
 			if( cursorBlink % (cursorBlinkTime * 2) < cursorBlinkTime ) {
 				cursorTile.dx += cursorX - scrollX;
+				cursorTile.dy += cursorY;
 				emitTile(ctx, cursorTile);
 				cursorTile.dx -= cursorX - scrollX;
+				cursorTile.dy -= cursorY;
+
 			}
 		}