// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Threading; using Tango.Scripting.Editors.Document; using Tango.Scripting.Editors.Editing; using System.Windows.Media; namespace Tango.Scripting.Editors.CodeCompletion { /// /// The code completion window. /// public class CompletionWindow : CompletionWindowBase { readonly CompletionList completionList = new CompletionList(); public event Action InsertionRequest; /// /// Gets the completion list used in this completion window. /// public CompletionList CompletionList { get { return completionList; } } /// /// Creates a new code completion window. /// public CompletionWindow(TextArea textArea) : base(textArea) { // keep height automatic this.CloseAutomatically = true; this.SizeToContent = SizeToContent.WidthAndHeight; this.WindowStyle = WindowStyle.None; this.ResizeMode = ResizeMode.NoResize; this.Content = completionList; // prevent user from resizing window to 0x0 this.MinHeight = 15; this.MinWidth = 30; //this.Background = new SolidColorBrush(Color.FromRgb(15, 15, 15)); this.Foreground = Brushes.Gainsboro; } public override void ShowCompletion() { base.ShowCompletion(); AttachEvents(); } public override void HideCompletion() { base.HideCompletion(); foreach (var item in completionList.ListBox.Items) { var box = completionList.ListBox.ItemContainerGenerator.ContainerFromItem(item) as CompletionListBoxItem; if (box != null && box.IsSelected) { box.toolTip.IsOpen = false; } } } void completionList_InsertionRequested(object sender, EventArgs e) { HideCompletion(); // The window must close before Complete() is called. // If the Complete callback pushes stacked input handlers, we don't want to pop those when the CC window closes. var item = completionList.SelectedItem; //if (item != null) // item.Complete(this.TextArea, new AnchorSegment(this.TextArea.Document, this.StartOffset, this.EndOffset - this.StartOffset), e); if (item != null) { InsertionRequest?.Invoke(item); } completionList.SelectedItem = null; } void AttachEvents() { this.completionList.InsertionRequested += completionList_InsertionRequested; this.TextArea.Caret.PositionChanged += CaretPositionChanged; this.TextArea.MouseWheel += textArea_MouseWheel; this.TextArea.PreviewTextInput += textArea_PreviewTextInput; } /// protected override void DetachEvents() { this.completionList.InsertionRequested -= completionList_InsertionRequested; this.TextArea.Caret.PositionChanged -= CaretPositionChanged; this.TextArea.MouseWheel -= textArea_MouseWheel; this.TextArea.PreviewTextInput -= textArea_PreviewTextInput; base.DetachEvents(); } /// protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (!e.Handled) { completionList.HandleKey(e); } } void textArea_PreviewTextInput(object sender, TextCompositionEventArgs e) { e.Handled = RaiseEventPair(this, PreviewTextInputEvent, TextInputEvent, new TextCompositionEventArgs(e.Device, e.TextComposition)); if (e.Text == " ") { HideCompletion(); } } void textArea_MouseWheel(object sender, MouseWheelEventArgs e) { e.Handled = RaiseEventPair(GetScrollEventTarget(), PreviewMouseWheelEvent, MouseWheelEvent, new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)); } UIElement GetScrollEventTarget() { if (completionList == null) return this; return completionList.ScrollViewer ?? completionList.ListBox ?? (UIElement)completionList; } /// /// Gets/Sets whether the completion window should close automatically. /// The default value is true. /// public bool CloseAutomatically { get; set; } /// protected override bool CloseOnFocusLost { get { return this.CloseAutomatically; } } /// /// 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. /// Has no effect if CloseAutomatically is false. /// public bool CloseWhenCaretAtBeginning { get; set; } void CaretPositionChanged(object sender, EventArgs e) { int offset = this.TextArea.Caret.Offset; if (offset == this.StartOffset) { if (CloseAutomatically && CloseWhenCaretAtBeginning) { HideCompletion(); } else { completionList.SelectItem(string.Empty); } return; } if (offset < this.StartOffset || offset > this.EndOffset) { if (CloseAutomatically) { HideCompletion(); } } else { TextDocument document = this.TextArea.Document; if (document != null) { completionList.SelectItem(document.GetText(this.StartOffset, offset - this.StartOffset)); } } } } }