Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3873 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61shortcuts
18 changed files with 454 additions and 35 deletions
@ -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 |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -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> |
@ -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));
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue