private async Task StartUIThreadEpilogueAsync(QuickInfoSessionState initialState, IList <ITrackingSpan> applicableToSpans, CancellationToken cancellationToken) { // Ensure we're back on the UI thread. await this.JoinableTaskContext.Factory.SwitchToMainThreadAsync(); if (applicableToSpans != null) { // Update the applicable-to span. this.ComputeApplicableToSpan(applicableToSpans); } // Check if any of our content is interactive and cache that so it's not done on mouse move. this.ComputeHasInteractiveContent(); // If we have results and a span for which to show them and we aren't cancelled update the tip. if ((initialState == QuickInfoSessionState.Dismissed) || !this.Content.Any() || (this.ApplicableToSpan == null) || cancellationToken.IsCancellationRequested) { // If we were unable to await some computation task and don't end up with // a visible presenter + content, ensure that we cleanup and change our state appropriately. await this.DismissAsync().ConfigureAwait(false); } }
private async Task <IList <Exception> > ComputeContentAndUpdateAsync(QuickInfoSessionState initialState, bool allowUpdate, CancellationToken cancellationToken) { IntellisenseUtilities.ThrowIfNotOnMainThread(this.JoinableTaskContext); // Alert subscribers on the UI thread. this.TransitionTo(QuickInfoSessionState.Calculating, allowUpdate); cancellationToken.ThrowIfCancellationRequested(); var failures = new FrugalList <Exception>(); // Find and create the sources. Sources cache is smart enough to // invalidate on content-type changed and free on view close. var sources = this.GetOrCreateSources(failures); // Compute quick info items. This method switches off the UI thread. // From here on out we're on an arbitrary thread. (IList <object> items, IList <ITrackingSpan> applicableToSpans)? results = await ComputeContentAsync(sources, failures, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Update our content, or put the empty list if there is none. Volatile.Write( ref this.content, results != null ? ImmutableList.CreateRange(results.Value.items) : ImmutableList <object> .Empty); await StartUIThreadEpilogueAsync(initialState, results?.applicableToSpans, cancellationToken).ConfigureAwait(false); return(failures); }
private async Task StartUIThreadEpilogueAsync( QuickInfoSessionState originalState, IList <ITrackingSpan> applicableToSpans, CancellationToken cancellationToken) { // Ensure we're back on the UI thread. await this.joinableTaskContext.Factory.SwitchToMainThreadAsync(); if (applicableToSpans != null) { // Update the applicable-to span. this.ComputeApplicableToSpan(applicableToSpans); } // Check if any of our content is interactive and cache that so it's not done on mouse move. this.ComputeHasInteractiveContent(); // If we have results and a span for which to show them and we aren't cancelled update the tip. if ((originalState == QuickInfoSessionState.Created) && this.Content.Any() && (this.ApplicableToSpan != null) && !cancellationToken.IsCancellationRequested) { this.CreateAndStartPresenter(); } }
private void TransitionTo(QuickInfoSessionState newState) { IntellisenseUtilities.ThrowIfNotOnMainThread(this.joinableTaskContext); var oldState = this.State; bool isValid = false; switch (newState) { case QuickInfoSessionState.Created: isValid = false; break; case QuickInfoSessionState.Calculating: isValid = oldState == QuickInfoSessionState.Created || oldState == QuickInfoSessionState.Visible; break; case QuickInfoSessionState.Dismissed: isValid = oldState == QuickInfoSessionState.Visible || oldState == QuickInfoSessionState.Calculating; break; case QuickInfoSessionState.Visible: isValid = oldState == QuickInfoSessionState.Calculating; break; } if (!isValid) { throw new InvalidOperationException($"Invalid {nameof(IAsyncQuickInfoSession)} state transition from {oldState} to {newState}"); } Volatile.Write(ref this.uiThreadWritableState, (int)newState); this.StateChanged?.Invoke(this, new QuickInfoSessionStateChangedEventArgs(oldState, newState)); }
/// <summary> /// Creates a new instance of <see cref="QuickInfoSessionStateChangedEventArgs"/>. /// </summary> /// <param name="oldState">The state before the transition.</param> /// <param name="newState">The state after the transition.</param> public QuickInfoSessionStateChangedEventArgs(QuickInfoSessionState oldState, QuickInfoSessionState newState) { this.OldState = oldState; this.NewState = newState; }