diff options
| author | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-04-09 01:47:48 +0300 |
|---|---|---|
| committer | Roy Ben Shabat <Roy.mail.net@gmail.com> | 2019-04-09 01:47:48 +0300 |
| commit | 080f1697e97e13461ec6df4d31c8924d01257a1b (patch) | |
| tree | b1fe0285de7bc9bc52e9e2195e66fe022bf8f5b3 /Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs | |
| parent | 1608e69a417bc5e40a607c3958c4a60f19f66f1a (diff) | |
| download | Tango-080f1697e97e13461ec6df4d31c8924d01257a1b.tar.gz Tango-080f1697e97e13461ec6df4d31c8924d01257a1b.zip | |
MERGE
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs new file mode 100644 index 000000000..c3d03646c --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/Folding/FoldingMargin.cs @@ -0,0 +1,343 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.TextFormatting; +using Tango.Scripting.Editors.Editing; +using Tango.Scripting.Editors.Rendering; +using Tango.Scripting.Editors.Utils; + +namespace Tango.Scripting.Editors.Folding +{ + /// <summary> + /// A margin that shows markers for foldings and allows to expand/collapse the foldings. + /// </summary> + public class FoldingMargin : AbstractMargin + { + /// <summary> + /// Gets/Sets the folding manager from which the foldings should be shown. + /// </summary> + public FoldingManager FoldingManager { get; set; } + + internal const double SizeFactor = Constants.PixelPerPoint; + + #region Brushes + /// <summary> + /// FoldingMarkerBrush dependency property. + /// </summary> + public static readonly DependencyProperty FoldingMarkerBrushProperty = + DependencyProperty.RegisterAttached("FoldingMarkerBrush", typeof(Brush), typeof(FoldingMargin), + new FrameworkPropertyMetadata(Brushes.Gainsboro)); + + /// <summary> + /// Gets/sets the Brush used for displaying the lines of folding markers. + /// </summary> + public Brush FoldingMarkerBrush { + get { return (Brush)GetValue(FoldingMarkerBrushProperty); } + set { SetValue(FoldingMarkerBrushProperty, value); } + } + + /// <summary> + /// FoldingMarkerBackgroundBrush dependency property. + /// </summary> + public static readonly DependencyProperty FoldingMarkerBackgroundBrushProperty = + DependencyProperty.RegisterAttached("FoldingMarkerBackgroundBrush", typeof(Brush), typeof(FoldingMargin), + new FrameworkPropertyMetadata(Brushes.Transparent)); + + /// <summary> + /// Gets/sets the Brush used for displaying the background of folding markers. + /// </summary> + public Brush FoldingMarkerBackgroundBrush { + get { return (Brush)GetValue(FoldingMarkerBackgroundBrushProperty); } + set { SetValue(FoldingMarkerBackgroundBrushProperty, value); } + } + + /// <summary> + /// SelectedFoldingMarkerBrush dependency property. + /// </summary> + public static readonly DependencyProperty SelectedFoldingMarkerBrushProperty = + DependencyProperty.RegisterAttached("SelectedFoldingMarkerBrush", + typeof(Brush), typeof(FoldingMargin), + new FrameworkPropertyMetadata(Brushes.Gainsboro)); + + /// <summary> + /// Gets/sets the Brush used for displaying the lines of selected folding markers. + /// </summary> + public Brush SelectedFoldingMarkerBrush { + get { return (Brush)GetValue(SelectedFoldingMarkerBrushProperty); } + set { SetValue(SelectedFoldingMarkerBrushProperty, value); } + } + + /// <summary> + /// SelectedFoldingMarkerBackgroundBrush dependency property. + /// </summary> + public static readonly DependencyProperty SelectedFoldingMarkerBackgroundBrushProperty = + DependencyProperty.RegisterAttached("SelectedFoldingMarkerBackgroundBrush", + typeof(Brush), typeof(FoldingMargin), + new FrameworkPropertyMetadata(Brushes.Transparent)); + + /// <summary> + /// Gets/sets the Brush used for displaying the background of selected folding markers. + /// </summary> + public Brush SelectedFoldingMarkerBackgroundBrush { + get { return (Brush)GetValue(SelectedFoldingMarkerBackgroundBrushProperty); } + set { SetValue(SelectedFoldingMarkerBackgroundBrushProperty, value); } + } + + static void OnUpdateBrushes(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + FoldingMargin m = null; + if (d is FoldingMargin) + m = (FoldingMargin)d; + else if (d is TextEditor) + m = ((TextEditor)d).TextArea.LeftMargins.FirstOrDefault(c => c is FoldingMargin) as FoldingMargin; + if (m == null) return; + if (e.Property.Name == FoldingMarkerBrushProperty.Name) + m.foldingControlPen = MakeFrozenPen((Brush)e.NewValue); + if (e.Property.Name == SelectedFoldingMarkerBrushProperty.Name) + m.selectedFoldingControlPen = MakeFrozenPen((Brush)e.NewValue); + } + #endregion + + /// <inheritdoc/> + protected override Size MeasureOverride(Size availableSize) + { + foreach (FoldingMarginMarker m in markers) { + m.Measure(availableSize); + } + double width = SizeFactor * (double)GetValue(TextBlock.FontSizeProperty); + return new Size(PixelSnapHelpers.RoundToOdd(width, PixelSnapHelpers.GetPixelSize(this).Width), 0); + } + + /// <inheritdoc/> + protected override Size ArrangeOverride(Size finalSize) + { + Size pixelSize = PixelSnapHelpers.GetPixelSize(this); + foreach (FoldingMarginMarker m in markers) { + int visualColumn = m.VisualLine.GetVisualColumn(m.FoldingSection.StartOffset - m.VisualLine.FirstDocumentLine.Offset); + TextLine textLine = m.VisualLine.GetTextLine(visualColumn); + double yPos = m.VisualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextMiddle) - TextView.VerticalOffset; + yPos -= m.DesiredSize.Height / 2; + double xPos = (finalSize.Width - m.DesiredSize.Width) / 2; + m.Arrange(new Rect(PixelSnapHelpers.Round(new Point(xPos, yPos), pixelSize), m.DesiredSize)); + } + return base.ArrangeOverride(finalSize); + } + + /// <inheritdoc/> + protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView) + { + if (oldTextView != null) { + oldTextView.VisualLinesChanged -= TextViewVisualLinesChanged; + } + base.OnTextViewChanged(oldTextView, newTextView); + if (newTextView != null) { + newTextView.VisualLinesChanged += TextViewVisualLinesChanged; + } + TextViewVisualLinesChanged(null, null); + } + + List<FoldingMarginMarker> markers = new List<FoldingMarginMarker>(); + + void TextViewVisualLinesChanged(object sender, EventArgs e) + { + foreach (FoldingMarginMarker m in markers) { + RemoveVisualChild(m); + } + markers.Clear(); + InvalidateVisual(); + if (TextView != null && FoldingManager != null && TextView.VisualLinesValid) { + foreach (VisualLine line in TextView.VisualLines) { + FoldingSection fs = FoldingManager.GetNextFolding(line.FirstDocumentLine.Offset); + if (fs == null) + continue; + if (fs.StartOffset <= line.LastDocumentLine.Offset + line.LastDocumentLine.Length) { + FoldingMarginMarker m = new FoldingMarginMarker { + IsExpanded = !fs.IsFolded, + VisualLine = line, + FoldingSection = fs + }; + + markers.Add(m); + AddVisualChild(m); + + m.IsMouseDirectlyOverChanged += delegate { InvalidateVisual(); }; + + InvalidateMeasure(); + continue; + } + } + } + } + + /// <inheritdoc/> + protected override int VisualChildrenCount { + get { return markers.Count; } + } + + /// <inheritdoc/> + protected override Visual GetVisualChild(int index) + { + return markers[index]; + } + + Pen foldingControlPen = MakeFrozenPen((Brush)FoldingMarkerBrushProperty.DefaultMetadata.DefaultValue); + Pen selectedFoldingControlPen = MakeFrozenPen((Brush)SelectedFoldingMarkerBrushProperty.DefaultMetadata.DefaultValue); + + static Pen MakeFrozenPen(Brush brush) + { + Pen pen = new Pen(brush, 1); + pen.Freeze(); + return pen; + } + + /// <inheritdoc/> + protected override void OnRender(DrawingContext drawingContext) + { + if (TextView == null || !TextView.VisualLinesValid) + return; + if (TextView.VisualLines.Count == 0 || FoldingManager == null) + return; + + var allTextLines = TextView.VisualLines.SelectMany(vl => vl.TextLines).ToList(); + Pen[] colors = new Pen[allTextLines.Count + 1]; + Pen[] endMarker = new Pen[allTextLines.Count]; + + CalculateFoldLinesForFoldingsActiveAtStart(allTextLines, colors, endMarker); + CalculateFoldLinesForMarkers(allTextLines, colors, endMarker); + DrawFoldLines(drawingContext, colors, endMarker); + + base.OnRender(drawingContext); + } + + /// <summary> + /// Calculates fold lines for all folding sections that start in front of the current view + /// and run into the current view. + /// </summary> + void CalculateFoldLinesForFoldingsActiveAtStart(List<TextLine> allTextLines, Pen[] colors, Pen[] endMarker) + { + int viewStartOffset = TextView.VisualLines[0].FirstDocumentLine.Offset; + int viewEndOffset = TextView.VisualLines.Last().LastDocumentLine.EndOffset; + var foldings = FoldingManager.GetFoldingsContaining(viewStartOffset); + int maxEndOffset = 0; + foreach (FoldingSection fs in foldings) { + int end = fs.EndOffset; + if (end <= viewEndOffset && !fs.IsFolded) { + int textLineNr = GetTextLineIndexFromOffset(allTextLines, end); + if (textLineNr >= 0) { + endMarker[textLineNr] = foldingControlPen; + } + } + if (end > maxEndOffset && fs.StartOffset < viewStartOffset) { + maxEndOffset = end; + } + } + if (maxEndOffset > 0) { + if (maxEndOffset > viewEndOffset) { + for (int i = 0; i < colors.Length; i++) { + colors[i] = foldingControlPen; + } + } else { + int maxTextLine = GetTextLineIndexFromOffset(allTextLines, maxEndOffset); + for (int i = 0; i <= maxTextLine; i++) { + colors[i] = foldingControlPen; + } + } + } + } + + /// <summary> + /// Calculates fold lines for all folding sections that start inside the current view + /// </summary> + void CalculateFoldLinesForMarkers(List<TextLine> allTextLines, Pen[] colors, Pen[] endMarker) + { + foreach (FoldingMarginMarker marker in markers) { + int end = marker.FoldingSection.EndOffset; + int endTextLineNr = GetTextLineIndexFromOffset(allTextLines, end); + if (!marker.FoldingSection.IsFolded && endTextLineNr >= 0) { + if (marker.IsMouseDirectlyOver) + endMarker[endTextLineNr] = selectedFoldingControlPen; + else if (endMarker[endTextLineNr] == null) + endMarker[endTextLineNr] = foldingControlPen; + } + int startTextLineNr = GetTextLineIndexFromOffset(allTextLines, marker.FoldingSection.StartOffset); + if (startTextLineNr >= 0) { + for (int i = startTextLineNr + 1; i < colors.Length && i - 1 != endTextLineNr; i++) { + if (marker.IsMouseDirectlyOver) + colors[i] = selectedFoldingControlPen; + else if (colors[i] == null) + colors[i] = foldingControlPen; + } + } + } + } + + /// <summary> + /// Draws the lines for the folding sections (vertical line with 'color', horizontal lines with 'endMarker') + /// Each entry in the input arrays corresponds to one TextLine. + /// </summary> + void DrawFoldLines(DrawingContext drawingContext, Pen[] colors, Pen[] endMarker) + { + // Because we are using PenLineCap.Flat (the default), for vertical lines, + // Y coordinates must be on pixel boundaries, whereas the X coordinate must be in the + // middle of a pixel. (and the other way round for horizontal lines) + Size pixelSize = PixelSnapHelpers.GetPixelSize(this); + double markerXPos = PixelSnapHelpers.PixelAlign(RenderSize.Width / 2, pixelSize.Width); + double startY = 0; + Pen currentPen = colors[0]; + + Pen p = new Pen(FoldingMarkerBrush, 1); + + int tlNumber = 0; + foreach (VisualLine vl in TextView.VisualLines) { + foreach (TextLine tl in vl.TextLines) { + if (endMarker[tlNumber] != null) { + double visualPos = GetVisualPos(vl, tl, pixelSize.Height); + drawingContext.DrawLine(p, new Point(markerXPos - pixelSize.Width / 2, visualPos), new Point(RenderSize.Width - 4, visualPos)); + } + if (colors[tlNumber + 1] != currentPen) { + double visualPos = GetVisualPos(vl, tl, pixelSize.Height); + if (currentPen != null) { + drawingContext.DrawLine(p, new Point(markerXPos, startY + pixelSize.Height), new Point(markerXPos, visualPos - pixelSize.Height / 2)); + } + currentPen = colors[tlNumber + 1]; + startY = visualPos; + } + tlNumber++; + } + } + if (currentPen != null) { + drawingContext.DrawLine(currentPen, new Point(markerXPos, startY + pixelSize.Height / 2), new Point(markerXPos, RenderSize.Height)); + } + } + + double GetVisualPos(VisualLine vl, TextLine tl, double pixelHeight) + { + double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.TextMiddle) - TextView.VerticalOffset; + return PixelSnapHelpers.PixelAlign(pos, pixelHeight); + } + + int GetTextLineIndexFromOffset(List<TextLine> textLines, int offset) + { + int lineNumber = TextView.Document.GetLineByOffset(offset).LineNumber; + VisualLine vl = TextView.GetVisualLine(lineNumber); + if (vl != null) { + int relOffset = offset - vl.FirstDocumentLine.Offset; + TextLine line = vl.GetTextLine(vl.GetVisualColumn(relOffset)); + return textLines.IndexOf(line); + } + return -1; + } + + static FoldingMargin() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(FoldingMargin), new FrameworkPropertyMetadata(typeof(FoldingMargin))); + } + } +} |
