Browse Source

AvalonEdit: Fixed caret rectangle calculation when there are inline UI elements that extend below the baseline.

Add VisualYPosition.TextBottom and VisualYPosition.BaseLine options. Fixed VisualYPosition.TextTop calculation.
pull/18/head
Daniel Grunwald 14 years ago
parent
commit
e17d6e0482
  1. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs
  2. 62
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs
  3. 7
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs
  4. 20
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualYPosition.cs

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs

@ -351,7 +351,7 @@ namespace ICSharpCode.AvalonEdit.Editing
TextLine textLine = visualLine.GetTextLine(position.VisualColumn); TextLine textLine = visualLine.GetTextLine(position.VisualColumn);
double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn); double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn);
double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop); double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineBottom); double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
return new Rect(xPos, return new Rect(xPos,
lineTop, lineTop,

62
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

@ -115,7 +115,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (newValue != null) { if (newValue != null) {
TextDocumentWeakEventManager.Changing.AddListener(newValue, this); TextDocumentWeakEventManager.Changing.AddListener(newValue, this);
formatter = TextFormatterFactory.Create(this); formatter = TextFormatterFactory.Create(this);
heightTree = new HeightTree(newValue, DefaultLineHeight); // measuring DefaultLineHeight depends on formatter InvalidateDefaultTextMetrics(); // measuring DefaultLineHeight depends on formatter
heightTree = new HeightTree(newValue, DefaultLineHeight);
cachedElements = new TextViewCachedElements(); cachedElements = new TextViewCachedElements();
} }
InvalidateMeasure(DispatcherPriority.Normal); InvalidateMeasure(DispatcherPriority.Normal);
@ -1369,29 +1370,64 @@ namespace ICSharpCode.AvalonEdit.Rendering
OnScrollChange(); OnScrollChange();
} }
bool defaultTextMetricsValid;
double wideSpaceWidth; // Width of an 'x'. Used as basis for the tab width, and for scrolling. double wideSpaceWidth; // Width of an 'x'. Used as basis for the tab width, and for scrolling.
double defaultLineHeight; // Height of a line containing 'x'. Used for scrolling. double defaultLineHeight; // Height of a line containing 'x'. Used for scrolling.
double defaultBaseline; // Baseline of a line containing 'x'. Used for TextTop/TextBottom calculation.
internal double WideSpaceWidth { /// <summary>
/// Gets the width of a 'wide space' (the space width used for calculating the tab size).
/// </summary>
/// <remarks>
/// This is the width of an 'x' in the current font.
/// We do not measure the width of an actual space as that would lead to tiny tabs in
/// some proportional fonts.
/// For monospaced fonts, this property will return the expected value, as 'x' and ' ' have the same width.
/// </remarks>
public double WideSpaceWidth {
get { get {
if (wideSpaceWidth == 0) { CalculateDefaultTextMetrics();
MeasureWideSpaceWidthAndDefaultLineHeight();
}
return wideSpaceWidth; return wideSpaceWidth;
} }
} }
double DefaultLineHeight { /// <summary>
/// Gets the default line height. This is the height of an empty line or a line containing regular text.
/// Lines that include formatted text or custom UI elements may have a different line height.
/// </summary>
public double DefaultLineHeight {
get { get {
if (defaultLineHeight == 0) { CalculateDefaultTextMetrics();
MeasureWideSpaceWidthAndDefaultLineHeight();
}
return defaultLineHeight; return defaultLineHeight;
} }
} }
void MeasureWideSpaceWidthAndDefaultLineHeight() /// <summary>
/// Gets the default baseline position. This is the difference between <see cref="VisualYPosition.TextTop"/>
/// and <see cref="VisualYPosition.Baseline"/> for a line containing regular text.
/// Lines that include formatted text or custom UI elements may have a different baseline.
/// </summary>
public double DefaultBaseline {
get {
CalculateDefaultTextMetrics();
return defaultBaseline;
}
}
void InvalidateDefaultTextMetrics()
{ {
defaultTextMetricsValid = false;
if (heightTree != null) {
// calculate immediately so that height tree gets updated
CalculateDefaultTextMetrics();
}
}
void CalculateDefaultTextMetrics()
{
if (defaultTextMetricsValid)
return;
defaultTextMetricsValid = true;
if (formatter != null) { if (formatter != null) {
var textRunProperties = CreateGlobalTextRunProperties(); var textRunProperties = CreateGlobalTextRunProperties();
using (var line = formatter.FormatLine( using (var line = formatter.FormatLine(
@ -1401,10 +1437,12 @@ namespace ICSharpCode.AvalonEdit.Rendering
null)) null))
{ {
wideSpaceWidth = Math.Max(1, line.WidthIncludingTrailingWhitespace); wideSpaceWidth = Math.Max(1, line.WidthIncludingTrailingWhitespace);
defaultBaseline = Math.Max(1, line.Baseline);
defaultLineHeight = Math.Max(1, line.Height); defaultLineHeight = Math.Max(1, line.Height);
} }
} else { } else {
wideSpaceWidth = FontSize / 2; wideSpaceWidth = FontSize / 2;
defaultBaseline = FontSize;
defaultLineHeight = FontSize + 3; defaultLineHeight = FontSize + 3;
} }
// Update heightTree.DefaultLineHeight, if a document is loaded. // Update heightTree.DefaultLineHeight, if a document is loaded.
@ -1831,7 +1869,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
// changing text formatter requires recreating the cached elements // changing text formatter requires recreating the cached elements
RecreateCachedElements(); RecreateCachedElements();
// and we need to re-measure the font metrics: // and we need to re-measure the font metrics:
MeasureWideSpaceWidthAndDefaultLineHeight(); InvalidateDefaultTextMetrics();
} else if (e.Property == Control.ForegroundProperty } else if (e.Property == Control.ForegroundProperty
|| e.Property == TextView.NonPrintableCharacterBrushProperty) || e.Property == TextView.NonPrintableCharacterBrushProperty)
{ {
@ -1848,7 +1886,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
// changing font properties requires recreating cached elements // changing font properties requires recreating cached elements
RecreateCachedElements(); RecreateCachedElements();
// and we need to re-measure the font metrics: // and we need to re-measure the font metrics:
MeasureWideSpaceWidthAndDefaultLineHeight(); InvalidateDefaultTextMetrics();
Redraw(); Redraw();
} }
} }

7
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs

@ -1,6 +1,7 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using System; using System;
@ -268,7 +269,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
case VisualYPosition.LineBottom: case VisualYPosition.LineBottom:
return pos + tl.Height; return pos + tl.Height;
case VisualYPosition.TextTop: case VisualYPosition.TextTop:
return pos + tl.Height - textView.FontSize; return pos + tl.Baseline - textView.DefaultBaseline;
case VisualYPosition.TextBottom:
return pos + tl.Baseline - textView.DefaultBaseline + textView.DefaultLineHeight;
case VisualYPosition.Baseline:
return pos + tl.Baseline;
default: default:
throw new ArgumentException("Invalid yPositionMode:" + yPositionMode); throw new ArgumentException("Invalid yPositionMode:" + yPositionMode);
} }

20
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualYPosition.cs

@ -15,18 +15,28 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </summary> /// </summary>
LineTop, LineTop,
/// <summary> /// <summary>
/// Returns the top of the text. If the line contains inline UI elements larger than the text, TextTop /// Returns the top of the text.
/// will be below LineTop. /// If the line contains inline UI elements larger than the text, TextTop may be below LineTop.
/// For a line containing regular text (all in the editor's main font), this will be equal to LineTop.
/// </summary> /// </summary>
TextTop, TextTop,
/// <summary> /// <summary>
/// Returns the bottom of the TextLine. This is the same as the bottom of the text (the text is always /// Returns the bottom of the TextLine.
/// aligned at the bottom border).
/// </summary> /// </summary>
LineBottom, LineBottom,
/// <summary> /// <summary>
/// The middle between LineTop and LineBottom. /// The middle between LineTop and LineBottom.
/// </summary> /// </summary>
LineMiddle LineMiddle,
/// <summary>
/// Returns the bottom of the text.
/// If the line contains inline UI elements larger than the text, TextBottom might be above LineBottom.
/// For a line containing regular text (all in the editor's main font), this will be equal to LineBottom.
/// </summary>
TextBottom,
/// <summary>
/// Returns the baseline of the text.
/// </summary>
Baseline
} }
} }

Loading…
Cancel
Save