using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using CommonLang;
using CommonUI.Cell.Game;
using CommonUI.Data;
using CommonLang.Log;
using CommonLang.Xml;
namespace CommonUI.Display.Text
{
///
/// 文字字符上的属性
///
public class TextAttribute : ICloneable
{
///
/// 字符颜色 0 表示无特性 (RGBA)
///
public uint fontColor = 0xffffffff;
///
/// 子尺寸 0 表示无特性
///
public float fontSize;
///
/// 字体名字,空表示无特性
///
public string fontName;
///
/// 字体
///
public FontStyle fontStyle = FontStyle.STYLE_PLAIN;
public bool underline
{
get
{
switch (fontStyle)
{
case FontStyle.STYLE_BOLD_ITALIC_UNDERLINED:
case FontStyle.STYLE_ITALIC_UNDERLINED:
case FontStyle.STYLE_BOLD_UNDERLINED:
case FontStyle.STYLE_UNDERLINED:
return true;
}
return false;
}
set
{
if (!value)
{
switch (fontStyle)
{
case FontStyle.STYLE_BOLD_ITALIC_UNDERLINED:
fontStyle = FontStyle.STYLE_BOLD_ITALIC;
break;
case FontStyle.STYLE_ITALIC_UNDERLINED:
fontStyle = FontStyle.STYLE_ITALIC;
break;
case FontStyle.STYLE_BOLD_UNDERLINED:
fontStyle = FontStyle.STYLE_BOLD;
break;
case FontStyle.STYLE_UNDERLINED:
fontStyle = FontStyle.STYLE_PLAIN;
break;
}
}
else
{
switch (fontStyle)
{
case FontStyle.STYLE_BOLD_ITALIC:
fontStyle = FontStyle.STYLE_BOLD_ITALIC_UNDERLINED;
break;
case FontStyle.STYLE_ITALIC:
fontStyle = FontStyle.STYLE_ITALIC_UNDERLINED;
break;
case FontStyle.STYLE_BOLD:
fontStyle = FontStyle.STYLE_BOLD_UNDERLINED;
break;
case FontStyle.STYLE_PLAIN:
fontStyle = FontStyle.STYLE_UNDERLINED;
break;
}
}
}
}
///
/// 描边
///
public TextBorderCount borderCount = TextBorderCount.Null;
///
/// 描边颜色 (RGBA)
///
public uint borderColor;
///
/// 此字符替换成图片,空表示无特性
///
public string resImage;
///
/// 图片渲染方式
///
public ImageZoom resImageZoom;
///
/// 此字符替换成动画,空表示无特性
///
public string resSprite;
///
/// 标记此处可以被点击触发事件,空表示无特性
///
public string link;
///
/// 行对齐方式
///
public RichTextAlignment anchor = RichTextAlignment.taNA;
///
/// 扩展的自定义渲染部分
///
public TextDrawable drawable;
public TextAttribute(
uint fColor = 0,
float fSize = 0,
string fName = null,
FontStyle fStyle = FontStyle.STYLE_PLAIN,
RichTextAlignment ta = RichTextAlignment.taNA,
TextBorderCount bCount = TextBorderCount.Null,
uint bColor = 0,
string rImage = null,
string rSprite = null,
string pLink = null,
ImageZoom rImageZoom = null,
TextDrawable drawable = null)
{
this.fontColor = fColor;
this.fontSize = fSize;
this.fontName = fName;
this.fontStyle = fStyle;
this.anchor = ta;
this.resImage = rImage;
this.resImageZoom = rImageZoom;
this.resSprite = rSprite;
this.borderCount = bCount;
this.borderColor = bColor;
this.link = pLink;
this.drawable = drawable;
}
public TextAttribute(TextAttribute other)
{
this.fontColor = other.fontColor;
this.fontSize = other.fontSize;
this.fontName = other.fontName;
this.fontStyle = other.fontStyle;
this.anchor = other.anchor;
this.borderCount = other.borderCount;
this.borderColor = other.borderColor;
this.resImage = other.resImage;
this.resImageZoom = CUtils.TryClone(other.resImageZoom);
this.resSprite = other.resSprite;
this.link = other.link;
this.drawable = other.drawable;
}
public object Clone()
{
return new TextAttribute(this);
}
public override bool Equals(object obj)
{
if (obj is TextAttribute)
{
return this.Equals(obj as TextAttribute);
}
return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public bool Equals(TextAttribute other)
{
if (other == this)
return true;
if (other == null)
return false;
if (this.fontColor != other.fontColor)
return false;
if (this.fontSize != other.fontSize)
return false;
if (!string.Equals(this.fontName, other.fontName))
return false;
if (this.fontStyle != other.fontStyle)
return false;
if (this.anchor != other.anchor)
return false;
if (this.borderCount != other.borderCount)
return false;
if (this.borderColor != other.borderColor)
return false;
if (!string.Equals(this.resImage, other.resImage))
return false;
if (!string.Equals(this.resSprite, other.resSprite))
return false;
if (!ImageZoom.Equals(this.resImageZoom, other.resImageZoom))
return false;
if (!string.Equals(this.link, other.link))
return false;
if (drawable != null && !drawable.Equals(other.drawable))
return false;
if (drawable == null && other.drawable != null)
{
return false;
}
return true;
}
public bool IsValid()
{
if (fontColor != 0)
return true;
if (fontSize != 0)
return true;
if (!string.IsNullOrEmpty(fontName))
return true;
if (anchor != RichTextAlignment.taNA)
return true;
if (borderCount > 0)
return true;
if (borderColor != 0)
return true;
if (!string.IsNullOrEmpty(resImage))
return true;
if (!string.IsNullOrEmpty(resSprite))
return true;
if (resImageZoom != null)
return true;
if (!string.IsNullOrEmpty(link))
return true;
if (drawable != null)
return true;
return false;
}
public void Combine(TextAttribute other, bool isCover = true)
{
if (other.fontColor != 0)
{
this.fontColor = other.fontColor;
}
if (other.fontSize != 0)
{
this.fontSize = other.fontSize;
}
if (!string.IsNullOrEmpty(other.fontName))
{
this.fontName = other.fontName;
}
if (other.fontStyle != 0)
{
this.fontStyle = other.fontStyle;
}
if (other.anchor != RichTextAlignment.taNA)
{
this.anchor = other.anchor;
}
if (other.borderCount > 0)
{
this.borderCount = other.borderCount;
}
if (other.borderColor != 0)
{
this.borderColor = other.borderColor;
}
if (!string.IsNullOrEmpty(other.resImage))
{
this.resImage = other.resImage;
}
if (!string.IsNullOrEmpty(other.resSprite))
{
this.resSprite = other.resSprite;
}
if (other.resImageZoom != null)
{
this.resImageZoom = CUtils.TryClone(other.resImageZoom);
}
if (other.drawable != null)
{
this.drawable = CUtils.TryClone(other.drawable);
}
if (isCover && !string.IsNullOrEmpty(other.link))
{
this.link = other.link;
}
else if (!isCover)
{
this.link += other.link;
}
}
public static TextAttribute Combine(TextAttribute src, TextAttribute dst)
{
TextAttribute ret = new TextAttribute(src);
ret.Combine(dst);
return ret;
}
}
//------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------------
public class ImageZoomParser : CommonLang.Parser.ParserAdapter
{
public ImageZoomParser() : base(typeof(ImageZoom)) { }
public override object StringToObject(string text)
{
return ImageZoom.FromString(text);
}
public override string ObjectToString(object obj)
{
return ImageZoom.ToString(obj as ImageZoom);
}
}
public class ImageZoom : ICloneable
{
static ImageZoom()
{
Parser.RegistParser(new ImageZoomParser());
}
public enum ImageFill
{
Clamp,
Repeat,
}
public ImageFill Filling = ImageFill.Clamp;
public float Width;
public float Height;
public object Clone()
{
ImageZoom ret = new ImageZoom();
ret.Filling = this.Filling;
ret.Width = this.Width;
ret.Height = this.Height;
return ret;
}
public static bool Equals(ImageZoom a, ImageZoom b)
{
if (a != null && b != null)
{
if (a.Filling != b.Filling)
return false;
if (a.Width != b.Width)
return false;
if (a.Height != b.Height)
return false;
return true;
}
return a == b;
}
public override string ToString()
{
return ToString(this);
}
public static ImageZoom FromString(string text)
{
string[] kvs = text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (kvs.Length >= 2)
{
ImageZoom ret = new ImageZoom();
ret.Width = float.Parse(kvs[0]);
ret.Height = float.Parse(kvs[1]);
if (kvs.Length >= 3)
{
ret.Filling = (ImageFill)Enum.Parse(typeof(ImageFill), kvs[2], true);
}
return ret;
}
return null;
}
public static string ToString(ImageZoom obj)
{
if (obj != null)
{
return string.Format("{0},{1},{2}", obj.Width, obj.Height, obj.Filling);
}
return null;
}
}
//------------------------------------------------------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////////////////////////////////
///
/// 含有属性的文字
///
public class AttributedString : ICloneable
{
private string text = "";
private List attributes = new List();
public AttributedString()
{
}
public AttributedString(string other, TextAttribute ta)
{
Append(other, ta);
}
public object Clone()
{
AttributedString ret = new AttributedString();
ret.text = this.text;
ret.attributes = CUtils.CloneList(this.attributes);
return ret;
}
public override bool Equals(object obj)
{
if (obj is AttributedString)
{
AttributedString other = obj as AttributedString;
if (other.text.Equals(this.text))
{
for (int i = text.Length - 1; i >= 0; --i)
{
if (!other.attributes[i].Equals(this.attributes[i]))
{
return false;
}
}
return true;
}
}
return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public bool SetAttribute(int index, int len, TextAttribute ta)
{
if (index + len <= attributes.Count)
{
int end = index + len;
for (int i = index; i < end; ++i)
{
attributes[i] = ta;
}
return true;
}
return false;
}
public AttributedString AddAttribute(TextAttribute attribute, bool isCover = true)
{
return AddAttribute(attribute, 0, text.Length, isCover);
}
public AttributedString AddAttribute(TextAttribute attribute, int beginIndex, int count, bool isCover = true)
{
int endIndex = beginIndex + count;
for (int i = beginIndex; i < endIndex; ++i)
{
attributes[i].Combine(attribute, isCover);
}
return this;
}
public AttributedString Append(AttributedString other)
{
if (other != null && !string.IsNullOrEmpty(other.text))
{
text += other.text;
for (int i = 0; i < other.text.Length; ++i)
{
attributes.Add(other.attributes[i]);
}
}
return this;
}
public AttributedString Append(string other)
{
if (attributes.Count > 0)
{
TextAttribute lastAttr = attributes[attributes.Count - 1];
Append(other, lastAttr);
}
else
{
Append(other, new TextAttribute(Color.COLOR_WHITE, 12));
}
return this;
}
public AttributedString Append(string other, TextAttribute ta)
{
if (!string.IsNullOrEmpty(other))
{
text += other;
for (int i = 0; i < other.Length; ++i)
{
attributes.Add(ta);
}
}
return this;
}
public AttributedString DeleteString(int beginIndex, int count)
{
text = text.Remove(beginIndex, count);
attributes.RemoveRange(beginIndex, count);
return this;
}
public AttributedString ClearString()
{
text = "";
attributes.Clear();
return this;
}
public int Length
{
get
{
return text.Length;
}
}
override public string ToString()
{
return text;
}
public char GetChar(int index)
{
if (index < text.Length)
{
return text[index];
}
return default(char);
}
public TextAttribute GetAttribute(int index)
{
if (index < attributes.Count)
{
return attributes[index];
}
return null;
}
}
//------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------------------------------
public class AttributedStringDecoder
{
protected Logger log = LoggerFactory.GetLogger("AttributedStringDecoder");
public const string TEXT_XML_KEY_COLOR = "color";
public const string TEXT_XML_KEY_SIZE = "size";
public const string TEXT_XML_KEY_FACE = "face";
public const string TEXT_XML_KEY_STYLE = "style";
public const string TEXT_XML_KEY_B_COUNT = "border";
public const string TEXT_XML_KEY_B_COLOR = "bcolor";
public const string TEXT_XML_KEY_RES_IMG = "img";
///
/// @"宽,高"
/// img_zoom = "width,height"
///
public const string TEXT_XML_KEY_RES_IMAGE_ZOOM = "img_zoom";
///
/// @"资源名,精灵名,动画ID"
/// spr = "res/sprite.xml,sprite_name,anim"
///
public const string TEXT_XML_KEY_RES_SPR = "spr";
public const string TEXT_XML_KEY_LINK = "link";
public const string TEXT_XML_KEY_LINE_ANCHOR = "anchor";
public const string TEXT_XML_NODE_BREAK = "br";
public const string TEXT_XML_NODE_SPACE = "p";
public virtual AttributedString CreateFromXML(XmlDocument xml, TextAttribute defaultTA = null)
{
AttributedString attr = new AttributedString();
TextAttribute curAttr = (defaultTA != null) ? defaultTA : new TextAttribute(Color.COLOR_WHITE, 16);
try
{
internalBuildXML(attr, xml.DocumentElement, curAttr);
}
catch (Exception err)
{
log.Error(err.Message, err);
}
return attr;
}
public virtual AttributedString CreateFromXML(string text, TextAttribute defaultTA = null)
{
XmlDocument xml = XmlUtil.FromString(text);
return CreateFromXML(xml, defaultTA);
}
protected virtual void DecodeAttribute(XmlElement node, XmlAttribute x_attr, TextAttribute attr)
{
switch (x_attr.Name)
{
case TEXT_XML_KEY_COLOR:
{
string kv = node.GetAttribute(TEXT_XML_KEY_COLOR);
uint argb = uint.Parse(kv, System.Globalization.NumberStyles.HexNumber);
attr.fontColor = Color.toRGBA(argb);
}
break;
case TEXT_XML_KEY_SIZE:
{
attr.fontSize = int.Parse(node.GetAttribute(TEXT_XML_KEY_SIZE));
}
break;
case TEXT_XML_KEY_STYLE:
{
string style = node.GetAttribute(TEXT_XML_KEY_STYLE);
int value;
if (int.TryParse(style, out value))
{
attr.fontStyle = (FontStyle)value;
}
else
{
attr.fontStyle = (FontStyle) Enum.Parse(typeof(FontStyle), style);
}
}
break;
case TEXT_XML_KEY_FACE:
{
attr.fontName = node.GetAttribute(TEXT_XML_KEY_FACE);
}
break;
case TEXT_XML_KEY_B_COUNT:
{
attr.borderCount = (Data.TextBorderCount)int.Parse(node.GetAttribute(TEXT_XML_KEY_B_COUNT));
}
break;
case TEXT_XML_KEY_B_COLOR:
{
string kv = node.GetAttribute(TEXT_XML_KEY_B_COLOR);
uint argb = uint.Parse(kv, System.Globalization.NumberStyles.HexNumber);
attr.borderColor = Color.toRGBA(argb);
}
break;
case TEXT_XML_KEY_RES_IMG:
{
attr.resImage = node.GetAttribute(TEXT_XML_KEY_RES_IMG);
}
break;
case TEXT_XML_KEY_RES_SPR:
{
attr.resSprite = node.GetAttribute(TEXT_XML_KEY_RES_SPR);
}
break;
case TEXT_XML_KEY_RES_IMAGE_ZOOM:
{
attr.resImageZoom = ImageZoom.FromString(node.GetAttribute(TEXT_XML_KEY_RES_IMAGE_ZOOM));
}
break;
case TEXT_XML_KEY_LINK:
{
attr.link = node.GetAttribute(TEXT_XML_KEY_LINK);
}
break;
case TEXT_XML_KEY_LINE_ANCHOR:
{
string value = node.GetAttribute(TEXT_XML_KEY_LINE_ANCHOR);
try
{
attr.anchor = (RichTextAlignment)Enum.Parse(typeof(RichTextAlignment), value, true);
}
catch (Exception err)
{
log.Error(err.Message, err);
}
}
break;
}
TextDrawable drawable = ITextDrawableFactory.Instance.CreateTextDrawable(x_attr.Name, x_attr.Value);
if (drawable != null)
{
attr.drawable = drawable;
}
}
private void internalBuildXML(AttributedString atext, XmlElement node, TextAttribute parentAttr)
{
string nname = node.Name;
TextAttribute attr = new TextAttribute();
foreach (XmlAttribute x_attr in node.Attributes)
{
DecodeAttribute(node, x_attr, attr);
}
attr = TextAttribute.Combine(parentAttr, attr);
if (!attr.IsValid())
{
attr = parentAttr;
}
if (node.ChildNodes.Count > 0)
{
foreach (XmlNode e in node.ChildNodes)
{
if (e is XmlText || e is XmlCDataSection)
{
atext.Append(e.Value, attr);
}
else if (e is XmlElement)
{
internalBuildXML(atext, e as XmlElement, attr);
}
}
}
if (string.Equals(nname, TEXT_XML_NODE_BREAK))
{
atext.Append("\n", attr);
}
else if (string.Equals(nname, TEXT_XML_NODE_SPACE))
{
atext.Append(" ", attr);
}
}
}
}