Browse Source

Merge branch '4.0' of f:\SD.git-bundle into 4.0

4.0
Daniel Grunwald 15 years ago
parent
commit
a2b6f99504
  1. 15
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/InlineObjectRun.cs
  2. 89
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextLayer.cs
  3. 179
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs
  4. 1
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLine.cs
  5. 3
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs

15
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/InlineObjectRun.cs

@ -38,11 +38,6 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -38,11 +38,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (context == null)
throw new ArgumentNullException("context");
// remove inline object if its already added, can happen e.g. when recreating textrun for word-wrapping
// TODO: certainly the text view should handle this internally? external code might want to use InlineObjectRun,
// but doesn't have access to textLayer.RemoveInlineObject
context.TextView.textLayer.RemoveInlineObject(this.Element);
return new InlineObjectRun(1, this.TextRunProperties, this.Element);
}
}
@ -55,6 +50,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -55,6 +50,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
UIElement element;
int length;
TextRunProperties properties;
internal Size desiredSize;
/// <summary>
/// Creates a new InlineObjectRun instance.
@ -122,11 +118,10 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -122,11 +118,10 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// <inheritdoc/>
public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
{
Size size = element.DesiredSize;
double baseline = TextBlock.GetBaselineOffset(element);
if (double.IsNaN(baseline))
baseline = size.Height;
return new TextEmbeddedObjectMetrics(size.Width, size.Height, baseline);
baseline = desiredSize.Height;
return new TextEmbeddedObjectMetrics(desiredSize.Width, desiredSize.Height, baseline);
}
/// <inheritdoc/>
@ -135,8 +130,8 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -135,8 +130,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (this.element.IsArrangeValid) {
double baseline = TextBlock.GetBaselineOffset(element);
if (double.IsNaN(baseline))
baseline = element.DesiredSize.Height;
return new Rect(new Point(0, -baseline), element.DesiredSize);
baseline = desiredSize.Height;
return new Rect(new Point(0, -baseline), desiredSize);
} else {
return Rect.Empty;
}

89
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextLayer.cs

@ -24,6 +24,11 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -24,6 +24,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </remarks>
sealed class TextLayer : Layer
{
/// <summary>
/// the index of the text layer in the layers collection
/// </summary>
internal int index;
public TextLayer(TextView textView) : base(textView, KnownLayer.Text)
{
}
@ -33,89 +38,5 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -33,89 +38,5 @@ namespace ICSharpCode.AvalonEdit.Rendering
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)) {
RemoveInlineObjectRun(ior);
return true;
}
return false;
});
visualLinesWithOutstandingInlineObjects.Clear();
}
// Remove InlineObjectRun.Element from TextLayer.
// Caller of RemoveInlineObjectRun will remove it from inlineObjects collection.
void RemoveInlineObjectRun(InlineObjectRun ior)
{
if (ior.Element.IsKeyboardFocusWithin) {
// When the inline element that has the focus is removed, WPF will reset the
// focus to the main window without raising appropriate LostKeyboardFocus events.
// To work around this, we manually set focus to the next focusable parent.
UIElement element = textView;
while (element != null && !element.Focusable) {
element = VisualTreeHelper.GetParent(element) as UIElement;
}
if (element != null)
Keyboard.Focus(element);
}
ior.VisualLine = null;
RemoveVisualChild(ior.Element);
}
/// <summary>
/// Removes the inline object that displays the specified UIElement.
/// </summary>
internal void RemoveInlineObject(UIElement element)
{
inlineObjects.RemoveAll(
ior => {
if (ior.Element == element) {
RemoveInlineObjectRun(ior);
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
}
}

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

@ -53,7 +53,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -53,7 +53,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
this.Options = new TextEditorOptions();
Debug.Assert(singleCharacterElementGenerator != null); // assert that the option change created the builtin element generators
layers = new UIElementCollection(this, this);
layers = new LayerCollection(this);
InsertLayer(textLayer, KnownLayer.Text, LayerInsertionPosition.Replace);
}
#endregion
@ -293,7 +293,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -293,7 +293,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
#region Layers
internal readonly TextLayer textLayer;
readonly UIElementCollection layers;
readonly LayerCollection layers;
/// <summary>
/// Gets the list of layers displayed in the text view.
@ -302,6 +302,47 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -302,6 +302,47 @@ namespace ICSharpCode.AvalonEdit.Rendering
get { return layers; }
}
sealed class LayerCollection : UIElementCollection
{
readonly TextView textView;
public LayerCollection(TextView textView)
: base(textView, textView)
{
this.textView = textView;
}
public override void Clear()
{
base.Clear();
textView.LayersChanged();
}
public override int Add(UIElement element)
{
int r = base.Add(element);
textView.LayersChanged();
return r;
}
public override void RemoveAt(int index)
{
base.RemoveAt(index);
textView.LayersChanged();
}
public override void RemoveRange(int index, int count)
{
base.RemoveRange(index, count);
textView.LayersChanged();
}
}
void LayersChanged()
{
textLayer.index = layers.IndexOf(textLayer);
}
/// <summary>
/// Inserts a new layer at a position specified relative to an existing layer.
/// </summary>
@ -351,18 +392,128 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -351,18 +392,128 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// <inheritdoc/>
protected override int VisualChildrenCount {
get { return layers.Count; }
get { return layers.Count + inlineObjects.Count; }
}
/// <inheritdoc/>
protected override Visual GetVisualChild(int index)
{
return layers[index];
int cut = textLayer.index + 1;
if (index < cut)
return layers[index];
else if (index < cut + inlineObjects.Count)
return inlineObjects[index - cut].Element;
else
return layers[index - inlineObjects.Count];
}
/// <inheritdoc/>
protected override System.Collections.IEnumerator LogicalChildren {
get { return layers.GetEnumerator(); }
get {
return inlineObjects.Select(io => io.Element).Concat(layers.Cast<UIElement>()).GetEnumerator();
}
}
#endregion
#region Inline object handling
List<InlineObjectRun> inlineObjects = new List<InlineObjectRun>();
/// <summary>
/// Adds a new inline object.
/// </summary>
internal void AddInlineObject(InlineObjectRun inlineObject)
{
Debug.Assert(inlineObject.VisualLine != null);
// Remove inline object if its already added, can happen e.g. when recreating textrun for word-wrapping
bool alreadyAdded = false;
for (int i = 0; i < inlineObjects.Count; i++) {
if (inlineObjects[i].Element == inlineObject.Element) {
RemoveInlineObjectRun(inlineObjects[i], true);
inlineObjects.RemoveAt(i);
alreadyAdded = true;
break;
}
}
inlineObjects.Add(inlineObject);
if (!alreadyAdded) {
AddVisualChild(inlineObject.Element);
}
inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
inlineObject.desiredSize = inlineObject.Element.DesiredSize;
}
void MeasureInlineObjects()
{
// As part of MeasureOverride(), re-measure the inline objects
foreach (InlineObjectRun inlineObject in inlineObjects) {
if (inlineObject.VisualLine.IsDisposed) {
// Don't re-measure inline objects that are going to be removed anyways.
// If the inline object will be reused in a different VisualLine, we'll measure it in the AddInlineObject() call.
continue;
}
inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
if (!inlineObject.Element.DesiredSize.IsClose(inlineObject.desiredSize)) {
// the element changed size -> recreate its parent visual line
inlineObject.desiredSize = inlineObject.Element.DesiredSize;
if (allVisualLines.Remove(inlineObject.VisualLine)) {
DisposeVisualLine(inlineObject.VisualLine);
}
}
}
}
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.
if (visualLine.hasInlineObjects) {
visualLinesWithOutstandingInlineObjects.Add(visualLine);
}
}
/// <summary>
/// Remove the inline objects that were marked for removal.
/// </summary>
void RemoveInlineObjectsNow()
{
if (visualLinesWithOutstandingInlineObjects.Count == 0)
return;
inlineObjects.RemoveAll(
ior => {
if (visualLinesWithOutstandingInlineObjects.Contains(ior.VisualLine)) {
RemoveInlineObjectRun(ior, false);
return true;
}
return false;
});
visualLinesWithOutstandingInlineObjects.Clear();
}
// Remove InlineObjectRun.Element from TextLayer.
// Caller of RemoveInlineObjectRun will remove it from inlineObjects collection.
void RemoveInlineObjectRun(InlineObjectRun ior, bool keepElement)
{
if (!keepElement && ior.Element.IsKeyboardFocusWithin) {
// When the inline element that has the focus is removed, WPF will reset the
// focus to the main window without raising appropriate LostKeyboardFocus events.
// To work around this, we manually set focus to the next focusable parent.
UIElement element = this;
while (element != null && !element.Focusable) {
element = VisualTreeHelper.GetParent(element) as UIElement;
}
if (element != null)
Keyboard.Focus(element);
}
ior.VisualLine = null;
if (!keepElement)
RemoveVisualChild(ior.Element);
}
#endregion
@ -409,7 +560,6 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -409,7 +560,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
{
VerifyAccess();
if (allVisualLines.Remove(visualLine)) {
visibleVisualLines = null;
DisposeVisualLine(visualLine);
InvalidateMeasure(redrawPriority);
}
@ -421,7 +571,6 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -421,7 +571,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
public void Redraw(int offset, int length, DispatcherPriority redrawPriority)
{
VerifyAccess();
bool removedLine = false;
bool changedSomethingBeforeOrInLine = false;
for (int i = 0; i < allVisualLines.Count; i++) {
VisualLine visualLine = allVisualLines[i];
@ -430,15 +579,11 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -430,15 +579,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (offset <= lineEnd) {
changedSomethingBeforeOrInLine = true;
if (offset + length >= lineStart) {
removedLine = true;
allVisualLines.RemoveAt(i--);
DisposeVisualLine(visualLine);
}
}
}
if (removedLine) {
visibleVisualLines = null;
}
if (changedSomethingBeforeOrInLine) {
// Repaint not only when something in visible area was changed, but also when anything in front of it
// was changed. We might have to redraw the line number margin. Or the highlighting changed.
@ -491,11 +636,12 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -491,11 +636,12 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (newVisualLines != null && newVisualLines.Contains(visualLine)) {
throw new ArgumentException("Cannot dispose visual line because it is in construction!");
}
visibleVisualLines = null;
visualLine.IsDisposed = true;
foreach (TextLine textLine in visualLine.TextLines) {
textLine.Dispose();
}
textLayer.RemoveInlineObjects(visualLine);
RemoveInlineObjects(visualLine);
}
#endregion
@ -676,11 +822,11 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -676,11 +822,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
ClearVisualLines();
lastAvailableSize = availableSize;
textLayer.RemoveInlineObjectsNow();
foreach (UIElement layer in layers) {
layer.Measure(availableSize);
}
MeasureInlineObjects();
InvalidateVisual(); // = InvalidateArrange+InvalidateRender
textLayer.InvalidateVisual();
@ -699,7 +845,8 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -699,7 +845,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
}
}
textLayer.RemoveInlineObjectsNow();
// remove inline objects only at the end, so that inline objects that were re-used are not removed from the editor
RemoveInlineObjectsNow();
maxWidth += AdditionalHorizontalScrollAmount;
double heightTreeHeight = this.DocumentHeight;
@ -946,7 +1093,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -946,7 +1093,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
foreach (var span in textLine.GetTextRunSpans()) {
InlineObjectRun inline = span.Value as InlineObjectRun;
if (inline != null && inline.VisualLine != null) {
Debug.Assert(textLayer.inlineObjects.Contains(inline));
Debug.Assert(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));
}

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

@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
{
TextView textView;
List<VisualLineElement> elements;
internal bool hasInlineObjects;
/// <summary>
/// Gets the document to which this VisualLine belongs.

3
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs

@ -42,7 +42,8 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -42,7 +42,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
InlineObjectRun inlineRun = run as InlineObjectRun;
if (inlineRun != null) {
inlineRun.VisualLine = VisualLine;
TextView.textLayer.AddInlineObject(inlineRun);
VisualLine.hasInlineObjects = true;
TextView.AddInlineObject(inlineRun);
}
return run;
}

Loading…
Cancel
Save