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

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

@ -13,6 +13,7 @@ using System.Windows.Media; @@ -13,6 +13,7 @@ using System.Windows.Media;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Editor;
@ -85,11 +86,12 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -85,11 +86,12 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (!bookmarkDict.ContainsKey(line))
bookmarkDict.Add(line, bm);
}
Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
foreach (VisualLine line in textView.VisualLines) {
int lineNumber = line.FirstDocumentLine.LineNumber;
IBookmark 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);
}
}

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

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

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

@ -24,7 +24,3 @@ using System.Security.Permissions; @@ -24,7 +24,3 @@ using System.Security.Permissions;
#endif
[assembly: AssemblyTrademark("")]
[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 @@ -79,7 +79,7 @@ namespace ICSharpCode.AvalonEdit.Editing
caretRectangle.Y - textView.VerticalOffset,
caretRectangle.Width,
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 @@ -42,6 +42,7 @@ namespace ICSharpCode.AvalonEdit.Editing
base.OnRender(drawingContext);
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToMiddleOfPixels = true;
geoBuilder.CornerRadius = textArea.SelectionCornerRadius;
foreach (var segment in textArea.Selection.Segments) {
geoBuilder.AddSegment(textView, segment);

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

@ -38,19 +38,20 @@ namespace ICSharpCode.AvalonEdit.Folding @@ -38,19 +38,20 @@ namespace ICSharpCode.AvalonEdit.Folding
m.Measure(availableSize);
}
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/>
protected override Size ArrangeOverride(Size finalSize)
{
Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
foreach (FoldingMarginMarker m in markers) {
int visualColumn = m.VisualLine.GetVisualColumn(m.FoldingSection.StartOffset - m.VisualLine.FirstDocumentLine.Offset);
TextLine textLine = m.VisualLine.GetTextLine(visualColumn);
double yPos = m.VisualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop) - TextView.VerticalOffset;
yPos += (textLine.Height - m.DesiredSize.Height) / 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);
}
@ -211,19 +212,19 @@ namespace ICSharpCode.AvalonEdit.Folding @@ -211,19 +212,19 @@ namespace ICSharpCode.AvalonEdit.Folding
// 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
// middle of a pixel. (and the other way round for horizontal lines)
Size pixelSize = PixelSnapHelpers.GetPixelSize();
double markerXPos = PixelSnapHelpers.PixelAlign(RenderSize.Width / 2);
Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
double markerXPos = PixelSnapHelpers.PixelAlign(RenderSize.Width / 2, pixelSize.Width);
double startY = 0;
Pen currentPen = colors[0];
int tlNumber = 0;
foreach (VisualLine vl in TextView.VisualLines) {
foreach (TextLine tl in vl.TextLines) {
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));
}
if (colors[tlNumber + 1] != currentPen) {
double visualPos = GetVisualPos(vl, tl);
double visualPos = GetVisualPos(vl, tl, pixelSize.Height);
if (currentPen != null) {
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 @@ -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;
return PixelSnapHelpers.PixelAlign(pos);
return PixelSnapHelpers.PixelAlign(pos, pixelHeight);
}
int GetTextLineIndexFromOffset(List<TextLine> textLines, int offset)

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

@ -51,7 +51,7 @@ namespace ICSharpCode.AvalonEdit.Folding @@ -51,7 +51,7 @@ namespace ICSharpCode.AvalonEdit.Folding
protected override Size MeasureCore(Size availableSize)
{
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);
}
@ -60,7 +60,7 @@ namespace ICSharpCode.AvalonEdit.Folding @@ -60,7 +60,7 @@ namespace ICSharpCode.AvalonEdit.Folding
Pen blackPen = new Pen(Brushes.Black, 1);
blackPen.StartLineCap = PenLineCap.Square;
blackPen.EndLineCap = PenLineCap.Square;
Size pixelSize = PixelSnapHelpers.GetPixelSize();
Size pixelSize = PixelSnapHelpers.GetPixelSize(this);
Rect rect = new Rect(pixelSize.Width / 2,
pixelSize.Height / 2,
this.RenderSize.Width - pixelSize.Width,
@ -70,7 +70,7 @@ namespace ICSharpCode.AvalonEdit.Folding @@ -70,7 +70,7 @@ namespace ICSharpCode.AvalonEdit.Folding
rect);
double middleX = rect.Left + rect.Width / 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,
new Point(rect.Left + 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 @@ -32,6 +32,16 @@ namespace ICSharpCode.AvalonEdit.Rendering
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>
/// Creates a new BackgroundGeometryBuilder instance.
/// </summary>
@ -44,9 +54,23 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -44,9 +54,23 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </summary>
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)) {
Rect roundedRect = PixelSnapHelpers.PixelAlign(r);
AddRectangle(roundedRect.Left, roundedRect.Top, roundedRect.Right, roundedRect.Bottom);
if (AlignToWholePixels) {
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 @@ -193,6 +193,7 @@ namespace ICSharpCode.AvalonEdit.Snippets
ISegment s = element.Segment;
if (s != null) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToMiddleOfPixels = true;
if (Layer == KnownLayer.Background) {
geoBuilder.AddSegment(textView, s);
drawingContext.DrawGeometry(backgroundBrush, null, geoBuilder.CreateGeometry());

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

@ -7,77 +7,83 @@ @@ -7,77 +7,83 @@
using System;
using System.Windows;
using System.Windows.Media;
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>
/// Aligns val on the next middle of a pixel.
/// </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.1 -> 0.5
// 0.5 -> 0.5
// 0.9 -> 0.5
// 1 -> 1.5
return Math.Round(val + 0.5) - 0.5;
return pixelSize * (Math.Round((val / pixelSize) + 0.5) - 0.5);
}
/// <summary>
/// Aligns the borders of rect on the middles of pixels.
/// </summary>
public static Rect PixelAlign(Rect rect)
public static Rect PixelAlign(Rect rect, Size pixelSize)
{
rect.X = PixelAlign(rect.X);
rect.Y = PixelAlign(rect.Y);
rect.Width = Round(rect.Width);
rect.Height = Round(rect.Height);
rect.X = PixelAlign(rect.X, pixelSize.Width);
rect.Y = PixelAlign(rect.Y, pixelSize.Height);
rect.Width = Round(rect.Width, pixelSize.Width);
rect.Height = Round(rect.Height, pixelSize.Height);
return rect;
}
public static Point Round(Point val)
{
return new Point(Round(val.X), Round(val.Y));
}
public static Rect Round(Rect rect)
/// <summary>
/// Rounds val to whole number of pixels.
/// </summary>
public static Point Round(Point val, Size pixelSize)
{
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>
/// Rounds val to a whole number of pixels.
/// Rounds val to whole number of pixels.
/// </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>
/// Rounds val to an even number of pixels.
/// Rounds val to a whole number of pixels.
/// </summary>
public static double RoundToEven(double val)
public static double Round(double val, double pixelSize)
{
// 0 -> 0
// 1 -> 2
// 2 -> 2
// 3 -> 4
return Math.Round(val / 2) * 2;
return pixelSize * Math.Round(val / pixelSize);
}
/// <summary>
/// Rounds val to an odd number of pixels.
/// Rounds val to an whole odd number of pixels.
/// </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 @@ -30,6 +30,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary>
/// 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.
/// </summary>
public static IProject CurrentProject {

Loading…
Cancel
Save