Text input and Focus in Adornment Layer (VSIX)

255 Views Asked by At

I am making a Visual Studio IDE extension that has an Inline Difference View feature.

I used CreateDifferenceView to create a FrameWorkElement, be aware that this method will create two inner IWpfTextView. I then used AddAdornment to attach it to a IWpfTextView.

The problem is that the adornments don't receive keyboard focus properly. Initially, I can type into it, but the commands like CTRL+A, CTRL+C, etc... don't work, and they will be received/processed by the host IWpfTextView instead of the adornments.

Learning from this blog, I have successfully got that working by using AddCommandFilter and pass the commands forward to the inner IVsTextView's that was created by CreateDifferenceView.

Now the keyboard commands are working properly, but one problem remains, the adornments still aren't getting proper focus. You can see that although I can select the text in the adornment, the selection is still shown as "out-of-focus" grey, and the host IWpfTextView doesn't lose its selection focus even when I clicked onto the adornments. Sorry that I had to attach a GIF, but there is no better way to describe the problem.:


What I had tried:

The MRE (MefProvider just imports those components):

var docview = await VS.Documents.GetActiveDocumentViewAsync();
var contentType = docview.TextBuffer.ContentType;

var leftBuffer = MefProvider.TextBufferFactoryService.CreateTextBuffer("left content", contentType);
var rightBuffer = MefProvider.TextBufferFactoryService.CreateTextBuffer("right content", contentType);

var diffBuffer = MefProvider.DifferenceBufferFactory.CreateDifferenceBuffer(leftBuffer, rightBuffer);
var diffViewer = MefProvider.DifferenceViewerFactory.CreateDifferenceView(diffBuffer);

var adornmentLayer = docview.TextView.GetAdornmentLayer("MyAdornmentLayer");
adornmentLayer.AddAdornment(AdornmentPositioningBehavior.OwnerControlled, null, null, diffViewer.VisualElement, null);
1

There are 1 best solutions below

0
thedemons On

The workaround is to use an undocumented internal function in Microsoft.VisualStudio.Platform.VSEditor.dll:

diffViewer.VisualElement.GotFocus += (o, e) => {
    AggregateFocusInterceptor.SetInterceptsAggregateFocus(hostView as DependencyObject, true);
};

diffViewer.VisualElement.LostFocus += (o, e) => {
    AggregateFocusInterceptor.SetInterceptsAggregateFocus(hostView as DependencyObject, false);
};

You could take a look at the internal function WpfTextView.QueueAggregateFocusCheck to see why this works.

Before trying this, I was getting a partially successful result by getting the ISpaceReservationManager of the adornment text view then adding an agent to it and firing its GotFocus event. This technically should work, however, right after the adornment text view got focus, the host view just steal the focus back. I suspect it might have something to do with the orders of the events being triggered.

I'm not accepting this as an answer because it's only a workaround and there should be a more correct way to do this.