From 080f1697e97e13461ec6df4d31c8924d01257a1b Mon Sep 17 00:00:00 2001 From: Roy Ben Shabat Date: Tue, 9 Apr 2019 01:47:48 +0300 Subject: MERGE --- .../Snippets/SnippetReplaceableTextElement.cs | 213 +++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs') diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs new file mode 100644 index 000000000..9bf872f11 --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Snippets/SnippetReplaceableTextElement.cs @@ -0,0 +1,213 @@ +// 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.Linq; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +using System.Windows.Threading; + +using Tango.Scripting.Editors.Document; +using Tango.Scripting.Editors.Rendering; + +namespace Tango.Scripting.Editors.Snippets +{ + /// + /// Text element that is supposed to be replaced by the user. + /// Will register an . + /// + [Serializable] + public class SnippetReplaceableTextElement : SnippetTextElement + { + /// + public override void Insert(InsertionContext context) + { + int start = context.InsertionPosition; + base.Insert(context); + int end = context.InsertionPosition; + context.RegisterActiveElement(this, new ReplaceableActiveElement(context, start, end)); + } + + /// + public override Inline ToTextRun() + { + return new Italic(base.ToTextRun()); + } + } + + /// + /// Interface for active element registered by . + /// + public interface IReplaceableActiveElement : IActiveElement + { + /// + /// Gets the current text inside the element. + /// + string Text { get; } + + /// + /// Occurs when the text inside the element changes. + /// + event EventHandler TextChanged; + } + + sealed class ReplaceableActiveElement : IReplaceableActiveElement, IWeakEventListener + { + readonly InsertionContext context; + readonly int startOffset, endOffset; + TextAnchor start, end; + + public ReplaceableActiveElement(InsertionContext context, int startOffset, int endOffset) + { + this.context = context; + this.startOffset = startOffset; + this.endOffset = endOffset; + } + + void AnchorDeleted(object sender, EventArgs e) + { + context.Deactivate(new SnippetEventArgs(DeactivateReason.Deleted)); + } + + public void OnInsertionCompleted() + { + // anchors must be created in OnInsertionCompleted because they should move only + // due to user insertions, not due to insertions of other snippet parts + start = context.Document.CreateAnchor(startOffset); + start.MovementType = AnchorMovementType.BeforeInsertion; + end = context.Document.CreateAnchor(endOffset); + end.MovementType = AnchorMovementType.AfterInsertion; + start.Deleted += AnchorDeleted; + end.Deleted += AnchorDeleted; + + // Be careful with references from the document to the editing/snippet layer - use weak events + // to prevent memory leaks when the text area control gets dropped from the UI while the snippet is active. + // The InsertionContext will keep us alive as long as the snippet is in interactive mode. + TextDocumentWeakEventManager.TextChanged.AddListener(context.Document, this); + + background = new Renderer { Layer = KnownLayer.Background, element = this }; + foreground = new Renderer { Layer = KnownLayer.Text, element = this }; + context.TextArea.TextView.BackgroundRenderers.Add(background); + context.TextArea.TextView.BackgroundRenderers.Add(foreground); + context.TextArea.Caret.PositionChanged += Caret_PositionChanged; + Caret_PositionChanged(null, null); + + this.Text = GetText(); + } + + public void Deactivate(SnippetEventArgs e) + { + TextDocumentWeakEventManager.TextChanged.RemoveListener(context.Document, this); + context.TextArea.TextView.BackgroundRenderers.Remove(background); + context.TextArea.TextView.BackgroundRenderers.Remove(foreground); + context.TextArea.Caret.PositionChanged -= Caret_PositionChanged; + } + + bool isCaretInside; + + void Caret_PositionChanged(object sender, EventArgs e) + { + ISegment s = this.Segment; + if (s != null) { + bool newIsCaretInside = s.Contains(context.TextArea.Caret.Offset); + if (newIsCaretInside != isCaretInside) { + isCaretInside = newIsCaretInside; + context.TextArea.TextView.InvalidateLayer(foreground.Layer); + } + } + } + + Renderer background, foreground; + + public string Text { get; private set; } + + string GetText() + { + if (start.IsDeleted || end.IsDeleted) + return string.Empty; + else + return context.Document.GetText(start.Offset, Math.Max(0, end.Offset - start.Offset)); + } + + public event EventHandler TextChanged; + + bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) + { + if (managerType == typeof(TextDocumentWeakEventManager.TextChanged)) { + string newText = GetText(); + if (this.Text != newText) { + this.Text = newText; + if (TextChanged != null) + TextChanged(this, e); + } + return true; + } + return false; + } + + public bool IsEditable { + get { return true; } + } + + public ISegment Segment { + get { + if (start.IsDeleted || end.IsDeleted) + return null; + else + return new SimpleSegment(start.Offset, Math.Max(0, end.Offset - start.Offset)); + } + } + + sealed class Renderer : IBackgroundRenderer + { + static readonly Brush backgroundBrush = CreateBackgroundBrush(); + static readonly Pen activeBorderPen = CreateBorderPen(); + + static Brush CreateBackgroundBrush() + { + SolidColorBrush b = new SolidColorBrush(Colors.LimeGreen); + b.Opacity = 0.4; + b.Freeze(); + return b; + } + + static Pen CreateBorderPen() + { + Pen p = new Pen(Brushes.Black, 1); + p.DashStyle = DashStyles.Dot; + p.Freeze(); + return p; + } + + internal ReplaceableActiveElement element; + + public KnownLayer Layer { get; set; } + + public void Draw(TextView textView, System.Windows.Media.DrawingContext drawingContext) + { + ISegment s = element.Segment; + if (s != null) { + BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder(); + geoBuilder.AlignToMiddleOfPixels = true; + if (Layer == KnownLayer.Background) { + geoBuilder.AddSegment(textView, s); + drawingContext.DrawGeometry(backgroundBrush, null, geoBuilder.CreateGeometry()); + } else { + // draw foreground only if active + if (element.isCaretInside) { + geoBuilder.AddSegment(textView, s); + foreach (BoundActiveElement boundElement in element.context.ActiveElements.OfType()) { + if (boundElement.targetElement == element) { + geoBuilder.AddSegment(textView, boundElement.Segment); + geoBuilder.CloseFigure(); + } + } + drawingContext.DrawGeometry(null, activeBorderPen, geoBuilder.CreateGeometry()); + } + } + } + } + } + } +} -- cgit v1.3.1