Browse Source

Improved AvalonEdit pixel snapping.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5234 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Daniel Grunwald 16 years ago
parent
commit
24358ad21e
  1. 1
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/BracketHighlightRenderer.cs
  2. 4
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs
  3. 1
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
  4. 4
      src/AddIns/Misc/ResourceToolkit/Project/Configuration/AssemblyInfo.cs
  5. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretLayer.cs
  6. 1
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionLayer.cs
  7. 17
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMargin.cs
  8. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMarginMarker.cs
  9. 28
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs
  10. 1
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetReplaceableTextElement.cs
  11. 66
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/PixelSnapHelpers.cs
  12. 1
      src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs

1
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/BracketHighlightRenderer.cs

@ -59,6 +59,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder(); BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder();
builder.CornerRadius = 1; builder.CornerRadius = 1;
builder.AlignToWholePixels = true;
builder.AddSegment(textView, new TextSegment() { StartOffset = result.OpeningBracketOffset, Length = result.OpeningBracketLength }); builder.AddSegment(textView, new TextSegment() { StartOffset = result.OpeningBracketOffset, Length = result.OpeningBracketLength });
builder.CloseFigure(); // prevent connecting the two segments builder.CloseFigure(); // prevent connecting the two segments

4
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs

@ -13,6 +13,7 @@ using System.Windows.Media;
using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.SharpDevelop.Bookmarks; using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
@ -85,11 +86,12 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (!bookmarkDict.ContainsKey(line)) if (!bookmarkDict.ContainsKey(line))
bookmarkDict.Add(line, bm); bookmarkDict.Add(line, bm);
} }
Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
foreach (VisualLine line in textView.VisualLines) { foreach (VisualLine line in textView.VisualLines) {
int lineNumber = line.FirstDocumentLine.LineNumber; int lineNumber = line.FirstDocumentLine.LineNumber;
IBookmark bm; IBookmark bm;
if (bookmarkDict.TryGetValue(lineNumber, out bm)) { if (bookmarkDict.TryGetValue(lineNumber, out bm)) {
Rect rect = new Rect(0, line.VisualTop - textView.VerticalOffset, 16, 16); Rect rect = new Rect(0, PixelSnapHelpers.Round(line.VisualTop - textView.VerticalOffset, pixelSize.Height), 16, 16);
drawingContext.DrawImage((bm.Image ?? BookmarkBase.DefaultBookmarkImage).ImageSource, rect); drawingContext.DrawImage((bm.Image ?? BookmarkBase.DefaultBookmarkImage).ImageSource, rect);
} }
} }

1
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs

@ -155,6 +155,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart)) { foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
if (marker.BackgroundColor != null) { if (marker.BackgroundColor != null) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder(); BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.CornerRadius = 3; geoBuilder.CornerRadius = 3;
geoBuilder.AddSegment(textView, marker); geoBuilder.AddSegment(textView, marker);
Geometry geometry = geoBuilder.CreateGeometry(); Geometry geometry = geoBuilder.CreateGeometry();

4
src/AddIns/Misc/ResourceToolkit/Project/Configuration/AssemblyInfo.cs

@ -24,7 +24,3 @@ using System.Security.Permissions;
#endif #endif
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution=true)]
[assembly: FileIOPermission(SecurityAction.RequestMinimum, AllLocalFiles=FileIOPermissionAccess.AllAccess)]
[assembly: UIPermission(SecurityAction.RequestMinimum, Clipboard=UIPermissionClipboard.OwnClipboard, Window=UIPermissionWindow.AllWindows)]

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

@ -79,7 +79,7 @@ namespace ICSharpCode.AvalonEdit.Editing
caretRectangle.Y - textView.VerticalOffset, caretRectangle.Y - textView.VerticalOffset,
caretRectangle.Width, caretRectangle.Width,
caretRectangle.Height); caretRectangle.Height);
drawingContext.DrawRectangle(caretBrush, null, PixelSnapHelpers.Round(r)); drawingContext.DrawRectangle(caretBrush, null, PixelSnapHelpers.Round(r, PixelSnapHelpers.GetPixelSize(this)));
} }
} }
} }

1
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SelectionLayer.cs

@ -42,6 +42,7 @@ namespace ICSharpCode.AvalonEdit.Editing
base.OnRender(drawingContext); base.OnRender(drawingContext);
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder(); BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToMiddleOfPixels = true;
geoBuilder.CornerRadius = textArea.SelectionCornerRadius; geoBuilder.CornerRadius = textArea.SelectionCornerRadius;
foreach (var segment in textArea.Selection.Segments) { foreach (var segment in textArea.Selection.Segments) {
geoBuilder.AddSegment(textView, segment); geoBuilder.AddSegment(textView, segment);

17
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMargin.cs

@ -38,19 +38,20 @@ namespace ICSharpCode.AvalonEdit.Folding
m.Measure(availableSize); m.Measure(availableSize);
} }
double width = SizeFactor * (double)GetValue(TextBlock.FontSizeProperty); double width = SizeFactor * (double)GetValue(TextBlock.FontSizeProperty);
return new Size(PixelSnapHelpers.RoundToOdd(width), 0); return new Size(PixelSnapHelpers.RoundToOdd(width, PixelSnapHelpers.GetPixelSize(this).Width), 0);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
foreach (FoldingMarginMarker m in markers) { foreach (FoldingMarginMarker m in markers) {
int visualColumn = m.VisualLine.GetVisualColumn(m.FoldingSection.StartOffset - m.VisualLine.FirstDocumentLine.Offset); int visualColumn = m.VisualLine.GetVisualColumn(m.FoldingSection.StartOffset - m.VisualLine.FirstDocumentLine.Offset);
TextLine textLine = m.VisualLine.GetTextLine(visualColumn); TextLine textLine = m.VisualLine.GetTextLine(visualColumn);
double yPos = m.VisualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop) - TextView.VerticalOffset; double yPos = m.VisualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop) - TextView.VerticalOffset;
yPos += (textLine.Height - m.DesiredSize.Height) / 2; yPos += (textLine.Height - m.DesiredSize.Height) / 2;
double xPos = (finalSize.Width - m.DesiredSize.Width) / 2; double xPos = (finalSize.Width - m.DesiredSize.Width) / 2;
m.Arrange(new Rect(PixelSnapHelpers.Round(new Point(xPos, yPos)), m.DesiredSize)); m.Arrange(new Rect(PixelSnapHelpers.Round(new Point(xPos, yPos), pixelSize), m.DesiredSize));
} }
return base.ArrangeOverride(finalSize); return base.ArrangeOverride(finalSize);
} }
@ -211,19 +212,19 @@ namespace ICSharpCode.AvalonEdit.Folding
// Because we are using PenLineCap.Flat (the default), for vertical lines, // Because we are using PenLineCap.Flat (the default), for vertical lines,
// Y coordinates must be on pixel boundaries, whereas the X coordinate must be in the // Y coordinates must be on pixel boundaries, whereas the X coordinate must be in the
// middle of a pixel. (and the other way round for horizontal lines) // middle of a pixel. (and the other way round for horizontal lines)
Size pixelSize = PixelSnapHelpers.GetPixelSize(); Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
double markerXPos = PixelSnapHelpers.PixelAlign(RenderSize.Width / 2); double markerXPos = PixelSnapHelpers.PixelAlign(RenderSize.Width / 2, pixelSize.Width);
double startY = 0; double startY = 0;
Pen currentPen = colors[0]; Pen currentPen = colors[0];
int tlNumber = 0; int tlNumber = 0;
foreach (VisualLine vl in TextView.VisualLines) { foreach (VisualLine vl in TextView.VisualLines) {
foreach (TextLine tl in vl.TextLines) { foreach (TextLine tl in vl.TextLines) {
if (endMarker[tlNumber] != null) { if (endMarker[tlNumber] != null) {
double visualPos = GetVisualPos(vl, tl); double visualPos = GetVisualPos(vl, tl, pixelSize.Height);
drawingContext.DrawLine(endMarker[tlNumber], new Point(markerXPos - pixelSize.Width / 2, visualPos), new Point(RenderSize.Width, visualPos)); drawingContext.DrawLine(endMarker[tlNumber], new Point(markerXPos - pixelSize.Width / 2, visualPos), new Point(RenderSize.Width, visualPos));
} }
if (colors[tlNumber + 1] != currentPen) { if (colors[tlNumber + 1] != currentPen) {
double visualPos = GetVisualPos(vl, tl); double visualPos = GetVisualPos(vl, tl, pixelSize.Height);
if (currentPen != null) { if (currentPen != null) {
drawingContext.DrawLine(currentPen, new Point(markerXPos, startY + pixelSize.Height / 2), new Point(markerXPos, visualPos - pixelSize.Height / 2)); drawingContext.DrawLine(currentPen, new Point(markerXPos, startY + pixelSize.Height / 2), new Point(markerXPos, visualPos - pixelSize.Height / 2));
} }
@ -238,10 +239,10 @@ namespace ICSharpCode.AvalonEdit.Folding
} }
} }
double GetVisualPos(VisualLine vl, TextLine tl) double GetVisualPos(VisualLine vl, TextLine tl, double pixelHeight)
{ {
double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) + tl.Height / 2 - TextView.VerticalOffset; double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) + tl.Height / 2 - TextView.VerticalOffset;
return PixelSnapHelpers.PixelAlign(pos); return PixelSnapHelpers.PixelAlign(pos, pixelHeight);
} }
int GetTextLineIndexFromOffset(List<TextLine> textLines, int offset) int GetTextLineIndexFromOffset(List<TextLine> textLines, int offset)

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMarginMarker.cs

@ -51,7 +51,7 @@ namespace ICSharpCode.AvalonEdit.Folding
protected override Size MeasureCore(Size availableSize) protected override Size MeasureCore(Size availableSize)
{ {
double size = MarginSizeFactor * FoldingMargin.SizeFactor * (double)GetValue(TextBlock.FontSizeProperty); double size = MarginSizeFactor * FoldingMargin.SizeFactor * (double)GetValue(TextBlock.FontSizeProperty);
size = PixelSnapHelpers.RoundToOdd(size); size = PixelSnapHelpers.RoundToOdd(size, PixelSnapHelpers.GetPixelSize(this).Width);
return new Size(size, size); return new Size(size, size);
} }
@ -60,7 +60,7 @@ namespace ICSharpCode.AvalonEdit.Folding
Pen blackPen = new Pen(Brushes.Black, 1); Pen blackPen = new Pen(Brushes.Black, 1);
blackPen.StartLineCap = PenLineCap.Square; blackPen.StartLineCap = PenLineCap.Square;
blackPen.EndLineCap = PenLineCap.Square; blackPen.EndLineCap = PenLineCap.Square;
Size pixelSize = PixelSnapHelpers.GetPixelSize(); Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
Rect rect = new Rect(pixelSize.Width / 2, Rect rect = new Rect(pixelSize.Width / 2,
pixelSize.Height / 2, pixelSize.Height / 2,
this.RenderSize.Width - pixelSize.Width, this.RenderSize.Width - pixelSize.Width,
@ -70,7 +70,7 @@ namespace ICSharpCode.AvalonEdit.Folding
rect); rect);
double middleX = rect.Left + rect.Width / 2; double middleX = rect.Left + rect.Width / 2;
double middleY = rect.Top + rect.Height / 2; double middleY = rect.Top + rect.Height / 2;
double space = PixelSnapHelpers.Round(rect.Width / 8) + pixelSize.Width; double space = PixelSnapHelpers.Round(rect.Width / 8, pixelSize.Width) + pixelSize.Width;
drawingContext.DrawLine(blackPen, drawingContext.DrawLine(blackPen,
new Point(rect.Left + space, middleY), new Point(rect.Left + space, middleY),
new Point(rect.Right - space, middleY)); new Point(rect.Right - space, middleY));

28
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs

@ -32,6 +32,16 @@ namespace ICSharpCode.AvalonEdit.Rendering
set { cornerRadius = value; } set { cornerRadius = value; }
} }
/// <summary>
/// Gets/Sets whether to align the geometry to whole pixels.
/// </summary>
public bool AlignToWholePixels { get; set; }
/// <summary>
/// Gets/Sets whether to align the geometry to the middle of pixels.
/// </summary>
public bool AlignToMiddleOfPixels { get; set; }
/// <summary> /// <summary>
/// Creates a new BackgroundGeometryBuilder instance. /// Creates a new BackgroundGeometryBuilder instance.
/// </summary> /// </summary>
@ -44,9 +54,23 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </summary> /// </summary>
public void AddSegment(TextView textView, ISegment segment) public void AddSegment(TextView textView, ISegment segment)
{ {
if (textView == null)
throw new ArgumentNullException("textView");
Size pixelSize = PixelSnapHelpers.GetPixelSize(textView);
foreach (Rect r in GetRectsForSegment(textView, segment)) { foreach (Rect r in GetRectsForSegment(textView, segment)) {
Rect roundedRect = PixelSnapHelpers.PixelAlign(r); if (AlignToWholePixels) {
AddRectangle(roundedRect.Left, roundedRect.Top, roundedRect.Right, roundedRect.Bottom); AddRectangle(PixelSnapHelpers.Round(r.Left, pixelSize.Width),
PixelSnapHelpers.Round(r.Top, pixelSize.Height),
PixelSnapHelpers.Round(r.Right, pixelSize.Width),
PixelSnapHelpers.Round(r.Bottom, pixelSize.Height));
} else if (AlignToMiddleOfPixels) {
AddRectangle(PixelSnapHelpers.PixelAlign(r.Left, pixelSize.Width),
PixelSnapHelpers.PixelAlign(r.Top, pixelSize.Height),
PixelSnapHelpers.PixelAlign(r.Right, pixelSize.Width),
PixelSnapHelpers.PixelAlign(r.Bottom, pixelSize.Height));
} else {
AddRectangle(r.Left, r.Top, r.Right, r.Bottom);
}
} }
} }

1
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Snippets/SnippetReplaceableTextElement.cs

@ -193,6 +193,7 @@ namespace ICSharpCode.AvalonEdit.Snippets
ISegment s = element.Segment; ISegment s = element.Segment;
if (s != null) { if (s != null) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder(); BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToMiddleOfPixels = true;
if (Layer == KnownLayer.Background) { if (Layer == KnownLayer.Background) {
geoBuilder.AddSegment(textView, s); geoBuilder.AddSegment(textView, s);
drawingContext.DrawGeometry(backgroundBrush, null, geoBuilder.CreateGeometry()); drawingContext.DrawGeometry(backgroundBrush, null, geoBuilder.CreateGeometry());

66
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/PixelSnapHelpers.cs

@ -7,77 +7,83 @@
using System; using System;
using System.Windows; using System.Windows;
using System.Windows.Media;
namespace ICSharpCode.AvalonEdit.Utils namespace ICSharpCode.AvalonEdit.Utils
{ {
static class PixelSnapHelpers /// <summary>
/// Contains static helper methods for aligning stuff on a whole number of
/// </summary>
public static class PixelSnapHelpers
{ {
public static Size GetPixelSize() /// <summary>
/// Gets the pixel size on the screen containing visual.
/// This method does not take transforms on visual into account.
/// </summary>
public static Size GetPixelSize(Visual visual)
{ {
return new Size(1, 1); Matrix matrix = PresentationSource.FromVisual(visual).CompositionTarget.TransformFromDevice;
return new Size(matrix.M11, matrix.M22);
} }
/// <summary> /// <summary>
/// Aligns val on the next middle of a pixel. /// Aligns val on the next middle of a pixel.
/// </summary> /// </summary>
public static double PixelAlign(double val) /// <param name="val">The value that should be aligned</param>
/// <param name="pixelSize">The size of one pixel</param>
public static double PixelAlign(double val, double pixelSize)
{ {
// 0 -> 0.5 // 0 -> 0.5
// 0.1 -> 0.5 // 0.1 -> 0.5
// 0.5 -> 0.5 // 0.5 -> 0.5
// 0.9 -> 0.5 // 0.9 -> 0.5
// 1 -> 1.5 // 1 -> 1.5
return Math.Round(val + 0.5) - 0.5; return pixelSize * (Math.Round((val / pixelSize) + 0.5) - 0.5);
} }
/// <summary> /// <summary>
/// Aligns the borders of rect on the middles of pixels. /// Aligns the borders of rect on the middles of pixels.
/// </summary> /// </summary>
public static Rect PixelAlign(Rect rect) public static Rect PixelAlign(Rect rect, Size pixelSize)
{ {
rect.X = PixelAlign(rect.X); rect.X = PixelAlign(rect.X, pixelSize.Width);
rect.Y = PixelAlign(rect.Y); rect.Y = PixelAlign(rect.Y, pixelSize.Height);
rect.Width = Round(rect.Width); rect.Width = Round(rect.Width, pixelSize.Width);
rect.Height = Round(rect.Height); rect.Height = Round(rect.Height, pixelSize.Height);
return rect; return rect;
} }
public static Point Round(Point val) /// <summary>
{ /// Rounds val to whole number of pixels.
return new Point(Round(val.X), Round(val.Y)); /// </summary>
} public static Point Round(Point val, Size pixelSize)
public static Rect Round(Rect rect)
{ {
return new Rect(Round(rect.X), Round(rect.Y), Round(rect.Width), Round(rect.Height)); return new Point(Round(val.X, pixelSize.Width), Round(val.Y, pixelSize.Height));
} }
/// <summary> /// <summary>
/// Rounds val to a whole number of pixels. /// Rounds val to whole number of pixels.
/// </summary> /// </summary>
public static double Round(double val) public static Rect Round(Rect rect, Size pixelSize)
{ {
return Math.Round(val); return new Rect(Round(rect.X, pixelSize.Width), Round(rect.Y, pixelSize.Height),
Round(rect.Width, pixelSize.Width), Round(rect.Height, pixelSize.Height));
} }
/// <summary> /// <summary>
/// Rounds val to an even number of pixels. /// Rounds val to a whole number of pixels.
/// </summary> /// </summary>
public static double RoundToEven(double val) public static double Round(double val, double pixelSize)
{ {
// 0 -> 0 return pixelSize * Math.Round(val / pixelSize);
// 1 -> 2
// 2 -> 2
// 3 -> 4
return Math.Round(val / 2) * 2;
} }
/// <summary> /// <summary>
/// Rounds val to an odd number of pixels. /// Rounds val to an whole odd number of pixels.
/// </summary> /// </summary>
public static double RoundToOdd(double val) public static double RoundToOdd(double val, double pixelSize)
{ {
return RoundToEven(val - 1) + 1; return Round(val - pixelSize, pixelSize * 2) + pixelSize;
} }
} }
} }

1
src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs

@ -30,6 +30,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary> /// <summary>
/// Gets/Sets the active project. /// Gets/Sets the active project.
/// Returns null if no project is active.
/// The getter is thread-safe; the setter may only be called on the main thread. /// The getter is thread-safe; the setter may only be called on the main thread.
/// </summary> /// </summary>
public static IProject CurrentProject { public static IProject CurrentProject {

Loading…
Cancel
Save