From a7f9782bd2d673368dc4f2903ed72ed1791f344f Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 7 Sep 2016 13:46:09 -0700 Subject: [PATCH] Suggestion tag improvements. 1. Increase opacity 2. Support user configuration of the suggestion tag color. 3. Refresh the color when the user changes fonts and colors. --- .../Core/EditorFeaturesResources.Designer.cs | 9 +++ .../Core/EditorFeaturesResources.resx | 3 + .../Implementation/Adornments/GraphicsTag.cs | 44 ++++++++++---- .../DiagnosticsSuggestionTaggerProvider.cs | 3 + .../Diagnostics/SuggestionTag.cs | 59 +++++++++++++++---- .../LineSeparators/LineSeparatorTag.cs | 23 +++++++- .../LineSeparatorTaggerProvider.cs | 6 +- .../AbstractAsynchronousTaggerProvider.cs | 9 ++- 8 files changed, 128 insertions(+), 28 deletions(-) diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs b/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs index 2d07b02e11e..1cd471135b9 100644 --- a/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs +++ b/src/EditorFeatures/Core/EditorFeaturesResources.Designer.cs @@ -1467,6 +1467,15 @@ internal class EditorFeaturesResources { } } + /// + /// Looks up a localized string similar to Suggestion ellipses (…). + /// + internal static string Suggestion_ellipses { + get { + return ResourceManager.GetString("Suggestion_ellipses", resourceCulture); + } + } + /// /// Looks up a localized string similar to 'symbol' cannot be a namespace.. /// diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.resx b/src/EditorFeatures/Core/EditorFeaturesResources.resx index 006622346b1..dbe2f1d2086 100644 --- a/src/EditorFeatures/Core/EditorFeaturesResources.resx +++ b/src/EditorFeatures/Core/EditorFeaturesResources.resx @@ -748,4 +748,7 @@ Do you want to proceed? Navigating... + + Suggestion ellipses (…) + \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs index 8443b37773a..d9a0460bbef 100644 --- a/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs +++ b/src/EditorFeatures/Core/Implementation/Adornments/GraphicsTag.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Diagnostics; using System.Windows.Media; +using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; @@ -11,31 +13,53 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments /// internal abstract class GraphicsTag : ITag { - protected static SolidColorBrush VerticalRuleBrush; - protected static Color VerticalRuleColor; + private IEditorFormatMapService _editorFormatMapService; + private IEditorFormatMap _editorFormatMap; - protected virtual void Initialize(IWpfTextView view) + protected virtual void Initialize(IWpfTextView view, + ref Brush graphicsTagBrush, + ref Color graphicsTagColor) { - if (VerticalRuleBrush != null) + if (graphicsTagBrush != null) { return; } + Debug.Assert(_editorFormatMap == null); + _editorFormatMap = _editorFormatMapService.GetEditorFormatMap("text"); + _editorFormatMap.FormatMappingChanged += OnFormatMappingChanged; + // TODO: Refresh this when the user changes fonts and colors - // TODO: Get from resources - var lightGray = Color.FromRgb(0xE0, 0xE0, 0xE0); + // If we can't get the color for some reason, fall back to a hardcoded value + // the editor has for outlining. + var lightGray = Color.FromRgb(0xA5, 0xA5, 0xA5); + + var color = this.GetColor(view, _editorFormatMap) ?? lightGray; - var outliningForegroundBrush = view.VisualElement.TryFindResource("outlining.verticalrule.foreground") as SolidColorBrush; - var color = outliningForegroundBrush?.Color ?? lightGray; + graphicsTagColor = color; + graphicsTagBrush = new SolidColorBrush(graphicsTagColor); + } - VerticalRuleColor = color; - VerticalRuleBrush = new SolidColorBrush(VerticalRuleColor); + protected abstract Color? GetColor(IWpfTextView view, IEditorFormatMap editorFormatMap); + + private void OnFormatMappingChanged(object sender, FormatItemsEventArgs e) + { + _editorFormatMap.FormatMappingChanged -= OnFormatMappingChanged; + _editorFormatMap = null; + this.ClearCachedFormatData(); } + protected abstract void ClearCachedFormatData(); + /// /// This method allows corresponding adornment manager to ask for a graphical glyph. /// public abstract GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds); + + internal void RegisterService(IEditorFormatMapService editorFormatMapService) + { + _editorFormatMapService = editorFormatMapService; + } } } \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs index 7cff93fae12..a372653c2fd 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/DiagnosticsSuggestionTaggerProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; @@ -27,11 +28,13 @@ internal partial class DiagnosticsSuggestionTaggerProvider : [ImportingConstructor] public DiagnosticsSuggestionTaggerProvider( + IEditorFormatMapService editorFormatMapService, IDiagnosticService diagnosticService, IForegroundNotificationService notificationService, [ImportMany] IEnumerable> listeners) : base(diagnosticService, notificationService, listeners) { + SuggestionTag.Instance.RegisterService(editorFormatMapService); } protected internal override bool IncludeDiagnostic(DiagnosticData diagnostic) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs index ab2ff32a60c..f06dd2f9ee3 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/SuggestionTag.cs @@ -1,32 +1,69 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.ComponentModel.Composition; using System.Windows.Media; using System.Windows.Shapes; using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; +using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics { + [Export(typeof(EditorFormatDefinition))] + [Name(SuggestionTagFormat.ResourceName)] + [UserVisible(true)] + internal sealed class SuggestionTagFormat : EditorFormatDefinition + { + public const string ResourceName = "SuggestionTagFormat"; + + public SuggestionTagFormat() + { + this.ForegroundColor = Color.FromArgb(200, 0xA5, 0xA5, 0xA5); + this.BackgroundCustomizable = false; + this.DisplayName = EditorFeaturesResources.Suggestion_ellipses; + } + } + /// /// Tag that specifies line separator. /// internal class SuggestionTag : GraphicsTag { + private static Brush s_graphicsTagBrush; + private static Color s_graphicsTagColor; + private static Pen s_graphicsTagPen; + public static readonly SuggestionTag Instance = new SuggestionTag(); - private static Pen s_pen; + protected override Color? GetColor( + IWpfTextView view, IEditorFormatMap editorFormatMap) + { + var property = editorFormatMap.GetProperties(SuggestionTagFormat.ResourceName)["ForegroundColor"]; + return property as Color?; + } + + protected override void ClearCachedFormatData() + { + s_graphicsTagBrush = null; + s_graphicsTagColor = default(Color); + s_graphicsTagPen = null; + } - protected override void Initialize(IWpfTextView view) + protected override void Initialize(IWpfTextView view, + ref Brush graphicsTagBrush, + ref Color graphicsTagColor) { - base.Initialize(view); + base.Initialize(view, ref graphicsTagBrush, ref graphicsTagColor); - if (s_pen != null) + if (s_graphicsTagPen != null) { return; } - var color = Color.FromArgb(200, VerticalRuleColor.R, VerticalRuleColor.G, VerticalRuleColor.B); - s_pen = new Pen + var color = graphicsTagColor; + s_graphicsTagPen = new Pen { Brush = new SolidColorBrush(color), DashStyle = DashStyles.Dot, @@ -37,7 +74,7 @@ protected override void Initialize(IWpfTextView view) public override GraphicsResult GetGraphics(IWpfTextView view, Geometry geometry) { - Initialize(view); + Initialize(view, ref s_graphicsTagBrush, ref s_graphicsTagColor); // We clip off a bit off the start of the line to prevent a half-square being // drawn. @@ -47,17 +84,17 @@ public override GraphicsResult GetGraphics(IWpfTextView view, Geometry geometry) var line = new Line { X1 = geometry.Bounds.Left, - Y1 = geometry.Bounds.Bottom - s_pen.Thickness, + Y1 = geometry.Bounds.Bottom - s_graphicsTagPen.Thickness, X2 = geometry.Bounds.Right, - Y2 = geometry.Bounds.Bottom - s_pen.Thickness, + Y2 = geometry.Bounds.Bottom - s_graphicsTagPen.Thickness, Clip = new RectangleGeometry { Rect = clipRectangle } }; // RenderOptions.SetEdgeMode(line, EdgeMode.Aliased); - ApplyPen(line, s_pen); + ApplyPen(line, s_graphicsTagPen); // Shift the line over to offset the clipping we did. - line.RenderTransform = new TranslateTransform(-s_pen.Thickness, 0); + line.RenderTransform = new TranslateTransform(-s_graphicsTagPen.Thickness, 0); return new GraphicsResult(line, null); } diff --git a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs index f60b309e17e..41909f4f854 100644 --- a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs +++ b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTag.cs @@ -5,6 +5,7 @@ using System.Windows.Controls; using System.Windows.Media; using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; +using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators @@ -14,17 +15,33 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators /// internal class LineSeparatorTag : GraphicsTag { + private static Brush s_graphicsTagBrush; + private static Color s_graphicsTagColor; + public static readonly LineSeparatorTag Instance = new LineSeparatorTag(); + protected override Color? GetColor( + IWpfTextView view, IEditorFormatMap editorFormatMap) + { + var brush = view.VisualElement.TryFindResource("outlining.verticalrule.foreground") as SolidColorBrush; + return brush?.Color; + } + + protected override void ClearCachedFormatData() + { + s_graphicsTagBrush = null; + s_graphicsTagColor = default(Color); + } + /// /// Creates a very long line at the bottom of bounds. /// public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) { - Initialize(view); + Initialize(view, ref s_graphicsTagBrush, ref s_graphicsTagColor); var border = new Border(); - border.BorderBrush = VerticalRuleBrush; + border.BorderBrush = s_graphicsTagBrush; border.BorderThickness = new Thickness(0, 0, 0, bottom: 1); border.Height = 1; border.Width = view.ViewportWidth; @@ -45,4 +62,4 @@ public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) () => view.ViewportWidthChanged -= viewportWidthChangedHandler); } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTaggerProvider.cs index b99fd02edba..9cb319905f8 100644 --- a/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/LineSeparators/LineSeparatorTaggerProvider.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; @@ -34,13 +35,16 @@ internal partial class LineSeparatorTaggerProvider : AsynchronousTaggerProvider< [ImportingConstructor] public LineSeparatorTaggerProvider( + IEditorFormatMapService editorFormatMapService, IForegroundNotificationService notificationService, [ImportMany] IEnumerable> asyncListeners) : base(new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.LineSeparators), notificationService) { + LineSeparatorTag.Instance.RegisterService(editorFormatMapService); } - protected override ITaggerEventSource CreateEventSource(ITextView textViewOpt, ITextBuffer subjectBuffer) + protected override ITaggerEventSource CreateEventSource( + ITextView textView, ITextBuffer subjectBuffer) { return TaggerEventSources.OnTextChanged(subjectBuffer, TaggerDelay.NearImmediate); } diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs index 73b51229dac..2e77828c490 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs @@ -109,9 +109,12 @@ private TagSource CreateTagSource(ITextView textViewOpt, ITextBuffer subjectBuff } var tagSource = GetOrCreateTagSource(textViewOpt, subjectBuffer); - return tagSource == null - ? null - : new Tagger(_asyncListener, _notificationService, tagSource, subjectBuffer) as IAccurateTagger; + if (tagSource == null) + { + return null; + } + + return new Tagger(_asyncListener, _notificationService, tagSource, subjectBuffer) as IAccurateTagger; } private TagSource GetOrCreateTagSource(ITextView textViewOpt, ITextBuffer subjectBuffer) -- GitLab