Skip to content

Commit

Permalink
Major update to UI
Browse files Browse the repository at this point in the history
Add navigation view and separate settings
  • Loading branch information
sabihoshi committed Apr 5, 2021
1 parent c005538 commit 1ff79ad
Show file tree
Hide file tree
Showing 18 changed files with 1,465 additions and 475 deletions.
9 changes: 8 additions & 1 deletion GenshinLyreMidiPlayer/GenshinLyreMidiPlayer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@
<UseWPF>true</UseWPF>
<StartupObject>GenshinLyreMidiPlayer.App</StartupObject>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>1.3.3</Version>
<Version>1.4.0</Version>
<ApplicationIcon>item_windsong_lyre.ico</ApplicationIcon>
<Nullable>enable</Nullable>
<RepositoryUrl>https://github.com/sabihoshi/GenshinLyreMidiPlayer</RepositoryUrl>
<Authors>sabihoshi</Authors>
<Product>Genshin Lyre MIDI Player</Product>
<Company />
<Copyright>sabihoshi</Copyright>
<Description>A music player that plays MIDI files into Genshin Impact's Windsong Lyre.</Description>
</PropertyGroup>

<ItemGroup>
Expand Down
28 changes: 28 additions & 0 deletions GenshinLyreMidiPlayer/ModernWPF/AnimatedContentControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Windows.Controls;
using GenshinLyreMidiPlayer.ModernWPF.Animation;
using GenshinLyreMidiPlayer.ViewModels;

namespace GenshinLyreMidiPlayer.ModernWPF
{
public class AnimatedContentControl : ContentControl
{
private static Transition Transition => SettingsPageViewModel.Transition.Object;

protected override void OnContentChanged(object? oldContent, object? newContent)
{
if (oldContent != null)
{
var exit = Transition.GetExitAnimation(oldContent, false);
exit?.Begin();
}

if (newContent != null)
{
var enter = Transition.GetEnterAnimation(newContent, false);
enter?.Begin();
}

base.OnContentChanged(oldContent, newContent);
}
}
}
65 changes: 65 additions & 0 deletions GenshinLyreMidiPlayer/ModernWPF/Animation/Animation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace GenshinLyreMidiPlayer.ModernWPF.Animation
{
public class Animation
{
private static readonly BitmapCache _defaultBitmapCache;
private readonly FrameworkElement _element;
private readonly Storyboard _storyboard;
private ClockState _currentState = ClockState.Stopped;

static Animation()
{
_defaultBitmapCache = new BitmapCache();
_defaultBitmapCache.Freeze();
}

public Animation(FrameworkElement element, Storyboard storyboard)
{
_element = element;
_storyboard = storyboard;
_storyboard.CurrentStateInvalidated += OnCurrentStateInvalidated;
_storyboard.Completed += OnCompleted;
}

public event EventHandler Completed;

public void Begin()
{
if (!(_element.CacheMode is BitmapCache))
_element.SetCurrentValue(UIElement.CacheModeProperty, GetBitmapCache());
_storyboard.Begin(_element, true);
}

public void Stop()
{
if (_currentState != ClockState.Stopped) _storyboard.Stop(_element);
_element.InvalidateProperty(UIElement.CacheModeProperty);
_element.InvalidateProperty(UIElement.RenderTransformProperty);
_element.InvalidateProperty(UIElement.RenderTransformOriginProperty);
}

private void OnCurrentStateInvalidated(object sender, EventArgs e)
{
if (sender is Clock clock) _currentState = clock.CurrentState;
}

private void OnCompleted(object sender, EventArgs e)
{
Completed?.Invoke(this, EventArgs.Empty);
}

private BitmapCache GetBitmapCache()
{
#if NETCOREAPP || NET462
return new BitmapCache(VisualTreeHelper.GetDpi(_element).PixelsPerDip);
#else
return _defaultBitmapCache;
#endif
}
}
}
74 changes: 74 additions & 0 deletions GenshinLyreMidiPlayer/ModernWPF/Animation/Transition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Windows;
using System.Windows.Media.Animation;

namespace GenshinLyreMidiPlayer.ModernWPF.Animation
{
/// <summary>
/// Provides parameter info for the Frame.Navigate method. Controls how the transition
/// animation runs during the navigation action.
/// </summary>
public class Transition : DependencyObject
{
internal static readonly KeySpline AccelerateKeySpline;
internal static readonly KeySpline DecelerateKeySpline;
internal static readonly PropertyPath OpacityPath = new PropertyPath(UIElement.OpacityProperty);

internal static readonly PropertyPath TranslateXPath =
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)");

internal static readonly PropertyPath TranslateYPath =
new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)");

internal static readonly PropertyPath ScaleXPath =
new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)");

internal static readonly PropertyPath ScaleYPath =
new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)");

internal static readonly TimeSpan ExitDuration = TimeSpan.FromMilliseconds(150);
internal static readonly TimeSpan EnterDuration = TimeSpan.FromMilliseconds(300);
internal static readonly TimeSpan MaxMoveDuration = TimeSpan.FromMilliseconds(500);

static Transition()
{
AccelerateKeySpline = new KeySpline(0.7, 0, 1, 0.5);
AccelerateKeySpline.Freeze();

DecelerateKeySpline = new KeySpline(0.1, 0.9, 0.2, 1);
DecelerateKeySpline.Freeze();
}

/// <summary>
/// Initializes a new instance of the Transition class.
/// </summary>
protected Transition()
{
}

//protected virtual string GetNavigationStateCore();
//protected virtual void SetNavigationStateCore(string navigationState);

protected virtual Animation GetEnterAnimation(FrameworkElement element, bool movingBackwards)
{
return null;
}

protected virtual Animation GetExitAnimation(FrameworkElement element, bool movingBackwards)
{
return null;
}

public Animation GetEnterAnimation(object element, bool movingBackwards)
{
return GetEnterAnimation(
(FrameworkElement) element, movingBackwards);
}

public Animation GetExitAnimation(object element, bool movingBackwards)
{
return GetExitAnimation(
(FrameworkElement) element, movingBackwards);
}
}
}
18 changes: 18 additions & 0 deletions GenshinLyreMidiPlayer/ModernWPF/Animation/TransitionCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using GenshinLyreMidiPlayer.ModernWPF.Animation.Transitions;

namespace GenshinLyreMidiPlayer.ModernWPF.Animation
{
public class TransitionCollection : List<CaptionedObject<Transition>>
{
public TransitionCollection()
{
Add(new CaptionedObject<Transition>(new EntranceTransition(), "Entrance"));
Add(new CaptionedObject<Transition>(new DrillInTransition(), "Drill in"));
Add(new CaptionedObject<Transition>(new SlideTransition(Direction.FromLeft), "Slide from Left"));
Add(new CaptionedObject<Transition>(new SlideTransition(Direction.FromRight), "Slide from Right"));
Add(new CaptionedObject<Transition>(new SlideTransition(Direction.FromBottom), "Slide from Bottom"));
Add(new CaptionedObject<Transition>(new SuppressTransition(), "Suppress"));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace GenshinLyreMidiPlayer.ModernWPF.Animation.Transitions
{
/// <summary>
/// Specifies the animation to run when a user navigates forward in a logical hierarchy,
/// like from a master list to a detail page.
/// </summary>
public sealed class DrillInTransition : Transition
{
protected override Animation GetEnterAnimation(FrameworkElement element, bool movingBackwards)
{
var storyboard = new Storyboard();

if (movingBackwards)
{
var scaleXAnim = new DoubleAnimationUsingKeyFrames
{
KeyFrames =
{
new DiscreteDoubleKeyFrame(1.15, TimeSpan.Zero),
new SplineDoubleKeyFrame(1, EnterDuration, DecelerateKeySpline)
}
};
Storyboard.SetTargetProperty(scaleXAnim, ScaleXPath);
storyboard.Children.Add(scaleXAnim);

var scaleYAnim = new DoubleAnimationUsingKeyFrames
{
KeyFrames =
{
new DiscreteDoubleKeyFrame(1.15, TimeSpan.Zero),
new SplineDoubleKeyFrame(1, EnterDuration, DecelerateKeySpline)
}
};
Storyboard.SetTargetProperty(scaleYAnim, ScaleYPath);
storyboard.Children.Add(scaleYAnim);

var opacityAnim = new DoubleAnimationUsingKeyFrames
{
KeyFrames =
{
new DiscreteDoubleKeyFrame(0, TimeSpan.Zero),
new SplineDoubleKeyFrame(1, EnterDuration, DecelerateKeySpline)
}
};
Storyboard.SetTargetProperty(opacityAnim, OpacityPath);
storyboard.Children.Add(opacityAnim);
}
else
{
var scaleXAnim = new DoubleAnimationUsingKeyFrames
{
KeyFrames =
{
new DiscreteDoubleKeyFrame(0.9, TimeSpan.Zero),
new SplineDoubleKeyFrame(1, MaxMoveDuration, DecelerateKeySpline)
}
};
Storyboard.SetTargetProperty(scaleXAnim, ScaleXPath);
storyboard.Children.Add(scaleXAnim);

var scaleYAnim = new DoubleAnimationUsingKeyFrames
{
KeyFrames =
{
new DiscreteDoubleKeyFrame(0.9, TimeSpan.Zero),
new SplineDoubleKeyFrame(1, MaxMoveDuration, DecelerateKeySpline)
}
};
Storyboard.SetTargetProperty(scaleYAnim, ScaleYPath);
storyboard.Children.Add(scaleYAnim);

var opacityAnim = new DoubleAnimationUsingKeyFrames
{
KeyFrames =
{
new DiscreteDoubleKeyFrame(0, TimeSpan.Zero),
new SplineDoubleKeyFrame(1, MaxMoveDuration, DecelerateKeySpline)
}
};
Storyboard.SetTargetProperty(opacityAnim, OpacityPath);
storyboard.Children.Add(opacityAnim);
}

element.SetCurrentValue(UIElement.RenderTransformProperty, new ScaleTransform());
element.SetCurrentValue(UIElement.RenderTransformOriginProperty, new Point(0.5, 0.5));

return new Animation(element, storyboard);
}

protected override Animation GetExitAnimation(FrameworkElement element, bool movingBackwards)
{
var storyboard = new Storyboard();

var opacityAnim = new DoubleAnimationUsingKeyFrames
{
KeyFrames =
{
new DiscreteDoubleKeyFrame(1, TimeSpan.Zero),
new SplineDoubleKeyFrame(0, ExitDuration, AccelerateKeySpline)
}
};
Storyboard.SetTargetProperty(opacityAnim, OpacityPath);
storyboard.Children.Add(opacityAnim);

return new Animation(element, storyboard);
}
}
}
Loading

0 comments on commit 1ff79ad

Please sign in to comment.