Esempio n. 1
0
        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));
        }
Esempio n. 2
0
 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
        }
Esempio n. 4
0
        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);
        }