Browse Source

added Text.getProgress in both Text and HtmlText, implemented splitText for HtmlText

Nicolas Cannasse 5 years ago
parent
commit
8b533eb62b
2 changed files with 126 additions and 8 deletions
  1. 116 7
      h2d/HtmlText.hx
  2. 10 1
      h2d/Text.hx

+ 116 - 7
h2d/HtmlText.hx

@@ -47,6 +47,10 @@ class HtmlText extends Text {
 		return font;
 	}
 
+	function parseText( text : String ) {
+		return try Xml.parse(text) catch( e : Dynamic ) throw "Could not parse " + text + " (" + e +")";
+	}
+
 	override function initGlyphs( text : String, rebuild = true, handleAlign = true, ?lines : Array<Int> ) {
 		if( rebuild ) {
 			glyphs.clear();
@@ -77,13 +81,13 @@ class HtmlText extends Text {
 		sizePos = 0;
 		calcYMin = 0;
 
-		var doc = try Xml.parse(text) catch( e : Dynamic ) throw "Could not parse " + text + " (" + e +")";
+		var doc = parseText(text);
 
 		var sizes = new Array<Float>();
 		prevChar = -1;
 		newLine = true;
 		for( e in doc )
-			buildSizes(e, font, sizes);
+			buildSizes(e, font, sizes, false);
 
 		prevChar = -1;
 		newLine = true;
@@ -101,7 +105,7 @@ class HtmlText extends Text {
 		calcDone = true;
 	}
 
-	function buildSizes( e : Xml, font : Font, sizes : Array<Float> ) {
+	function buildSizes( e : Xml, font : Font, sizes : Array<Float>, forSplit ) {
 		if( e.nodeType == Xml.Element ) {
 			var len = 0.;
 			var nodeName = e.nodeName.toLowerCase();
@@ -131,7 +135,7 @@ class HtmlText extends Text {
 			}
 			sizes.push(len);
 			for( child in e )
-				buildSizes(child, font, sizes);
+				buildSizes(child, font, sizes, forSplit);
 			switch( nodeName ) {
 			case "p":
 				sizes.push( -1);// break
@@ -149,8 +153,15 @@ class HtmlText extends Text {
 				if( cc == "\n".code || font.charset.isBreakChar(cc) ) {
 					if( cc != "\n".code && !font.charset.isSpace(cc) )
 						xp += sz;
-					sizes.push( -(xp + 1));
-					return;
+					if( !forSplit ) {
+						sizes.push( -(xp + 1));
+						return;
+					}
+					sizes.push(xp);
+					if( font.charset.isSpace(cc) )
+						sizes.push(sz);
+					xp = 0;
+					continue;
 				}
 				xp += sz + letterSpacing;
 			}
@@ -178,6 +189,103 @@ class HtmlText extends Text {
 		return size;
 	}
 
+	override function splitText(text:String):String {
+		if( realMaxWidth < 0 )
+			return text;
+		yPos = 0;
+		xMax = 0;
+		sizePos = 0;
+		calcYMin = 0;
+
+		var doc = parseText(text);
+
+		/*
+			This might require a global refactoring at some point.
+			We would need a way to somehow build an AST from the XML representation
+			with all sizes and word breaks so analysis is much more easy.
+		*/
+
+		var sizes = new Array<Float>();
+		prevChar = -1;
+		newLine = true;
+		for( e in doc )
+			buildSizes(e, font, sizes, true);
+		xMax = 0;
+		function addBreaks( e : Xml ) {
+			if( e.nodeType == Xml.Element ) {
+				var sz = sizes[sizePos++];
+				if( sz < 0 )
+					xMax = 0;
+				else
+					xMax += sz;
+				for( x in e )
+					addBreaks(x);
+				if( e.nodeName == "p" ) {
+					sizePos++;
+					xMax = 0;
+				}
+			} else {
+				var text = htmlToText(e.nodeValue);
+				var startI = 0, prevI = 0;
+				for( i in 0...text.length ) {
+					var cc = text.charCodeAt(i);
+					if( cc == "\n".code || font.charset.isBreakChar(cc) ) {
+						var sz = sizes[sizePos++];
+						var sp = font.charset.isSpace(cc) ? sizes[sizePos++] : 0;
+						xMax += sz;
+						if( xMax > realMaxWidth ) {
+							var index = Lambda.indexOf(e.parent,e);
+							var pre = text.substr(startI,prevI - startI);
+							if( pre != "" )
+								e.parent.insertChild(Xml.createPCData(pre),index++);
+							e.parent.insertChild(Xml.createElement("br"),index);
+							e.nodeValue = text.substr(prevI+1);
+							startI = prevI+1;
+							xMax = sz;
+						}
+						xMax += sp + letterSpacing;
+						prevI = i;
+					}
+				}
+				var sz = sizes[sizePos++];
+				xMax += sz;
+				if( xMax > realMaxWidth ) {
+					e.parent.insertChild(Xml.createElement("br"),Lambda.indexOf(e.parent,e));
+					xMax = sz;
+				}
+			}
+		}
+		for( d in doc )
+			addBreaks(d);
+		return doc.toString();
+	}
+
+	override function getTextProgress(text:String, progress:Float):String {
+		if( progress >= text.length )
+			return text;
+		var doc = parseText(text);
+		function progressRec(e:Xml) {
+			if( progress <= 0 ) {
+				e.parent.removeChild(e);
+				return;
+			}
+			if( e.nodeType == Xml.Element ) {
+				for( x in [for( x in e ) x] )
+					progressRec(x);
+			} else {
+				var text = htmlToText(e.nodeValue);
+				if( text.length > progress ) {
+					text = text.substr(0, Std.int(progress));
+					e.nodeValue = text;
+				}
+				progress -= text.length;
+			}
+		}
+		for( x in [for( x in doc ) x] )
+			progressRec(x);
+		return doc.toString();
+	}
+
 	function addNode( e : Xml, font : Font, rebuild : Bool, handleAlign:Bool, sizes : Array<Float>, ?lines : Array<Int> = null ) {
 		sizePos++;
 		var calcLines = !handleAlign && !rebuild && lines != null;
@@ -286,6 +394,7 @@ class HtmlText extends Text {
 				addNode(child, font, rebuild, handleAlign, sizes, lines);
 			switch( nodeName ) {
 			case "p":
+				sizePos++;
 				makeLineBreak();
 			default:
 			}
@@ -295,7 +404,7 @@ class HtmlText extends Text {
 				@:privateAccess glyphs.curColor.load(prevColor);
 		} else {
 			newLine = false;
-			var t = splitText(htmlToText(e.nodeValue), xPos, remainingSize(sizes));
+			var t = splitRawText(htmlToText(e.nodeValue), xPos, remainingSize(sizes));
 			var dy = this.font.baseLine - font.baseLine;
 			for( i in 0...t.length ) {
 				var cc = t.charCodeAt(i);

+ 10 - 1
h2d/Text.hx

@@ -160,7 +160,11 @@ class Text extends Drawable {
 		}
 	}
 
-	public function splitText( text : String, leftMargin = 0., afterData = 0. ) {
+	public function splitText( text : String ) {
+		return splitRawText(text,0,0);
+	}
+
+	function splitRawText( text : String, leftMargin : Float, afterData : Float ) {
 		if( realMaxWidth < 0 )
 			return text;
 		var lines = [], rest = text, restPos = 0;
@@ -217,6 +221,11 @@ class Text extends Drawable {
 		return lines.join("\n");
 	}
 
+	public function getTextProgress( text : String, progress : Float ) {
+		if( progress >= text.length ) return text;
+		return text.substr(0, Std.int(progress));
+	}
+
 	function initGlyphs( text : String, rebuild = true, handleAlign = true, lines : Array<Int> = null ) : Void {
 		if( rebuild ) glyphs.clear();
 		var x = 0., y = 0., xMax = 0., xMin = 0., prevChar = -1;