您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
1993 行
66 KiB
1993 行
66 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using AOT;
|
|
using Unity.UIWidgets.async;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.services;
|
|
using UnityEngine;
|
|
|
|
namespace Unity.UIWidgets.ui {
|
|
public enum FontStyle {
|
|
normal,
|
|
|
|
italic
|
|
}
|
|
|
|
public class FontWeight {
|
|
public static readonly FontWeight w100 = new FontWeight(0);
|
|
|
|
public static readonly FontWeight w200 = new FontWeight(1);
|
|
|
|
public static readonly FontWeight w300 = new FontWeight(2);
|
|
|
|
public static readonly FontWeight w400 = new FontWeight(3);
|
|
|
|
public static readonly FontWeight w500 = new FontWeight(4);
|
|
|
|
public static readonly FontWeight w600 = new FontWeight(5);
|
|
|
|
public static readonly FontWeight w700 = new FontWeight(6);
|
|
|
|
public static readonly FontWeight w800 = new FontWeight(7);
|
|
|
|
public static readonly FontWeight w900 = new FontWeight(8);
|
|
|
|
public static readonly FontWeight normal = w400;
|
|
|
|
public static readonly FontWeight bold = w700;
|
|
|
|
public static readonly List<FontWeight> values = new List<FontWeight> {
|
|
w100, w200, w300, w400, w500, w600, w700, w800, w900
|
|
};
|
|
|
|
public static readonly Dictionary<int, string> map = new Dictionary<int, string> {
|
|
{0, "FontWeight.w100"},
|
|
{1, "FontWeight.w200"},
|
|
{2, "FontWeight.w300"},
|
|
{3, "FontWeight.w400"},
|
|
{4, "FontWeight.w500"},
|
|
{5, "FontWeight.w600"},
|
|
{6, "FontWeight.w700"},
|
|
{7, "FontWeight.w800"},
|
|
{8, "FontWeight.w900"}
|
|
};
|
|
|
|
public readonly int index;
|
|
|
|
public FontWeight(int index) {
|
|
this.index = index;
|
|
}
|
|
|
|
public static FontWeight lerp(FontWeight a, FontWeight b, float t) {
|
|
if (a == null && b == null) {
|
|
return null;
|
|
}
|
|
|
|
return values[
|
|
MathUtils.lerpNullableFloat(a?.index ?? normal.index, b?.index ?? normal.index, t: t).round()
|
|
.clamp(0, 8)];
|
|
}
|
|
|
|
public override string ToString() {
|
|
return map[key: index];
|
|
}
|
|
}
|
|
|
|
public class FontFeature : IEquatable<FontFeature> {
|
|
internal static readonly int _kEncodedSize = 8;
|
|
|
|
public readonly string feature;
|
|
|
|
public readonly int value;
|
|
|
|
public FontFeature(string feature, int value = 1) {
|
|
D.assert(feature != null);
|
|
D.assert(feature.Length == 4);
|
|
D.assert(value >= 0);
|
|
this.feature = feature;
|
|
this.value = value;
|
|
}
|
|
|
|
public bool Equals(FontFeature other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return feature == other.feature && value == other.value;
|
|
}
|
|
|
|
public FontFeature enable(string feature) {
|
|
return new FontFeature(feature: feature);
|
|
}
|
|
|
|
public FontFeature disable(string feature) {
|
|
return new FontFeature(feature: feature, 0);
|
|
}
|
|
|
|
public FontFeature randomize() {
|
|
return new FontFeature("rand");
|
|
}
|
|
|
|
public static FontFeature stylisticSet(int value) {
|
|
D.assert(value >= 1);
|
|
D.assert(value <= 20);
|
|
return new FontFeature($"ss{value.ToString().PadLeft(2, '0')}");
|
|
}
|
|
|
|
public FontFeature slashedZero() {
|
|
return new FontFeature("zero");
|
|
}
|
|
|
|
public FontFeature oldstyleFigures() {
|
|
return new FontFeature("onum");
|
|
}
|
|
|
|
public FontFeature proportionalFigures() {
|
|
return new FontFeature("pnum");
|
|
}
|
|
|
|
public FontFeature tabularFigures() {
|
|
return new FontFeature("tnum");
|
|
}
|
|
|
|
internal void _encode(byte[] byteData) {
|
|
D.assert(() => {
|
|
foreach (var charUnit in feature.ToCharArray()) {
|
|
if (charUnit <= 0x20 && charUnit >= 0x7F) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
for (var i = 0; i < 4; i++) {
|
|
byteData[i] = (byte) feature[index: i];
|
|
}
|
|
|
|
byteData[4] = (byte) (value >> 0);
|
|
byteData[5] = (byte) (value >> 8);
|
|
byteData[6] = (byte) (value >> 16);
|
|
byteData[7] = (byte) (value >> 24);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"FontFeature({feature}, {value})";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((FontFeature) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
return ((feature != null ? feature.GetHashCode() : 0) * 397) ^ value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum TextAlign {
|
|
left,
|
|
|
|
right,
|
|
|
|
center,
|
|
|
|
justify,
|
|
|
|
start,
|
|
|
|
end
|
|
}
|
|
|
|
public enum TextBaseline {
|
|
alphabetic,
|
|
|
|
ideographic
|
|
}
|
|
|
|
public class TextDecoration : IEquatable<TextDecoration> {
|
|
public static readonly TextDecoration none = new TextDecoration(0x0);
|
|
|
|
public static readonly TextDecoration underline = new TextDecoration(0x1);
|
|
|
|
public static readonly TextDecoration overline = new TextDecoration(0x2);
|
|
|
|
public static readonly TextDecoration lineThrough = new TextDecoration(0x4);
|
|
|
|
internal readonly int _mask;
|
|
|
|
public TextDecoration(int _mask) {
|
|
this._mask = _mask;
|
|
}
|
|
|
|
public bool Equals(TextDecoration other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return _mask == other._mask;
|
|
}
|
|
|
|
public TextDecoration combine(List<TextDecoration> decorations) {
|
|
var mask = 0;
|
|
foreach (var decoration in decorations) {
|
|
mask |= decoration._mask;
|
|
}
|
|
|
|
return new TextDecoration(_mask: mask);
|
|
}
|
|
|
|
public bool contains(TextDecoration other) {
|
|
return (_mask | other._mask) == _mask;
|
|
}
|
|
|
|
public override string ToString() {
|
|
if (_mask == 0) {
|
|
return "TextDecoration.none";
|
|
}
|
|
|
|
var values = new List<string>();
|
|
if ((_mask & underline._mask) != 0) {
|
|
values.Add("underline");
|
|
}
|
|
|
|
if ((_mask & overline._mask) != 0) {
|
|
values.Add("overline");
|
|
}
|
|
|
|
if ((_mask & lineThrough._mask) != 0) {
|
|
values.Add("lineThrough");
|
|
}
|
|
|
|
if (values.Count == 1) {
|
|
return $"TextDecoration.{values[0]}";
|
|
}
|
|
|
|
return $"TextDecoration.combine([{string.Join(", ", values: values)}])";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((TextDecoration) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
return _mask;
|
|
}
|
|
}
|
|
|
|
|
|
public enum TextDecorationStyle {
|
|
solid,
|
|
|
|
doubleLine,
|
|
|
|
dotted,
|
|
|
|
dashed,
|
|
|
|
wavy
|
|
}
|
|
|
|
public class TextHeightBehavior : IEquatable<TextHeightBehavior> {
|
|
public readonly bool applyHeightToFirstAscent;
|
|
|
|
public readonly bool applyHeightToLastDescent;
|
|
|
|
public TextHeightBehavior(
|
|
bool applyHeightToFirstAscent = true,
|
|
bool applyHeightToLastDescent = true
|
|
) {
|
|
this.applyHeightToFirstAscent = applyHeightToFirstAscent;
|
|
this.applyHeightToLastDescent = applyHeightToLastDescent;
|
|
}
|
|
|
|
public bool Equals(TextHeightBehavior other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return applyHeightToFirstAscent == other.applyHeightToFirstAscent &&
|
|
applyHeightToLastDescent == other.applyHeightToLastDescent;
|
|
}
|
|
|
|
public static TextHeightBehavior fromEncoded(int encoded) {
|
|
return new TextHeightBehavior((encoded & 0x1) == 0, (encoded & 0x2) == 0);
|
|
}
|
|
|
|
public int encode() {
|
|
return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return "TextHeightBehavior(\n" +
|
|
$" applyHeightToFirstAscent: {applyHeightToFirstAscent}, \n" +
|
|
$" applyHeightToLastDescent: {applyHeightToLastDescent} \n" +
|
|
")";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((TextHeightBehavior) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
return (applyHeightToFirstAscent.GetHashCode() * 397) ^ applyHeightToLastDescent.GetHashCode();
|
|
}
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct Float32List {
|
|
public IntPtr data;
|
|
public int length;
|
|
}
|
|
|
|
public static partial class ui_ {
|
|
static readonly object VoidObject = new object();
|
|
|
|
static readonly byte[] _fontChangeMessage = JSONMessageCodec.instance.encodeMessage(
|
|
new Dictionary<string, string> {
|
|
{"type", "fontsChange"}
|
|
});
|
|
|
|
internal static int[] _encodeTextStyle(
|
|
Color color,
|
|
TextDecoration decoration,
|
|
Color decorationColor,
|
|
TextDecorationStyle? decorationStyle,
|
|
float? decorationThickness,
|
|
FontWeight fontWeight,
|
|
FontStyle? fontStyle,
|
|
TextBaseline? textBaseline,
|
|
string fontFamily,
|
|
List<string> fontFamilyFallback,
|
|
float? fontSize,
|
|
float? letterSpacing,
|
|
float? wordSpacing,
|
|
float? height,
|
|
Locale locale,
|
|
Paint background,
|
|
Paint foreground,
|
|
List<Shadow> shadows,
|
|
List<FontFeature> fontFeatures
|
|
) {
|
|
var result = new int[8];
|
|
if (color != null) {
|
|
result[0] |= 1 << 1;
|
|
result[1] = (int) color.value;
|
|
}
|
|
|
|
if (decoration != null) {
|
|
result[0] |= 1 << 2;
|
|
result[2] = decoration._mask;
|
|
}
|
|
|
|
if (decorationColor != null) {
|
|
result[0] |= 1 << 3;
|
|
result[3] = (int) decorationColor.value;
|
|
}
|
|
|
|
if (decorationStyle != null) {
|
|
result[0] |= 1 << 4;
|
|
result[4] = (int) decorationStyle;
|
|
}
|
|
|
|
if (fontWeight != null) {
|
|
result[0] |= 1 << 5;
|
|
result[5] = fontWeight.index;
|
|
}
|
|
|
|
if (fontStyle != null) {
|
|
result[0] |= 1 << 6;
|
|
result[6] = (int) fontStyle;
|
|
}
|
|
|
|
if (textBaseline != null) {
|
|
result[0] |= 1 << 7;
|
|
result[7] = (int) textBaseline;
|
|
}
|
|
|
|
if (decorationThickness != null) {
|
|
result[0] |= 1 << 8;
|
|
}
|
|
|
|
if (fontFamily != null || fontFamilyFallback != null && fontFamilyFallback.isNotEmpty()) {
|
|
result[0] |= 1 << 9;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (fontSize != null) {
|
|
result[0] |= 1 << 10;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (letterSpacing != null) {
|
|
result[0] |= 1 << 11;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (wordSpacing != null) {
|
|
result[0] |= 1 << 12;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (height != null) {
|
|
result[0] |= 1 << 13;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (locale != null) {
|
|
result[0] |= 1 << 14;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (background != null) {
|
|
result[0] |= 1 << 15;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (foreground != null) {
|
|
result[0] |= 1 << 16;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (shadows != null) {
|
|
result[0] |= 1 << 17;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (fontFeatures != null) {
|
|
result[0] |= 1 << 18;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal static List<int> _encodeParagraphStyle(
|
|
TextAlign? textAlign,
|
|
TextDirection? textDirection,
|
|
int? maxLines,
|
|
string fontFamily,
|
|
float? fontSize,
|
|
float? height,
|
|
TextHeightBehavior textHeightBehavior,
|
|
FontWeight fontWeight,
|
|
FontStyle? fontStyle,
|
|
StrutStyle strutStyle,
|
|
string ellipsis,
|
|
Locale locale
|
|
) {
|
|
var result = new List<int>(new int[7]);
|
|
if (textAlign != null) {
|
|
result[0] |= 1 << 1;
|
|
result[1] = (int) textAlign;
|
|
}
|
|
|
|
if (textDirection != null) {
|
|
result[0] |= 1 << 2;
|
|
result[2] = (int) textDirection;
|
|
}
|
|
|
|
if (fontWeight != null) {
|
|
result[0] |= 1 << 3;
|
|
result[3] = fontWeight.index;
|
|
}
|
|
|
|
if (fontStyle != null) {
|
|
result[0] |= 1 << 4;
|
|
result[4] = (int) fontStyle;
|
|
}
|
|
|
|
if (maxLines != null) {
|
|
result[0] |= 1 << 5;
|
|
result[5] = maxLines.Value;
|
|
}
|
|
|
|
if (textHeightBehavior != null) {
|
|
result[0] |= 1 << 6;
|
|
result[6] = textHeightBehavior.encode();
|
|
}
|
|
|
|
if (fontFamily != null) {
|
|
result[0] |= 1 << 7;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (fontSize != null) {
|
|
result[0] |= 1 << 8;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (height != null) {
|
|
result[0] |= 1 << 9;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (strutStyle != null) {
|
|
result[0] |= 1 << 10;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (ellipsis != null) {
|
|
result[0] |= 1 << 11;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
if (locale != null) {
|
|
result[0] |= 1 << 12;
|
|
// Passed separately to native.
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
internal static unsafe void setFloat(this byte[] bytes, int byteOffset, float value) {
|
|
D.assert(byteOffset >= 0 && byteOffset + 4 < bytes.Length);
|
|
#if UNITY_ANDROID
|
|
byte[] vOut = BitConverter.GetBytes(value);
|
|
for (int i = 0; i < vOut.Length; i++) {
|
|
bytes[byteOffset + i] = vOut[i];
|
|
}
|
|
#else
|
|
fixed (byte* b = &bytes[byteOffset]) {
|
|
*(float*) b = value;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
internal static byte[] _encodeStrut(
|
|
string fontFamily,
|
|
List<string> fontFamilyFallback,
|
|
float? fontSize,
|
|
float? height,
|
|
float? leading,
|
|
FontWeight fontWeight,
|
|
FontStyle? fontStyle,
|
|
bool? forceStrutHeight) {
|
|
if (fontFamily == null &&
|
|
fontSize == null &&
|
|
height == null &&
|
|
leading == null &&
|
|
fontWeight == null &&
|
|
fontStyle == null &&
|
|
forceStrutHeight == null) {
|
|
return new byte[0];
|
|
}
|
|
|
|
var data = new byte[15]; // Max size is 15 bytes
|
|
var bitmask = 0; // 8 bit mask
|
|
var byteCount = 1;
|
|
if (fontWeight != null) {
|
|
bitmask |= 1 << 0;
|
|
data[byteCount] = (byte) fontWeight.index;
|
|
byteCount += 1;
|
|
}
|
|
|
|
if (fontStyle != null) {
|
|
bitmask |= 1 << 1;
|
|
data[byteCount] = (byte) fontStyle;
|
|
byteCount += 1;
|
|
}
|
|
|
|
if (fontFamily != null || fontFamilyFallback != null && fontFamilyFallback.isNotEmpty()) {
|
|
bitmask |= 1 << 2;
|
|
// passed separately to native
|
|
}
|
|
|
|
if (fontSize != null) {
|
|
bitmask |= 1 << 3;
|
|
data.setFloat(byteOffset: byteCount, (float) fontSize);
|
|
byteCount += 4;
|
|
}
|
|
|
|
if (height != null) {
|
|
bitmask |= 1 << 4;
|
|
data.setFloat(byteOffset: byteCount, (float) height);
|
|
byteCount += 4;
|
|
}
|
|
|
|
if (leading != null) {
|
|
bitmask |= 1 << 5;
|
|
data.setFloat(byteOffset: byteCount, (float) leading);
|
|
byteCount += 4;
|
|
}
|
|
|
|
if (forceStrutHeight != null) {
|
|
bitmask |= 1 << 6;
|
|
// We store this boolean directly in the bitmask since there is
|
|
// extra space in the 16 bit int.
|
|
bitmask |= (forceStrutHeight.Value ? 1 : 0) << 7;
|
|
}
|
|
|
|
data[0] = (byte) bitmask;
|
|
return data.range(0, length: byteCount);
|
|
}
|
|
|
|
public static unsafe Future loadFontFromList(byte[] list, string fontFamily = null) {
|
|
return _futurize((_Callback<object> callback) => {
|
|
// var completer = new Promise(true);
|
|
var completerHandle = GCHandle.Alloc(value: callback);
|
|
fixed (byte* listPtr = list) {
|
|
Font_LoadFontFromList(list: listPtr, size: list.Length, callback: _loadFontCallback,
|
|
(IntPtr) completerHandle,
|
|
fontFamily: fontFamily);
|
|
}
|
|
|
|
return null;
|
|
});
|
|
}
|
|
|
|
[MonoPInvokeCallback(typeof(_loadFontFromListCallback))]
|
|
static void _loadFontCallback(IntPtr callbackHandle) {
|
|
var completerHandle = (GCHandle) callbackHandle;
|
|
var callback = (_Callback<object>) completerHandle.Target;
|
|
completerHandle.Free();
|
|
_sendFontChangeMessage();
|
|
try {
|
|
callback(result: VoidObject);
|
|
}
|
|
catch (Exception ex) {
|
|
Debug.LogException(exception: ex);
|
|
}
|
|
}
|
|
|
|
static void _sendFontChangeMessage() {
|
|
Window.instance.onPlatformMessage?.Invoke("uiwidgets/system", data: _fontChangeMessage,
|
|
_ => { });
|
|
}
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern unsafe void Font_LoadFontFromList(byte* list, int size, _loadFontFromListCallback callback,
|
|
IntPtr callbackHandle,
|
|
string fontFamily);
|
|
|
|
|
|
public static float[] toFloatArrayAndFree(this Float32List data) {
|
|
var result = new float[data.length];
|
|
Marshal.Copy(source: data.data, destination: result, 0, length: data.length);
|
|
Lists_Free(data: data.data);
|
|
return result;
|
|
}
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
public static extern void Lists_Free(IntPtr data);
|
|
|
|
delegate void _loadFontFromListCallback(IntPtr callbackHandle);
|
|
}
|
|
|
|
public class TextStyle : IEquatable<TextStyle> {
|
|
internal readonly Paint _background;
|
|
internal readonly float? _decorationThickness;
|
|
|
|
internal readonly int[] _encoded;
|
|
internal readonly string _fontFamily;
|
|
internal readonly List<string> _fontFamilyFallback;
|
|
internal readonly List<FontFeature> _fontFeatures;
|
|
internal readonly float? _fontSize;
|
|
internal readonly Paint _foreground;
|
|
internal readonly float? _height;
|
|
internal readonly float? _letterSpacing;
|
|
internal readonly Locale _locale;
|
|
internal readonly List<Shadow> _shadows;
|
|
internal readonly float? _wordSpacing;
|
|
|
|
public TextStyle(
|
|
Color color = null,
|
|
TextDecoration decoration = null,
|
|
Color decorationColor = null,
|
|
TextDecorationStyle? decorationStyle = null,
|
|
float? decorationThickness = null,
|
|
FontWeight fontWeight = null,
|
|
FontStyle? fontStyle = null,
|
|
TextBaseline? textBaseline = null,
|
|
string fontFamily = null,
|
|
List<string> fontFamilyFallback = null,
|
|
float? fontSize = null,
|
|
float? letterSpacing = null,
|
|
float? wordSpacing = null,
|
|
float? height = null,
|
|
Locale locale = null,
|
|
Paint background = null,
|
|
Paint foreground = null,
|
|
List<Shadow> shadows = null,
|
|
List<FontFeature> fontFeatures = null
|
|
) {
|
|
D.assert(color == null || foreground == null, () =>
|
|
"Cannot provide both a color and a foreground\n" +
|
|
"The color argument is just a shorthand for \"foreground: Paint()..color = color\"."
|
|
);
|
|
_encoded = ui_._encodeTextStyle(
|
|
color: color,
|
|
decoration: decoration,
|
|
decorationColor: decorationColor,
|
|
decorationStyle: decorationStyle,
|
|
decorationThickness: decorationThickness,
|
|
fontWeight: fontWeight,
|
|
fontStyle: fontStyle,
|
|
textBaseline: textBaseline,
|
|
fontFamily: fontFamily,
|
|
fontFamilyFallback: fontFamilyFallback,
|
|
fontSize: fontSize,
|
|
letterSpacing: letterSpacing,
|
|
wordSpacing: wordSpacing,
|
|
height: height,
|
|
locale: locale,
|
|
background: background,
|
|
foreground: foreground,
|
|
shadows: shadows,
|
|
fontFeatures: fontFeatures
|
|
);
|
|
|
|
_fontFamily = fontFamily ?? "";
|
|
_fontFamilyFallback = fontFamilyFallback;
|
|
_fontSize = fontSize;
|
|
_letterSpacing = letterSpacing;
|
|
_wordSpacing = wordSpacing;
|
|
_height = height;
|
|
_decorationThickness = decorationThickness;
|
|
_locale = locale;
|
|
_background = background;
|
|
_foreground = foreground;
|
|
_shadows = shadows;
|
|
_fontFeatures = fontFeatures;
|
|
}
|
|
|
|
public bool Equals(TextStyle other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return _encoded.equalsList(list: other._encoded) && Equals(objA: _fontFamily, objB: other._fontFamily) &&
|
|
_fontFamilyFallback.equalsList(list: other._fontFamilyFallback) &&
|
|
Nullable.Equals(n1: _fontSize, n2: other._fontSize) &&
|
|
Nullable.Equals(n1: _letterSpacing, n2: other._letterSpacing) &&
|
|
Nullable.Equals(n1: _wordSpacing, n2: other._wordSpacing) &&
|
|
Nullable.Equals(n1: _height, n2: other._height) &&
|
|
Nullable.Equals(n1: _decorationThickness, n2: other._decorationThickness) &&
|
|
Equals(objA: _locale, objB: other._locale) && Equals(objA: _background, objB: other._background) &&
|
|
Equals(objA: _foreground, objB: other._foreground) && _shadows.equalsList(list: other._shadows) &&
|
|
_fontFeatures.equalsList(list: other._fontFeatures);
|
|
}
|
|
|
|
public override string ToString() {
|
|
var color = (_encoded[0] & 0x00002) == 0x00002
|
|
? new Color((uint) _encoded[1]).ToString()
|
|
: "unspecified";
|
|
var decoration = (_encoded[0] & 0x00004) == 0x00004
|
|
? new TextDecoration(_encoded[2]).ToString()
|
|
: "unspecified";
|
|
var decorationColor = (_encoded[0] & 0x00008) == 0x00008
|
|
? new Color((uint) _encoded[3]).ToString()
|
|
: "unspecified";
|
|
var decorationStyle = (_encoded[0] & 0x00010) == 0x00010
|
|
? ((TextDecorationStyle) _encoded[4]).ToString()
|
|
: "unspecified";
|
|
// The decorationThickness is not in encoded order in order to keep it near the other decoration properties.
|
|
var decorationThickness = (_encoded[0] & 0x00100) == 0x00100
|
|
? _decorationThickness.ToString()
|
|
: "unspecified";
|
|
var fontWeight = (_encoded[0] & 0x00020) == 0x00020
|
|
? FontWeight.values[_encoded[5]].ToString()
|
|
: "unspecified";
|
|
var fontStyle = (_encoded[0] & 0x00040) == 0x00040
|
|
? ((FontStyle) _encoded[6]).ToString()
|
|
: "unspecified";
|
|
var textBaseline = (_encoded[0] & 0x00080) == 0x00080
|
|
? ((TextBaseline) _encoded[7]).ToString()
|
|
: "unspecified";
|
|
var fontFamily = (_encoded[0] & 0x00200) == 0x00200
|
|
&& _fontFamily != null
|
|
? _fontFamily
|
|
: "unspecified";
|
|
var fontFamilyFallback = (_encoded[0] & 0x00200) == 0x00200
|
|
&& _fontFamilyFallback != null
|
|
&& _fontFamilyFallback.isNotEmpty()
|
|
? _fontFamilyFallback.ToString()
|
|
: "unspecified";
|
|
var fontSize = (_encoded[0] & 0x00400) == 0x00400 ? _fontSize.ToString() : "unspecified";
|
|
var letterSpacing = (_encoded[0] & 0x00800) == 0x00800 ? "_letterSpacing}x" : "unspecified";
|
|
var wordSpacing = (_encoded[0] & 0x01000) == 0x01000 ? "_wordSpacing}x" : "unspecified";
|
|
var height = (_encoded[0] & 0x02000) == 0x02000 ? "_height}x" : "unspecified";
|
|
var locale = (_encoded[0] & 0x04000) == 0x04000 ? _locale.ToString() : "unspecified";
|
|
var background = (_encoded[0] & 0x08000) == 0x08000 ? _locale.ToString() : "unspecified";
|
|
var foreground = (_encoded[0] & 0x10000) == 0x10000 ? _locale.ToString() : "unspecified";
|
|
var shadows = (_encoded[0] & 0x20000) == 0x20000 ? _locale.ToString() : "unspecified";
|
|
|
|
var fontFeatures =
|
|
(_encoded[0] & 0x40000) == 0x40000 ? _fontFeatures.ToString() : "unspecified";
|
|
|
|
return "TextStyle(\n" +
|
|
$"color: {color}\n" +
|
|
$"decoration: {decoration}\n" +
|
|
$"decorationColor: {decorationColor}\n" +
|
|
$"decorationStyle: {decorationStyle}\n" +
|
|
// The decorationThickness is not in encoded order in order to keep it near the other decoration properties.
|
|
$"decorationThickness: {decorationThickness}\n" +
|
|
$"fontWeight: {fontWeight}\n" +
|
|
$"fontStyle: {fontStyle}\n" +
|
|
$"textBaseline: {textBaseline}\n" +
|
|
$"fontFamily: {fontFamily}\n" +
|
|
$"fontFamilyFallback: {fontFamilyFallback}\n" +
|
|
$"fontSize: {fontSize}\n" +
|
|
$"letterSpacing: {letterSpacing}\n" +
|
|
$"wordSpacing: {wordSpacing}\n" +
|
|
$"height: {height}\n" +
|
|
$"locale: {locale}\n" +
|
|
$"background: {background}\n" +
|
|
$"foreground: {foreground}\n" +
|
|
$"shadows: {shadows}\n" +
|
|
$"fontFeatures: {fontFeatures}\n" +
|
|
")";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((TextStyle) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
var hashCode = _encoded != null ? _encoded.GetHashCode() : 0;
|
|
hashCode = (hashCode * 397) ^ (_fontFamily != null ? _fontFamily.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_fontFamilyFallback != null ? _fontFamilyFallback.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ _fontSize.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ _letterSpacing.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ _wordSpacing.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ _height.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ _decorationThickness.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ (_locale != null ? _locale.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_background != null ? _background.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_foreground != null ? _foreground.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_shadows != null ? _shadows.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_fontFeatures != null ? _fontFeatures.GetHashCode() : 0);
|
|
return hashCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ParagraphStyle : IEquatable<ParagraphStyle> {
|
|
internal readonly string _ellipsis;
|
|
|
|
internal readonly List<int> _encoded;
|
|
internal readonly string _fontFamily;
|
|
internal readonly float? _fontSize;
|
|
internal readonly float? _height;
|
|
internal readonly Locale _locale;
|
|
internal readonly StrutStyle _strutStyle;
|
|
|
|
public ParagraphStyle(
|
|
TextAlign? textAlign = null,
|
|
TextDirection? textDirection = null,
|
|
int? maxLines = null,
|
|
string fontFamily = null,
|
|
float? fontSize = null,
|
|
float? height = null,
|
|
TextHeightBehavior textHeightBehavior = null,
|
|
FontWeight fontWeight = null,
|
|
FontStyle? fontStyle = null,
|
|
StrutStyle strutStyle = null,
|
|
string ellipsis = null,
|
|
Locale locale = null
|
|
) {
|
|
_encoded = ui_._encodeParagraphStyle(
|
|
textAlign: textAlign,
|
|
textDirection: textDirection,
|
|
maxLines: maxLines,
|
|
fontFamily: fontFamily,
|
|
fontSize: fontSize,
|
|
height: height,
|
|
textHeightBehavior: textHeightBehavior,
|
|
fontWeight: fontWeight,
|
|
fontStyle: fontStyle,
|
|
strutStyle: strutStyle,
|
|
ellipsis: ellipsis,
|
|
locale: locale
|
|
);
|
|
_fontFamily = fontFamily;
|
|
_fontSize = fontSize;
|
|
_height = height;
|
|
_strutStyle = strutStyle;
|
|
_ellipsis = ellipsis;
|
|
_locale = locale;
|
|
}
|
|
|
|
public bool Equals(ParagraphStyle other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return _encoded.equalsList(list: other._encoded) && _fontFamily == other._fontFamily &&
|
|
Nullable.Equals(n1: _fontSize, n2: other._fontSize) &&
|
|
Nullable.Equals(n1: _height, n2: other._height) &&
|
|
Equals(objA: _strutStyle, objB: other._strutStyle) && _ellipsis == other._ellipsis &&
|
|
Equals(objA: _locale, objB: other._locale);
|
|
}
|
|
|
|
public override string ToString() {
|
|
var textAlign = (_encoded[0] & 0x002) == 0x002
|
|
? ((TextAlign) _encoded[1]).ToString()
|
|
: "unspecified";
|
|
var textDirection = (_encoded[0] & 0x004) == 0x004
|
|
? ((TextDirection) _encoded[2]).ToString()
|
|
: "unspecified";
|
|
var fontWeight = (_encoded[0] & 0x008) == 0x008
|
|
? FontWeight.values[_encoded[3]].ToString()
|
|
: "unspecified";
|
|
var fontStyle = (_encoded[0] & 0x010) == 0x010
|
|
? ((FontStyle) _encoded[4]).ToString()
|
|
: "unspecified";
|
|
var maxLines = (_encoded[0] & 0x020) == 0x020 ? _encoded[5].ToString() : "unspecified";
|
|
var textHeightBehavior = (_encoded[0] & 0x040) == 0x040
|
|
? TextHeightBehavior.fromEncoded(_encoded[6]).ToString()
|
|
: "unspecified";
|
|
var fontFamily = (_encoded[0] & 0x080) == 0x080 ? _fontFamily : "unspecified";
|
|
var fontSize = (_encoded[0] & 0x100) == 0x100 ? _fontSize.ToString() : "unspecified";
|
|
var height = (_encoded[0] & 0x200) == 0x200 ? $"{_height}x" : "unspecified";
|
|
var ellipsis = (_encoded[0] & 0x400) == 0x400 ? _ellipsis : "unspecified";
|
|
var locale = (_encoded[0] & 0x800) == 0x800 ? _locale.ToString() : "unspecified";
|
|
return "ParagraphStyle(\n" +
|
|
$"textAlign: {textAlign}\n" +
|
|
$"textDirection: {textDirection}\n" +
|
|
$"fontWeight: {fontWeight}\n" +
|
|
$"fontStyle: {fontStyle}\n" +
|
|
$"maxLines: {maxLines}\n" +
|
|
$"textHeightBehavior: {textHeightBehavior}$\n" +
|
|
$"fontFamily: {fontFamily}\n" +
|
|
$"fontSize: {fontSize}\n" +
|
|
$"height: {height}\n" +
|
|
$"ellipsis: {ellipsis}\n" +
|
|
$"locale: {locale}\n" +
|
|
")";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((ParagraphStyle) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
var hashCode = _encoded != null ? _encoded.GetHashCode() : 0;
|
|
hashCode = (hashCode * 397) ^ (_fontFamily != null ? _fontFamily.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ _fontSize.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ _height.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ (_strutStyle != null ? _strutStyle.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_ellipsis != null ? _ellipsis.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_locale != null ? _locale.GetHashCode() : 0);
|
|
return hashCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class StrutStyle : IEquatable<StrutStyle> {
|
|
internal readonly byte[] _encoded; // Most of the data for strut is encoded.
|
|
internal readonly string _fontFamily;
|
|
internal readonly List<string> _fontFamilyFallback;
|
|
|
|
public StrutStyle(
|
|
string fontFamily = null,
|
|
List<string> fontFamilyFallback = null,
|
|
float? fontSize = null,
|
|
float? height = null,
|
|
float? leading = null,
|
|
FontWeight fontWeight = null,
|
|
FontStyle? fontStyle = null,
|
|
bool? forceStrutHeight = null
|
|
) {
|
|
_encoded = ui_._encodeStrut(
|
|
fontFamily: fontFamily,
|
|
fontFamilyFallback: fontFamilyFallback,
|
|
fontSize: fontSize,
|
|
height: height,
|
|
leading: leading,
|
|
fontWeight: fontWeight,
|
|
fontStyle: fontStyle,
|
|
forceStrutHeight: forceStrutHeight
|
|
);
|
|
_fontFamily = fontFamily;
|
|
_fontFamilyFallback = fontFamilyFallback;
|
|
}
|
|
|
|
public bool Equals(StrutStyle other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return _encoded.equalsList(list: other._encoded) && _fontFamily == other._fontFamily &&
|
|
_fontFamilyFallback.equalsList(list: other._fontFamilyFallback);
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((StrutStyle) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
var hashCode = _encoded != null ? _encoded.GetHashCode() : 0;
|
|
hashCode = (hashCode * 397) ^ (_fontFamily != null ? _fontFamily.GetHashCode() : 0);
|
|
hashCode = (hashCode * 397) ^ (_fontFamilyFallback != null ? _fontFamilyFallback.GetHashCode() : 0);
|
|
return hashCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum TextDirection {
|
|
rtl,
|
|
ltr,
|
|
}
|
|
|
|
public class TextBox : IEquatable<TextBox> {
|
|
public readonly float bottom;
|
|
|
|
public readonly TextDirection direction;
|
|
|
|
public readonly float left;
|
|
|
|
public readonly float right;
|
|
|
|
public readonly float top;
|
|
|
|
TextBox(
|
|
float left,
|
|
float top,
|
|
float right,
|
|
float bottom,
|
|
TextDirection direction) {
|
|
this.left = left;
|
|
this.top = top;
|
|
this.right = right;
|
|
this.bottom = bottom;
|
|
this.direction = direction;
|
|
}
|
|
|
|
public float start {
|
|
get { return direction == TextDirection.ltr ? left : right; }
|
|
}
|
|
|
|
public float end {
|
|
get { return direction == TextDirection.ltr ? right : left; }
|
|
}
|
|
|
|
public bool Equals(TextBox other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return left.Equals(obj: other.left) && top.Equals(obj: other.top) && right.Equals(obj: other.right) &&
|
|
bottom.Equals(obj: other.bottom) && direction == other.direction;
|
|
}
|
|
|
|
public static TextBox fromLTRBD(
|
|
float left,
|
|
float top,
|
|
float right,
|
|
float bottom,
|
|
TextDirection direction) {
|
|
return new TextBox(left: left, top: top, right: right, bottom: bottom, direction: direction);
|
|
}
|
|
|
|
public Rect toRect() {
|
|
return Rect.fromLTRB(left: left, top: top, right: right, bottom);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return
|
|
$"TextBox.fromLTRBD({left.ToString("N1")}, {top.ToString("N1")}, {right.ToString("N1")}, {bottom.ToString("N1")}, {direction})";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((TextBox) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
var hashCode = left.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ top.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ right.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ bottom.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ (int) direction;
|
|
return hashCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum TextAffinity {
|
|
upstream,
|
|
|
|
downstream
|
|
}
|
|
|
|
public class TextPosition : IEquatable<TextPosition> {
|
|
public readonly TextAffinity affinity;
|
|
|
|
public readonly int offset;
|
|
|
|
public TextPosition(
|
|
int offset,
|
|
TextAffinity affinity = TextAffinity.downstream) {
|
|
this.offset = offset;
|
|
this.affinity = affinity;
|
|
}
|
|
|
|
public bool Equals(TextPosition other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return offset == other.offset && affinity == other.affinity;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"TextPosition(offset: {offset}, affinity: {affinity})";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((TextPosition) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
return (offset * 397) ^ (int) affinity;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TextRange : IEquatable<TextRange> {
|
|
public static readonly TextRange empty = new TextRange(-1, -1);
|
|
|
|
public readonly int end;
|
|
|
|
public readonly int start;
|
|
|
|
public TextRange(
|
|
int start, int end) {
|
|
D.assert(start >= -1);
|
|
D.assert(end >= -1);
|
|
this.start = start;
|
|
this.end = end;
|
|
}
|
|
|
|
public bool isValid {
|
|
get { return start >= 0 && end >= 0; }
|
|
}
|
|
|
|
public bool isCollapsed {
|
|
get { return start == end; }
|
|
}
|
|
|
|
public bool isNormalized {
|
|
get { return end >= start; }
|
|
}
|
|
|
|
public bool Equals(TextRange other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return start == other.start && end == other.end;
|
|
}
|
|
|
|
public static TextRange collapsed(int offset) {
|
|
D.assert(offset >= -1);
|
|
return new TextRange(start: offset, end: offset);
|
|
}
|
|
|
|
public string textBefore(string text) {
|
|
D.assert(result: isNormalized);
|
|
return text.Substring(0, length: start);
|
|
}
|
|
|
|
public string textAfter(string text) {
|
|
D.assert(result: isNormalized);
|
|
return text.Substring(startIndex: end);
|
|
}
|
|
|
|
public string textInside(string text) {
|
|
D.assert(result: isNormalized);
|
|
return text.Substring(startIndex: start, end - start);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"TextRange(start: {start}, end: {end})";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((TextRange) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
return (start * 397) ^ end;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ParagraphConstraints : IEquatable<ParagraphConstraints> {
|
|
public readonly float width;
|
|
|
|
public ParagraphConstraints(float width) {
|
|
this.width = width;
|
|
}
|
|
|
|
public bool Equals(ParagraphConstraints other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return width.Equals(obj: other.width);
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"ParagraphConstraints(width: {width})";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((ParagraphConstraints) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
return width.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public enum BoxHeightStyle {
|
|
tight,
|
|
|
|
max,
|
|
|
|
includeLineSpacingMiddle,
|
|
|
|
includeLineSpacingTop,
|
|
|
|
includeLineSpacingBottom,
|
|
|
|
strut
|
|
}
|
|
|
|
public enum BoxWidthStyle {
|
|
tight,
|
|
|
|
max
|
|
}
|
|
|
|
public enum PlaceholderAlignment {
|
|
baseline,
|
|
|
|
aboveBaseline,
|
|
|
|
belowBaseline,
|
|
|
|
top,
|
|
|
|
bottom,
|
|
|
|
middle
|
|
}
|
|
|
|
public class LineMetrics : IEquatable<LineMetrics> {
|
|
public readonly float ascent;
|
|
|
|
public readonly float baseline;
|
|
|
|
public readonly float descent;
|
|
|
|
public readonly bool hardBreak;
|
|
|
|
public readonly float height;
|
|
|
|
public readonly float left;
|
|
|
|
public readonly int lineNumber;
|
|
|
|
public readonly float unscaledAscent;
|
|
|
|
public readonly float width;
|
|
|
|
public LineMetrics(
|
|
bool hardBreak,
|
|
float ascent,
|
|
float descent,
|
|
float unscaledAscent,
|
|
float height,
|
|
float width,
|
|
float left,
|
|
float baseline,
|
|
int lineNumber) {
|
|
this.hardBreak = hardBreak;
|
|
this.ascent = ascent;
|
|
this.descent = descent;
|
|
this.unscaledAscent = unscaledAscent;
|
|
this.height = height;
|
|
this.width = width;
|
|
this.left = left;
|
|
this.baseline = baseline;
|
|
this.lineNumber = lineNumber;
|
|
}
|
|
|
|
public bool Equals(LineMetrics other) {
|
|
if (ReferenceEquals(null, objB: other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: other)) {
|
|
return true;
|
|
}
|
|
|
|
return hardBreak == other.hardBreak && ascent.Equals(obj: other.ascent) &&
|
|
descent.Equals(obj: other.descent) &&
|
|
unscaledAscent.Equals(obj: other.unscaledAscent) && height.Equals(obj: other.height) &&
|
|
width.Equals(obj: other.width) && left.Equals(obj: other.left) &&
|
|
baseline.Equals(obj: other.baseline) &&
|
|
lineNumber == other.lineNumber;
|
|
}
|
|
|
|
public override string ToString() {
|
|
return $"LineMetrics(hardBreak: {hardBreak}, \n" +
|
|
$"ascent: {ascent}, " +
|
|
$"descent: {descent}, " +
|
|
$"unscaledAscent: {unscaledAscent}, " +
|
|
$"height: {height}, " +
|
|
$"width: {width}, " +
|
|
$"left: {left}, " +
|
|
$"baseline: {baseline}, " +
|
|
$"lineNumber: {lineNumber})";
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, objB: obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, objB: obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((LineMetrics) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
var hashCode = hardBreak.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ ascent.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ descent.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ unscaledAscent.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ height.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ width.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ left.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ baseline.GetHashCode();
|
|
hashCode = (hashCode * 397) ^ lineNumber;
|
|
return hashCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class Paragraph : NativeWrapper {
|
|
internal Paragraph(IntPtr ptr) {
|
|
_setPtr(ptr: ptr);
|
|
}
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern float Paragraph_width(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern float Paragraph_height(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern float Paragraph_longestLine(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern float Paragraph_minIntrinsicWidth(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern float Paragraph_maxIntrinsicWidth(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern float Paragraph_alphabeticBaseline(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern float Paragraph_ideographicBaseline(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
[return: MarshalAs(UnmanagedType.U1)]
|
|
static extern bool Paragraph_didExceedMaxLines(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern void Paragraph_layout(IntPtr ptr, float width);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern Float32List Paragraph_getRectsForRange(IntPtr ptr, int start, int end,
|
|
int boxHeightStyle,
|
|
int boxWidthStyle);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern Float32List Paragraph_getRectsForPlaceholders(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern unsafe void Paragraph_getPositionForOffset(IntPtr ptr, float dx, float dy, int* encodedPtr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern unsafe void Paragraph_getWordBoundary(IntPtr ptr, int offset, int* boundaryPtr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern unsafe void Paragraph_getLineBoundary(IntPtr ptr, int offset, int* boundaryPtr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern void Paragraph_paint(IntPtr ptr, IntPtr canvas, float x, float y);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern Float32List Paragraph_computeLineMetrics(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern void Paragraph_dispose(IntPtr ptr);
|
|
|
|
public void layout(ParagraphConstraints constraints) {
|
|
_layout(width: constraints.width);
|
|
}
|
|
|
|
void _layout(float width) {
|
|
Paragraph_layout(ptr: _ptr, width: width);
|
|
}
|
|
|
|
List<TextBox> _decodeTextBoxes(float[] encoded, int size) {
|
|
var count = size / 5;
|
|
var boxes = new List<TextBox>();
|
|
var position = 0;
|
|
for (var index = 0; index < count; index += 1) {
|
|
boxes.Add(TextBox.fromLTRBD(
|
|
encoded[position++],
|
|
encoded[position++],
|
|
encoded[position++],
|
|
encoded[position++],
|
|
(TextDirection) encoded[position++]
|
|
));
|
|
}
|
|
|
|
return boxes;
|
|
}
|
|
|
|
public override void DisposePtr(IntPtr ptr) {
|
|
Paragraph_dispose(ptr: ptr);
|
|
}
|
|
|
|
public float width() {
|
|
return Paragraph_width(ptr: _ptr);
|
|
}
|
|
|
|
public float height() {
|
|
return Paragraph_height(ptr: _ptr);
|
|
}
|
|
|
|
public float longestLine() {
|
|
return Paragraph_longestLine(ptr: _ptr);
|
|
}
|
|
|
|
public float minIntrinsicWidth() {
|
|
return Paragraph_minIntrinsicWidth(ptr: _ptr);
|
|
}
|
|
|
|
public float maxIntrinsicWidth() {
|
|
return Paragraph_maxIntrinsicWidth(ptr: _ptr);
|
|
}
|
|
|
|
public float alphabeticBaseline() {
|
|
return Paragraph_alphabeticBaseline(ptr: _ptr);
|
|
}
|
|
|
|
public float ideographicBaseline() {
|
|
return Paragraph_ideographicBaseline(ptr: _ptr);
|
|
}
|
|
|
|
public bool didExceedMaxLines() {
|
|
return Paragraph_didExceedMaxLines(ptr: _ptr);
|
|
}
|
|
|
|
public List<TextBox> getBoxesForRange(int start, int end,
|
|
BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight,
|
|
BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight) {
|
|
var data = _getBoxesForRange(start: start, end: end, (int) boxHeightStyle, (int) boxWidthStyle)
|
|
.toFloatArrayAndFree();
|
|
return _decodeTextBoxes(encoded: data, size: data.Length);
|
|
}
|
|
|
|
// See paragraph.cc for the layout of this return value.
|
|
Float32List _getBoxesForRange(int start, int end, int boxHeightStyle, int boxWidthStyle) {
|
|
return Paragraph_getRectsForRange(ptr: _ptr, start: start, end: end, boxHeightStyle: boxHeightStyle,
|
|
boxWidthStyle: boxWidthStyle);
|
|
}
|
|
|
|
|
|
public List<TextBox> getBoxesForPlaceholders() {
|
|
var data = _getBoxesForPlaceholders().toFloatArrayAndFree();
|
|
return _decodeTextBoxes(encoded: data, size: data.Length);
|
|
}
|
|
|
|
Float32List _getBoxesForPlaceholders() {
|
|
return Paragraph_getRectsForPlaceholders(ptr: _ptr);
|
|
}
|
|
|
|
public unsafe TextPosition getPositionForOffset(Offset offset) {
|
|
var encoded = new int[2];
|
|
fixed (int* encodedPtr = encoded) {
|
|
_getPositionForOffset(dx: offset.dx, dy: offset.dy, encodedPtr: encodedPtr);
|
|
}
|
|
|
|
return new TextPosition(encoded[0], (TextAffinity) encoded[1]);
|
|
}
|
|
|
|
unsafe void _getPositionForOffset(float dx, float dy, int* encodedPtr) {
|
|
Paragraph_getPositionForOffset(ptr: _ptr, dx: dx, dy: dy, encodedPtr: encodedPtr);
|
|
}
|
|
|
|
public unsafe TextRange getWordBoundary(TextPosition position) {
|
|
var boundary = new int[2];
|
|
fixed (int* boundaryPtr = boundary) {
|
|
_getWordBoundary(offset: position.offset, boundaryPtr: boundaryPtr);
|
|
}
|
|
|
|
return new TextRange(boundary[0], boundary[1]);
|
|
}
|
|
|
|
unsafe void _getWordBoundary(int offset, int* boundaryPtr) {
|
|
Paragraph_getWordBoundary(ptr: _ptr, offset: offset, boundaryPtr: boundaryPtr);
|
|
}
|
|
|
|
public unsafe TextRange getLineBoundary(TextPosition position) {
|
|
var boundary = new int[2];
|
|
fixed (int* boundaryPtr = boundary) {
|
|
_getLineBoundary(offset: position.offset, boundaryPtr: boundaryPtr);
|
|
}
|
|
|
|
return new TextRange(boundary[0], boundary[1]);
|
|
}
|
|
|
|
unsafe void _getLineBoundary(int offset, int* boundaryPtr) {
|
|
Paragraph_getLineBoundary(ptr: _ptr, offset: offset, boundaryPtr: boundaryPtr);
|
|
}
|
|
|
|
internal void _paint(Canvas canvas, float x, float y) {
|
|
Paragraph_paint(ptr: _ptr, canvas: canvas._ptr, x: x, y: y);
|
|
}
|
|
|
|
public List<LineMetrics> computeLineMetrics() {
|
|
var data = _computeLineMetrics().toFloatArrayAndFree();
|
|
var count = data.Length / 9;
|
|
var position = 0;
|
|
var metrics = new List<LineMetrics>();
|
|
|
|
for (var index = 0; index < count; index += 1) {
|
|
metrics.Add(new LineMetrics(
|
|
data[position++] != 0,
|
|
data[position++],
|
|
data[position++],
|
|
data[position++],
|
|
data[position++],
|
|
data[position++],
|
|
data[position++],
|
|
data[position++],
|
|
(int) data[position++]
|
|
));
|
|
}
|
|
|
|
return metrics;
|
|
}
|
|
|
|
Float32List _computeLineMetrics() {
|
|
return Paragraph_computeLineMetrics(ptr: _ptr);
|
|
}
|
|
}
|
|
|
|
public class ParagraphBuilder : NativeWrapper {
|
|
public ParagraphBuilder(ParagraphStyle style) {
|
|
List<string> strutFontFamilies = null;
|
|
var strutStyle = style._strutStyle;
|
|
if (strutStyle != null) {
|
|
strutFontFamilies = new List<string>();
|
|
var fontFamily = strutStyle._fontFamily;
|
|
if (fontFamily != null) {
|
|
strutFontFamilies.Add(item: fontFamily);
|
|
}
|
|
|
|
if (strutStyle._fontFamilyFallback != null) {
|
|
strutFontFamilies.AddRange(collection: strutStyle._fontFamilyFallback);
|
|
}
|
|
}
|
|
|
|
_setPtr(_constructor(
|
|
style._encoded.ToArray(),
|
|
strutStyle?._encoded.ToArray(),
|
|
fontFamily: style._fontFamily,
|
|
strutFontFamilies?.ToArray(),
|
|
style._fontSize ?? 0,
|
|
style._height ?? 0,
|
|
ellipsis: style._ellipsis,
|
|
_encodeLocale(locale: style._locale)
|
|
));
|
|
}
|
|
|
|
public int placeholderCount { get; private set; }
|
|
|
|
public List<float> placeholderScales { get; } = new List<float>();
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern IntPtr ParagraphBuilder_constructor(
|
|
int[] encoded,
|
|
int encodeSize,
|
|
byte[] strutData,
|
|
int structDataSize,
|
|
string fontFamily,
|
|
string[] structFontFamily,
|
|
int structFontFamilySize,
|
|
float fontSize,
|
|
float height,
|
|
[MarshalAs(unmanagedType: UnmanagedType.LPWStr)]
|
|
string ellipsis,
|
|
string locale);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern unsafe void ParagraphBuilder_pushStyle(
|
|
IntPtr ptr,
|
|
int[] encoded,
|
|
int encodedSize,
|
|
string[] fontFamilies,
|
|
int fontFamiliesSize,
|
|
float fontSize,
|
|
float letterSpacing,
|
|
float wordSpacing,
|
|
float height,
|
|
float decorationThickness,
|
|
string locale,
|
|
IntPtr* backgroundObjects,
|
|
byte[] backgroundData,
|
|
IntPtr* foregroundObjects,
|
|
byte[] foregroundData,
|
|
byte[] shadowsData,
|
|
int shadowDataSize,
|
|
byte[] fontFeaturesData,
|
|
int fontFeatureDataSize
|
|
);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern void ParagraphBuilder_pop(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern IntPtr ParagraphBuilder_addText(IntPtr ptr, [MarshalAs(unmanagedType: UnmanagedType.LPWStr)]
|
|
string text);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern IntPtr ParagraphBuilder_addPlaceholder(IntPtr ptr, float width, float height, int alignment,
|
|
float baselineOffset, int baseline);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern IntPtr ParagraphBuilder_build(IntPtr ptr);
|
|
|
|
[DllImport(dllName: NativeBindings.dllName)]
|
|
static extern void ParagraphBuilder_dispose(IntPtr ptr);
|
|
|
|
IntPtr _constructor(
|
|
int[] encoded,
|
|
byte[] structData,
|
|
string fontFamily,
|
|
string[] structFontFamily,
|
|
float fontSize,
|
|
float height,
|
|
string ellipsis,
|
|
string locale
|
|
) {
|
|
return ParagraphBuilder_constructor(
|
|
encoded: encoded,
|
|
encoded == null ? 0 : encoded.Length,
|
|
strutData: structData,
|
|
structData == null ? 0 : structData.Length,
|
|
fontFamily: fontFamily,
|
|
structFontFamily: structFontFamily,
|
|
structFontFamily == null ? 0 : structFontFamily.Length,
|
|
fontSize: fontSize,
|
|
height: height,
|
|
ellipsis: ellipsis,
|
|
locale: locale
|
|
);
|
|
}
|
|
|
|
public override void DisposePtr(IntPtr ptr) {
|
|
ParagraphBuilder_dispose(ptr: ptr);
|
|
}
|
|
|
|
public void pushStyle(TextStyle style) {
|
|
var fullFontFamilies = new List<string>();
|
|
|
|
fullFontFamilies.Add(item: style._fontFamily);
|
|
if (style._fontFamilyFallback != null) {
|
|
fullFontFamilies.AddRange(collection: style._fontFamilyFallback);
|
|
}
|
|
|
|
byte[] encodedFontFeatures = null;
|
|
var fontFeatures = style._fontFeatures;
|
|
if (fontFeatures != null) {
|
|
encodedFontFeatures = new byte[fontFeatures.Count * FontFeature._kEncodedSize];
|
|
var byteOffset = 0;
|
|
foreach (var feature in fontFeatures) {
|
|
var byteFeature = new byte[FontFeature._kEncodedSize - byteOffset];
|
|
Array.Copy(sourceArray: encodedFontFeatures, 0, destinationArray: byteFeature, 0,
|
|
length: FontFeature._kEncodedSize);
|
|
feature._encode(byteData: byteFeature);
|
|
byteOffset += FontFeature._kEncodedSize;
|
|
}
|
|
}
|
|
|
|
_pushStyle(
|
|
style._encoded.ToArray(),
|
|
fullFontFamilies.ToArray(),
|
|
fontSize: style._fontSize,
|
|
letterSpacing: style._letterSpacing,
|
|
wordSpacing: style._wordSpacing,
|
|
height: style._height,
|
|
decorationThickness: style._decorationThickness,
|
|
_encodeLocale(locale: style._locale),
|
|
backgroundObjects: style._background?._objectPtrs,
|
|
backgroundData: style._background?._data,
|
|
foregroundObjects: style._foreground?._objectPtrs,
|
|
foregroundData: style._foreground?._data,
|
|
Shadow._encodeShadows(shadows: style._shadows),
|
|
fontFeaturesData: encodedFontFeatures
|
|
);
|
|
}
|
|
|
|
unsafe void _pushStyle(
|
|
int[] encoded,
|
|
string[] fontFamilies,
|
|
float? fontSize,
|
|
float? letterSpacing,
|
|
float? wordSpacing,
|
|
float? height,
|
|
float? decorationThickness,
|
|
string locale,
|
|
IntPtr[] backgroundObjects,
|
|
byte[] backgroundData,
|
|
IntPtr[] foregroundObjects,
|
|
byte[] foregroundData,
|
|
byte[] shadowsData,
|
|
byte[] fontFeaturesData
|
|
) {
|
|
fixed (IntPtr* backgroundObjectsPtr = backgroundObjects)
|
|
fixed (IntPtr* foregroundObjectsPtr = foregroundObjects) {
|
|
ParagraphBuilder_pushStyle(ptr: _ptr,
|
|
encoded: encoded,
|
|
encodedSize: encoded.Length,
|
|
fontFamilies: fontFamilies,
|
|
fontFamiliesSize: fontFamilies.Length,
|
|
fontSize.GetValueOrDefault(0),
|
|
letterSpacing.GetValueOrDefault(0),
|
|
wordSpacing.GetValueOrDefault(0),
|
|
height.GetValueOrDefault(0),
|
|
decorationThickness.GetValueOrDefault(0),
|
|
locale: locale,
|
|
backgroundObjects: backgroundObjectsPtr,
|
|
backgroundData: backgroundData,
|
|
foregroundObjects: foregroundObjectsPtr,
|
|
foregroundData: foregroundData,
|
|
shadowsData: shadowsData,
|
|
shadowsData?.Length ?? 0,
|
|
fontFeaturesData: fontFeaturesData,
|
|
fontFeaturesData?.Length ?? 0
|
|
);
|
|
}
|
|
}
|
|
|
|
static string _encodeLocale(Locale locale) {
|
|
return locale?.ToString() ?? "";
|
|
}
|
|
|
|
public void pop() {
|
|
ParagraphBuilder_pop(ptr: _ptr);
|
|
}
|
|
|
|
public void addText(string text) {
|
|
var error = _addText(text: text);
|
|
if (error != IntPtr.Zero) {
|
|
throw new Exception(Marshal.PtrToStringAnsi(ptr: error));
|
|
}
|
|
}
|
|
|
|
IntPtr _addText(string text) {
|
|
return ParagraphBuilder_addText(ptr: _ptr, text: text);
|
|
}
|
|
|
|
public void addPlaceholder(float width, float height, PlaceholderAlignment alignment,
|
|
TextBaseline baseline,
|
|
float scale = 1.0f,
|
|
float? baselineOffset = null
|
|
) {
|
|
// Require a baseline to be specified if using a baseline-based alignment.
|
|
D.assert(alignment == PlaceholderAlignment.aboveBaseline ||
|
|
alignment == PlaceholderAlignment.belowBaseline ||
|
|
alignment == PlaceholderAlignment.baseline);
|
|
// Default the baselineOffset to height if null. This will place the placeholder
|
|
// fully above the baseline, similar to [PlaceholderAlignment.aboveBaseline].
|
|
var baselineOffsetFloat = baselineOffset ?? height;
|
|
_addPlaceholder(width * scale, height * scale, (int) alignment, baselineOffsetFloat * scale,
|
|
(int) baseline);
|
|
placeholderCount++;
|
|
placeholderScales.Add(item: scale);
|
|
}
|
|
|
|
IntPtr _addPlaceholder(float width, float height, int alignment, float baselineOffset,
|
|
int baseline) {
|
|
return ParagraphBuilder_addPlaceholder(ptr: _ptr, width: width, height: height, alignment: alignment,
|
|
baselineOffset: baselineOffset, baseline: baseline);
|
|
}
|
|
|
|
public Paragraph build() {
|
|
var paragraph = new Paragraph(_build());
|
|
return paragraph;
|
|
}
|
|
|
|
IntPtr _build() {
|
|
return ParagraphBuilder_build(ptr: _ptr);
|
|
}
|
|
}
|
|
}
|