Forráskód Böngészése

HtmlText handle TextAlign correctly (#353)

Pascal Peridont 7 éve
szülő
commit
0b8a1b2ec8
3 módosított fájl, 284 hozzáadás és 29 törlés
  1. 72 23
      h2d/HtmlText.hx
  2. 8 6
      h2d/Text.hx
  3. 204 0
      samples/HtmlText.hx

+ 72 - 23
h2d/HtmlText.hx

@@ -1,13 +1,17 @@
 package h2d;
 package h2d;
 
 
+import h2d.Text;
+
 class HtmlText extends Text {
 class HtmlText extends Text {
 
 
 	var elements : Array<Sprite> = [];
 	var elements : Array<Sprite> = [];
 	var xPos : Int;
 	var xPos : Int;
 	var yPos : Int;
 	var yPos : Int;
 	var xMax : Int;
 	var xMax : Int;
+	var xMin : Int;
 	var sizePos : Int;
 	var sizePos : Int;
 	var dropMatrix : h3d.shader.ColorMatrix;
 	var dropMatrix : h3d.shader.ColorMatrix;
+	var prevChar : Int;
 
 
 	override function draw(ctx:RenderContext) {
 	override function draw(ctx:RenderContext) {
 		if( dropShadow != null ) {
 		if( dropShadow != null ) {
@@ -47,22 +51,48 @@ class HtmlText extends Text {
 			elements = [];
 			elements = [];
 		}
 		}
 		glyphs.setDefaultColor(textColor);
 		glyphs.setDefaultColor(textColor);
+		
 		xPos = 0;
 		xPos = 0;
+		xMin = 0;
+		
+		var align = handleAlign ? textAlign : Left;
+		switch( align ) {
+			case Center, Right, MultilineCenter, MultilineRight:
+				lines = [];
+				initGlyphs(text, false, false, lines);
+				var max = if( align == MultilineCenter || align == MultilineRight ) calcWidth else realMaxWidth < 0 ? 0 : Std.int(realMaxWidth);
+				var k = align == Center || align == MultilineCenter ? 1 : 0;
+				for( i in 0...lines.length )
+					lines[i] = (max - lines[i]) >> k;
+				xPos = lines.shift();
+				xMin = xPos;
+			default:
+		}
+		
 		yPos = 0;
 		yPos = 0;
 		xMax = 0;
 		xMax = 0;
 		sizePos = 0;
 		sizePos = 0;
 		calcYMin = 0;
 		calcYMin = 0;
+		
 		var doc = try Xml.parse(text) catch( e : Dynamic ) throw "Could not parse " + text + " (" + e +")";
 		var doc = try Xml.parse(text) catch( e : Dynamic ) throw "Could not parse " + text + " (" + e +")";
+		
 		var sizes = [];
 		var sizes = [];
+		prevChar = -1;
 		for( e in doc )
 		for( e in doc )
-			buildSizes(e,sizes);
+			buildSizes(e, sizes);
+		
+		prevChar = -1;
 		for( e in doc )
 		for( e in doc )
-			addNode(e, font, rebuild, sizes);
+			addNode(e, font, rebuild, handleAlign, sizes, lines);
+		
+		if (!handleAlign && !rebuild && lines != null) lines.push(xPos);
+		if( xPos > xMax ) xMax = xPos;
 
 
 		var x = xPos, y = yPos;
 		var x = xPos, y = yPos;
-		calcWidth = x > xMax ? x : xMax;
-		calcHeight = y > 0 && x == 0 ? y - lineSpacing : y + font.lineHeight;
-		calcSizeHeight = y > 0 && x == 0 ? y + (font.baseLine - font.lineHeight - lineSpacing) : y + font.baseLine;
+		calcXMin = xMin;
+		calcWidth = xMax - xMin;
+		calcHeight = y + font.lineHeight;
+		calcSizeHeight = y + (font.baseLine > 0 ? font.baseLine : font.lineHeight);
 		calcDone = true;
 		calcDone = true;
 	}
 	}
 
 
@@ -91,22 +121,20 @@ class HtmlText extends Text {
 			font = prevFont;
 			font = prevFont;
 		} else {
 		} else {
 			var text = htmlToText(e.nodeValue);
 			var text = htmlToText(e.nodeValue);
-			var prevChar = -1;
-			var xPos = 0;
+			var xp = 0;
 			for( i in 0...text.length ) {
 			for( i in 0...text.length ) {
 				var cc = text.charCodeAt(i);
 				var cc = text.charCodeAt(i);
-				var stop = false;
 				var e = font.getChar(cc);
 				var e = font.getChar(cc);
 				var sz = e.getKerningOffset(prevChar) + e.width;
 				var sz = e.getKerningOffset(prevChar) + e.width;
 				if( cc == "\n".code || font.charset.isBreakChar(cc) ) {
 				if( cc == "\n".code || font.charset.isBreakChar(cc) ) {
 					if( cc != "\n".code && !font.charset.isSpace(cc) )
 					if( cc != "\n".code && !font.charset.isSpace(cc) )
-						xPos += sz;
-					sizes.push( -(xPos + 1));
+						xp += sz;
+					sizes.push( -(xp + 1));
 					return;
 					return;
 				}
 				}
-				xPos += sz + letterSpacing;
+				xp += sz + letterSpacing;
 			}
 			}
-			sizes.push(xPos);
+			sizes.push(xp);
 		}
 		}
 	}
 	}
 
 
@@ -138,8 +166,10 @@ class HtmlText extends Text {
 		return size;
 		return size;
 	}
 	}
 
 
-	function addNode( e : Xml, font : Font, rebuild : Bool, sizes : Array<Int> ) {
+	function addNode( e : Xml, font : Font, rebuild : Bool, handleAlign:Bool, sizes : Array<Int>, ?lines : Array<Int> = null ) {
 		sizePos++;
 		sizePos++;
+		var calcLines = !handleAlign && !rebuild && lines != null;
+		var align = handleAlign ? textAlign : Left;
 		if( e.nodeType == Xml.Element ) {
 		if( e.nodeType == Xml.Element ) {
 			var prevColor = null, prevGlyphs = null;
 			var prevColor = null, prevGlyphs = null;
 			switch( e.nodeName.toLowerCase() ) {
 			switch( e.nodeName.toLowerCase() ) {
@@ -167,8 +197,16 @@ class HtmlText extends Text {
 				}
 				}
 			case "br":
 			case "br":
 				if( xPos > xMax ) xMax = xPos;
 				if( xPos > xMax ) xMax = xPos;
-				xPos = 0;
+				if( calcLines ) lines.push(xPos);
+				switch( align ) {
+					case Left:
+						xPos = 0;
+					case Right, Center, MultilineCenter, MultilineRight:
+						xPos = lines.shift();
+						if( xPos < xMin ) xMin = xPos;
+				}
 				yPos += font.lineHeight + lineSpacing;
 				yPos += font.lineHeight + lineSpacing;
+				prevChar = -1;
 			case "img":
 			case "img":
 				var i = loadImage(e.get("src"));
 				var i = loadImage(e.get("src"));
 				if( i == null ) i = Tile.fromColor(0xFF00FF, 8, 8);
 				if( i == null ) i = Tile.fromColor(0xFF00FF, 8, 8);
@@ -190,30 +228,41 @@ class HtmlText extends Text {
 			default:
 			default:
 			}
 			}
 			for( child in e )
 			for( child in e )
-				addNode(child, font, rebuild, sizes);
+				addNode(child, font, rebuild, handleAlign, sizes, lines);
 			if( prevGlyphs != null )
 			if( prevGlyphs != null )
 				glyphs = prevGlyphs;
 				glyphs = prevGlyphs;
 			if( prevColor != null )
 			if( prevColor != null )
 				@:privateAccess glyphs.curColor.load(prevColor);
 				@:privateAccess glyphs.curColor.load(prevColor);
 		} else {
 		} else {
 			var t = splitText(htmlToText(e.nodeValue), xPos, remainingSize(sizes));
 			var t = splitText(htmlToText(e.nodeValue), xPos, remainingSize(sizes));
-			var prevChar = -1;
 			var dy = this.font.baseLine - font.baseLine;
 			var dy = this.font.baseLine - font.baseLine;
 			for( i in 0...t.length ) {
 			for( i in 0...t.length ) {
 				var cc = t.charCodeAt(i);
 				var cc = t.charCodeAt(i);
+				var e = font.getChar(cc);
+				
 				if( cc == "\n".code ) {
 				if( cc == "\n".code ) {
 					if( xPos > xMax ) xMax = xPos;
 					if( xPos > xMax ) xMax = xPos;
-					xPos = 0;
+					if( calcLines ) lines.push(xPos);
+					switch( align ) {
+						case Left:
+							xPos = 0;
+						case Right, Center, MultilineCenter, MultilineRight:
+							xPos = lines.shift();
+							if( xPos < xMin ) xMin = xPos;
+					}
 					yPos += font.lineHeight + lineSpacing;
 					yPos += font.lineHeight + lineSpacing;
 					prevChar = -1;
 					prevChar = -1;
 					continue;
 					continue;
 				}
 				}
-				var e = font.getChar(cc);
-				xPos += e.getKerningOffset(prevChar);
-				if( rebuild ) glyphs.add(xPos, yPos + dy, e.t);
-				if( yPos == 0 && e.t.dy+dy < calcYMin ) calcYMin = e.t.dy + dy;
-				xPos += e.width + letterSpacing;
-				prevChar = cc;
+				else {
+					if (e != null) {
+						xPos += e.getKerningOffset(prevChar);
+						if( rebuild ) glyphs.add(xPos, yPos + dy, e.t);
+						if( yPos == 0 && e.t.dy+dy < calcYMin ) calcYMin = e.t.dy + dy;
+						xPos += e.width + letterSpacing;
+					}
+					prevChar = cc;
+				}
 			}
 			}
 		}
 		}
 	}
 	}

+ 8 - 6
h2d/Text.hx

@@ -233,11 +233,7 @@ class Text extends Drawable {
 			var offs = e.getKerningOffset(prevChar);
 			var offs = e.getKerningOffset(prevChar);
 			var esize = e.width + offs;
 			var esize = e.width + offs;
 			// if the next word goes past the max width, change it into a newline
 			// if the next word goes past the max width, change it into a newline
-			if( e != null ) {
-				if( rebuild ) glyphs.add(x + offs, y, e.t);
-				if( y == 0 && e.t.dy < yMin ) yMin = e.t.dy;
-				x += esize + letterSpacing;
-			}
+			
 			if( cc == '\n'.code ) {
 			if( cc == '\n'.code ) {
 				if( x > xMax ) xMax = x;
 				if( x > xMax ) xMax = x;
 				if( calcLines ) lines.push(x);
 				if( calcLines ) lines.push(x);
@@ -250,8 +246,14 @@ class Text extends Drawable {
 				}
 				}
 				y += dl;
 				y += dl;
 				prevChar = -1;
 				prevChar = -1;
-			} else
+			} else {
+				if( e != null ) {
+					if( rebuild ) glyphs.add(x + offs, y, e.t);
+					if( y == 0 && e.t.dy < yMin ) yMin = e.t.dy;
+					x += esize + letterSpacing;
+				}
 				prevChar = cc;
 				prevChar = cc;
+			}
 		}
 		}
 		if( calcLines ) lines.push(x);
 		if( calcLines ) lines.push(x);
 		if( x > xMax ) xMax = x;
 		if( x > xMax ) xMax = x;

+ 204 - 0
samples/HtmlText.hx

@@ -0,0 +1,204 @@
+import h2d.Drawable;
+import h2d.Flow;
+import h2d.Font;
+import h2d.Graphics;
+import h2d.Sprite;
+import h2d.Text.Align;
+
+class HtmlTextWidget extends Sprite
+{
+	public var align: Align;
+	public var textField: h2d.HtmlText;
+	public var back: Graphics;
+
+	public function new(parent:h2d.Scene, font: Font, str:String, align:h2d.Text.Align){
+		super(parent);
+		this.align = align;
+		back = new Graphics(this);
+
+		var tf = new h2d.HtmlText(font, this);
+		tf.textColor = 0xffffff;
+		tf.textAlign = align;
+		tf.text = str;
+		textField = tf;
+
+		refreshBounds();
+	}
+
+	public function refreshBounds() {
+		back.clear();
+
+		var bounds = textField.getBounds(this);
+		var size = textField.getSize();
+
+		back.beginFill(0x5050ff,  0.5);
+		back.drawRect(bounds.x, 0, size.width, size.height);
+		back.endFill();
+
+		back.lineStyle(1, 0x50ff50);
+		back.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
+
+		back.lineStyle(1, 0xff5050);
+		back.moveTo(bounds.x, 0);
+		back.lineTo(bounds.x + textField.textWidth, 0);
+		back.moveTo(bounds.x, 0);
+		back.lineTo(bounds.x, textField.textHeight);
+	}
+
+	public function setMaxWidth(w:Int) {
+		textField.maxWidth = w;
+		refreshBounds();
+	}
+}
+
+class HtmlText extends hxd.App {
+
+	var textWidgets:Array<HtmlTextWidget> = [];
+	var resizeWidgets: Array<HtmlTextWidget> = [];
+
+	override function init() {
+		
+		// Enable global scaling
+		// s2d.scale(1.25);
+
+		var font = hxd.res.DefaultFont.get();
+		// var font = hxd.Res.customFont.toFont();
+
+		var multilineText = "This is a multiline <font color=\"#FF00FF\">text.<br/>Lorem</font> ipsum dolor";
+		var singleText = "Hello simple text";
+
+		var xpos = 0;
+		var yoffset = 10.0;
+
+		function createWidget(str:String, align:h2d.Text.Align) {
+			var w = new HtmlTextWidget(s2d, font, str, align);
+			w.x = xpos;
+			w.y = yoffset;
+			textWidgets.push(w);
+			return w;
+		}
+
+		// Static single and multiline widgets
+		xpos += 450;
+		for (a in [Align.Left, Align.Center, Align.Right, Align.MultilineCenter, Align.MultilineRight]) {
+			var w = createWidget("", a);
+			var label = new h2d.HtmlText(font, w);
+			label.text = Std.string(a);
+			label.x = 5;
+			label.alpha = 0.5;
+			yoffset += w.textField.textHeight + 10;
+			var w = createWidget(singleText, a);
+			yoffset += w.textField.textHeight + 10;
+			var w = createWidget(multilineText, a);
+			yoffset += w.textField.textHeight + 10;
+		}
+
+		// Resized widgets
+		xpos += 200;
+		yoffset = 10;
+		var longText = "Lorem ipsum dolor sit amet, fabulas repudiare accommodare nec ut.<br />Ut nec facete maiestatis, <font color=\"#FF00FF\">partem debitis eos id</font>, perfecto ocurreret repudiandae cum no.";
+		//var longText = "Lorem ipsum dolor sit amet, fabulas repudiare accommodare nec ut.Ut nec facete maiestatis, <font color=\"#FF00FF\">partem debitis eos id</font>, perfecto ocurreret repudiandae cum no.";
+		for (a in [Align.Left, Align.Center, Align.Right, Align.MultilineCenter, Align.MultilineRight]) {
+			var w = createWidget(longText, a);
+			w.setMaxWidth(200);
+			resizeWidgets.push(w);
+			yoffset += 100;
+		}
+		
+		// Flows
+		function createText(parent:Sprite, str : String, align:Align) {
+			var tf = new h2d.HtmlText(font, parent);
+			tf.textColor = 0xffffff;
+			tf.textAlign = align;
+			tf.text = str;
+			tf.maxWidth = 150;
+			return tf;
+		}
+
+		function createFlow(parent:Sprite) {
+			var flow = new Flow(parent);
+			flow.debug = true;
+			flow.horizontalSpacing = 5;
+			flow.verticalSpacing = 5;
+			flow.padding = 5;
+			return flow;
+		}
+
+
+		yoffset = 0;
+		var flow = createFlow(s2d);
+		flow.verticalAlign = FlowAlign.Middle;
+		createText(flow, singleText, Align.Left);
+		createText(flow, multilineText, Align.Left);
+
+		yoffset += flow.getBounds().height + 10;
+
+		var flow = createFlow(s2d);
+		flow.y = yoffset;
+		flow.multiline = false;
+		flow.verticalAlign = FlowAlign.Middle;
+		createText(flow, multilineText, Align.Center);
+		createText(flow, multilineText, Align.Right);
+
+		yoffset += flow.getBounds().height + 10;
+
+		var flow = createFlow(s2d);
+		flow.y = yoffset;
+		flow.verticalAlign = FlowAlign.Middle;
+		flow.maxWidth = 150;
+		createText(flow, singleText, Align.Left);
+		createText(flow, multilineText, Align.Center);
+
+		yoffset += flow.getBounds().height + 10;
+
+		var flow = createFlow(s2d);
+		flow.y = yoffset;
+		flow.horizontalAlign = FlowAlign.Middle;
+		flow.maxWidth = 150;
+		flow.isVertical = true;
+		createText(flow, singleText, Align.Left);
+		createText(flow, multilineText, Align.Right);
+
+		yoffset += flow.getBounds().height + 10;
+
+		{
+			var flow = createFlow(s2d);
+			flow.y = yoffset;
+			flow.horizontalAlign = FlowAlign.Left;
+			flow.maxWidth = 360;
+			flow.horizontalSpacing = 8;
+			flow.isVertical = false;
+			var f = createText(flow, "short text", Align.Right);
+			createText(flow, singleText, Align.Left);
+			yoffset += flow.getBounds().height + 10;
+		}
+
+
+		var flow = createFlow(s2d);
+		flow.y = yoffset;
+		flow.x = 100;
+		flow.horizontalAlign = FlowAlign.Middle;
+		flow.isVertical = true;
+		{
+			var f1 = createFlow(flow);
+			createText(f1, multilineText, Align.Left);
+			var f2 = createFlow(flow);
+			createText(f2, multilineText, Align.Left);
+		}
+
+		onResize();
+	}
+
+
+	override function update(dt:Float) {
+		for (w in resizeWidgets) {
+			w.setMaxWidth(Std.int(300 + Math.sin(haxe.Timer.stamp() * 0.5) * 100.0));
+		}
+	}
+
+	static function main() {
+		hxd.Res.initEmbed();
+		new HtmlText();
+	}
+
+}