Wednesday, June 27, 2018

QuickInfoSession is dismissed prematurely when using UserControls in quickInfoContent

Leave a Comment

First some boilerplate code; question below.

I have a standard QuickInfoSourceProvider:

[Export(typeof(IIntellisenseControllerProvider))] internal sealed class QuickInfoControllerProvider : IIntellisenseControllerProvider {     [Import]     private IQuickInfoBroker _quickInfoBroker = null;      public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList<ITextBuffer> subjectBuffers)     {         return new QuickInfoController(textView, subjectBuffers, this._quickInfoBroker);     } } 

And a standard QuickInfoController:

internal sealed class QuickInfoController : IIntellisenseController {     private readonly IList<ITextBuffer> _subjectBuffers;     private readonly IQuickInfoBroker _quickInfoBroker;     private ITextView _textView;      internal QuickInfoController(         ITextView textView,         IList<ITextBuffer> subjectBuffers,         IQuickInfoBroker quickInfoBroker)     {         this._textView = textView;         this._subjectBuffers = subjectBuffers;         this._quickInfoBroker = quickInfoBroker;         this._textView.MouseHover += (o, e) => {             SnapshotPoint? point = GetMousePosition(new SnapshotPoint(this._textView.TextSnapshot, e.Position));             if (point.HasValue)             {                 ITrackingPoint triggerPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position, PointTrackingMode.Positive);                 if (!this._quickInfoBroker.IsQuickInfoActive(this._textView))                 {                     this._quickInfoBroker.TriggerQuickInfo(this._textView, triggerPoint, false);                 }             }         };     }     private SnapshotPoint? GetMousePosition(SnapshotPoint topPosition)     {         return this._textView.BufferGraph.MapDownToFirstMatch(             topPosition,             PointTrackingMode.Positive,             snapshot => this._subjectBuffers.Contains(snapshot.TextBuffer),             PositionAffinity.Predecessor         );     } } 

The QuickInfoSourceProvider is also standaard:

[Export(typeof(IQuickInfoSourceProvider))] internal sealed class QuickInfoSourceProvider : IQuickInfoSourceProvider {     public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer buffer)     {         Func<QuickInfoSource> sc = delegate () {             return new QuickInfoSource(buffer);         };         return buffer.Properties.GetOrCreateSingletonProperty(sc);     } } 

Now things become interesting: instead of adding a plain string into quickInfoContent I pass in a UserControl (BugWindow); but still, pretty standard:

internal sealed class QuickInfoSource : IQuickInfoSource {     private readonly ITextBuffer _sourceBuffer;     public QuickInfoSource(ITextBuffer buffer)     {         this._sourceBuffer = buffer;     }     public void AugmentQuickInfoSession(IQuickInfoSession session, IList<object> quickInfoContent, out ITrackingSpan applicableToSpan)     {         var snapshot = this._sourceBuffer.CurrentSnapshot;         var triggerPoint = (SnapshotPoint)session.GetTriggerPoint(snapshot);         applicableToSpan = snapshot.CreateTrackingSpan(new SnapshotSpan(triggerPoint, triggerPoint), SpanTrackingMode.EdgeInclusive);         quickInfoContent.Add(new BugWindow());     } } 

The BugWindow.xaml contains an expander, and it is with this expander that the trouble starts. First the code:

<UserControl x:Class="BugWindow"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              x:Name="MainWindow">     <Expander GotMouseCapture="GotMouseCapture_Click" IsExpanded="False">Blah</Expander> </UserControl> 

The BugWindow.xaml.cs contains some code to help me understand the problem. My first thoughts were to prevent the mouse event from bubbling to VS, but these experiments did not help...

public partial class BugWindow : UserControl {     public BugWindow()     {         InitializeComponent();          this.MainWindow.MouseLeftButtonDown += (o, i) =>          {             //i.Handled = true; // dont let the mouse event from inside this window bubble up to VS         };           this.MainWindow.PreviewMouseLeftButtonDown += (o, i) =>         {             //i.Handled = true; // if true then no event is able to bubble to the gui         };     }      private void GotMouseCapture_Click(object sender, RoutedEventArgs e)     {         //e.Handled = true;     } } 

What works: if you hover the mouse over a keyword, my BugWindow appears and the expander is closed (because it is closed by default). If you click the expander, it opens and the text "Blah" appears.

Bug: if you leave open the expander and scroll down, the tooltip will disappear when the span the tooltip is applicable to (applicableSpan) is outside the visible window. We are now in the "bug state". If you hover the mouse over any keyword, my BugWindow will appear. If you click the expander, the window will incorrectly disappear. In fact, the quickInfoSession will be dismissed, as if you were clicking where no keywords are, which is interpreted as dismissing the session. Buttons have the same bug behaviour.

Question: How can I debug this: my mouse event gets lost or hijacked. And I like to find out why that happens. But to be honest, I just like my expander to behave as expected.

Edit: A minimal github repo with this bug can be found at https://github.com/HJLebbink/vsix-bug-quickinfosession. Please use the github issues for help.

0 Answers

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment