microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.27k stars 674 forks source link

Proposal: NavigationView: Allow developers to turn off the selection indicator animation via an API #2878

Open Felix-Dev opened 4 years ago

Felix-Dev commented 4 years ago

Proposal: NavigationView: Allow developers to turn off the selection indicator animation via an API

Summary

There is currently no (reasonable) way for developers to turn off the animation of the selection indicator used in the NavigationView control as it is currently embedded in the NavigationView implementation and not exposed to the outside world in any way. While users can disable animations in Windows which would disable this particular animation, this is not an option to developers who want to ship their app with a NavigationView powered in-app navigation experience simply without this animation.

Why would developers want to disable the selection indicator animation?

There are a lot of Windows apps out there which use an in-app navigation design closely resembling the one provided by the NavigationView control. Among these apps are, for example, Windows Settings, Microsoft To Do, Your Phone, OneNote for Windows 10 and legacy Edge UWP. See below for images:

Windows Settings (Windows Settings) MS Todo (Microsoft To Do)
image (Your Phone) image (OneNote for Windows 10)
image (Edge UWP)

The key detail for this proposal here is that for all these apps their selection indicator is not animated. Whereas developers, who want to utilize the powerful NavigationView to create similar such navigation designs, will automatically get the selection indicator animation included with no way for them to turn it off for their app. This means that developers who want to match these showcased in-app navigation designs might not currently be able to utilize the power of the NavigationView here. Instead, they might be forced to re-build their in-app navigation from the ground up using controls like the SplitView and the ListView...and if hierarchical navigation support is required for their in-app navigation, this task will soon become quite complex and time-consuming.

Rationale

The NavigationView control is a powerful in-app navigation control which keeps getting more and more feature rich. Just recently, for example, we have seen support for hierarchical navigation being added. It would be a tremendous shame if developers would be forced to miss out on all these provided navigation features for free simply because they cannot remove the selection indicator animation. This animation is such a small part of the overall NavigationView control that it shouldn't act as a blocker when it comes to building a powerful in-app navigation experience where most of the functionality is already provided "for free" by the NavigationView control.

Along with proposal https://github.com/microsoft/microsoft-ui-xaml/issues/2877, this proposal will enable developers to easily create in-app navigation experiences matching the showcased in-app navigation UI examples in well-known Windows apps while being able to utilize the powerful NavigationView control.

Scope

Capability Priority
This proposal will allow developers to turn off the NavigationView's selection indicator animation via an API. Must
This proposal will allow developers to provide a custom selection indicator animation. Could
This proposal will remove or change the default in-built selection indicator animation. Won't

API Proposal

There are at least two possible ways we could provide an API for developers to use here. Note that the APIs presented below are just meant to provide a conceptual overview for now. I do not claim their designs are perfect or final.

API Example 1 - Theme resource and lightweight styling

We could introduce a new theme resource for the NavigationView:

<x:Boolean x:Key="IsSelectionIndicatorAnimationEnabled">True</x:Boolean>

This theme resource would essentially act as a flag which can be set by the developer to control whether the selection indicator animation will be played or not. An example to turn off the animation could then look like this:

<NavigationView>
    <NavigationView.Resources>
        <x:Boolean x:Key="IsSelectionIndicatorAnimationEnabled">False</x:Boolean>
    </NavigationView.Resources>
</NavigationView>

This approach could also be realized by adding a new boolean API to the NavigationView class, enabling developers to easily set this flag at runtime back-and-forth as well, like so:

public class NavigationView
{
    bool IsSelectionIndicatorAnimationEnabled { get; set; } = true;

    static Windows.UI.Xaml.DependencyProperty IsSelectionIndicatorAnimationEnabledProperty { get; };
}

API Example 2 - Create a new INavigationViewSelectionIndicatorAnimator API in the NavigationView class

Alternatively, a new API can be created on the NavigationView class which the developer can use to turn off the animation or even provide a custom animation, if they so wish. This could conceptually look like the following:

public class NavigationView
{
    INavigationViewSelectionIndicatorAnimator SelectionIndicatorAnimator { get; set; };

    static Windows.UI.Xaml.DependencyProperty SelectionIndicatorAnimatorProperty { get; };
}

where the interface INavigationViewSelectionIndicatorAnimator is defined like this:

interface INavigationViewSelectionIndicatorAnimator
{
    // Raised when the animation is completed.
    event Windows.Foundation.EventHandler<Object> AnimationCompleted;

    /// <summary>
    /// Called before <see cref="INavigationViewSelectionIndicatorAnimator.StartAnimation()"/> is called. Allows the developer to check if 
    /// an animation is already running and how to treat such an animation. The return value can be used to decide if a new animation should be played
    /// or not.
    /// </summary>
    /// <param name="prevIndicator">The source selection indicator for the animation to be started.</param>
    /// <param name="nextIndicator">The target selection indicator for the animation to be started.</param>
    /// <param name="currentAnimationPrevIndicator">The source selection indicator of the currently running animation, if any.</param>
    /// <param name="currentAnimationNextIndicator">The target selection indicator of the currently running animation, if any.</param>
    /// <returns>True to indicate <see cref="INavigationViewSelectionIndicatorAnimator.StartAnimation()"/> should be called next, false otherwise </returns>
    bool PrepareForAnimation(
        Windows.UI.Xaml.UIElement prevIndicator,
        Windows.UI.Xaml.UIElement nextIndicator,
        Windows.UI.Xaml.UIElement currentAnimationPrevIndicator,
        Windows.UI.Xaml.UIElement currentAnimationNextIndicator);

    /// <summary>
    /// Start a selection indicator animation. Will be called when the return value of <see cref="INavigationViewSelectionIndicatorAnimator.PrepareForAnimation()" was <c>true</c>./>
    /// </summary>
    /// <param name="navigationViewVisual">The visual object of the NavigationView. Can be used to create composition animations. </param>
    /// <param name="container">The NavigationView UI element containing the NavigationView items and selection indicators. 
    /// Can be used to obtain the position of a selection indicator in the coordinate system of the container.</param>
    /// <param name="prevIndicator">The source selection indicator for the animation.</param>
    /// <param name="nextIndicator">The target selection indicator for the animation.</param>
    /// <param name="isTopNavigationView">Specifies whether the NavigationView is in Left mode or Top pane display mode.</param>
    void StartAnimation(
        Windows.UI.Composition.Visual navigationViewVisual, 
        Windows.UI.Xaml.UIElement container, 
        Windows.UI.Xaml.UIElement prevIndicator, 
        Windows.UI.Xaml.UIElement nextIndicator, 
        bool isTopNavigationView);
}

WinUI would then provide the current selection indicator animation as an implementation of the INavigationViewSelectionIndicatorAnimator interface, for example as a class named NavigationViewSelectionIndicatorDefaultAnimator living in the Microsoft.UI.Xaml.Controls namespace.

The default style of the WinUI NavigationView (in NavigationView.xaml) could then look like this:

<local:NavigationViewSelectionIndicatorDefaultAnimator x:Key="SelectionIndicatorDefaultAnimator" />

<Style TargetType="local:NavigationView">
    <Setter Property="SelectionIndicatorAnimator" Value="{StaticResource SelectionIndicatorDefaultAnimator}" />
</Style>

and developers can set the SelectionIndicatorAnimator API to null if they don't want a selection indicator animation to be played, like so:

<NavigationView SelectionIndicatorAnimator="{x:Null}"/>

This approach, while more challenging to implement, has some benefits compared to the approach using a theme resource (or boolean property):

Open Questions

Well, I guess if this proposal will get the OK sign: How will the API look like? Should we provide an option to developers to potentially create their own selection indicator animation?

Additional Context

I realize there was a proposal asking for the removal of the selection indicator animation which was declined. To my understanding, the motivation behind that proposal, and the reason why it was declined, was that the selection indicator animation did not play nicely with the NavigationViewItems added in the NavigationView.PaneFooter area of the NavigationView. Instead of thus disabling the animation (or providing such an option) PR #1997 was created to make the selection indicator play nicely with those items as well.

To stress it again: This issue has a completely different motivation. It asks for the option to disable the animation so that developers can provide delightful in-app navigation experiences using the powerful NavigationView control "for free". Developers shouldn't be forced to have the re-built a potentially complex in-app navigation experience simply because a small detail like the selection indicator animation stands in the way.

mdtauk commented 4 years ago

This proposal is fine and all, but I vote that the default remain animated. :)

YuliKl commented 4 years ago

@Felix-Dev, thank you for submitting this detailed proposal. At this point, I still need some help to understand your motivation for this request. Would you personally consider re-implementing NavigationView's functionality for the express purpose of removing the selector animation? So far, other than in proposal #376 that you've noted, I haven't actually hear of any apps wanting to remove the animation. I would love to understand if a new API along these lines would actual see any usage.

Felix-Dev commented 4 years ago

This proposal is fine and all, but I vote that the default remain animated. :)

@mdtauk, Yes, as you can see in the Scope section of this proposal, I'm not asking to change the default here.

@YuliKl Other than giving you the app examples above (which are featuring an in-app navigation design without this animation) I can only tell you that I myself had to "re-build" a secondary in-app navigation experience using a SplitView and a ListView to create an in-app Settings UI matching the UWP Edge's Settings pane you can see above (my primary in-app navigation was powered by a NavigationView where I had no issues with the animation). I explicitly did not want to have the default selection indicator animation in that case so using the NavigationView was not an option for me. In that sense, it would have been nice if the NavigationView would have have given me an option to turn off the default navigation. As that was not the case, I grudgingly had to pass on using the NavigationView here.

I believe apps like OneNote for Windows 10, YourPhone and Microsoft To Do show that there is a case to make to have a selection indicator which does not animate. However, if I want to use the power of the NavigationView control to easily create in-app navigation designs, then I will always get the selection indicator animation "for free" as well, with no option to turn it off. We should build that flexibility into the NavigationView control so that it can be used in all in-app navigation scenarios - whether designers want the selection indicator to be animated or not.

I don't have any numbers how much such an API would be used and it's true we didn't have any requests here so far (other than that one proposal which had a quite different motivation than mine). All I can say is that I view this API suggestion as a reasonable one to have - based on my personal experience and the different in-app navigation designs we can find in today's Windows apps (as shown in the previous posts). Some of them have an animated selection indicator (like the Windows Security App), others like Microsoft To do or OneNote for Windows 10 do not. In my opinion this should be reflected in the NavigationView API.

This API could also complement the Frame.Navigate() API used to do the actual content navigation for which I can specifiy a NavigationTransitionInfo of type SuppressNavigationTransitionInfo. So, if I now have navigation view which content is not animated in and out of view, an animated selection indicator will create a visual disconnect: The navigated to content is already visible, yet the selection indicator is still being animated to reach the content's menu item in the navigation view pane. However, in that case, an "instant" experience would be significantly better: No animation, no delay. The content is instantly shown and so is the selection indicator instantly at the right position. As such, this API would enable developers to create a visibly coherent content transition in the NavigationView.

mdtauk commented 4 years ago

... I can only tell you that I myself had to "re-build" my in-app navigation using a SplitView and a ListView to match UWP Edge's Settings pane you can see above. I explicitly did not want to have the default selection indicator animation in that case so using the NavigationView was not really an option for me....

Why do you want to take the joy away from user interaction 😁

I personally will be happier when Settings moves to using WinUI, adding those little animated features, and changing the Acrylic to 80% but not ditching it entirely.

Felix-Dev commented 4 years ago

@mdtauk As we have seen in the Windows 10X preview builds, its Settings app already uses WinUI so I'm sure at some point in the future we will see the Windows 10 Settings app making the jump too.

Why do you want to take the joy away from user interaction

To be fair, the user experience can still be a delightful one even if that animation is not existent in one part of the app. I am using, for example, a NavigationView with animation for the top-level in-app navigation in that app's case so you can still play around in the app simply to see that small animation. It just didn't feel right and necessary to me in that specific app context and it wasn't the design I wanted to use there (a similar design can be seen in the OneNote for Windows 10 app where the ribbon features an animated selection indicator and the left pane features a "static" one).

BreeceW commented 4 years ago

My issue with the NavigationView animation is that it starts with a delay that causes the indicator to jump around if you click around too fast. A much nicer experience is the ribbon in the OneNote UWP, where the indicator elegantly slides into place where you click it. Try clicking around quickly in any default NavigationView and OneNote to see how this feels.

As far as user experience goes, for users of NavigationView, it’s fine if a user just clicks on one item. But if, say, a user clicked on the wrong item and then corrected themself, they would see the indicator jump in a glitchy looking way. It is this user experience that makes me wonder if it is why apps like Your Phone disable this.

Basically—the default animation looks glitchy, and people probably want to disable it because of that.

Felix-Dev commented 4 years ago

@YuliKl Any update on this?

andrew-mi commented 2 years ago

I had this issue with NavigationView selector animation being super buggy and really annoying. Turns out that SelectionFollowsFocus="Enabled" causes the selection to jump around 3 times when the user clicks an item - firing additional navigation events (easy to miss) but also breaks the animation.