/// <summary> /// Recursives the prepare spannable indexes. /// </summary> /// <param name="context">The context.</param> /// <param name="text">The text.</param> /// <param name="size">The size.</param> /// <param name="builder">The builder.</param> /// <param name="modules">The modules.</param> /// <param name="start">The start.</param> /// <exception cref="System.ArgumentException"> /// Unknown resource + stroke + in \ + text + \ /// or /// Unknown resource + stroke + in \ + text + \ /// or /// Unknown resource + stroke + in \ + text + \ /// or /// Unknown resource + stroke + in \ + text + \ /// or /// Unknown expression + stroke + in \ + text + \ /// </exception> private static void RecursivePrepareSpannableIndexes(Context context, String text, Single size, SpannableStringBuilder builder, IList <IIconModule> modules, Int32 start) { // Try to find a {...} in the string and extract expression from it var stringText = builder.ToString(); var startIndex = stringText.IndexOf("{", start, StringComparison.Ordinal); if (startIndex == -1) { return; } var endIndex = stringText.IndexOf("}", startIndex, StringComparison.Ordinal) + 1; var expression = stringText.Substring(startIndex + 1, endIndex - 2); // Split the expression and retrieve the icon key var strokes = expression.Split(' '); var key = strokes[0]; // Loop through the descriptors to find a key match IIconModule module = null; IIcon icon = null; for (var i = 0; i < modules.Count; i++) { module = modules[i]; icon = module.GetIcon(key); if (icon != null) { break; } } // If no match, ignore and continue if (icon == null) { RecursivePrepareSpannableIndexes(context, text, size, builder, modules, endIndex); return; } // See if any more stroke within {} should be applied var iconSizePx = spToPx(context, size); var iconColor = Int32.MaxValue; var iconSizeRatio = -1f; var spin = false; var baselineAligned = false; for (var i = 1; i < strokes.Length; i++) { var stroke = strokes[i]; // Look for "spin" if (stroke.Equals("spin", StringComparison.OrdinalIgnoreCase)) { spin = true; } // Look for "baseline" else if (stroke.Equals("baseline", StringComparison.OrdinalIgnoreCase)) { baselineAligned = true; } // Look for an icon size else if (Regex.IsMatch(stroke, "([0-9]*(\\.[0-9]*)?)dp")) { iconSizePx = dpToPx(context, Convert.ToSingle(stroke.Substring(0, stroke.Length - 2))); } else if (Regex.IsMatch(stroke, "([0-9]*(\\.[0-9]*)?)sp")) { iconSizePx = spToPx(context, Convert.ToSingle(stroke.Substring(0, stroke.Length - 2))); } else if (Regex.IsMatch(stroke, "([0-9]*)px")) { iconSizePx = Convert.ToInt32(stroke.Substring(0, stroke.Length - 2)); } else if (Regex.IsMatch(stroke, "@dimen/(.*)")) { iconSizePx = GetPxFromDimen(context, context.PackageName, stroke.Substring(7)); if (iconSizePx < 0) { throw new ArgumentException("Unknown resource " + stroke + " in \"" + text + "\""); } } else if (Regex.IsMatch(stroke, "@android:dimen/(.*)")) { iconSizePx = GetPxFromDimen(context, ANDROID_PACKAGE_NAME, stroke.Substring(15)); if (iconSizePx < 0) { throw new ArgumentException("Unknown resource " + stroke + " in \"" + text + "\""); } } else if (Regex.IsMatch(stroke, "([0-9]*(\\.[0-9]*)?)%")) { iconSizeRatio = Convert.ToSingle(stroke.Substring(0, stroke.Length - 1)) / 100f; } // Look for an icon color else if (Regex.IsMatch(stroke, "#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})")) { iconColor = Color.ParseColor(stroke); } else if (Regex.IsMatch(stroke, "@color/(.*)")) { iconColor = GetColorFromResource(context, context.PackageName, stroke.Substring(7)); if (iconColor == Int32.MaxValue) { throw new ArgumentException("Unknown resource " + stroke + " in \"" + text + "\""); } } else if (Regex.IsMatch(stroke, "@android:color/(.*)")) { iconColor = GetColorFromResource(context, ANDROID_PACKAGE_NAME, stroke.Substring(15)); if (iconColor == Int32.MaxValue) { throw new ArgumentException("Unknown resource " + stroke + " in \"" + text + "\""); } } else { throw new ArgumentException("Unknown expression " + stroke + " in \"" + text + "\""); } } // Replace the character and apply the typeface builder = (SpannableStringBuilder)builder.Replace(startIndex, endIndex, "" + icon.Character); builder.SetSpan(new CustomTypefaceSpan(icon, module.ToTypeface(context), iconSizePx, iconSizeRatio, iconColor, spin, baselineAligned), startIndex, startIndex + 1, SpanTypes.InclusiveExclusive); RecursivePrepareSpannableIndexes(context, text, size, builder, modules, startIndex); }
/// <summary> /// Recursives the prepare spannable indexes. /// </summary> /// <param name="modules">The modules.</param> /// <param name="text">The text.</param> /// <param name="size">The size.</param> /// <param name="color">The color.</param> /// <param name="builder">The builder.</param> /// <param name="start">The start.</param> /// <exception cref="System.ArgumentException">Unknown expression + stroke + in \ + text + \</exception> private static void RecursivePrepareSpannableIndexes(IList <IIconModule> modules, String text, nfloat size, UIColor color, NSMutableAttributedString builder, Int32 start) { // Try to find a {...} in the string and extract expression from it var startIndex = builder.Value.IndexOf("{", start, StringComparison.Ordinal); if (startIndex == -1) { return; } var endIndex = builder.Value.IndexOf("}", startIndex, StringComparison.Ordinal); var expression = builder.Value.Substring(startIndex + 1, endIndex - startIndex - 1); // Split the expression and retrieve the icon key var strokes = expression.Split(' '); var key = strokes[0]; // Loop through the descriptors to find a key match IIconModule module = null; IIcon icon = null; for (var i = 0; i < modules.Count; i++) { module = modules[i]; icon = module.GetIcon(key); if (icon != null) { break; } } // If no match, ignore and continue if (icon == null) { RecursivePrepareSpannableIndexes(modules, text, size, color, builder, endIndex); return; } // See if any more stroke within {} should be applied var iconSizePt = nfloat.MinValue; var iconColor = color; var iconSizeRatio = nfloat.MinValue; var baselineAligned = false; for (var i = 1; i < strokes.Length; i++) { var stroke = strokes[i]; // Look for "baseline" if (stroke.Equals("baseline", StringComparison.OrdinalIgnoreCase)) { baselineAligned = true; } // Look for an icon size else if (Regex.IsMatch(stroke, "([0-9]*)px")) { iconSizePt = Convert.ToInt32(stroke.Substring(0, stroke.Length - 2)); } else if (Regex.IsMatch(stroke, "([0-9]*)pt")) { iconSizePt = Convert.ToInt32(stroke.Substring(0, stroke.Length - 2)); } else if (Regex.IsMatch(stroke, "([0-9]*(\\.[0-9]*)?)%")) { iconSizeRatio = Convert.ToSingle(stroke.Substring(0, stroke.Length - 1)) / 100f; } // Look for an icon color else if (Regex.IsMatch(stroke, "#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})")) { if (stroke.Length == 7) { var red = Int32.Parse(stroke.Substring(1, 2), NumberStyles.HexNumber); var blue = Int32.Parse(stroke.Substring(3, 2), NumberStyles.HexNumber); var green = Int32.Parse(stroke.Substring(5, 2), NumberStyles.HexNumber); iconColor = new UIColor(red / 255f, blue / 255f, green / 255f, 1f); } else if (stroke.Length == 9) { var alpha = Int32.Parse(stroke.Substring(1, 2), NumberStyles.HexNumber); var red = Int32.Parse(stroke.Substring(3, 2), NumberStyles.HexNumber); var blue = Int32.Parse(stroke.Substring(5, 2), NumberStyles.HexNumber); var green = Int32.Parse(stroke.Substring(7, 2), NumberStyles.HexNumber); iconColor = new UIColor(red / 255f, blue / 255f, green / 255f, alpha / 255f); } } else { throw new ArgumentException("Unknown expression " + stroke + " in \"" + text + "\""); } } if (iconSizePt == nfloat.MinValue) { iconSizePt = size; } if (iconSizeRatio != nfloat.MinValue) { iconSizePt = iconSizePt * iconSizeRatio; } var attributes = new UIStringAttributes { Font = module.ToUIFont(iconSizePt) }; if (baselineAligned == true) { attributes.BaselineOffset = 0f; } if (iconColor != UIColor.DarkTextColor) { attributes.ForegroundColor = iconColor; } var replaceString = new NSAttributedString($"{icon.Character}", attributes); // Replace the character and apply the typeface builder.Replace(new NSRange(startIndex, endIndex - startIndex + 1), replaceString); RecursivePrepareSpannableIndexes(modules, text, size, color, builder, startIndex); }
/// <summary> /// Recursives the prepare spannable indexes. /// </summary> /// <param name="modules">The modules.</param> /// <param name="text">The text.</param> /// <param name="size">The size.</param> /// <param name="builder">The builder.</param> /// <param name="start">The start.</param> /// <exception cref="System.ArgumentException">Unknown expression + stroke + in \ + text + \</exception> private static void RecursivePrepareSpannableIndexes(IList <IIconModule> modules, String text, Double size, Span builder, Int32 start) { // Try to find a {...} in the string and extract expression from it var startIndex = text.IndexOf("{", start, StringComparison.Ordinal); if (startIndex > start) { builder.Inlines.Add(new Run { Text = text.Substring(start, startIndex - start) }); } else if (startIndex == -1) { if (start < text.Length - 1) { builder.Inlines.Add(new Run { Text = text.Substring(start) }); } return; } var endIndex = text.IndexOf("}", startIndex, StringComparison.Ordinal); var expression = text.Substring(startIndex + 1, endIndex - startIndex - 1); // Split the expression and retrieve the icon key var strokes = expression.Split(' '); var key = strokes[0]; // Loop through the descriptors to find a key match IIconModule module = null; IIcon icon = null; for (var i = 0; i < modules.Count; i++) { module = modules[i]; icon = module.GetIcon(key); if (icon != null) { break; } } // If no match, ignore and continue if (icon == null) { RecursivePrepareSpannableIndexes(modules, text, size, builder, endIndex + 1); return; } // See if any more stroke within {} should be applied var iconSizePt = Double.MinValue; var iconColor = Colors.Black; var iconSizeRatio = Single.MinValue; for (var i = 1; i < strokes.Length; i++) { var stroke = strokes[i]; // Look for an icon size if (Regex.IsMatch(stroke, "([0-9]*)px")) { iconSizePt = Convert.ToInt32(stroke.Substring(0, stroke.Length - 2)); } else if (Regex.IsMatch(stroke, "([0-9]*)pt")) { iconSizePt = Convert.ToInt32(stroke.Substring(0, stroke.Length - 2)); } else if (Regex.IsMatch(stroke, "([0-9]*(\\.[0-9]*)?)%")) { iconSizeRatio = Convert.ToSingle(stroke.Substring(0, stroke.Length - 1)) / 100f; } // Look for an icon color else if (Regex.IsMatch(stroke, "#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})")) { if (stroke.Length == 7) { var red = Byte.Parse(stroke.Substring(1, 2), NumberStyles.HexNumber); var blue = Byte.Parse(stroke.Substring(3, 2), NumberStyles.HexNumber); var green = Byte.Parse(stroke.Substring(5, 2), NumberStyles.HexNumber); iconColor = Color.FromArgb(0, red, blue, green); } else if (stroke.Length == 9) { var alpha = Byte.Parse(stroke.Substring(1, 2), NumberStyles.HexNumber); var red = Byte.Parse(stroke.Substring(3, 2), NumberStyles.HexNumber); var blue = Byte.Parse(stroke.Substring(5, 2), NumberStyles.HexNumber); var green = Byte.Parse(stroke.Substring(7, 2), NumberStyles.HexNumber); iconColor = Color.FromArgb(alpha, red, blue, green); } } else { throw new ArgumentException("Unknown expression " + stroke + " in \"" + text + "\""); } } if (Math.Abs(iconSizePt - Double.MinValue) < Double.Epsilon) { iconSizePt = size; } if (Math.Abs(iconSizeRatio - Single.MinValue) > Single.Epsilon) { iconSizePt = iconSizePt * iconSizeRatio; } var replaceString = new Run { FontFamily = module.ToFontFamily(), Text = $"{icon.Character}" }; if (iconColor != Colors.Black) { replaceString.Foreground = new SolidColorBrush(iconColor); } builder.Inlines.Add(replaceString); RecursivePrepareSpannableIndexes(modules, text, size, builder, endIndex + 1); }