Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Copyright (c) CodeScene. All rights reserved.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Codescene.VSExtension.Core.Interfaces;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.Web.WebView2.Wpf;
Expand All @@ -19,6 +21,7 @@ internal sealed class WebView2AirspaceGuard : IVsWindowFrameEvents, IDisposable
private readonly FrameworkElement _host;
private readonly WebView2 _webView;
private readonly ILogger _logger;
private IVsUIShell _shell;
private IVsUIShell7 _shell7;
private DTE _dte;
private WindowEvents _windowEvents;
Expand All @@ -37,6 +40,7 @@ public WebView2AirspaceGuard(FrameworkElement host, WebView2 webView, ILogger lo
ThreadHelper.ThrowIfNotOnUIThread();

var service = ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShell));
_shell = service as IVsUIShell;
_shell7 = service as IVsUIShell7;
if (_shell7 != null)
{
Expand Down Expand Up @@ -113,6 +117,7 @@ public void Dispose()
_advised = false;
}

_shell = null;
_shell7 = null;
_dte = null;
}
Expand Down Expand Up @@ -208,6 +213,48 @@ private static bool HasArea(RECT rect)
return rect.Right > rect.Left && rect.Bottom > rect.Top;
}

private static bool IsOnScreen(Window window, HashSet<Guid> onScreenKinds)
{
ThreadHelper.ThrowIfNotOnUIThread();
return Guid.TryParse(window.ObjectKind, out var kind) && onScreenKinds.Contains(kind);
}

private static bool IsOverlappingFlyout(Window window, RECT ourRect, HashSet<Guid> onScreenKinds)
{
ThreadHelper.ThrowIfNotOnUIThread();

if (!IsAutoHideToolWindow(window) || !IsOnScreen(window, onScreenKinds))
{
return false;
}

return TryGetDteWindowRect(window, ourRect, out var overlay) && Intersects(ourRect, overlay);
}

private static bool IsFrameOnScreen(IVsWindowFrame frame)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (frame == null)
{
return false;
}

return ErrorHandler.Succeeded(frame.IsOnScreen(out var onScreen)) && onScreen != 0;
}

private static bool TryGetOnScreenSlot(IVsWindowFrame frame, out Guid slot)
{
ThreadHelper.ThrowIfNotOnUIThread();
slot = Guid.Empty;

if (!IsFrameOnScreen(frame))
{
return false;
}

return ErrorHandler.Succeeded(frame.GetGuidProperty((int)__VSFPROPID.VSFPROPID_GuidPersistenceSlot, out slot));
}

private void OnDteWindowActivated(Window gotFocus, Window lostFocus)
{
ThreadHelper.ThrowIfNotOnUIThread();
Expand Down Expand Up @@ -272,7 +319,7 @@ private void Recalculate()
}
}

ApplyWebViewVisibility(HasOverlappingAutoHideWindow(ourRect));
ApplyWebViewVisibility(TryFindOverlappingAutoHideWindow(ourRect, out var overlayCaption), overlayCaption);
}
catch (Exception ex)
{
Expand All @@ -294,14 +341,18 @@ private bool IsHostLaidOut()
return _host.ActualWidth > 0 && _host.ActualHeight > 0 && PresentationSource.FromVisual(_host) != null;
}

private void ApplyWebViewVisibility(bool hide)
private void ApplyWebViewVisibility(bool hide, string overlayCaption)
{
var target = hide ? Visibility.Hidden : Visibility.Visible;
if (_webView.Visibility == target)
{
return;
}

_logger.Debug(hide
? $"Hiding WebView2 while auto-hidden tool window '{overlayCaption}' overlaps it."
: "Showing WebView2; no auto-hidden tool window overlaps it.");

_webView.Visibility = target;

#pragma warning disable VSTHRD001
Expand All @@ -315,29 +366,61 @@ private void ApplyWebViewVisibility(bool hide)
}
}

private bool HasOverlappingAutoHideWindow(RECT ourRect)
private bool TryFindOverlappingAutoHideWindow(RECT ourRect, out string caption)
{
ThreadHelper.ThrowIfNotOnUIThread();
caption = null;

if (_dte?.Windows == null)
{
return false;
}

var onScreenKinds = GetOnScreenToolWindowKinds();
if (onScreenKinds.Count == 0)
{
return false;
}

foreach (Window window in _dte.Windows)
{
if (!IsAutoHideToolWindow(window) || !TryGetDteWindowRect(window, ourRect, out var overlay))
if (IsOverlappingFlyout(window, ourRect, onScreenKinds))
{
continue;
caption = window.Caption;
return true;
}
}

return false;
}

if (Intersects(ourRect, overlay))
private HashSet<Guid> GetOnScreenToolWindowKinds()
{
ThreadHelper.ThrowIfNotOnUIThread();
var kinds = new HashSet<Guid>();

if (!TryGetToolWindowFrames(out var frames))
{
return kinds;
}

var buffer = new IVsWindowFrame[1];
while (frames.Next(1, buffer, out var fetched) == VSConstants.S_OK && fetched == 1)
{
if (TryGetOnScreenSlot(buffer[0], out var slot))
{
return true;
kinds.Add(slot);
}
}

return false;
return kinds;
}

private bool TryGetToolWindowFrames(out IEnumWindowFrames frames)
{
ThreadHelper.ThrowIfNotOnUIThread();
frames = null;
return _shell != null && ErrorHandler.Succeeded(_shell.GetToolWindowEnum(out frames)) && frames != null;
}

private bool TryGetHostRect(out RECT rect)
Expand Down
Loading