From cfa3078528d18753667f59dd1daa311a8920bc31 Mon Sep 17 00:00:00 2001 From: "Frank A. Krueger" Date: Sun, 15 Apr 2018 21:21:45 -0700 Subject: [PATCH] Improve word wrap measurements --- Ooui.Forms/Extensions/FontExtensions.cs | 46 ++++++++++++------------- Ooui/UI.cs | 2 +- Samples/WrappingTextSample.cs | 24 ++++++++----- 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/Ooui.Forms/Extensions/FontExtensions.cs b/Ooui.Forms/Extensions/FontExtensions.cs index 5588bda..f4c5860 100644 --- a/Ooui.Forms/Extensions/FontExtensions.cs +++ b/Ooui.Forms/Extensions/FontExtensions.cs @@ -44,7 +44,7 @@ namespace Ooui.Forms.Extensions return Size.Zero; var fontHeight = fontSize; - var lineHeight = fontHeight * 1.4; + var lineHeight = (int)(fontSize * 1.42857143); // Floor is intentional -- browsers round down var isBold = fontAttrs.HasFlag (FontAttributes.Bold); @@ -55,40 +55,40 @@ namespace Ooui.Forms.Extensions var lines = 1; var maxPWidth = 0.0; var pwidthConstraint = double.IsPositiveInfinity (widthConstraint) ? double.PositiveInfinity : widthConstraint / fontSize; - var lastSpaceWidth = -1.0; - - // Tiny little padding to account for sampling errors - var pwidthHack = 1.0e-6; - var plineHack = 0.333; + var firstSpaceX = -1.0; + var lastSpaceIndex = -1; + var lineStartIndex = 0; var n = text != null ? text.Length : 0; - for (var i = 0; i < n; i++) { var c = (int)text[i]; var pw = (c < 128) ? props[c] : avgp; // Should we wrap? - if (px + pw + plineHack > pwidthConstraint) { + if (px + pw > pwidthConstraint && lastSpaceIndex > 0) { lines++; - if (lastSpaceWidth > 0) { - maxPWidth = Math.Max (maxPWidth, lastSpaceWidth + pwidthHack); - px = pw - lastSpaceWidth; - lastSpaceWidth = -1; - } - else { - maxPWidth = Math.Max (maxPWidth, px + pwidthHack); - px = 0; - } + maxPWidth = Math.Max (maxPWidth, firstSpaceX); + i = lastSpaceIndex; + while (i < n && text[i] == ' ') i++; + i--; + px = 0; + firstSpaceX = -1; + lastSpaceIndex = -1; + lineStartIndex = i + 1; } - if (c == ' ') { - lastSpaceWidth = pw; + else { + if (c == ' ') { + if (i >= lineStartIndex && text[i-1] != ' ') + firstSpaceX = px; + lastSpaceIndex = i; + } + px += pw; } - px += pw; } - maxPWidth = Math.Max (maxPWidth, px + pwidthHack); - var width = fontSize * maxPWidth; + maxPWidth = Math.Max (maxPWidth, px); + var width = (int)Math.Ceiling (fontSize * maxPWidth); var height = lines * lineHeight; - // Console.WriteLine ($"MEASURE TEXT SIZE {widthConstraint}x{heightConstraint} \"{text}\" == {width}x{height}"); + //Console.WriteLine ($"MEASURE TEXT SIZE {widthConstraint}x{heightConstraint} ==> {width}x{height} \"{text}\""); return new Size (width, height); } diff --git a/Ooui/UI.cs b/Ooui/UI.cs index e232702..50bf1a8 100644 --- a/Ooui/UI.cs +++ b/Ooui/UI.cs @@ -392,7 +392,7 @@ namespace Ooui "); writer.WriteLine (BodyHeaderHtml); - writer.WriteLine (@"
"); + writer.WriteLine (@"
"); writer.WriteLine (initialHtml); writer.Write (@"
diff --git a/Samples/WrappingTextSample.cs b/Samples/WrappingTextSample.cs index 7f0f310..9305d4f 100644 --- a/Samples/WrappingTextSample.cs +++ b/Samples/WrappingTextSample.cs @@ -24,17 +24,11 @@ namespace Samples rows.Children.Add (row1); var row0s = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.Azure }; - row0s.Children.Add (new Label { Text = shortText, LineBreakMode = LineBreakMode.WordWrap, WidthRequest = 100 }); - row0s.Children.Add (new Label { Text = mediumText, LineBreakMode = LineBreakMode.WordWrap, WidthRequest = 100 }); - row0s.Children.Add (new Label { Text = longText, LineBreakMode = LineBreakMode.WordWrap, WidthRequest = 100 }); + row0s.Children.Add (new Label { Text = shortText, LineBreakMode = LineBreakMode.WordWrap, WidthRequest = 200 }); + row0s.Children.Add (new Label { Text = mediumText, LineBreakMode = LineBreakMode.WordWrap, WidthRequest = 200 }); + row0s.Children.Add (new Label { Text = longText, LineBreakMode = LineBreakMode.WordWrap, WidthRequest = 200 }); rows.Children.Add (new ScrollView { Content = row0s }); - var row1s = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.GhostWhite }; - row1s.Children.Add (new Label { Text = shortText, FontAttributes = FontAttributes.Bold, WidthRequest = 100, HorizontalOptions = LayoutOptions.Start }); - row1s.Children.Add (new Label { Text = mediumText, FontAttributes = FontAttributes.Bold, WidthRequest = 100, HorizontalOptions = LayoutOptions.FillAndExpand }); - row1s.Children.Add (new Label { Text = longText, FontAttributes = FontAttributes.Bold, WidthRequest = 100, HorizontalOptions = LayoutOptions.End }); - rows.Children.Add (new ScrollView { Content = row1s }); - var row2 = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.Azure }; row2.Children.Add (new Entry { Text = shortText, FontSize = 8, VerticalOptions = LayoutOptions.Center }); row2.Children.Add (new Entry { Text = shortText, FontSize = 16, VerticalOptions = LayoutOptions.Center }); @@ -66,6 +60,18 @@ namespace Samples row6.Children.Add (new TimePicker { VerticalOptions = LayoutOptions.Center }); rows.Children.Add (row6); + var row7 = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.Azure }; + row7.Children.Add (new Label { Text = shortText, FontSize = 32, LineBreakMode = LineBreakMode.WordWrap }); + row7.Children.Add (new Label { Text = mediumText, FontSize = 16, LineBreakMode = LineBreakMode.WordWrap }); + row7.Children.Add (new Label { Text = longText, FontSize = 8, LineBreakMode = LineBreakMode.WordWrap }); + rows.Children.Add (row7); + + var row8 = new StackLayout { Orientation = StackOrientation.Horizontal, BackgroundColor = Color.GhostWhite }; + row8.Children.Add (new Label { Text = shortText, FontSize = 32, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Start }); + row8.Children.Add (new Label { Text = mediumText, FontSize = 16, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.FillAndExpand }); + row8.Children.Add (new Label { Text = longText, FontSize = 8, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.End }); + rows.Children.Add (row8); + var page = new ContentPage { Content = rows