public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) { DebugMessage("ENTER(" + widthConstraint + "," + heightConstraint + ")"); var desiredSize = MeasureOverride(new Windows.Foundation.Size(widthConstraint, heightConstraint)); var minSize = new Xamarin.Forms.Size(10, Element != null ? FontExtensions.LineHeightForFontSize(Element.DecipheredMinFontSize()):10); DebugMessage("EXIT(" + desiredSize + ")"); return(new SizeRequest(new Xamarin.Forms.Size(desiredSize.Width, desiredSize.Height), minSize)); }
public double LineHeight(string fontFamily, double fontSize, FontAttributes fontAttributes) { return(FontExtensions.LineHeightForFontSize(fontSize)); }
internal static void SetAndFormatText(this TextBlock winTextBlock, Forms9Patch.Label f9pLabel, double altFontSize = -1) { var label = f9pLabel; var textBlock = winTextBlock; if (f9pLabel == null || textBlock == null) { return; } textBlock.Text = ""; textBlock.Inlines.Clear(); textBlock.FontSize = (altFontSize > 0 ? altFontSize : label.DecipheredFontSize()); textBlock.LineHeight = FontExtensions.LineHeightForFontSize(textBlock.FontSize); textBlock.LineStackingStrategy = Windows.UI.Xaml.LineStackingStrategy.BaselineToBaseline; textBlock.FontFamily = FontService.GetWinFontFamily(label.FontFamily); if (label.Text != null) { textBlock.Text = label.Text; return; } if (label.FormattedText != null) { var formattedText = label.FormattedText; for (var i = 0; i < formattedText.Spans.Count; i++) { textBlock.Inlines.Add(formattedText.Spans[i].ToRun()); } return; } var text = label.F9PFormattedString?.Text; if (label.F9PFormattedString is HTMLMarkupString htmlMarkupString) { text = htmlMarkupString.UnmarkedText; } if (string.IsNullOrWhiteSpace(text) || text == label.F9PFormattedString?.Text) { // there isn't any markup! textBlock.Text = text ?? ""; return; } #region Layout font-spans (MetaFonts) var metaFonts = new List <MetaFont>(); var baseMetaFont = new MetaFont( label.FontFamily, textBlock.FontSize, //(altFontSize > 0 ? altFontSize : label.DecipheredFontSize()), //(label.FontSize < 0 ? (double)Windows.UI.Xaml.Application.Current.Resources["ControlContentThemeFontSize"] : label.FontSize)), //label.FontSize, (label.FontAttributes & Xamarin.Forms.FontAttributes.Bold) > 0, //textBlock.FontWeight.Weight >= Windows.UI.Text.FontWeights.Bold.Weight, (label.FontAttributes & Xamarin.Forms.FontAttributes.Italic) > 0, // (textBlock.FontStyle & Windows.UI.Text.FontStyle.Italic) > 0, textColor: label.TextColor //, //backgroundColor: label.BackgroundColor ); var MathMetaFont = new MetaFont(baseMetaFont) { Family = "STIXGeneral" //FontService.ReconcileFontFamily("Forms9Patch.Resources.Fonts.STIXGeneral.otf", P42.Utils.ReflectionExtensions.GetAssembly(typeof(Forms9Patch.Label))) }; for (int i = 0; i < text.Length; i++) { if (i + 1 < text.Length && text[i] == '\ud835' && text[i + 1] >= '\udc00' && text[i + 1] <= '\udeff') { metaFonts.Add(new MetaFont(MathMetaFont)); metaFonts.Add(new MetaFont(MathMetaFont)); // there are two because we're using a double byte unicode character i++; } else { metaFonts.Add(new MetaFont(baseMetaFont)); } } #endregion #region Apply non-font Spans foreach (var span in label.F9PFormattedString._spans) { int spanStart = span.Start; int spanEnd = span.End; //spanEnd++; if (spanEnd >= text.Length) { spanEnd = text.Length - 1; } for (int i = spanStart; i <= spanEnd; i++) { switch (span.Key) { case FontFamilySpan.SpanKey: // TextElement.FontFamily var fontFamily = ((FontFamilySpan)span).FontFamilyName; metaFonts[i].Family = fontFamily; break; case FontSizeSpan.SpanKey: // TextElement.FontSize float size = ((FontSizeSpan)span).Size; metaFonts[i].Size = (size < 0 ? metaFonts[i].Size * (-size) : size); break; case BoldSpan.SpanKey: // Bold span // TextElement.FontWeight (Thin, ExtraLight, Light, SemiLight, Normal, Medium, SemiBold, Bold, ExtraBold, Black, ExtraBlack) metaFonts[i].Bold = true; break; case ItalicsSpan.SpanKey: // Italic span // TextElement.FontStyle (Normal, Italic, Oblique) metaFonts[i].Italic = true; break; case FontColorSpan.SpanKey: // TextElement.Foreground metaFonts[i].TextColor = ((FontColorSpan)span).Color; break; case UnderlineSpan.SpanKey: // Underline span // TextElement.TextDecorations = None, Strikethought, Underline metaFonts[i].Underline = true; break; case StrikethroughSpan.SpanKey: // TextElement.TextDecorations = None, Strikethought, Underline metaFonts[i].Strikethrough = true; break; case SuperscriptSpan.SpanKey: // Run with Typographic.Variants=FontVariants.Superscript while using Cambria metaFonts[i].Baseline = FontBaseline.Superscript; break; case SubscriptSpan.SpanKey: // Run with Typographic.Varients=FontVariants.Subscript while using Cambria metaFonts[i].Baseline = FontBaseline.Subscript; break; case NumeratorSpan.SpanKey: // no UWP solution - need to use SuperScript metaFonts[i].Baseline = FontBaseline.Numerator; break; case DenominatorSpan.SpanKey: // no UWP solution - need to use Subscript metaFonts[i].Baseline = FontBaseline.Denominator; break; case ActionSpan.SpanKey: // Hyperlink span ?? metaFonts[i].Action = new MetaFontAction((ActionSpan)span); break; case BackgroundColorSpan.SpanKey: // if Win10 fall creator's update, there is a solution: create TextHighlighter, set its BackgroundColor and add the Range (Start/End) to it's Ranges, and add to TextBlock.Highlighters metaFonts[i].BackgroundColor = ((BackgroundColorSpan)span).Color; break; } } } #endregion #region Convert MetaFonts to InLines var inlineColection = new List <Inline>(); // run through MetaFonts to see if we need to set new Font attributes var lastMetaFont = baseMetaFont; int startIndex = 0; for (int i = 0; i < metaFonts.Count; i++) { var metaFont = metaFonts[i]; if (lastMetaFont != metaFont) { // we are at the start of a new span if (i > 0) // && lastMetaFont != baseMetaFont) { AddInline(textBlock, label, lastMetaFont, text, startIndex, i - startIndex); } lastMetaFont = metaFont; startIndex = i; } } AddInline(textBlock, label, lastMetaFont, text, startIndex, text.Length - startIndex); #endregion }
protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize) { if (DebugCondition) { System.Diagnostics.Debug.WriteLine(""); } DebugMessage("[" + _measureOverrideInvocation + "] pre-Enter availableSize=[" + availableSize + "] ElemmentSize=[" + Element.Bounds.Size + "] PageSize=[" + Xamarin.Forms.Application.Current.MainPage.Bounds.Size + "]"); DebugMessage("[" + _measureOverrideInvocation + "] \t\t availWidth>=Page.Width=[" + (Math.Round(availableSize.Width) >= Math.Round(Xamarin.Forms.Application.Current.MainPage.Width)) + "]"); DebugMessage("[" + _measureOverrideInvocation + "] \t\t availHeight>=Page.Height=[" + (Math.Round(availableSize.Height) >= Math.Round(Xamarin.Forms.Application.Current.MainPage.Height)) + "]"); DebugMessage("[" + _measureOverrideInvocation + "] \t\t Element.Parent=[" + Element.Parent + "]"); var label = Element; var textBlock = Control; if (label == null || textBlock == null || availableSize.Width == 0 || availableSize.Height == 0) { return(new Windows.Foundation.Size(0, 0)); } if (LayoutValid && _lastAvailableSize.Width <= availableSize.Width && _lastAvailableSize.Height <= availableSize.Height && _lastElementSize == Element.Bounds.Size && DateTime.Now - _lastMeasure < TimeSpan.FromSeconds(1)) { return(_lastMeasureOverrideResult); } //_lastAvailableSize = availableSize; if (DebugCondition) { System.Diagnostics.Debug.WriteLine(""); } DebugMessage("[" + _measureOverrideInvocation + "] ENTER availableSize=[" + availableSize + "]"); //Element.IsInNativeLayout = true; label.SetIsInNativeLayout(true); double width = (Math.Round(availableSize.Width) >= Math.Round(Xamarin.Forms.Application.Current.MainPage.Width)) && label.Width > 0 ? Math.Min(label.Width, availableSize.Width) : availableSize.Width; double height = (Math.Round(availableSize.Height) >= Math.Round(Xamarin.Forms.Application.Current.MainPage.Height)) && label.Height > 0 ? Math.Min(label.Height, availableSize.Height) : availableSize.Height; if (Double.IsInfinity(availableSize.Width)) { width = availableSize.Width; } if (Double.IsInfinity(availableSize.Height)) { height = availableSize.Height; } if (Element.Width > 0 && Element.Height > 0 && !Double.IsInfinity(width) && !Double.IsInfinity(height)) // This line was causing UWP to fail to correctly update width of BcOperandLabel during editing. //if (Element.Width > width && Element.Height > height && !Double.IsInfinity(width) && !Double.IsInfinity(height)) { width = Math.Max(Element.Width, width); height = Math.Max(Element.Height, height); } DebugMessage("[" + _measureOverrideInvocation + "] \t\t width=[" + width + "] height=[" + height + "]"); if (DebugCondition) { System.Diagnostics.Debug.WriteLine(""); } //double width = !double.IsInfinity(availableSize.Width) && label.Width > 0 ? Math.Min(label.Width, availableSize.Width) : availableSize.Width; //double height = !double.IsInfinity(availableSize.Height) && label.Height > 0 ? Math.Min(label.Height, availableSize.Height) : availableSize.Height; //double width = availableSize.Width; //double height = availableSize.Height; var result = new Windows.Foundation.Size(width, height); if (textBlock != null) { // reset FontSize var tmpFontSize = label.DecipheredFontSize(); if (DebugCondition) { System.Diagnostics.Debug.WriteLine(""); } if (Element.SynchronizedFontSize > Element.MinFontSize) { tmpFontSize = Element.SynchronizedFontSize; } var minFontSize = label.DecipheredMinFontSize(); textBlock.MaxLines = 0; // int.MaxValue / 3; textBlock.MaxWidth = double.PositiveInfinity; textBlock.MaxHeight = double.PositiveInfinity; textBlock.MinHeight = 0; textBlock.MinWidth = 0; //textBlock.LineStackingStrategy = LineStackingStrategy. // textBlock.TextWrapping = TextWrapping.WrapWholeWords; UpdateLineBreakMode(textBlock); double tmpHt = -1; if (label.Lines == 0) { // do our best job to fit the existing space. textBlock.SetAndFormatText(label, tmpFontSize); textBlock.Measure(new Windows.Foundation.Size(width, double.PositiveInfinity)); if (textBlock.DesiredSize.Width - width > Precision || textBlock.DesiredSize.Height - height > Precision) { tmpFontSize = ZeroLinesFit(label, textBlock, minFontSize, tmpFontSize, width, height); } } else if (label.AutoFit == AutoFit.Lines) { tmpHt = height; if (availableSize.Height > int.MaxValue / 3) { tmpHt = height = label.Lines * FontExtensions.LineHeightForFontSize(tmpFontSize); } else // set the font size to fit Label.Lines into the available height { tmpFontSize = FontExtensions.FontSizeFromLineHeight(height / label.Lines); } tmpFontSize = FontExtensions.ClipFontSize(tmpFontSize, label); textBlock.SetAndFormatText(label, tmpFontSize); textBlock.Measure(new Windows.Foundation.Size(width, height)); } else if (label.AutoFit == AutoFit.Width) { if (DebugCondition) { System.Diagnostics.Debug.WriteLine(""); } //textBlock.TextWrapping = TextWrapping.Wrap; //textBlock.TextTrimming = TextTrimming.CharacterEllipsis; textBlock.SetAndFormatText(label, tmpFontSize); textBlock.Measure(new Windows.Foundation.Size(width, double.PositiveInfinity)); //if (textBlock.DesiredSize.Height / textBlock.LineHeight > label.Lines) if (textBlock.ActualWidth > textBlock.DesiredSize.Width || textBlock.DesiredSize.Height / textBlock.LineHeight > label.Lines) { tmpFontSize = WidthAndLinesFit(label, textBlock, label.Lines, minFontSize, tmpFontSize, width); } } // autofit is off! // No need to do anything at the moment. Will textBlock.SetAndFormat and textBlock.Measure at the end //{ // textBlock.SetAndFormatText(label, tmpFontSize); // textBlock?.Measure(new Windows.Foundation.Size(w, double.PositiveInfinity)); //} // none of these should happen so let's keep an eye out for it to be sure everything upstream is working if (tmpFontSize > label.DecipheredFontSize()) { throw new Exception("fitting somehow returned a tmpFontSize > label.FontSize"); } if (tmpFontSize < label.DecipheredMinFontSize()) { throw new Exception("fitting somehow returned a tmpFontSize < label.MinFontSize"); } // the following doesn't apply when where growing //if (tmpFontSize > label.DecipheredMinFontSize() && (textBlock.DesiredSize.Width > Math.Ceiling(w) || textBlock.DesiredSize.Height > Math.Ceiling(Math.Max(availableSize.Height, label.Height))) ) // throw new Exception("We should never exceed the available bounds if the FontSize is greater than label.MinFontSize"); // we needed the following in Android as well. Xamarin layout really doesn't like this to be changed in real time. if (Element != null && Control != null) // multipicker test was getting here with Element and Control both null { if (tmpFontSize == Element.FontSize || (Element.FontSize == -1 && tmpFontSize == FontExtensions.DefaultFontSize())) { if (DebugCondition) { System.Diagnostics.Debug.WriteLine(""); } Element.FittedFontSize = -1; } else { if (DebugCondition) { System.Diagnostics.Debug.WriteLine(""); } Element.FittedFontSize = tmpFontSize; } } DebugMessage("[" + _measureOverrideInvocation + "] Element.FittedFontSize=[" + Element.FittedFontSize + "]"); /* * var syncFontSize = ((ILabel)label).SynchronizedFontSize; * DebugMessage("[" + _measureOverrideInvocation + "] syncFontSize=[" + syncFontSize+"]"); * if (syncFontSize >= 0 && tmpFontSize != syncFontSize) * { * tmpHt = -1; * textBlock.SetAndFormatText(label, syncFontSize); * textBlock.Measure(new Windows.Foundation.Size(width, double.PositiveInfinity)); * } * else */ { textBlock.SetAndFormatText(label, tmpFontSize); textBlock.Measure(new Windows.Foundation.Size(width, double.PositiveInfinity)); } result = new Windows.Foundation.Size(Math.Ceiling(textBlock.DesiredSize.Width), Math.Ceiling(tmpHt > -1 ? tmpHt : textBlock.DesiredSize.Height)); if (DebugCondition && label.Width > 0 && label.Height > 0 && (textBlock.DesiredSize.Width > label.Width || textBlock.DesiredSize.Height > label.Height)) { System.Diagnostics.Debug.WriteLine(""); } DebugMessage("[" + _measureOverrideInvocation + "] result=[" + result + "] FontSize=[" + textBlock.FontSize + "] LineHeight=[" + textBlock.LineHeight + "]"); textBlock.MaxLines = label.Lines; } label.SetIsInNativeLayout(false); DebugMessage("[" + _measureOverrideInvocation + "] EXIT"); LayoutValid = true; _lastAvailableSize = availableSize; _lastElementSize = Element.Bounds.Size; _lastAutoFit = label.AutoFit; _lastLines = label.Lines; _lastMeasure = DateTime.Now; _lastMeasureOverrideResult = result; if (DebugCondition) { _measureOverrideInvocation++; } return(result); }