Browse Source

AvalonEdit: add support for layers (gives users more control about z-order than adorners).

Fixed bugs in text drag+drop.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3842 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
b4abfae690
  1. 15
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/NewLineFinder.cs
  2. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Caret.cs
  3. 13
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretLayer.cs
  4. 17
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs
  5. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingSection.cs
  6. 14
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/IBackgroundRenderer.cs
  7. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/InlineObjectRun.cs
  8. 48
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Layer.cs
  9. 95
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LayerPosition.cs
  10. 23
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionColorizer.cs
  11. 51
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionLayer.cs
  12. 30
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs
  13. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs
  14. 107
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextLayer.cs
  15. 182
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs
  16. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLineTextSource.cs
  17. 14
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

15
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/NewLineFinder.cs

@ -67,5 +67,20 @@ namespace ICSharpCode.AvalonEdit.Document @@ -67,5 +67,20 @@ namespace ICSharpCode.AvalonEdit.Document
b.Append(input, lastEndOffset, input.Length - lastEndOffset);
return b.ToString();
}
/// <summary>
/// Gets the newline sequence used in the document at the specified line.
/// </summary>
public static string GetNewLineFromDocument(TextDocument document, int lineNumber)
{
DocumentLine line = document.GetLineByNumber(lineNumber);
if (line.DelimiterLength == 0) {
if (lineNumber > 1)
line = document.GetLineByNumber(lineNumber - 1);
else
return Environment.NewLine;
}
return document.GetText(line.Offset + line.Length, line.DelimiterLength);
}
}
}

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Caret.cs

@ -23,7 +23,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -23,7 +23,7 @@ namespace ICSharpCode.AvalonEdit.Gui
{
readonly TextArea textArea;
readonly TextView textView;
CaretAdorner caretAdorner;
CaretLayer caretAdorner;
bool visible;
internal Caret(TextArea textArea)
@ -32,8 +32,8 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -32,8 +32,8 @@ namespace ICSharpCode.AvalonEdit.Gui
this.textView = textArea.TextView;
position = new TextViewPosition(1, 1, 0);
caretAdorner = new CaretAdorner(textView);
textView.Adorners.Add(caretAdorner);
caretAdorner = new CaretLayer(textView);
textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace);
textView.VisualLinesChanged += TextView_VisualLinesChanged;
textView.ScrollOffsetChanged += TextView_ScrollOffsetChanged;
}

13
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretAdorner.cs → src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretLayer.cs

@ -14,19 +14,15 @@ using ICSharpCode.AvalonEdit.Utils; @@ -14,19 +14,15 @@ using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Gui
{
sealed class CaretAdorner : FrameworkElement
sealed class CaretLayer : Layer
{
TextView textView;
bool isVisible;
Rect caretRectangle;
DoubleAnimationUsingKeyFrames blinkAnimation;
public CaretAdorner(TextView textView)
public CaretLayer(TextView textView) : base(textView, KnownLayer.Caret)
{
this.textView = textView;
this.IsHitTestVisible = false;
blinkAnimation = new DoubleAnimationUsingKeyFrames();
blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(1, KeyTime.FromPercent(0)));
blinkAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame(0, KeyTime.FromPercent(0.5)));
@ -66,11 +62,6 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -66,11 +62,6 @@ namespace ICSharpCode.AvalonEdit.Gui
BeginAnimation(OpacityProperty, null);
}
protected override Size MeasureOverride(Size constraint)
{
return caretRectangle.Size;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);

17
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/EditingCommandHandler.cs

@ -55,25 +55,12 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -55,25 +55,12 @@ namespace ICSharpCode.AvalonEdit.Gui
{
TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) {
string newLine = GetLineDelimiter(textArea.Document, textArea.Caret.Line);
string newLine = NewLineFinder.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
textArea.ReplaceSelectionWithText(newLine);
textArea.Caret.BringCaretToView();
args.Handled = true;
}
}
static string GetLineDelimiter(TextDocument document, int lineNumber)
{
DocumentLine line = document.GetLineByNumber(lineNumber);
if (line.DelimiterLength == 0) {
// TODO: add line delimiter setting
if (lineNumber > 1)
line = document.GetLineByNumber(lineNumber - 1);
else
return Environment.NewLine;
}
return document.GetText(line.Offset + line.Length, line.DelimiterLength);
}
#endregion
#region Tab
@ -234,7 +221,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -234,7 +221,7 @@ namespace ICSharpCode.AvalonEdit.Gui
TextArea textArea = GetTextArea(target);
if (textArea != null && textArea.Document != null) {
// convert text back to correct newlines for this document
string newLine = GetLineDelimiter(textArea.Document, textArea.Caret.Line);
string newLine = NewLineFinder.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
string text = NewLineFinder.NormalizeNewLines(Clipboard.GetText(), newLine);
textArea.ReplaceSelectionWithText(text);
textArea.Caret.BringCaretToView();

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingSection.cs

@ -6,8 +6,8 @@ @@ -6,8 +6,8 @@
// </file>
using System;
using ICSharpCode.AvalonEdit.Document;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.AvalonEdit.Gui
{

14
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/IBackgroundRenderer.cs

@ -11,13 +11,23 @@ using System.Windows.Media; @@ -11,13 +11,23 @@ using System.Windows.Media;
namespace ICSharpCode.AvalonEdit.Gui
{
/// <summary>
/// Background renderers allow drawing behind the text layer.
/// Background renderers draw in the background of a known layer.
/// You can use background renderers to draw non-interactive elements on the TextView
/// without introducing new UIElements.
/// </summary>
/// <remarks>Background renderer will draw only if their associated known
/// layer chooses to draw them. For example, background renderers in the caret
/// layer will be invisible when the caret is hidden.</remarks>
public interface IBackgroundRenderer
{
/// <summary>
/// Gets the layer on which this background renderer should draw.
/// </summary>
KnownLayer Layer { get; }
/// <summary>
/// Causes the background renderer to draw.
/// </summary>
void Draw(DrawingContext dc);
void Draw(DrawingContext drawingContext);
}
}

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/InlineObjectRun.cs

@ -43,7 +43,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -43,7 +43,7 @@ namespace ICSharpCode.AvalonEdit.Gui
throw new ArgumentNullException("context");
// remove inline object if its already added, can happen e.g. when recreating textrun for word-wrapping
context.TextView.RemoveInlineObject(this.Element);
context.TextView.textLayer.RemoveInlineObject(this.Element);
return new InlineObjectRun(1, this.TextRunProperties, this.Element);
}

48
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Layer.cs

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
namespace ICSharpCode.AvalonEdit.Gui
{
/// <summary>
/// Base class for known layers.
/// </summary>
class Layer : UIElement
{
protected readonly TextView textView;
protected readonly KnownLayer knownLayer;
public Layer(TextView textView, KnownLayer knownLayer)
{
Debug.Assert(textView != null);
this.textView = textView;
this.knownLayer = knownLayer;
this.IsHitTestVisible = false;
}
protected override GeometryHitTestResult HitTestCore(GeometryHitTestParameters hitTestParameters)
{
return null;
}
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
{
return null;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
textView.RenderBackground(drawingContext, knownLayer);
}
}
}

95
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LayerPosition.cs

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Windows;
namespace ICSharpCode.AvalonEdit.Gui
{
/// <summary>
/// An enumeration of well-known layers.
/// </summary>
public enum KnownLayer
{
/// <summary>
/// This layer is in the background.
/// There is no UIElement to represent this layer, it is directly drawn in the TextView.
/// It is invalid to replace the background layer or insert
/// </summary>
/// <remarks>This layer is below the Selection layer.</remarks>
Background,
/// <summary>
/// This layer contains the selection rectangle.
/// </summary>
/// <remarks>This layer is between the Background and the Text layers.</remarks>
Selection,
/// <summary>
/// This layer contains the text and inline UI elements.
/// </summary>
/// <remarks>This layer is between the Selection and the Caret layers.</remarks>
Text,
/// <summary>
/// This layer contains the blinking caret.
/// </summary>
/// <remarks>This layer is above the Text layer.</remarks>
Caret
}
/// <summary>
/// Specifies where a new layer is inserted, in relation to an old layer.
/// </summary>
public enum LayerInsertionPosition
{
/// <summary>
/// The new layer is inserted below the specified layer.
/// </summary>
Below,
/// <summary>
/// The new layer replaces the specified layer. The old layer is removed
/// from the <see cref="TextView.Layers"/> collection.
/// </summary>
Replace,
/// <summary>
/// The new layer is inserted above the specified layer.
/// </summary>
Above
}
sealed class LayerPosition : IComparable<LayerPosition>
{
internal static readonly DependencyProperty LayerPositionProperty =
DependencyProperty.RegisterAttached("LayerPosition", typeof(LayerPosition), typeof(LayerPosition));
public static void SetLayerPosition(UIElement layer, LayerPosition value)
{
layer.SetValue(LayerPositionProperty, value);
}
public static LayerPosition GetLayerPosition(UIElement layer)
{
return (LayerPosition)layer.GetValue(LayerPositionProperty);
}
internal readonly KnownLayer KnownLayer;
internal readonly LayerInsertionPosition Position;
public LayerPosition(KnownLayer knownLayer, LayerInsertionPosition position)
{
this.KnownLayer = knownLayer;
this.Position = position;
}
public int CompareTo(LayerPosition other)
{
int r = this.KnownLayer.CompareTo(other.KnownLayer);
if (r != 0)
return r;
else
return this.Position.CompareTo(other.Position);
}
}
}

23
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionColorizer.cs

@ -6,17 +6,12 @@ @@ -6,17 +6,12 @@
// </file>
using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Gui
{
sealed class SelectionColorizer : ColorizingTransformer, IBackgroundRenderer
sealed class SelectionColorizer : ColorizingTransformer
{
TextArea textArea;
@ -49,21 +44,5 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -49,21 +44,5 @@ namespace ICSharpCode.AvalonEdit.Gui
});
}
}
public void Draw(DrawingContext dc)
{
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AddSegments(textArea.TextView, textArea.Selection.Segments);
PathGeometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
SolidColorBrush lightHighlightBrush = new SolidColorBrush(SystemColors.HighlightColor);
lightHighlightBrush.Opacity = 0.7;
lightHighlightBrush.Freeze();
Pen pen = new Pen(SystemColors.HighlightBrush, 1);
//pen.LineJoin = PenLineJoin.Round;
pen.Freeze();
dc.DrawGeometry(lightHighlightBrush, pen, geometry);
}
}
}
}

51
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionLayer.cs

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Windows;
using System.Windows.Media;
namespace ICSharpCode.AvalonEdit.Gui
{
sealed class SelectionLayer : Layer, IWeakEventListener
{
readonly TextArea textArea;
public SelectionLayer(TextArea textArea) : base(textArea.TextView, KnownLayer.Selection)
{
this.textArea = textArea;
TextViewWeakEventManager.VisualLinesChanged.AddListener(textView, this);
}
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType == typeof(TextViewWeakEventManager.VisualLinesChanged)) {
InvalidateVisual();
return true;
}
return false;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AddSegments(textView, textArea.Selection.Segments);
PathGeometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
SolidColorBrush lightHighlightBrush = new SolidColorBrush(SystemColors.HighlightColor);
lightHighlightBrush.Opacity = 0.7;
lightHighlightBrush.Freeze();
Pen pen = new Pen(SystemColors.HighlightBrush, 1);
//pen.LineJoin = PenLineJoin.Round;
pen.Freeze();
drawingContext.DrawGeometry(lightHighlightBrush, pen, geometry);
}
}
}
}

30
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/SelectionMouseHandler.cs

@ -6,15 +6,15 @@ @@ -6,15 +6,15 @@
// </file>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
using System.Windows.Threading;
namespace ICSharpCode.AvalonEdit.Gui
{
@ -107,6 +107,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -107,6 +107,7 @@ namespace ICSharpCode.AvalonEdit.Gui
{
try {
e.Effects = GetEffect(e);
textArea.Caret.Show();
} catch (Exception ex) {
OnDragException(ex);
}
@ -147,6 +148,8 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -147,6 +148,8 @@ namespace ICSharpCode.AvalonEdit.Gui
void textArea_DragLeave(object sender, DragEventArgs e)
{
e.Handled = true;
if (!textArea.IsKeyboardFocusWithin)
textArea.Caret.Hide();
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
@ -164,6 +167,10 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -164,6 +167,10 @@ namespace ICSharpCode.AvalonEdit.Gui
e.Effects = DragDropEffects.None;
} else {
Debug.WriteLine("Drop: insert at " + start);
string newLine = NewLineFinder.GetNewLineFromDocument(textArea.Document, textArea.Caret.Line);
text = NewLineFinder.NormalizeNewLines(text, newLine);
// Mark the undo group with the currentDragDescriptor, if the drag
// is originating from the same control. This allows combining
// the undo groups when text is moved.
@ -225,8 +232,10 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -225,8 +232,10 @@ namespace ICSharpCode.AvalonEdit.Gui
textArea.ReleaseMouseCapture();
string text = textArea.Selection.GetText(textArea.Document);
DataObject dataObject = new DataObject();
dataObject.SetText(text);
text = NewLineFinder.NormalizeNewLines(text, Environment.NewLine);
DataObject dataObject = new DataObject(text);
// we cannot use DataObject.SetText - then we cannot drag to SciTe
// (but dragging to Word works in both cases)
DragDropEffects allowedEffects = DragDropEffects.All;
var deleteOnMove = textArea.Selection.Segments.Select(s => new AnchorSegment(textArea.Document, s)).ToList();
@ -234,9 +243,16 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -234,9 +243,16 @@ namespace ICSharpCode.AvalonEdit.Gui
object dragDescriptor = new object();
this.currentDragDescriptor = dragDescriptor;
Debug.WriteLine("DoDragDrop with allowedEffects=" + allowedEffects);
DragDropEffects resultEffect = DragDrop.DoDragDrop(textArea, dataObject, allowedEffects);
Debug.WriteLine("DoDragDrop done, resultEffect=" + resultEffect);
DragDropEffects resultEffect;
try {
Debug.WriteLine("DoDragDrop with allowedEffects=" + allowedEffects);
resultEffect = DragDrop.DoDragDrop(textArea, dataObject, allowedEffects);
Debug.WriteLine("DoDragDrop done, resultEffect=" + resultEffect);
} catch (COMException ex) {
// ignore COM errors - don't crash on badly implemented drop targets
Debug.WriteLine("DoDragDrop failed: " + ex.ToString());
return;
}
this.currentDragDescriptor = null;

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextArea.cs

@ -72,9 +72,8 @@ namespace ICSharpCode.AvalonEdit @@ -72,9 +72,8 @@ namespace ICSharpCode.AvalonEdit
Margin = new Thickness(2, 0, 2, 0)
});
SelectionColorizer sc = new SelectionColorizer(this);
textView.LineTransformers.Add(sc);
textView.BackgroundRenderer.Add(sc);
textView.LineTransformers.Add(new SelectionColorizer(this));
textView.InsertLayer(new SelectionLayer(this), KnownLayer.Selection, LayerInsertionPosition.Replace);
caret = new Caret(this);
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, ExecuteUndo, CanExecuteUndo));

107
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextLayer.cs

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace ICSharpCode.AvalonEdit.Gui
{
/// <summary>
/// The control that contains the text.
///
/// This control is used to allow other UIElements to be placed inside the TextView but
/// behind the text.
/// The text rendering process (VisualLine creation) is controlled by the TextView, this
/// class simply displays the created Visual Lines.
/// </summary>
/// <remarks>
/// This class does not contain any input handling and is invisible to hit testing. Input
/// is handled by the TextView.
/// This allows UIElements that are displayed behind the text, but still can react to mouse input.
/// </remarks>
sealed class TextLayer : Layer
{
public TextLayer(TextView textView) : base(textView, KnownLayer.Text)
{
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
textView.RenderTextLayer(drawingContext);
}
#region Inline object handling
internal List<InlineObjectRun> inlineObjects = new List<InlineObjectRun>();
/// <summary>
/// Adds a new inline object.
/// </summary>
internal void AddInlineObject(InlineObjectRun inlineObject)
{
inlineObjects.Add(inlineObject);
AddVisualChild(inlineObject.Element);
inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
List<VisualLine> visualLinesWithOutstandingInlineObjects = new List<VisualLine>();
internal void RemoveInlineObjects(VisualLine visualLine)
{
// Delay removing inline objects:
// A document change immediately invalidates affected visual lines, but it does not
// cause an immediate redraw.
// To prevent inline objects from flickering when they are recreated, we delay removing
// inline objects until the next redraw.
visualLinesWithOutstandingInlineObjects.Add(visualLine);
}
internal void RemoveInlineObjectsNow()
{
inlineObjects.RemoveAll(
ior => {
if (visualLinesWithOutstandingInlineObjects.Contains(ior.VisualLine)) {
ior.VisualLine = null;
RemoveVisualChild(ior.Element);
return true;
}
return false;
});
visualLinesWithOutstandingInlineObjects.Clear();
}
/// <summary>
/// Removes the inline object that displays the specified UIElement.
/// </summary>
internal void RemoveInlineObject(UIElement element)
{
inlineObjects.RemoveAll(
ior => {
if (ior.Element == element) {
ior.VisualLine = null;
RemoveVisualChild(ior.Element);
return true;
}
return false;
});
}
/// <inheritdoc/>
protected override int VisualChildrenCount {
get { return inlineObjects.Count; }
}
/// <inheritdoc/>
protected override Visual GetVisualChild(int index)
{
return inlineObjects[index].Element;
}
#endregion
}
}

182
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
@ -46,10 +47,12 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -46,10 +47,12 @@ namespace ICSharpCode.AvalonEdit.Gui
/// </summary>
public TextView()
{
textLayer = new TextLayer(this);
elementGenerators.CollectionChanged += delegate { Redraw(); };
lineTransformers.CollectionChanged += delegate { Redraw(); };
backgroundRenderer.CollectionChanged += delegate { InvalidateVisual(); };
adorners = new UIElementCollection(this, this);
layers = new UIElementCollection(this, this);
InsertLayer(textLayer, KnownLayer.Text, LayerInsertionPosition.Replace);
}
#endregion
@ -122,7 +125,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -122,7 +125,7 @@ namespace ICSharpCode.AvalonEdit.Gui
}
#endregion
#region Collection Properties
#region ElementGenerators+LineTransformers Properties
readonly ObservableCollection<VisualLineElementGenerator> elementGenerators = new ObservableCollection<VisualLineElementGenerator>();
/// <summary>
@ -140,14 +143,75 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -140,14 +143,75 @@ namespace ICSharpCode.AvalonEdit.Gui
public ObservableCollection<IVisualLineTransformer> LineTransformers {
get { return lineTransformers; }
}
#endregion
readonly UIElementCollection adorners;
#region Layers
internal readonly TextLayer textLayer;
readonly UIElementCollection layers;
/// <summary>
/// Gets a collection where text view adorners can be added.
/// Gets the list of layers displayed in the text view.
/// </summary>
public UIElementCollection Adorners {
get { return adorners; }
public UIElementCollection Layers {
get { return layers; }
}
/// <summary>
/// Inserts a new layer at a position specified relative to an existing layer.
/// </summary>
/// <param name="layer">The new layer to insert.</param>
/// <param name="referencedLayer">The existing layer</param>
/// <param name="position">Specifies whether</param>
public void InsertLayer(UIElement layer, KnownLayer referencedLayer, LayerInsertionPosition position)
{
if (layer == null)
throw new ArgumentNullException("layer");
if (!Enum.IsDefined(typeof(KnownLayer), referencedLayer))
throw new InvalidEnumArgumentException("referencedLayer", (int)referencedLayer, typeof(KnownLayer));
if (!Enum.IsDefined(typeof(LayerInsertionPosition), position))
throw new InvalidEnumArgumentException("position", (int)position, typeof(LayerInsertionPosition));
if (referencedLayer == KnownLayer.Background && position != LayerInsertionPosition.Above)
throw new InvalidOperationException("Cannot replace or insert below the background layer.");
LayerPosition newPosition = new LayerPosition(referencedLayer, position);
LayerPosition.SetLayerPosition(layer, newPosition);
for (int i = 0; i < layers.Count; i++) {
LayerPosition p = LayerPosition.GetLayerPosition(layers[i]);
if (p != null) {
if (p.KnownLayer == referencedLayer && p.Position == LayerInsertionPosition.Replace) {
// found the referenced layer
switch (position) {
case LayerInsertionPosition.Below:
layers.Insert(i, layer);
return;
case LayerInsertionPosition.Above:
layers.Insert(i + 1, layer);
return;
case LayerInsertionPosition.Replace:
layers[i] = layer;
return;
}
} else if (p.KnownLayer == referencedLayer && p.Position == LayerInsertionPosition.Above
|| p.KnownLayer > referencedLayer) {
// we skipped the insertion position (referenced layer does not exist?)
layers.Insert(i, layer);
return;
}
}
}
// inserting after all existing layers:
layers.Add(layer);
}
/// <inheritdoc/>
protected override int VisualChildrenCount {
get { return layers.Count; }
}
/// <inheritdoc/>
protected override Visual GetVisualChild(int index)
{
return layers[index];
}
#endregion
@ -242,7 +306,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -242,7 +306,7 @@ namespace ICSharpCode.AvalonEdit.Gui
foreach (TextLine textLine in visualLine.TextLines) {
textLine.Dispose();
}
RemoveInlineObjects(visualLine);
textLayer.RemoveInlineObjects(visualLine);
}
#endregion
@ -395,13 +459,17 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -395,13 +459,17 @@ namespace ICSharpCode.AvalonEdit.Gui
ClearVisualLines();
lastAvailableSize = availableSize;
RemoveInlineObjectsNow();
textLayer.RemoveInlineObjectsNow();
foreach (UIElement layer in layers) {
layer.Measure(availableSize);
}
InvalidateVisual(); // = InvalidateArrange+InvalidateRender
textLayer.InvalidateVisual();
if (document == null)
return Size.Empty;
InvalidateVisual(); // = InvalidateArrange+InvalidateRender
double maxWidth;
inMeasure = true;
try {
@ -410,7 +478,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -410,7 +478,7 @@ namespace ICSharpCode.AvalonEdit.Gui
inMeasure = false;
}
RemoveInlineObjectsNow();
textLayer.RemoveInlineObjectsNow();
SetScrollData(availableSize,
new Size(maxWidth, heightTree.TotalHeight),
@ -569,76 +637,6 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -569,76 +637,6 @@ namespace ICSharpCode.AvalonEdit.Gui
}
#endregion
#region Inline object handling
List<InlineObjectRun> inlineObjects = new List<InlineObjectRun>();
/// <summary>
/// Adds a new inline object.
/// </summary>
internal void AddInlineObject(InlineObjectRun inlineObject)
{
inlineObjects.Add(inlineObject);
AddVisualChild(inlineObject.Element);
inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
List<VisualLine> visualLinesWithOutstandingInlineObjects = new List<VisualLine>();
void RemoveInlineObjects(VisualLine visualLine)
{
// Delay removing inline objects:
// A document change immediately invalidates affected visual lines, but it does not
// cause an immediate redraw.
// To prevent inline objects from flickering when they are recreated, we delay removing
// inline objects until the next redraw.
visualLinesWithOutstandingInlineObjects.Add(visualLine);
}
void RemoveInlineObjectsNow()
{
inlineObjects.RemoveAll(
ior => {
if (visualLinesWithOutstandingInlineObjects.Contains(ior.VisualLine)) {
ior.VisualLine = null;
RemoveVisualChild(ior.Element);
return true;
}
return false;
});
visualLinesWithOutstandingInlineObjects.Clear();
}
/// <summary>
/// Removes the inline object that displays the specified UIElement.
/// </summary>
public void RemoveInlineObject(UIElement element)
{
inlineObjects.RemoveAll(
ior => {
if (ior.Element == element) {
ior.VisualLine = null;
RemoveVisualChild(ior.Element);
return true;
}
return false;
});
}
/// <inheritdoc/>
protected override int VisualChildrenCount {
get { return inlineObjects.Count + adorners.Count; }
}
/// <inheritdoc/>
protected override Visual GetVisualChild(int index)
{
if (index < inlineObjects.Count)
return inlineObjects[index].Element;
else
return adorners[index - inlineObjects.Count];
}
#endregion
#region Arrange
/// <summary>
/// Arrange implementation.
@ -663,7 +661,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -663,7 +661,7 @@ namespace ICSharpCode.AvalonEdit.Gui
// double maxWidth = 0;
foreach (UIElement adorner in adorners) {
foreach (UIElement adorner in layers) {
adorner.Arrange(new Rect(new Point(0, 0), finalSize));
}
@ -675,7 +673,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -675,7 +673,7 @@ namespace ICSharpCode.AvalonEdit.Gui
foreach (var span in textLine.GetTextRunSpans()) {
InlineObjectRun inline = span.Value as InlineObjectRun;
if (inline != null && inline.VisualLine != null) {
Debug.Assert(inlineObjects.Contains(inline));
Debug.Assert(textLayer.inlineObjects.Contains(inline));
double distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(offset, 0));
inline.Element.Arrange(new Rect(new Point(pos.X + distance, pos.Y), inline.Element.DesiredSize));
}
@ -695,7 +693,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -695,7 +693,7 @@ namespace ICSharpCode.AvalonEdit.Gui
readonly ObservableCollection<IBackgroundRenderer> backgroundRenderer = new ObservableCollection<IBackgroundRenderer>();
/// <summary>
/// Gets a collection where line transformers can be registered.
/// Gets the list of background renderers.
/// </summary>
public ObservableCollection<IBackgroundRenderer> BackgroundRenderer {
get { return backgroundRenderer; }
@ -704,8 +702,20 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -704,8 +702,20 @@ namespace ICSharpCode.AvalonEdit.Gui
/// <inheritdoc/>
protected override void OnRender(DrawingContext drawingContext)
{
foreach (IBackgroundRenderer r in backgroundRenderer)
r.Draw(drawingContext);
RenderBackground(drawingContext, KnownLayer.Background);
}
internal void RenderBackground(DrawingContext drawingContext, KnownLayer layer)
{
foreach (IBackgroundRenderer bg in backgroundRenderer) {
if (bg.Layer == layer) {
bg.Draw(drawingContext);
}
}
}
internal void RenderTextLayer(DrawingContext drawingContext)
{
Point pos = new Point(-scrollOffset.X, -clippedPixelsOnTop);
foreach (VisualLine visualLine in allVisualLines) {
foreach (TextLine textLine in visualLine.TextLines) {

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLineTextSource.cs

@ -46,7 +46,7 @@ namespace ICSharpCode.AvalonEdit.Gui @@ -46,7 +46,7 @@ namespace ICSharpCode.AvalonEdit.Gui
InlineObjectRun inlineRun = run as InlineObjectRun;
if (inlineRun != null) {
inlineRun.VisualLine = VisualLine;
TextView.AddInlineObject(inlineRun);
TextView.textLayer.AddInlineObject(inlineRun);
}
return run;
}

14
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -106,7 +106,7 @@ @@ -106,7 +106,7 @@
</Compile>
<Compile Include="Gui\AbstractMargin.cs" />
<Compile Include="Gui\Caret.cs" />
<Compile Include="Gui\CaretAdorner.cs">
<Compile Include="Gui\CaretLayer.cs">
<DependentUpon>Caret.cs</DependentUpon>
</Compile>
<Compile Include="Gui\CaretNavigationCommandHandler.cs">
@ -125,6 +125,12 @@ @@ -125,6 +125,12 @@
</Compile>
<Compile Include="Gui\IBackgroundRenderer.cs" />
<Compile Include="Gui\IReadOnlySectionProvider.cs" />
<Compile Include="Gui\Layer.cs">
<DependentUpon>TextView.cs</DependentUpon>
</Compile>
<Compile Include="Gui\LayerPosition.cs">
<DependentUpon>TextView.cs</DependentUpon>
</Compile>
<Compile Include="Gui\NoReadOnlySections.cs">
<DependentUpon>IReadOnlySectionProvider.cs</DependentUpon>
</Compile>
@ -132,9 +138,15 @@ @@ -132,9 +138,15 @@
<Compile Include="Gui\SelectionColorizer.cs">
<DependentUpon>Selection.cs</DependentUpon>
</Compile>
<Compile Include="Gui\SelectionLayer.cs">
<DependentUpon>Selection.cs</DependentUpon>
</Compile>
<Compile Include="Gui\SelectionMouseHandler.cs">
<DependentUpon>Selection.cs</DependentUpon>
</Compile>
<Compile Include="Gui\TextLayer.cs">
<DependentUpon>TextView.cs</DependentUpon>
</Compile>
<Compile Include="Gui\TextSegmentReadOnlySectionProvider.cs">
<DependentUpon>IReadOnlySectionProvider.cs</DependentUpon>
</Compile>

Loading…
Cancel
Save