/// <summary> /// Menu-command handler, undo the last occurrence /// </summary> /// <param name="sender"></param> /// <param name="e"></param> internal override void OnCommandInvoked(object sender, EventArgs e) { if (!View.HasAggregateFocus) { return; } if (AdornmentLayer.Selector.Selections.Count > 1) { AdornmentLayer.Selector.Selections.RemoveAt(AdornmentLayer.Selector.Selections.Count - 1); if (AdornmentLayer.Selector.Selections.Count == 1) { AdornmentLayer.Selector.HasWrappedDocument = false; } } if (AdornmentLayer.Selector.Selections.Any()) { AdornmentLayer.DrawAdornments(); View.Caret.MoveTo(AdornmentLayer.Selector.Selections.Last().Caret.GetPoint(AdornmentLayer.Snapshot)); View.ViewScroller.EnsureSpanVisible( new SnapshotSpan(View.Caret.Position.BufferPosition, 0) ); } }
/// <summary> /// Need to remove the tags if they intersect with the editor view, but only if the option /// to place the tags at the end of the editor is selected. /// </summary> private void TextView_ViewportWidthChanged(object sender, EventArgs e) { // this method should only run on UI thread as we do WPF here. Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); if (AdornmentLayer is null) { return; } var document = TextView.TextBuffer.AsTextContainer()?.GetOpenDocumentInCurrentContext(); if (document is null) { AdornmentLayer.RemoveAllAdornments(); return; } var option = _globalOptions.GetOption(InlineDiagnosticsOptions.Location, document.Project.Language); if (option == InlineDiagnosticsLocations.PlacedAtEndOfEditor) { var normalizedCollectionSpan = new NormalizedSnapshotSpanCollection(TextView.TextViewLines.FormattedSpan); UpdateSpans_CallOnlyOnUIThread(normalizedCollectionSpan, removeOldTags: true); } }
public void CleanUp() { if (Image != null) { AdornmentLayer.RemoveAdornment(Image); Image = null; } }
protected override void AddAdornment(ITextViewLine viewLine, TagSnapshotRange <LinkTag> tagRange, TextBounds bounds) { // Create the adornment var element = CreateDecorator(bounds.Width); var location = new Point(Math.Round(bounds.Left), bounds.Bottom - 2); // Add the adornment to the layer AdornmentLayer.AddAdornment(element, location, viewLine, tagRange.SnapshotRange, TextRangeTrackingModes.ExpandBothEdges, null); }
private void CreateVisuals() { Image = CreateImage(); Canvas.SetTop(Image, TextView.ViewportTop); Canvas.SetLeft(Image, CalcLeft()); AdornmentLayer.AddAdornment(null, Image); }
private void SetCommonProperties(AdornmentLayer layer) { layer.Name = Name; layer.Width = width; layer.Height = Height; layer.Location = Location; layer.XOffsetInPixel = offsetX; layer.YOffsetInPixel = offsetY; layer.BackgroundMask = BackMask; }
private void CreateVisuals() { var geo = new LineGeometry(new Point(0, 0), new Point(0, TextView.ViewportHeight)).FreezeAnd(); var img = geo.ToImage(null, Pen); Top = TextView.ViewportTop; Left = TextView.Caret.Left; Canvas.SetTop(img, Top); Canvas.SetLeft(img, Left); Panel.SetZIndex(img, 101); Image = img; AdornmentLayer.AddAdornment(typeof(LineIndicatorAdornment), Image); }
/// <summary> /// Menu-command handler, puts cursors at a selections line-ends /// </summary> /// <param name="sender"></param> /// <param name="e"></param> internal override void OnCommandInvoked(object sender, EventArgs e) { if (!View.HasAggregateFocus || View.Selection.IsEmpty) { return; } AdornmentLayer.Selector.ConvertSelectionToMultipleCursors(); if (AdornmentLayer.Selector.Selections.Any()) { AdornmentLayer.DrawAdornments(); } }
/// <summary> /// Menu-command handler, aka Ctrl+D /// </summary> /// <param name="sender"></param> /// <param name="e"></param> internal override void OnCommandInvoked(object sender, EventArgs e) { if (!View.HasAggregateFocus) { return; } AdornmentLayer.Selector.SelectAllOccurrences(); if (AdornmentLayer.Selector.Selections.Any()) { AdornmentLayer.DrawAdornments(); } }
/// <summary> /// Menu-command handler, selects previous exact occurrence /// </summary> /// <param name="sender"></param> /// <param name="e"></param> internal override void OnCommandInvoked(object sender, EventArgs e) { if (!View.HasAggregateFocus) { return; } AdornmentLayer.Selector.SelectNextOccurrence(reverseDirection: true, exactMatch: true); if (AdornmentLayer.Selector.Selections.Any()) { AdornmentLayer.DrawAdornments(); } }
/// <summary> /// Menu-command handler, adds a new caret one line above the active caret /// </summary> /// <param name="sender"></param> /// <param name="e"></param> internal override void OnCommandInvoked(object sender, EventArgs e) { if (!View.HasAggregateFocus) { return; } if (!AdornmentLayer.Selector.Selections.Any()) { // Add current caret AdornmentLayer.Selector.AddCurrentCaretToSelections(); } AdornmentLayer.Selector.AddCaretAbove(); AdornmentLayer.DrawAdornments(); }
protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection) { // this method should only run on UI thread as we do WPF here. Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); var viewSnapshot = TextView.TextSnapshot; var viewLines = TextView.TextViewLines; foreach (var changedSpan in changedSpanCollection) { // is there any effect on the view? if (!viewLines.IntersectsBufferSpan(changedSpan)) { continue; } var tagSpans = TagAggregator.GetTags(changedSpan); foreach (var tagMappingSpan in tagSpans) { if (!ShouldDrawTag(changedSpan, tagMappingSpan, out _)) { continue; } if (!TryMapToSingleSnapshotSpan(tagMappingSpan.Span, TextView.TextSnapshot, out var span)) { continue; } // add the visual to the adornment layer. var geometry = viewLines.GetMarkerGeometry(span); if (geometry != null) { var tag = tagMappingSpan.Tag; var graphicsResult = tag.GetGraphics(TextView, geometry, format: null); AdornmentLayer.AddAdornment( behavior: AdornmentPositioningBehavior.TextRelative, visualSpan: span, tag: tag, adornment: graphicsResult.VisualElement, removedCallback: delegate { graphicsResult.Dispose(); }); } } } }
/// <summary> /// Iterates through the mapping of line number to span and draws the diagnostic in the appropriate position on the screen, /// as well as adding the tag to the adornment layer. /// </summary> protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection) { // this method should only run on UI thread as we do WPF here. Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); if (changedSpanCollection.IsEmpty()) { return; } var viewLines = TextView.TextViewLines; using var _ = PooledDictionary <int, IMappingTagSpan <InlineDiagnosticsTag> > .GetInstance(out var map); AddSpansOnEachLine(changedSpanCollection, map); foreach (var(lineNum, tagMappingSpan) in map) { // Mapping the IMappingTagSpan back up to the TextView's visual snapshot to ensure there will // be no adornments drawn on disjoint spans. if (!TryMapToSingleSnapshotSpan(tagMappingSpan.Span, TextView.TextSnapshot, out var span)) { continue; } var geometry = viewLines.GetMarkerGeometry(span); if (geometry is null) { continue; } // Need to get the SnapshotPoint to be able to get the IWpfTextViewLine var point = tagMappingSpan.Span.Start.GetPoint(TextView.TextSnapshot, PositionAffinity.Predecessor); if (point == null) { continue; } var lineView = viewLines.GetTextViewLineContainingBufferPosition(point.Value); if (lineView is null) { continue; } // Looking for IEndOfLineTags and seeing if they exist on the same line as where the // diagnostic would be drawn. If they are the same, then we do not want to draw // the diagnostic. var obstructingTags = _endLineTagAggregator.GetTags(lineView.Extent); if (obstructingTags.Where(tag => tag.Tag.Type is not "Inline Diagnostics").Any()) { continue; } var tag = tagMappingSpan.Tag; var classificationType = _classificationRegistryService.GetClassificationType(InlineDiagnosticsTag.GetClassificationId(tag.ErrorType)); var graphicsResult = tag.GetGraphics(TextView, geometry, GetFormat(classificationType)); var visualElement = graphicsResult.VisualElement; // Only place the diagnostics if the diagnostic would not intersect with the editor window if (lineView.Right >= TextView.ViewportWidth - visualElement.DesiredSize.Width) { continue; } Canvas.SetLeft(visualElement, tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfCode ? lineView.Right : tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfEditor ? TextView.ViewportRight - visualElement.DesiredSize.Width : throw ExceptionUtilities.UnexpectedValue(tag.Location)); Canvas.SetTop(visualElement, geometry.Bounds.Bottom - visualElement.DesiredSize.Height); AdornmentLayer.AddAdornment( behavior: AdornmentPositioningBehavior.TextRelative, visualSpan: lineView.Extent, tag: tag, adornment: visualElement, removedCallback: delegate { graphicsResult.Dispose(); }); } }
/// <summary> /// Iterates through the mapping of line number to span and draws the diagnostic in the appropriate position on the screen, /// as well as adding the tag to the adornment layer. /// </summary> protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection) { // this method should only run on UI thread as we do WPF here. Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); if (changedSpanCollection.IsEmpty()) { return; } var viewLines = TextView.TextViewLines; using var _ = PooledDictionary <IWpfTextViewLine, IMappingTagSpan <InlineDiagnosticsTag> > .GetInstance(out var map); // First loop iterates through the snap collection and determines if an inline diagnostic can be drawn. // Creates a mapping of the view line to the IMappingTagSpan with getting the first error that appears // on the line if there are multiple. foreach (var changedSpan in changedSpanCollection) { if (!viewLines.IntersectsBufferSpan(changedSpan)) { continue; } var tagSpans = TagAggregator.GetTags(changedSpan); foreach (var tagMappingSpan in tagSpans) { if (!ShouldDrawTag(changedSpan, tagMappingSpan, out var mappedPoint)) { continue; } var viewLine = viewLines.GetTextViewLineContainingBufferPosition(mappedPoint); // If the line does not have an associated tagMappingSpan and changedSpan, then add the first one. if (!map.TryGetValue(viewLine, out var value)) { map.Add(viewLine, tagMappingSpan); } else if (value.Tag.ErrorType is not PredefinedErrorTypeNames.SyntaxError && tagMappingSpan.Tag.ErrorType is PredefinedErrorTypeNames.SyntaxError) { // Draw the first instance of an error, if what is stored in the map at a specific line is // not an error, then replace it. Otherwise, just get the first warning on the line. map[viewLine] = tagMappingSpan; } } } // Second loop iterates through the map to go through and create the graphics that is being drawn // on the canvas as well adding the tag to the Inline Diagnostics adornment layer. foreach (var(lineView, tagMappingSpan) in map) { // Looking for IEndOfLineTags and seeing if they exist on the same line as where the // diagnostic would be drawn. If they are the same, then we do not want to draw // the diagnostic. var obstructingTags = _endLineTagAggregator.GetTags(lineView.Extent); if (obstructingTags.Where(tag => tag.Tag.Type is not "Inline Diagnostics").Any()) { continue; } var tag = tagMappingSpan.Tag; var classificationType = _classificationRegistryService.GetClassificationType(InlineDiagnosticsTag.GetClassificationId(tag.ErrorType)); // Pass in null! because the geometry is unused for drawing anything for Inline Diagnostics var graphicsResult = tag.GetGraphics(TextView, unused: null !, GetFormat(classificationType)); var visualElement = graphicsResult.VisualElement; // Only place the diagnostics if the diagnostic would not intersect with the editor window if (lineView.Right >= TextView.ViewportWidth - visualElement.DesiredSize.Width) { graphicsResult.Dispose(); continue; } Canvas.SetLeft(visualElement, tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfCode ? lineView.Right : tag.Location == InlineDiagnosticsLocations.PlacedAtEndOfEditor ? TextView.ViewportRight - visualElement.DesiredSize.Width : throw ExceptionUtilities.UnexpectedValue(tag.Location)); Canvas.SetTop(visualElement, lineView.Bottom - visualElement.DesiredSize.Height); AdornmentLayer.AddAdornment( behavior: AdornmentPositioningBehavior.TextRelative, visualSpan: lineView.Extent, tag: tag, adornment: visualElement, removedCallback: delegate { graphicsResult.Dispose(); }); } }
public CommandTarget(IWpfTextView view) { this.view = view; this.adornmentLayer = view.Properties .GetProperty <AdornmentLayer>(typeof(AdornmentLayer)); }
protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection) { // this method should only run on UI thread as we do WPF here. Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); var viewSnapshot = TextView.TextSnapshot; var viewLines = TextView.TextViewLines; foreach (var changedSpan in changedSpanCollection) { if (!viewLines.IntersectsBufferSpan(changedSpan)) { continue; } var tagSpans = TagAggregator.GetTags(changedSpan); foreach (var tagMappingSpan in tagSpans) { if (!ShouldDrawTag(changedSpan, tagMappingSpan, out _)) { continue; } if (!TryMapToSingleSnapshotSpan(tagMappingSpan.Span, TextView.TextSnapshot, out var span)) { continue; } if (!TryMapHoleSpans(tagMappingSpan.Tag.OrderedHoleSpans, out var orderedHoleSpans)) { continue; } if (VisibleBlock.CreateVisibleBlock(span, orderedHoleSpans, TextView) is not VisibleBlock block) { continue; } var tag = tagMappingSpan.Tag; var brush = tag.GetBrush(TextView); foreach (var(start, end) in block.YSegments) { var line = new Line { SnapsToDevicePixels = true, StrokeThickness = 1.0, X1 = block.X, X2 = block.X, Y1 = start, Y2 = end, Stroke = brush, }; AdornmentLayer.AddAdornment( behavior: AdornmentPositioningBehavior.TextRelative, visualSpan: span, tag: block, adornment: line, removedCallback: delegate { }); } } } }
public static ScaleLineOrBarAdornmentLayerViewModel CreateInstance(AdornmentLayer scaleLayer) { ScaleLineOrBarAdornmentLayerViewModel newLayer = new ScaleLineOrBarAdornmentLayerViewModel(); if (scaleLayer is ScaleBarAdornmentLayer) { newLayer.ScaleType = ScaleType.ScaleBar; newLayer.NumericFormatType = ScaleNumericFormatType.None; string formatString = ((ScaleBarAdornmentLayer)scaleLayer).TextStyle.NumericFormat.ToUpperInvariant(); switch (formatString) { case "C": newLayer.NumericFormatType = ScaleNumericFormatType.Currency; break; case "D": newLayer.NumericFormatType = ScaleNumericFormatType.Decimal; break; case "E": newLayer.NumericFormatType = ScaleNumericFormatType.Scientific; break; case "F": newLayer.NumericFormatType = ScaleNumericFormatType.FixedPoint; break; case "G": newLayer.NumericFormatType = ScaleNumericFormatType.General; break; case "N": newLayer.NumericFormatType = ScaleNumericFormatType.Number; break; case "P": newLayer.NumericFormatType = ScaleNumericFormatType.Percent; break; case "R": newLayer.NumericFormatType = ScaleNumericFormatType.RoundTrip; break; case "X": newLayer.NumericFormatType = ScaleNumericFormatType.Hexadecimal; break; default: break; } newLayer.selectedScaleBarUnitSystem = ((ScaleBarAdornmentLayer)scaleLayer).UnitFamily; } else { newLayer.ScaleType = ScaleType.ScaleLine; newLayer.selectedScaleLineUnitSystem = ((ScaleLineAdornmentLayer)scaleLayer).UnitSystem; } newLayer.Name = scaleLayer.Name; newLayer.Width = (int)scaleLayer.Width; newLayer.Location = scaleLayer.Location; newLayer.OffsetX = (int)scaleLayer.XOffsetInPixel; newLayer.OffsetY = (int)scaleLayer.YOffsetInPixel; newLayer.BackMask = scaleLayer.BackgroundMask; newLayer.Height = (int)scaleLayer.Height; if (scaleLayer is ScaleBarAdornmentLayer) { ScaleBarAdornmentLayer scaleBarLayer = (ScaleBarAdornmentLayer)scaleLayer; newLayer.MaximumWidth = scaleBarLayer.MaxWidth; newLayer.Thickness = scaleBarLayer.Thickness; newLayer.ForeColor = scaleBarLayer.BarBrush; newLayer.AlteColor = scaleBarLayer.AlternateBarBrush; newLayer.SelectedScaleBarUnitSystem = scaleBarLayer.UnitFamily; } return(newLayer); }
protected CmdBase(IWpfTextView view) { this.view = view; this.adornmentLayer = view.Properties .GetProperty <AdornmentLayer>(typeof(AdornmentLayer)); }