Browse Source

CodeCompletionWindow for AvalonEdit

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3873 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
7d5a15e1ba
  1. 20
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs
  2. 9
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml
  3. 75
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml.cs
  4. 178
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs
  5. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/BackgroundGeometryBuilder.cs
  6. 9
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/Caret.cs
  7. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs
  8. 4
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs
  9. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/ITextViewConnect.cs
  10. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/LineNumberMargin.cs
  11. 1
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs
  12. 42
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextView.cs
  13. 39
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLine.cs
  14. 36
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualYPosition.cs
  15. 19
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs
  16. 13
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  17. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Properties/CodeAnalysisDictionary.xml
  18. 32
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs

20
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionList.cs

@ -0,0 +1,20 @@
// <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.Controls;
namespace ICSharpCode.AvalonEdit.CodeCompletion
{
/// <summary>
/// The listbox used inside the CompletionWindow.
/// </summary>
public class CompletionList : ListBox
{
}
}

9
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml

@ -0,0 +1,9 @@
<cc:CompletionWindowBase
x:Class="ICSharpCode.AvalonEdit.CodeCompletion.CompletionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:ICSharpCode.AvalonEdit.CodeCompletion"
Height="300" Width="200"
>
<cc:CompletionList/>
</cc:CompletionWindowBase>

75
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindow.xaml.cs

@ -0,0 +1,75 @@
using ICSharpCode.AvalonEdit.Document;
using System;
namespace ICSharpCode.AvalonEdit.CodeCompletion
{
/// <summary>
/// The code completion window.
/// </summary>
public partial class CompletionWindow : CompletionWindowBase
{
TextDocument document;
int startOffset;
int endOffset;
/// <summary>
/// Creates a new code completion window.
/// </summary>
public CompletionWindow(TextArea textArea) : base(textArea)
{
InitializeComponent();
document = textArea.TextView.Document;
startOffset = endOffset = textArea.Caret.Offset;
}
/// <inheritdoc/>
protected override void AttachEvents()
{
base.AttachEvents();
document.Changing += textArea_Document_Changing;
this.TextArea.Caret.PositionChanged += CaretPositionChanged;
}
/// <inheritdoc/>
protected override void DetachEvents()
{
document.Changing -= textArea_Document_Changing;
this.TextArea.Caret.PositionChanged -= CaretPositionChanged;
base.DetachEvents();
}
void textArea_Document_Changing(object sender, DocumentChangeEventArgs e)
{
// => startOffset test required so that this startOffset/endOffset are not incremented again
// for BeforeStartKey characters
if (e.Offset >= startOffset && e.Offset <= endOffset) {
endOffset += e.InsertionLength - e.RemovalLength;
} else {
Close();
}
}
/// <summary>
/// When this flag is set, code completion closes if the caret moves to the
/// beginning of the allowed range. This is useful in Ctrl+Space and "complete when typing",
/// but not in dot-completion.
/// </summary>
public bool CloseWhenCaretAtBeginning { get; set; }
void CaretPositionChanged(object sender, EventArgs e)
{
int offset = this.TextArea.Caret.Offset;
if (offset == startOffset) {
if (CloseWhenCaretAtBeginning)
Close();
return;
}
if (offset < startOffset || offset > endOffset) {
Close();
} else {
//codeCompletionListView.SelectItemWithStart(document.GetText(startOffset, offset - startOffset));
}
}
}
}

178
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/CodeCompletion/CompletionWindowBase.cs

@ -0,0 +1,178 @@
// <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.Input;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Gui;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.CodeCompletion
{
/// <summary>
/// Base class for completion windows. Handles positioning the window at the caret.
/// </summary>
public class CompletionWindowBase : Window
{
static CompletionWindowBase()
{
WindowStyleProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(WindowStyle.None));
ShowActivatedProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(Boxes.False));
ShowInTaskbarProperty.OverrideMetadata(typeof(CompletionWindowBase), new FrameworkPropertyMetadata(Boxes.False));
}
/// <summary>
/// Gets the parent TextArea.
/// </summary>
public TextArea TextArea { get; private set; }
Window parentWindow;
/// <summary>
/// Creates a new CompletionWindowBase.
/// </summary>
public CompletionWindowBase(TextArea textArea)
{
if (textArea == null)
throw new ArgumentNullException("textArea");
this.TextArea = textArea;
parentWindow = Window.GetWindow(textArea);
}
#region Event Handlers
/// <summary>
/// Attaches events to the text area.
/// </summary>
protected virtual void AttachEvents()
{
this.TextArea.LostFocus += TextAreaLostFocus;
this.TextArea.TextView.ScrollOffsetChanged += TextViewScrollOffsetChanged;
this.TextArea.TextView.DocumentChanged += TextViewDocumentChanged;
if (parentWindow != null) {
parentWindow.LocationChanged += parentWindow_LocationChanged;
parentWindow.Deactivated += parentWindowDeactivated;
}
}
/// <summary>
/// Detaches events from the text area.
/// </summary>
protected virtual void DetachEvents()
{
this.TextArea.LostFocus -= TextAreaLostFocus;
this.TextArea.TextView.ScrollOffsetChanged -= TextViewScrollOffsetChanged;
this.TextArea.TextView.DocumentChanged -= TextViewDocumentChanged;
if (parentWindow != null) {
parentWindow.LocationChanged -= parentWindow_LocationChanged;
parentWindow.Deactivated -= parentWindowDeactivated;
}
}
void TextViewScrollOffsetChanged(object sender, EventArgs e)
{
UpdatePosition();
}
void TextViewDocumentChanged(object sender, EventArgs e)
{
Close();
}
void TextAreaLostFocus(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background);
}
void parentWindow_LocationChanged(object sender, EventArgs e)
{
UpdatePosition();
}
void parentWindowDeactivated(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background);
}
/// <inheritdoc/>
protected override void OnDeactivated(EventArgs e)
{
base.OnDeactivated(e);
Dispatcher.BeginInvoke(new Action(CloseIfFocusLost), DispatcherPriority.Background);
}
#endregion
void CloseIfFocusLost()
{
Debug.WriteLine("CloseIfFocusLost");
if (!this.IsActive && parentWindow != null && !parentWindow.IsActive) {
// close if parent window looses focus
Close();
}
if (!this.TextArea.IsFocused) {
Close();
}
}
/// <inheritdoc/>
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
SetPosition();
AttachEvents();
}
/// <inheritdoc/>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
DetachEvents();
}
Point visualLocation, visualLocationTop;
/// <summary>
/// Positions the completion window at the caret position.
/// </summary>
void SetPosition()
{
TextView textView = this.TextArea.TextView;
visualLocation = textView.GetVisualPosition(this.TextArea.Caret.Position, VisualYPosition.LineBottom);
visualLocationTop = textView.GetVisualPosition(this.TextArea.Caret.Position, VisualYPosition.LineTop);
UpdatePosition();
}
void UpdatePosition()
{
TextView textView = this.TextArea.TextView;
Point location = textView.PointToScreen(visualLocation - textView.ScrollOffset);
Point locationTop = textView.PointToScreen(visualLocationTop - textView.ScrollOffset);
Size completionWindowSize = new Size(this.ActualWidth, this.ActualHeight);
Rect bounds = new Rect(location, completionWindowSize);
Rect workingScreen = System.Windows.Forms.Screen.GetWorkingArea(location.ToSystemDrawing()).ToWpf();
if (!workingScreen.Contains(bounds)) {
if (bounds.Left < workingScreen.Left) {
bounds.X = workingScreen.Left;
} else if (bounds.Right > workingScreen.Right) {
bounds.X = workingScreen.Right - bounds.Width;
}
if (bounds.Bottom > workingScreen.Bottom) {
bounds.Y = locationTop.Y - bounds.Height;
}
if (bounds.Y < workingScreen.Top) {
bounds.Y = workingScreen.Top;
}
}
this.Left = bounds.X;
this.Top = bounds.Y;
}
}
}

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

@ -76,7 +76,7 @@ namespace ICSharpCode.AvalonEdit.Gui
TextLine lastTextLine = vl.TextLines.Last(); TextLine lastTextLine = vl.TextLines.Last();
foreach (TextLine line in vl.TextLines) { foreach (TextLine line in vl.TextLines) {
double y = vl.GetTextLineVisualTop(line); double y = vl.GetTextLineVisualYPosition(line, VisualYPosition.LineTop);
int visualStartCol = vl.GetTextLineVisualStartColumn(line); int visualStartCol = vl.GetTextLineVisualStartColumn(line);
int visualEndCol = visualStartCol + line.Length; int visualEndCol = visualStartCol + line.Length;
if (line != lastTextLine) if (line != lastTextLine)

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

@ -213,14 +213,13 @@ namespace ICSharpCode.AvalonEdit.Gui
TextLine textLine = visualLine.GetTextLine(position.VisualColumn); TextLine textLine = visualLine.GetTextLine(position.VisualColumn);
double xPos = textLine.GetDistanceFromCharacterHit(new CharacterHit(position.VisualColumn, 0)); double xPos = textLine.GetDistanceFromCharacterHit(new CharacterHit(position.VisualColumn, 0));
double lineTop = visualLine.GetTextLineVisualTop(textLine); double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
double lineBottom = lineTop + textLine.Height; double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineBottom);
double fontSize = (double)textArea.GetValue(TextBlock.FontSizeProperty);
return new Rect(xPos, return new Rect(xPos,
lineBottom - fontSize, lineTop,
SystemParameters.CaretWidth, SystemParameters.CaretWidth,
fontSize); lineBottom - lineTop);
} }
/// <summary> /// <summary>

4
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/CaretNavigationCommandHandler.cs

@ -280,14 +280,14 @@ namespace ICSharpCode.AvalonEdit.Gui
case CaretMovementType.PageDown: case CaretMovementType.PageDown:
{ {
// Page up/down: find the target line using its visual position // Page up/down: find the target line using its visual position
double yPos = visualLine.GetTextLineVisualTop(textLine) + textLine.Height / 2; double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
if (direction == CaretMovementType.PageUp) if (direction == CaretMovementType.PageUp)
yPos -= textArea.TextView.RenderSize.Height; yPos -= textArea.TextView.RenderSize.Height;
else else
yPos += textArea.TextView.RenderSize.Height; yPos += textArea.TextView.RenderSize.Height;
DocumentLine newLine = textArea.TextView.GetDocumentLineByVisualTop(yPos); DocumentLine newLine = textArea.TextView.GetDocumentLineByVisualTop(yPos);
targetVisualLine = textArea.TextView.GetOrConstructVisualLine(newLine); targetVisualLine = textArea.TextView.GetOrConstructVisualLine(newLine);
targetLine = targetVisualLine.GetTextLineByVisualTop(yPos); targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
break; break;
} }
default: default:

4
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/FoldingMargin.cs

@ -45,7 +45,7 @@ namespace ICSharpCode.AvalonEdit.Gui
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.GetTextLineVisualTop(textLine) - 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(new Point(xPos, yPos), m.DesiredSize)); m.Arrange(new Rect(new Point(xPos, yPos), m.DesiredSize));
@ -213,7 +213,7 @@ namespace ICSharpCode.AvalonEdit.Gui
double GetVisualPos(VisualLine vl, TextLine tl) double GetVisualPos(VisualLine vl, TextLine tl)
{ {
double pos = vl.GetTextLineVisualTop(tl) + tl.Height / 2 - TextView.VerticalOffset; double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) + tl.Height / 2 - TextView.VerticalOffset;
return Math.Round(pos) + 0.5; return Math.Round(pos) + 0.5;
} }

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

@ -11,7 +11,7 @@ namespace ICSharpCode.AvalonEdit.Gui
{ {
/// <summary> /// <summary>
/// Allows <see cref="VisualLineElementGenerator"/>s, <see cref="IVisualLineTransformer"/>s and /// Allows <see cref="VisualLineElementGenerator"/>s, <see cref="IVisualLineTransformer"/>s and
/// <see cref="IBackgroundRenderer"/> to be notified when they are added or removed from a text view. /// <see cref="IBackgroundRenderer"/>s to be notified when they are added or removed from a text view.
/// </summary> /// </summary>
public interface ITextViewConnect public interface ITextViewConnect
{ {

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

@ -175,7 +175,7 @@ namespace ICSharpCode.AvalonEdit.Gui
VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y); VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y);
if (vl == null) if (vl == null)
return SimpleSegment.Invalid; return SimpleSegment.Invalid;
TextLine tl = vl.GetTextLineByVisualTop(pos.Y); TextLine tl = vl.GetTextLineByVisualYPosition(pos.Y);
int visualStartColumn = vl.GetTextLineVisualStartColumn(tl); int visualStartColumn = vl.GetTextLineVisualStartColumn(tl);
int visualEndColumn = visualStartColumn + tl.Length; int visualEndColumn = visualStartColumn + tl.Length;
int relStart = vl.FirstDocumentLine.Offset; int relStart = vl.FirstDocumentLine.Offset;

1
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/TextEditor.cs

@ -625,6 +625,7 @@ namespace ICSharpCode.AvalonEdit
/// <summary> /// <summary>
/// Gets/sets the encoding used when the file is saved. /// Gets/sets the encoding used when the file is saved.
/// </summary> /// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Encoding Encoding { get; set; } public Encoding Encoding { get; set; }
/// <summary> /// <summary>

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

@ -82,7 +82,7 @@ namespace ICSharpCode.AvalonEdit.Gui
((TextView)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue); ((TextView)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue);
} }
double LineHeight { internal double FontSize {
get { get {
return (double)GetValue(TextBlock.FontSizeProperty); return (double)GetValue(TextBlock.FontSizeProperty);
} }
@ -107,7 +107,7 @@ namespace ICSharpCode.AvalonEdit.Gui
ClearVisualLines(); ClearVisualLines();
if (newValue != null) { if (newValue != null) {
TextDocumentWeakEventManager.Changing.AddListener(newValue, this); TextDocumentWeakEventManager.Changing.AddListener(newValue, this);
heightTree = new HeightTree(newValue, LineHeight + 3); heightTree = new HeightTree(newValue, FontSize + 3);
formatter = TextFormatter.Create(); formatter = TextFormatter.Create();
} }
InvalidateMeasure(DispatcherPriority.Normal); InvalidateMeasure(DispatcherPriority.Normal);
@ -579,7 +579,7 @@ namespace ICSharpCode.AvalonEdit.Gui
{ {
return new GlobalTextRunProperties { return new GlobalTextRunProperties {
typeface = this.CreateTypeface(), typeface = this.CreateTypeface(),
fontRenderingEmSize = LineHeight, fontRenderingEmSize = FontSize,
foregroundBrush = (Brush)GetValue(Control.ForegroundProperty), foregroundBrush = (Brush)GetValue(Control.ForegroundProperty),
cultureInfo = CultureInfo.CurrentCulture cultureInfo = CultureInfo.CurrentCulture
}; };
@ -606,7 +606,7 @@ namespace ICSharpCode.AvalonEdit.Gui
Debug.WriteLine("Building line " + documentLine.LineNumber); Debug.WriteLine("Building line " + documentLine.LineNumber);
VisualLine visualLine = new VisualLine(documentLine); VisualLine visualLine = new VisualLine(this, documentLine);
VisualLineTextSource textSource = new VisualLineTextSource(visualLine) { VisualLineTextSource textSource = new VisualLineTextSource(visualLine) {
Document = document, Document = document,
GlobalTextRunProperties = globalTextRunProperties, GlobalTextRunProperties = globalTextRunProperties,
@ -868,12 +868,12 @@ namespace ICSharpCode.AvalonEdit.Gui
void IScrollInfo.LineUp() void IScrollInfo.LineUp()
{ {
((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y - LineHeight); ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y - FontSize);
} }
void IScrollInfo.LineDown() void IScrollInfo.LineDown()
{ {
((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y + LineHeight); ((IScrollInfo)this).SetVerticalOffset(scrollOffset.Y + FontSize);
} }
void IScrollInfo.LineLeft() void IScrollInfo.LineLeft()
@ -909,14 +909,14 @@ namespace ICSharpCode.AvalonEdit.Gui
void IScrollInfo.MouseWheelUp() void IScrollInfo.MouseWheelUp()
{ {
((IScrollInfo)this).SetVerticalOffset( ((IScrollInfo)this).SetVerticalOffset(
scrollOffset.Y - (SystemParameters.WheelScrollLines * LineHeight)); scrollOffset.Y - (SystemParameters.WheelScrollLines * FontSize));
OnScrollChange(); OnScrollChange();
} }
void IScrollInfo.MouseWheelDown() void IScrollInfo.MouseWheelDown()
{ {
((IScrollInfo)this).SetVerticalOffset( ((IScrollInfo)this).SetVerticalOffset(
scrollOffset.Y + (SystemParameters.WheelScrollLines * LineHeight)); scrollOffset.Y + (SystemParameters.WheelScrollLines * FontSize));
OnScrollChange(); OnScrollChange();
} }
@ -936,7 +936,7 @@ namespace ICSharpCode.AvalonEdit.Gui
double WideSpaceWidth { double WideSpaceWidth {
get { get {
return LineHeight / 2; return FontSize / 2;
} }
} }
@ -1116,6 +1116,30 @@ namespace ICSharpCode.AvalonEdit.Gui
} }
#endregion #endregion
#region Visual Position <-> TextViewPosition
/// <summary>
/// Gets the visual position from a text view position.
/// </summary>
/// <param name="position">The text view position.</param>
/// <param name="yPositionMode">The mode how to retrieve the Y position.</param>
/// <returns>The position in WPF device-independent pixels relative
/// to the top left corner of the document.</returns>
public Point GetVisualPosition(TextViewPosition position, VisualYPosition yPositionMode)
{
VerifyAccess();
if (this.Document == null)
throw new InvalidOperationException("There is no document assigned to the TextView");
DocumentLine documentLine = this.Document.GetLineByNumber(position.Line);
VisualLine visualLine = GetOrConstructVisualLine(documentLine);
int visualColumn = position.VisualColumn;
if (visualColumn < 0) {
int offset = documentLine.Offset + position.Column - 1;
visualColumn = visualLine.GetVisualColumn(offset - visualLine.FirstDocumentLine.Offset);
}
return visualLine.GetVisualPosition(visualColumn, yPositionMode);
}
#endregion
#region Service Provider #region Service Provider
readonly ServiceContainer services = new ServiceContainer(); readonly ServiceContainer services = new ServiceContainer();

39
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualLine.cs

@ -23,6 +23,7 @@ namespace ICSharpCode.AvalonEdit.Gui
/// </summary> /// </summary>
public sealed class VisualLine public sealed class VisualLine
{ {
TextView textView;
List<VisualLineElement> elements; List<VisualLineElement> elements;
/// <summary> /// <summary>
@ -60,9 +61,11 @@ namespace ICSharpCode.AvalonEdit.Gui
/// </summary> /// </summary>
public double VisualTop { get; internal set; } public double VisualTop { get; internal set; }
internal VisualLine(DocumentLine firstDocumentLine) internal VisualLine(TextView textView, DocumentLine firstDocumentLine)
{ {
Debug.Assert(textView != null);
Debug.Assert(firstDocumentLine != null); Debug.Assert(firstDocumentLine != null);
this.textView = textView;
this.FirstDocumentLine = firstDocumentLine; this.FirstDocumentLine = firstDocumentLine;
} }
@ -223,18 +226,30 @@ namespace ICSharpCode.AvalonEdit.Gui
/// </summary> /// </summary>
/// <returns>Distance in device-independent pixels /// <returns>Distance in device-independent pixels
/// from the top of the document to the top of the specified text line.</returns> /// from the top of the document to the top of the specified text line.</returns>
public double GetTextLineVisualTop(TextLine textLine) public double GetTextLineVisualYPosition(TextLine textLine, VisualYPosition yPositionMode)
{ {
if (!TextLines.Contains(textLine)) if (textLine == null)
throw new ArgumentException("textLine is not a line in this VisualLine"); throw new ArgumentNullException("textLine");
double pos = VisualTop; double pos = VisualTop;
foreach (TextLine tl in TextLines) { foreach (TextLine tl in TextLines) {
if (tl == textLine) if (tl == textLine) {
break; switch (yPositionMode) {
else case VisualYPosition.LineTop:
return pos;
case VisualYPosition.LineMiddle:
return pos + tl.Height / 2;
case VisualYPosition.LineBottom:
return pos + tl.Height;
case VisualYPosition.TextTop:
return pos + tl.Height - textView.FontSize;
default:
throw new ArgumentException("Invalid yPositionMode:" + yPositionMode);
}
} else {
pos += tl.Height; pos += tl.Height;
}
} }
return pos; throw new ArgumentException("textLine is not a line in this VisualLine");
} }
/// <summary> /// <summary>
@ -257,7 +272,7 @@ namespace ICSharpCode.AvalonEdit.Gui
/// <summary> /// <summary>
/// Gets a TextLine by the visual position. /// Gets a TextLine by the visual position.
/// </summary> /// </summary>
public TextLine GetTextLineByVisualTop(double visualTop) public TextLine GetTextLineByVisualYPosition(double visualTop)
{ {
const double epsilon = 0.0001; const double epsilon = 0.0001;
double pos = this.VisualTop; double pos = this.VisualTop;
@ -274,11 +289,11 @@ namespace ICSharpCode.AvalonEdit.Gui
/// </summary> /// </summary>
/// <returns>Position in device-independent pixels /// <returns>Position in device-independent pixels
/// relative to the top left of the document.</returns> /// relative to the top left of the document.</returns>
public Point GetVisualPosition(int visualColumn) public Point GetVisualPosition(int visualColumn, VisualYPosition yPositionMode)
{ {
TextLine textLine = GetTextLine(visualColumn); TextLine textLine = GetTextLine(visualColumn);
double xPos = textLine.GetDistanceFromCharacterHit(new CharacterHit(visualColumn, 0)); double xPos = textLine.GetDistanceFromCharacterHit(new CharacterHit(visualColumn, 0));
double yPos = GetTextLineVisualTop(textLine); double yPos = GetTextLineVisualYPosition(textLine, yPositionMode);
return new Point(xPos, yPos); return new Point(xPos, yPos);
} }
@ -287,7 +302,7 @@ namespace ICSharpCode.AvalonEdit.Gui
/// </summary> /// </summary>
public int GetVisualColumn(Point point) public int GetVisualColumn(Point point)
{ {
TextLine textLine = GetTextLineByVisualTop(point.Y); TextLine textLine = GetTextLineByVisualYPosition(point.Y);
CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X); CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X);
return ch.FirstCharacterIndex + ch.TrailingLength; return ch.FirstCharacterIndex + ch.TrailingLength;
} }

36
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Gui/VisualYPosition.cs

@ -0,0 +1,36 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
namespace ICSharpCode.AvalonEdit.Gui
{
/// <summary>
/// An enum that specifies the possible Y positions that can be returned by VisualLine.GetVisualPosition.
/// </summary>
public enum VisualYPosition
{
/// <summary>
/// Returns the top of the TextLine.
/// </summary>
LineTop,
/// <summary>
/// Returns the top of the text. If the line contains inline UI elements larger than the text, TextTop
/// will be below LineTop.
/// </summary>
TextTop,
/// <summary>
/// Returns the bottom of the TextLine. This is the same as the bottom of the text (the text is always
/// aligned at the bottom border).
/// </summary>
LineBottom,
/// <summary>
/// The middle between LineTop and LineBottom.
/// </summary>
LineMiddle
}
}

19
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs

@ -24,6 +24,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
readonly TextView textView; readonly TextView textView;
readonly HighlightingRuleSet ruleSet; readonly HighlightingRuleSet ruleSet;
DocumentHighlighter highlighter; DocumentHighlighter highlighter;
bool isInTextView;
/// <summary> /// <summary>
/// Creates a new HighlightingColorizer instance. /// Creates a new HighlightingColorizer instance.
@ -53,25 +54,39 @@ namespace ICSharpCode.AvalonEdit.Highlighting
void OnDocumentChanged() void OnDocumentChanged()
{ {
if (highlighter != null && isInTextView) {
textView.Services.RemoveService(typeof(DocumentHighlighter));
}
TextDocument document = textView.Document; TextDocument document = textView.Document;
if (document != null) if (document != null)
highlighter = new TextViewDocumentHighlighter(this, document, ruleSet); highlighter = new TextViewDocumentHighlighter(this, document, ruleSet);
else else
highlighter = null; highlighter = null;
if (highlighter != null && isInTextView) {
textView.Services.AddService(typeof(DocumentHighlighter), highlighter);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnAddToTextView(TextView textView) protected override void OnAddToTextView(TextView textView)
{ {
base.OnAddToTextView(textView); base.OnAddToTextView(textView);
textView.Services.AddService(typeof(DocumentHighlighter), highlighter); isInTextView = true;
if (highlighter != null) {
textView.Services.AddService(typeof(DocumentHighlighter), highlighter);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnRemoveFromTextView(TextView textView) protected override void OnRemoveFromTextView(TextView textView)
{ {
base.OnRemoveFromTextView(textView); base.OnRemoveFromTextView(textView);
textView.Services.RemoveService(typeof(DocumentHighlighter)); isInTextView = false;
if (highlighter != null) {
textView.Services.RemoveService(typeof(DocumentHighlighter));
}
} }
int currentLineEndOffset; int currentLineEndOffset;

13
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -59,6 +59,8 @@
<Reference Include="System.Data.DataSetExtensions"> <Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference> </Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq"> <Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
@ -71,6 +73,12 @@
<Compile Include="..\..\..\Main\GlobalAssemblyInfo.cs"> <Compile Include="..\..\..\Main\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link> <Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile> </Compile>
<Compile Include="CodeCompletion\CompletionWindowBase.cs" />
<Compile Include="CodeCompletion\CompletionList.cs" />
<Compile Include="CodeCompletion\CompletionWindow.xaml.cs">
<DependentUpon>CompletionWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Document\DocumentChangeOperation.cs"> <Compile Include="Document\DocumentChangeOperation.cs">
<DependentUpon>UndoStack.cs</DependentUpon> <DependentUpon>UndoStack.cs</DependentUpon>
</Compile> </Compile>
@ -209,6 +217,9 @@
<Compile Include="Gui\TextEditor.cs" /> <Compile Include="Gui\TextEditor.cs" />
<Compile Include="Gui\TextView.cs" /> <Compile Include="Gui\TextView.cs" />
<Compile Include="Gui\VisualLine.cs" /> <Compile Include="Gui\VisualLine.cs" />
<Compile Include="Gui\VisualYPosition.cs">
<DependentUpon>VisualLine.cs</DependentUpon>
</Compile>
<Compile Include="Gui\WhitespaceElementGenerator.cs" /> <Compile Include="Gui\WhitespaceElementGenerator.cs" />
<Compile Include="Highlighting\DocumentHighlighter.cs" /> <Compile Include="Highlighting\DocumentHighlighter.cs" />
<Compile Include="Highlighting\HighlightedLine.cs" /> <Compile Include="Highlighting\HighlightedLine.cs" />
@ -303,8 +314,10 @@
<Folder Include="Highlighting" /> <Folder Include="Highlighting" />
<Folder Include="Highlighting\Xshd" /> <Folder Include="Highlighting\Xshd" />
<Folder Include="Highlighting\Resources" /> <Folder Include="Highlighting\Resources" />
<Folder Include="CodeCompletion" />
<Folder Include="Utils" /> <Folder Include="Utils" />
<Folder Include="themes" /> <Folder Include="themes" />
<Page Include="CodeCompletion\CompletionWindow.xaml" />
<Page Include="Gui\TextEditor.xaml" /> <Page Include="Gui\TextEditor.xaml" />
<Page Include="themes\generic.xaml" /> <Page Include="themes\generic.xaml" />
</ItemGroup> </ItemGroup>

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Properties/CodeAnalysisDictionary.xml

@ -10,6 +10,8 @@
<Word>Colorizer</Word> <Word>Colorizer</Word>
<Word>Renderer</Word> <Word>Renderer</Word>
<Word>Deletable</Word> <Word>Deletable</Word>
<!-- required so that FxCop accepts 'yPositionMode' -->
<Word>y</Word>
</Recognized> </Recognized>
<Unrecognized> <Unrecognized>
<!-- Disable Lineup as a single word - LineUp is the spelling used in WPF --> <!-- Disable Lineup as a single word - LineUp is the spelling used in WPF -->

32
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/ExtensionMethods.cs

@ -19,6 +19,7 @@ namespace ICSharpCode.AvalonEdit.Utils
{ {
static class ExtensionMethods static class ExtensionMethods
{ {
#region Epsilon / IsClose
public const double Epsilon = 1e-8; public const double Epsilon = 1e-8;
/// <summary> /// <summary>
@ -46,7 +47,9 @@ namespace ICSharpCode.AvalonEdit.Utils
{ {
return IsClose(d1.X, d2.X) && IsClose(d1.Y, d2.Y); return IsClose(d1.X, d2.X) && IsClose(d1.Y, d2.Y);
} }
#endregion
#region CreateTypeface
/// <summary> /// <summary>
/// Creates typeface from the framework element. /// Creates typeface from the framework element.
/// </summary> /// </summary>
@ -57,7 +60,9 @@ namespace ICSharpCode.AvalonEdit.Utils
(FontWeight)fe.GetValue(TextBlock.FontWeightProperty), (FontWeight)fe.GetValue(TextBlock.FontWeightProperty),
(FontStretch)fe.GetValue(TextBlock.FontStretchProperty)); (FontStretch)fe.GetValue(TextBlock.FontStretchProperty));
} }
#endregion
#region AddRange / Sequence
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> elements) public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> elements)
{ {
foreach (T e in elements) foreach (T e in elements)
@ -71,7 +76,9 @@ namespace ICSharpCode.AvalonEdit.Utils
{ {
yield return value; yield return value;
} }
#endregion
#region XML reading
/// <summary> /// <summary>
/// Gets the value of the attribute, or null if the attribute does not exist. /// Gets the value of the attribute, or null if the attribute does not exist.
/// </summary> /// </summary>
@ -101,7 +108,9 @@ namespace ICSharpCode.AvalonEdit.Utils
else else
return XmlConvert.ToBoolean(attributeValue); return XmlConvert.ToBoolean(attributeValue);
} }
#endregion
#region ISegment extensions
/// <summary> /// <summary>
/// Gets the end offset of the segment. /// Gets the end offset of the segment.
/// </summary> /// </summary>
@ -141,5 +150,28 @@ namespace ICSharpCode.AvalonEdit.Utils
else else
return new SimpleSegment(start, end - start); return new SimpleSegment(start, end - start);
} }
#endregion
#region System.Drawing <-> WPF conversions
public static System.Drawing.Point ToSystemDrawing(this Point p)
{
return new System.Drawing.Point((int)p.X, (int)p.Y);
}
public static Point ToWpf(this System.Drawing.Point p)
{
return new Point(p.X, p.Y);
}
public static Size ToWpf(this System.Drawing.Size s)
{
return new Size(s.Width, s.Height);
}
public static Rect ToWpf(this System.Drawing.Rectangle rect)
{
return new Rect(rect.Location.ToWpf(), rect.Size.ToWpf());
}
#endregion
} }
} }

Loading…
Cancel
Save