-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Feature: Added swipe gesture for back/forward navigation #12043
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 27 commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
057d31c
Add invalid name warning
heftymouse dc0c048
Add string
heftymouse da9ef96
fix regression
heftymouse a949b58
Tabify
heftymouse 4d142e9
Merge branch 'main' into main
yaira2 31de5cf
Merge branch 'main' of https://github.com/files-community/Files
heftymouse 9f0c951
fix color change not updating button
heftymouse 968bd67
Merge branch 'main' into main
yaira2 8056161
fix changing back to original color not updating button
heftymouse 77eac60
Merge branch 'main' of https://github.com/heftymouse/Files
heftymouse 6518deb
Switch to infobar and add tooltip
heftymouse 6232f3e
Made infobar compact
heftymouse 56a9969
Move style
heftymouse 4f233e3
Unswitch to infobar
heftymouse 0ef9905
Merge branch 'main' of https://github.com/Files-community/Files
heftymouse e9d1917
Readd tooltip
heftymouse feb3663
Merge branch 'main' of https://github.com/Files-community/Files
heftymouse c571d92
remove merge conflict markers
heftymouse fbb739c
Stop creating tags with existing name, formatting
heftymouse ff7ba88
Change servicing branch pattern
heftymouse 8155003
big oopsie
heftymouse a1496ee
Merge branch 'files-community:main' into main
heftymouse 4da33df
Merge branch 'main' of https://github.com/heftymouse/Files
heftymouse a01127b
Add swipe gesture for back/forward navigation
heftymouse ac93652
remove weird extra usings
heftymouse 8a34e8c
fix animation not running on reaching history start/end
heftymouse 67f830e
changes
heftymouse 8389ce7
do not fire event if navigation cannot occur
heftymouse 0519bf6
dispose stuff
heftymouse 35c1532
fix overscroll
heftymouse e2335d4
add setting
heftymouse 155f920
change style
heftymouse 2ca3b65
Update src/Files.App/Files.App.csproj
yaira2 699e152
Merge branch 'main' into pr/12043
yaira2 dce7720
Fixed reference to GeneralSettingsService
yaira2 141cfae
remove setting
heftymouse f8709ce
Increased threshold
yaira2 a4bffc1
Merge branch 'main' into main
yaira2 946003f
add animation for reaching navigation threshold
heftymouse 7d2f80b
tweak scale anim
heftymouse 7bf5a80
more adjustments
heftymouse dd451c6
add license header
heftymouse 8af9582
use windows 11 accent brush
heftymouse 9422e53
remove ellipse
heftymouse ca5c848
Merge branch 'main' into main
yaira2 4c51132
change to border
heftymouse a22a4cb
Merge branch 'main' of https://github.com/heftymouse/Files
heftymouse d3c5b23
Merge branch 'main' into main
yaira2 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
using Microsoft.UI.Composition; | ||
using Microsoft.UI.Composition.Interactions; | ||
using Microsoft.UI.Input; | ||
using Microsoft.UI.Xaml; | ||
using Microsoft.UI.Xaml.Hosting; | ||
using Microsoft.UI.Xaml.Input; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Numerics; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Files.App.Views | ||
{ | ||
internal class NavigationInteractionTracker | ||
{ | ||
public bool CanGoForward | ||
{ | ||
get | ||
{ | ||
_props.TryGetBoolean(nameof(CanGoForward), out bool val); | ||
return val; | ||
} | ||
set | ||
{ | ||
_props.InsertBoolean(nameof(CanGoForward), value); | ||
} | ||
} | ||
|
||
public bool CanGoBack | ||
{ | ||
get | ||
{ | ||
_props.TryGetBoolean(nameof(CanGoBack), out bool val); | ||
return val; | ||
} | ||
set | ||
{ | ||
_props.InsertBoolean(nameof(CanGoBack), value); | ||
} | ||
} | ||
|
||
private UIElement _rootElement; | ||
private UIElement _backIcon; | ||
private UIElement _forwardIcon; | ||
|
||
private Visual _rootVisual; | ||
private Visual _backVisual; | ||
private Visual _forwardVisual; | ||
|
||
private InteractionTracker _tracker; | ||
private VisualInteractionSource _source; | ||
private InteractionTrackerOwner _trackerOwner; | ||
private CompositionPropertySet _props; | ||
|
||
public event EventHandler<SwipeNavigationEventArgs>? NavigationRequested; | ||
|
||
public NavigationInteractionTracker(UIElement rootElement, UIElement backIcon, UIElement forwardIcon) | ||
{ | ||
_rootElement = rootElement; | ||
_backIcon = backIcon; | ||
_forwardIcon = forwardIcon; | ||
|
||
ElementCompositionPreview.SetIsTranslationEnabled(_backIcon, true); | ||
ElementCompositionPreview.SetIsTranslationEnabled(_forwardIcon, true); | ||
_rootVisual = ElementCompositionPreview.GetElementVisual(_rootElement); | ||
_backVisual = ElementCompositionPreview.GetElementVisual(_backIcon); | ||
_forwardVisual = ElementCompositionPreview.GetElementVisual(_forwardIcon); | ||
|
||
_props = _rootVisual.Compositor.CreatePropertySet(); | ||
CanGoBack = false; | ||
CanGoForward = false; | ||
|
||
rootElement.AddHandler(UIElement.PointerPressedEvent, new PointerEventHandler(PointerPressed), true); | ||
|
||
SetupInteractionTracker(); | ||
SetupAnimations(); | ||
} | ||
|
||
[MemberNotNull(nameof(_tracker), nameof(_source), nameof(_trackerOwner))] | ||
private void SetupInteractionTracker() | ||
{ | ||
var compositor = _rootVisual.Compositor; | ||
|
||
_trackerOwner = new(this); | ||
_tracker = InteractionTracker.CreateWithOwner(compositor, _trackerOwner); | ||
_tracker.MinPosition = new Vector3(-96f); | ||
_tracker.MaxPosition = new Vector3(96f); | ||
|
||
_source = VisualInteractionSource.Create(_rootVisual); | ||
_source.ManipulationRedirectionMode = VisualInteractionSourceRedirectionMode.CapableTouchpadOnly; | ||
_source.PositionXSourceMode = InteractionSourceMode.EnabledWithoutInertia; | ||
_source.PositionXChainingMode = InteractionChainingMode.Auto; | ||
_source.PositionYSourceMode = InteractionSourceMode.Disabled; | ||
_tracker.InteractionSources.Add(_source); | ||
} | ||
|
||
private void SetupAnimations() | ||
{ | ||
var compositor = _rootVisual.Compositor; | ||
|
||
var backResistance = CreateResistanceCondition(-96f, 0f); | ||
var forwardResistance = CreateResistanceCondition(0f, 96f); | ||
List<CompositionConditionalValue> conditionalValues = new() { backResistance, forwardResistance }; | ||
_source.ConfigureDeltaPositionXModifiers(conditionalValues); | ||
|
||
var backAnim = compositor.CreateExpressionAnimation("props.CanGoBack ? (-clamp(tracker.Position.X, -96, 0) * 2) - 48 : this.CurrentValue - 48"); | ||
backAnim.SetReferenceParameter("tracker", _tracker); | ||
backAnim.SetReferenceParameter("props", _props); | ||
_backVisual.StartAnimation("Translation.X", backAnim); | ||
|
||
var forwardAnim = compositor.CreateExpressionAnimation("props.CanGoForward ? (-clamp(tracker.Position.X, 0, 96) * 2) + 48 : this.CurrentValue + 48"); | ||
forwardAnim.SetReferenceParameter("tracker", _tracker); | ||
forwardAnim.SetReferenceParameter("props", _props); | ||
_forwardVisual.StartAnimation("Translation.X", forwardAnim); | ||
} | ||
|
||
private void PointerPressed(object sender, PointerRoutedEventArgs e) | ||
{ | ||
if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch) | ||
{ | ||
_source.TryRedirectForManipulation(e.GetCurrentPoint(_rootElement)); | ||
} | ||
} | ||
|
||
private CompositionConditionalValue CreateResistanceCondition(float minValue, float maxValue) | ||
{ | ||
var compositor = _rootVisual.Compositor; | ||
|
||
var resistance = CompositionConditionalValue.Create(compositor); | ||
var resistanceCondition = compositor.CreateExpressionAnimation($"tracker.Position.X > {minValue} && tracker.Position.X < {maxValue}"); | ||
resistanceCondition.SetReferenceParameter("tracker", _tracker); | ||
var resistanceValue = compositor.CreateExpressionAnimation($"source.DeltaPosition.X * (1 - sqrt(1 - square((tracker.Position.X / {minValue + maxValue}) - 1)))"); | ||
resistanceValue.SetReferenceParameter("source", _source); | ||
resistanceValue.SetReferenceParameter("tracker", _tracker); | ||
resistance.Condition = resistanceCondition; | ||
resistance.Value = resistanceValue; | ||
|
||
return resistance; | ||
} | ||
|
||
private class InteractionTrackerOwner : IInteractionTrackerOwner | ||
{ | ||
private NavigationInteractionTracker _parent; | ||
private bool _shouldBounceBack; | ||
|
||
public InteractionTrackerOwner(NavigationInteractionTracker parent) | ||
{ | ||
_parent = parent; | ||
} | ||
|
||
public void CustomAnimationStateEntered(InteractionTracker sender, InteractionTrackerCustomAnimationStateEnteredArgs args) | ||
{ | ||
|
||
} | ||
|
||
public void IdleStateEntered(InteractionTracker sender, InteractionTrackerIdleStateEnteredArgs args) | ||
{ | ||
if (!_shouldBounceBack) | ||
return; | ||
|
||
var compositor = _parent._rootVisual.Compositor; | ||
var springAnim = compositor.CreateSpringVector3Animation(); | ||
springAnim.FinalValue = new(0f); | ||
springAnim.DampingRatio = 1f; | ||
_parent._tracker.TryUpdatePositionWithAnimation(springAnim); | ||
_shouldBounceBack = false; | ||
|
||
if (Math.Abs(sender.Position.X) < 64) | ||
return; | ||
|
||
EventHandler<SwipeNavigationEventArgs>? navEvent = _parent.NavigationRequested; | ||
if (navEvent is null) | ||
return; | ||
|
||
navEvent(_parent, sender.Position.X > 0 ? SwipeNavigationEventArgs.Forward : SwipeNavigationEventArgs.Back); | ||
} | ||
|
||
|
||
public void InteractingStateEntered(InteractionTracker sender, InteractionTrackerInteractingStateEnteredArgs args) | ||
{ | ||
_shouldBounceBack = true; | ||
} | ||
|
||
// required to implement IInteractionTrackerOwner | ||
public void InertiaStateEntered(InteractionTracker sender, InteractionTrackerInertiaStateEnteredArgs args) { } | ||
public void RequestIgnored(InteractionTracker sender, InteractionTrackerRequestIgnoredArgs args) { } | ||
public void ValuesChanged(InteractionTracker sender, InteractionTrackerValuesChangedArgs args) { } | ||
} | ||
} | ||
|
||
public enum SwipeNavigationEventArgs | ||
{ | ||
Back, | ||
Forward | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.