Browse Source

Fix Windows IME

Fix TextWrapping for glyph cluster boundaries when an emergency break is taken
Remove redudant TextLayout creation for the TextBlock.ArrangeOverride
pull/10936/head
Benedikt Stebner 4 years ago
parent
commit
82c86010c1
  1. 86
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  2. 76
      src/Avalonia.Controls/TextBlock.cs
  3. 37
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

86
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@ -57,21 +57,21 @@ namespace Avalonia.Media.TextFormatting
switch (paragraphProperties.TextWrapping) switch (paragraphProperties.TextWrapping)
{ {
case TextWrapping.NoWrap: case TextWrapping.NoWrap:
{ {
var textLine = new TextLineImpl(shapedTextRuns.ToArray(), firstTextSourceIndex, var textLine = new TextLineImpl(shapedTextRuns.ToArray(), firstTextSourceIndex,
textSourceLength, textSourceLength,
paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak); paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
textLine.FinalizeLine(); textLine.FinalizeLine();
return textLine; return textLine;
} }
case TextWrapping.WrapWithOverflow: case TextWrapping.WrapWithOverflow:
case TextWrapping.Wrap: case TextWrapping.Wrap:
{ {
return PerformTextWrapping(shapedTextRuns, false, firstTextSourceIndex, paragraphWidth, return PerformTextWrapping(shapedTextRuns, false, firstTextSourceIndex, paragraphWidth,
paragraphProperties, resolvedFlowDirection, nextLineBreak, objectPool); paragraphProperties, resolvedFlowDirection, nextLineBreak, objectPool);
} }
default: default:
throw new ArgumentOutOfRangeException(nameof(paragraphProperties.TextWrapping)); throw new ArgumentOutOfRangeException(nameof(paragraphProperties.TextWrapping));
} }
@ -568,9 +568,9 @@ namespace Avalonia.Media.TextFormatting
return false; return false;
} }
private static bool TryMeasureLength(IReadOnlyList<TextRun> textRuns, double paragraphWidth, out int measuredLength) private static int MeasureLength(IReadOnlyList<TextRun> textRuns, double paragraphWidth)
{ {
measuredLength = 0; var measuredLength = 0;
var currentWidth = 0.0; var currentWidth = 0.0;
for (var i = 0; i < textRuns.Count; ++i) for (var i = 0; i < textRuns.Count; ++i)
@ -583,25 +583,59 @@ namespace Avalonia.Media.TextFormatting
{ {
if (shapedTextCharacters.ShapedBuffer.Length > 0) if (shapedTextCharacters.ShapedBuffer.Length > 0)
{ {
var firstCluster = shapedTextCharacters.ShapedBuffer[0].GlyphCluster; var runLength = 0;
var lastCluster = firstCluster;
for (var j = 0; j < shapedTextCharacters.ShapedBuffer.Length; j++) for (var j = 0; j < shapedTextCharacters.ShapedBuffer.Length; j++)
{ {
var glyphInfo = shapedTextCharacters.ShapedBuffer[j]; var currentInfo = shapedTextCharacters.ShapedBuffer[j];
if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth) var clusterWidth = currentInfo.GlyphAdvance;
GlyphInfo nextInfo = default;
while (j + 1 < shapedTextCharacters.ShapedBuffer.Length)
{ {
measuredLength += Math.Max(0, lastCluster - firstCluster); nextInfo = shapedTextCharacters.ShapedBuffer[j + 1];
if (currentInfo.GlyphCluster == nextInfo.GlyphCluster)
{
clusterWidth += nextInfo.GlyphAdvance;
j++;
continue;
}
return measuredLength != 0; break;
}
var clusterLength = Math.Max(0, nextInfo.GlyphCluster - currentInfo.GlyphCluster);
if(clusterLength == 0)
{
clusterLength = currentRun.Length - runLength;
}
if(clusterLength == 0)
{
clusterLength = shapedTextCharacters.GlyphRun.Metrics.FirstCluster + currentRun.Length - currentInfo.GlyphCluster;
}
if (currentWidth + clusterWidth > paragraphWidth)
{
if (runLength == 0 && measuredLength == 0)
{
runLength = clusterLength;
}
return measuredLength + runLength;
} }
lastCluster = glyphInfo.GlyphCluster; currentWidth += clusterWidth;
currentWidth += glyphInfo.GlyphAdvance; runLength += clusterLength;
} }
measuredLength += currentRun.Length; measuredLength += runLength;
} }
break; break;
@ -611,7 +645,7 @@ namespace Avalonia.Media.TextFormatting
{ {
if (currentWidth + drawableTextRun.Size.Width >= paragraphWidth) if (currentWidth + drawableTextRun.Size.Width >= paragraphWidth)
{ {
return measuredLength != 0; return measuredLength;
} }
measuredLength += currentRun.Length; measuredLength += currentRun.Length;
@ -628,7 +662,7 @@ namespace Avalonia.Media.TextFormatting
} }
} }
return measuredLength != 0; return measuredLength;
} }
/// <summary> /// <summary>
@ -675,9 +709,11 @@ namespace Avalonia.Media.TextFormatting
return CreateEmptyTextLine(firstTextSourceIndex, paragraphWidth, paragraphProperties); return CreateEmptyTextLine(firstTextSourceIndex, paragraphWidth, paragraphProperties);
} }
if (!TryMeasureLength(textRuns, paragraphWidth, out var measuredLength)) var measuredLength = MeasureLength(textRuns, paragraphWidth);
if(measuredLength == 0)
{ {
measuredLength = 1;
} }
var currentLength = 0; var currentLength = 0;

76
src/Avalonia.Controls/TextBlock.cs

@ -663,7 +663,6 @@ namespace Avalonia.Controls
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
_constraint = availableSize.Deflate(padding); _constraint = availableSize.Deflate(padding);
_textLayout?.Dispose(); _textLayout?.Dispose();
_textLayout = null; _textLayout = null;
@ -690,18 +689,18 @@ namespace Avalonia.Controls
inline.BuildTextRun(textRuns); inline.BuildTextRun(textRuns);
} }
foreach (var textRun in textRuns) _textRuns = textRuns;
foreach (var textRun in _textRuns)
{ {
if (textRun is EmbeddedControlRun controlRun && if (textRun is EmbeddedControlRun controlRun &&
controlRun.Control is Control control) controlRun.Control is Control control)
{ {
VisualChildren.Add(control); VisualChildren.Add(control);
control.Measure(Size.Infinity); control.Measure(Size.Infinity);
} }
} }
_textRuns = textRuns;
} }
var measuredSize = TextLayout.Bounds.Size.Inflate(padding); var measuredSize = TextLayout.Bounds.Size.Inflate(padding);
@ -711,64 +710,39 @@ namespace Avalonia.Controls
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
var textWidth = Math.Ceiling(TextLayout.Bounds.Width);
if (finalSize.Width < textWidth)
{
finalSize = finalSize.WithWidth(textWidth);
}
var scale = LayoutHelper.GetLayoutScale(this);
var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
if (HasComplexContent) if (HasComplexContent)
{ {
ArrangeComplexContent(TextLayout, padding); var scale = LayoutHelper.GetLayoutScale(this);
}
if (MathUtilities.AreClose(_constraint.Inflate(padding).Width, finalSize.Width)) var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale);
{
return finalSize;
}
_constraint = new Size(Math.Ceiling(finalSize.Deflate(padding).Width), double.PositiveInfinity); var currentY = padding.Top;
_textLayout?.Dispose(); foreach (var textLine in TextLayout.TextLines)
_textLayout = null;
if (HasComplexContent)
{
ArrangeComplexContent(TextLayout, padding);
}
return finalSize;
}
private static void ArrangeComplexContent(TextLayout textLayout, Thickness padding)
{
var currentY = padding.Top;
foreach (var textLine in textLayout.TextLines)
{
var currentX = padding.Left + textLine.Start;
foreach (var run in textLine.TextRuns)
{ {
if (run is DrawableTextRun drawable) var currentX = padding.Left + textLine.Start;
foreach (var run in textLine.TextRuns)
{ {
if (drawable is EmbeddedControlRun controlRun if (run is DrawableTextRun drawable)
&& controlRun.Control is Control control)
{ {
control.Arrange(new Rect(new Point(currentX, currentY), control.DesiredSize)); if (drawable is EmbeddedControlRun controlRun
&& controlRun.Control is Control control)
{
control.Arrange(
new Rect(new Point(currentX, currentY),
new Size(control.DesiredSize.Width, textLine.Height)));
}
currentX += drawable.Size.Width;
} }
currentX += drawable.Size.Width;
} }
}
currentY += textLine.Height; currentY += textLine.Height;
}
} }
return finalSize;
} }
protected override AutomationPeer OnCreateAutomationPeer() protected override AutomationPeer OnCreateAutomationPeer()

37
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -717,36 +717,25 @@ namespace Avalonia.Win32
} }
case WindowsMessage.WM_IME_COMPOSITION: case WindowsMessage.WM_IME_COMPOSITION:
{ {
var previousComposition = Imm32InputMethod.Current.Composition;
var flags = (GCS)ToInt32(lParam); var flags = (GCS)ToInt32(lParam);
var currentComposition = Imm32InputMethod.Current.GetCompositionString(GCS.GCS_COMPSTR); if ((flags & GCS.GCS_COMPSTR) != 0)
{
var currentComposition = Imm32InputMethod.Current.GetCompositionString(GCS.GCS_COMPSTR);
Imm32InputMethod.Current.CompositionChanged(currentComposition); Imm32InputMethod.Current.CompositionChanged(currentComposition);
}
switch (flags) if ((flags & GCS.GCS_RESULTSTR) != 0)
{ {
case GCS.GCS_RESULTSTR: var result = Imm32InputMethod.Current.GetCompositionString(GCS.GCS_RESULTSTR);
{
if(!string.IsNullOrEmpty(previousComposition) && ToInt32(wParam) >= 32)
{
Imm32InputMethod.Current.Composition = previousComposition;
_ignoreWmChar = true; if (!string.IsNullOrEmpty(result))
} {
break; Imm32InputMethod.Current.Composition = result;
}
case GCS.GCS_RESULTREADCLAUSE | GCS.GCS_RESULTSTR | GCS.GCS_RESULTCLAUSE: _ignoreWmChar = true;
{ }
// Chinese IME sends WM_CHAR after composition has finished.
break;
}
case GCS.GCS_RESULTREADSTR | GCS.GCS_RESULTREADCLAUSE | GCS.GCS_RESULTSTR | GCS.GCS_RESULTCLAUSE:
{
// Japanese IME sends WM_CHAR after composition has finished.
break;
}
} }
break; break;

Loading…
Cancel
Save