) {
function createInteractive() {
if(aHrefs == null || aHrefs.length == 0)
return;
aInteractive = new Interactive(0, metrics[sizePos].baseLine, this);
aInteractive.propagateEvents = propagateInteractiveNode;
var href = aHrefs[aHrefs.length-1];
aInteractive.onClick = function(event) {
onHyperlink(href);
}
aInteractive.onOver = function(event) {
onOverHyperlink(href);
}
aInteractive.onOut = function(event) {
onOutHyperlink(href);
}
aInteractive.x = xPos;
aInteractive.y = yPos;
elements.push(aInteractive);
}
inline function finalizeInteractive() {
if(aInteractive != null) {
aInteractive.width = xPos - aInteractive.x;
aInteractive = null;
}
}
inline function makeLineBreak()
{
finalizeInteractive();
if( xPos > xMax ) xMax = xPos;
yPos += metrics[sizePos].height + lineSpacing;
nextLine(align, metrics[++sizePos].width);
createInteractive();
}
if( e.nodeType == Xml.Element ) {
var prevColor = null, prevGlyphs = null;
var oldAlign = align;
var nodeName = e.nodeName.toLowerCase();
inline function setFont( v : String ) {
font = loadFont(v);
if( prevGlyphs == null ) prevGlyphs = glyphs;
var prev = glyphs;
glyphs = new TileGroup(font == null ? null : font.tile, this);
if ( font != null ) {
switch( font.type ) {
case SignedDistanceField(channel, alphaCutoff, smoothing):
var shader = new h3d.shader.SignedDistanceField();
shader.channel = channel;
shader.alphaCutoff = alphaCutoff;
shader.smoothing = smoothing;
shader.autoSmoothing = smoothing == -1;
glyphs.smooth = this.smooth;
glyphs.addShader(shader);
default:
}
}
@:privateAccess glyphs.curColor.load(prev.curColor);
elements.push(glyphs);
}
switch( nodeName ) {
case "font":
for( a in e.attributes() ) {
var v = e.get(a);
switch( a.toLowerCase() ) {
case "color":
if( prevColor == null ) prevColor = @:privateAccess glyphs.curColor.clone();
if( v.charCodeAt(0) == '#'.code && v.length == 4 )
v = "#" + v.charAt(1) + v.charAt(1) + v.charAt(2) + v.charAt(2) + v.charAt(3) + v.charAt(3);
glyphs.setDefaultColor(Std.parseInt("0x" + v.substr(1)));
case "opacity":
if( prevColor == null ) prevColor = @:privateAccess glyphs.curColor.clone();
@:privateAccess glyphs.curColor.a *= Std.parseFloat(v);
case "face":
setFont(v);
default:
}
}
case "p":
for( a in e.attributes() ) {
switch( a.toLowerCase() ) {
case "align":
var v = e.get(a);
if ( v != null )
switch( v.toLowerCase() ) {
case "left":
align = Left;
case "center":
align = Center;
case "right":
align = Right;
case "multiline-center":
align = MultilineCenter;
case "multiline-right":
align = MultilineRight;
//?justify
}
default:
}
}
if ( !newLine ) {
makeLineBreak();
newLine = true;
prevChar = -1;
} else {
nextLine(align, metrics[sizePos].width);
}
case "b","bold":
setFont("bold");
case "i","italic":
setFont("italic");
case "br":
makeLineBreak();
newLine = true;
prevChar = -1;
case "img":
var i : Tile = loadImage(e.get("src"));
if ( i == null ) i = Tile.fromColor(0xFF00FF, 8, 8);
var py = yPos;
switch(imageVerticalAlign) {
case Bottom:
py += metrics[sizePos].baseLine - i.height;
case Middle:
py += metrics[sizePos].baseLine - i.height/2;
case Top:
}
if( py + i.dy < calcYMin )
calcYMin = py + i.dy;
if( rebuild ) {
var b = new Bitmap(i, this);
b.x = xPos;
b.y = py;
elements.push(b);
}
newLine = false;
prevChar = -1;
xPos += i.width + imageSpacing;
case "a":
if( e.exists("href") ) {
finalizeInteractive();
if( aHrefs == null )
aHrefs = [];
aHrefs.push(e.get("href"));
createInteractive();
}
default:
}
for( child in e )
addNode(child, font, align, rebuild, metrics);
align = oldAlign;
switch( nodeName ) {
case "p":
if ( newLine ) {
nextLine(align, metrics[sizePos].width);
} else if ( sizePos < metrics.length - 2 || metrics[sizePos + 1].width != 0 ) {
// Condition avoid extra empty line if was the last tag.
makeLineBreak();
newLine = true;
prevChar = -1;
}
case "a":
if( aHrefs.length > 0 ) {
finalizeInteractive();
aHrefs.pop();
createInteractive();
}
default:
}
if( prevGlyphs != null )
glyphs = prevGlyphs;
if( prevColor != null )
@:privateAccess glyphs.curColor.load(prevColor);
} else if (e.nodeValue.length != 0) {
newLine = false;
var t = e.nodeValue;
var dy = metrics[sizePos].baseLine - font.baseLine;
for( i in 0...t.length ) {
var cc = t.charCodeAt(i);
if( cc == "\n".code ) {
makeLineBreak();
dy = metrics[sizePos].baseLine - font.baseLine;
prevChar = -1;
continue;
}
else {
var fc = font.getChar(cc);
if (fc != null) {
xPos += fc.getKerningOffset(prevChar);
if( rebuild ) glyphs.add(xPos, yPos + dy, fc.t);
if( yPos == 0 && fc.t.dy+dy < calcYMin ) calcYMin = fc.t.dy + dy;
xPos += fc.width + letterSpacing;
}
prevChar = cc;
}
}
}
}
function set_imageSpacing(s) {
if (imageSpacing == s) return s;
imageSpacing = s;
rebuild();
return s;
}
override function set_textColor(c) {
if( this.textColor == c ) return c;
this.textColor = c;
rebuild();
return c;
}
function set_condenseWhite(value: Bool) {
if ( this.condenseWhite != value ) {
this.condenseWhite = value;
rebuild();
}
return value;
}
function set_propagateInteractiveNode(value: Bool) {
if ( this.propagateInteractiveNode != value ) {
this.propagateInteractiveNode = value;
rebuild();
}
return value;
}
function set_imageVerticalAlign(align) {
if ( this.imageVerticalAlign != align ) {
this.imageVerticalAlign = align;
rebuild();
}
return align;
}
function set_lineHeightMode(v) {
if ( this.lineHeightMode != v ) {
this.lineHeightMode = v;
rebuild();
}
return v;
}
override function getBoundsRec( relativeTo : Object, out : h2d.col.Bounds, forSize : Bool ) {
if( forSize )
for( i in elements )
if( hxd.impl.Api.isOfType(i,h2d.Bitmap) || hxd.impl.Api.isOfType(i,h2d.Interactive) )
i.visible = false;
super.getBoundsRec(relativeTo, out, forSize);
if( forSize )
for( i in elements )
i.visible = true;
}
}
private typedef LineInfo = {
var width : Float;
var height : Float;
var baseLine : Float;
}
private typedef SplitNode = {
var node : Xml;
var prevChar : Int;
var pos : Int;
var width : Float;
var height : Float;
var baseLine : Float;
var font : h2d.Font;
}