File: BackgroundWorkIndicator\BackgroundWorkIndicatorScope.cs
Web Access
Project: src\src\EditorFeatures\Core.Wpf\Microsoft.CodeAnalysis.EditorFeatures.Wpf_nxlhoa10_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures.Wpf)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Threading;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.BackgroundWorkIndicator;
 
internal partial class WpfBackgroundWorkIndicatorFactory
{
    /// <summary>
    /// Implementation of an <see cref="IUIThreadOperationScope"/> for the background work indicator. Allows for
    /// features to create nested work with descriptions/progress that will update the all-up indicator tool-tip
    /// shown to the user.
    /// </summary>
    private sealed class BackgroundWorkIndicatorScope : IUIThreadOperationScope, IProgress<ProgressInfo>
    {
        private readonly BackgroundWorkIndicatorContext _context;
 
        // Mutable state of this scope.  Can be mutated by a client, at which point we'll ask our owning context to
        // update the tooltip accordingly.
 
        private string _description;
        private ProgressInfo _progressInfo;
 
        public IUIThreadOperationContext Context => _context;
        public IProgress<ProgressInfo> Progress => this;
 
        public BackgroundWorkIndicatorScope(
            BackgroundWorkIndicatorContext indicator, string description)
        {
            _context = indicator;
            _description = description;
        }
 
        /// <summary>
        /// Retrieves a threadsafe snapshot of our data for our owning context to use to build the tooltip ui.
        /// </summary>
        public (string description, ProgressInfo progressInfo) ReadData_MustBeCalledUnderLock()
        {
            Contract.ThrowIfFalse(Monitor.IsEntered(_context.Gate));
            return (_description, _progressInfo);
        }
 
        /// <summary>
        /// On disposal, just remove ourselves from our parent context.  It will update the UI accordingly.
        /// </summary>
        void IDisposable.Dispose()
            => _context.RemoveScope(this);
 
        bool IUIThreadOperationScope.AllowCancellation
        {
            get => true;
            set { }
        }
 
        string IUIThreadOperationScope.Description
        {
            get
            {
                lock (_context.Gate)
                    return _description;
            }
            set
            {
                lock (_context.Gate)
                {
                    _description = value;
                }
 
                // We changed.  Enqueue work to make sure the UI reflects this.
                _context.EnqueueUIUpdate();
            }
        }
 
        void IProgress<ProgressInfo>.Report(ProgressInfo value)
        {
            lock (_context.Gate)
            {
                _progressInfo = value;
            }
 
            // We changed.  Enqueue work to make sure the UI reflects this.
            _context.EnqueueUIUpdate();
        }
    }
}