diff --git a/UniSky.Services/Implementation/SettingsService.cs b/UniSky.Services/Implementation/SettingsService.cs index 7d9f7c7..834963c 100644 --- a/UniSky.Services/Implementation/SettingsService.cs +++ b/UniSky.Services/Implementation/SettingsService.cs @@ -65,6 +65,13 @@ public bool TryRead(string key, out T? value) { if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString) { + if (typeof(T) == typeof(string) && !valueString.StartsWith('"')) + { + // :D + value = (T)(object)valueString; + return true; + } + value = JsonSerializer.Deserialize(valueString, Options); return true; } diff --git a/UniSky.Services/ModerationService.cs b/UniSky.Services/ModerationService.cs index 1f9a398..f65a04a 100644 --- a/UniSky.Services/ModerationService.cs +++ b/UniSky.Services/ModerationService.cs @@ -15,10 +15,12 @@ namespace UniSky.Services; public record struct LabelStrings(string Name, string Description); -public class ModerationService(IProtocolService protocolService, ILogger logger) : IModerationService +public class ModerationService( + IProtocolService protocolService, + ILogger logger) : IModerationService { private readonly ResourceLoader resources - = ResourceLoader.GetForCurrentView(); + = ResourceLoader.GetForViewIndependentUse(); public ModerationOptions ModerationOptions { get; set; } diff --git a/UniSky/App.xaml.cs b/UniSky/App.xaml.cs index 834555b..ad59db7 100644 --- a/UniSky/App.xaml.cs +++ b/UniSky/App.xaml.cs @@ -136,7 +136,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs e) // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation // parameter - rootFrame.Navigate(typeof(RootPage), e.Arguments); + rootFrame.Navigate(typeof(RootPage), e.SplashScreen); } // Ensure the current window is active @@ -152,7 +152,7 @@ private void OnProtocolActivated(ProtocolActivatedEventArgs e) { rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; - rootFrame.Navigate(typeof(RootPage)); + rootFrame.Navigate(typeof(RootPage), e.SplashScreen); Window.Current.Content = rootFrame; } diff --git a/UniSky/Controls/UniformGrid/TakenSpotsReferenceHolder.cs b/UniSky/Controls/UniformGrid/TakenSpotsReferenceHolder.cs new file mode 100644 index 0000000..d7da65e --- /dev/null +++ b/UniSky/Controls/UniformGrid/TakenSpotsReferenceHolder.cs @@ -0,0 +1,90 @@ +// 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.Collections; +using System.Drawing; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// Referencable class object we can use to have a reference shared between + /// our and + /// iterator. + /// This is used so we can better isolate our logic and make it easier to test. + /// + internal sealed class TakenSpotsReferenceHolder + { + /// + /// The instance used to efficiently track empty spots. + /// + private readonly BitArray spotsTaken; + + /// + /// Initializes a new instance of the class. + /// + /// The number of rows to track. + /// The number of columns to track. + public TakenSpotsReferenceHolder(int rows, int columns) + { + Height = rows; + Width = columns; + + this.spotsTaken = new BitArray(rows * columns); + } + + /// + /// Gets the height of the grid to monitor. + /// + public int Height { get; } + + /// + /// Gets the width of the grid to monitor. + /// + public int Width { get; } + + /// + /// Gets or sets the value of a specified grid cell. + /// + /// The vertical offset. + /// The horizontal offset. + public bool this[int i, int j] + { + get => this.spotsTaken[(i * Width) + j]; + set => this.spotsTaken[(i * Width) + j] = value; + } + + /// + /// Fills the specified area in the current grid with a given value. + /// If invalid coordinates are given, they will simply be ignored and no exception will be thrown. + /// + /// The value to fill the target area with. + /// The row to start on (inclusive, 0-based index). + /// The column to start on (inclusive, 0-based index). + /// The positive width of area to fill. + /// The positive height of area to fill. + public void Fill(bool value, int row, int column, int width, int height) + { + Rectangle bounds = new Rectangle(0, 0, Width, Height); + + // Precompute bounds to skip branching in main loop + bounds.Intersect(new Rectangle(column, row, width, height)); + + for (int i = bounds.Top; i < bounds.Bottom; i++) + { + for (int j = bounds.Left; j < bounds.Right; j++) + { + this[i, j] = value; + } + } + } + + /// + /// Resets the current reference holder. + /// + public void Reset() + { + this.spotsTaken.SetAll(false); + } + } +} \ No newline at end of file diff --git a/UniSky/Controls/UniformGrid/UniformGrid.Helpers.cs b/UniSky/Controls/UniformGrid/UniformGrid.Helpers.cs new file mode 100644 index 0000000..612fc8b --- /dev/null +++ b/UniSky/Controls/UniformGrid/UniformGrid.Helpers.cs @@ -0,0 +1,179 @@ +// 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.Collections.Generic; +using System.Linq; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// The UniformGrid control presents information within a Grid with even spacing. + /// + public partial class UniformGrid : Grid + { + // Provides the next spot in the boolean array with a 'false' value. +#pragma warning disable SA1009 // Closing parenthesis must be followed by a space. + internal static IEnumerable<(int row, int column)> GetFreeSpot(TakenSpotsReferenceHolder arrayref, int firstcolumn, bool topdown) +#pragma warning restore SA1009 // Closing parenthesis must be followed by a space. + { + if (topdown) + { + var rows = arrayref.Height; + + // Layout spots from Top-Bottom, Left-Right (right-left handled automatically by Grid with Flow-Direction). + // Effectively transpose the Grid Layout. + for (int c = 0; c < arrayref.Width; c++) + { + int start = (c == 0 && firstcolumn > 0 && firstcolumn < rows) ? firstcolumn : 0; + for (int r = start; r < rows; r++) + { + if (!arrayref[r, c]) + { + yield return (r, c); + } + } + } + } + else + { + var columns = arrayref.Width; + + // Layout spots as normal from Left-Right. + // (right-left handled automatically by Grid with Flow-Direction + // during its layout, internal model is always left-right). + for (int r = 0; r < arrayref.Height; r++) + { + int start = (r == 0 && firstcolumn > 0 && firstcolumn < columns) ? firstcolumn : 0; + for (int c = start; c < columns; c++) + { + if (!arrayref[r, c]) + { + yield return (r, c); + } + } + } + } + } + + // Based on the number of visible children, + // returns the dimensions of the + // grid we need to hold all elements. +#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly + internal static (int rows, int columns) GetDimensions(FrameworkElement[] visible, int rows, int cols, int firstColumn) +#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly + { + // If a dimension isn't specified, we need to figure out the other one (or both). + if (rows == 0 || cols == 0) + { + // Calculate the size & area of all objects in the grid to know how much space we need. + var count = Math.Max(1, visible.Sum(item => GetRowSpan(item) * GetColumnSpan(item))); + + if (rows == 0) + { + if (cols > 0) + { + // Bound check + var first = (firstColumn >= cols || firstColumn < 0) ? 0 : firstColumn; + + // If we have columns but no rows, calculate rows based on column offset and number of children. + rows = (count + first + (cols - 1)) / cols; + return (rows, cols); + } + else + { + // Otherwise, determine square layout if both are zero. + var size = (int)Math.Ceiling(Math.Sqrt(count)); + + // Figure out if firstColumn is in bounds + var first = (firstColumn >= size || firstColumn < 0) ? 0 : firstColumn; + + rows = (int)Math.Ceiling(Math.Sqrt(count + first)); + return (rows, rows); + } + } + else if (cols == 0) + { + // If we have rows and no columns, then calculate columns needed based on rows + cols = (count + (rows - 1)) / rows; + + // Now that we know a rough size of our shape, see if the FirstColumn effects that: + var first = (firstColumn >= cols || firstColumn < 0) ? 0 : firstColumn; + + cols = (count + first + (rows - 1)) / rows; + } + } + + return (rows, cols); + } + + // Used to interleave specified row dimensions with automatic rows added to use + // underlying Grid layout for main arrange of UniformGrid. + internal void SetupRowDefinitions(int rows) + { + // Mark initial definitions so we don't erase them. + foreach (var rd in RowDefinitions) + { + if (GetAutoLayout(rd) == null) + { + SetAutoLayout(rd, false); + } + } + + // Remove non-autolayout rows we've added and then add them in the right spots. + if (rows != RowDefinitions.Count) + { + for (int r = RowDefinitions.Count - 1; r >= 0; r--) + { + if (GetAutoLayout(RowDefinitions[r]) == true) + { + RowDefinitions.RemoveAt(r); + } + } + + for (int r = this.RowDefinitions.Count; r < rows; r++) + { + var rd = new RowDefinition(); + SetAutoLayout(rd, true); + this.RowDefinitions.Insert(r, rd); + } + } + } + + // Used to interleave specified column dimensions with automatic columns added to use + // underlying Grid layout for main arrange of UniformGrid. + internal void SetupColumnDefinitions(int columns) + { + // Mark initial definitions so we don't erase them. + foreach (var cd in ColumnDefinitions) + { + if (GetAutoLayout(cd) == null) + { + SetAutoLayout(cd, false); + } + } + + // Remove non-autolayout columns we've added and then add them in the right spots. + if (columns != ColumnDefinitions.Count) + { + for (int c = ColumnDefinitions.Count - 1; c >= 0; c--) + { + if (GetAutoLayout(ColumnDefinitions[c]) == true) + { + this.ColumnDefinitions.RemoveAt(c); + } + } + + for (int c = ColumnDefinitions.Count; c < columns; c++) + { + var cd = new ColumnDefinition(); + SetAutoLayout(cd, true); + ColumnDefinitions.Insert(c, cd); + } + } + } + } +} \ No newline at end of file diff --git a/UniSky/Controls/UniformGrid/UniformGrid.Properties.cs b/UniSky/Controls/UniformGrid/UniformGrid.Properties.cs new file mode 100644 index 0000000..5b4b32d --- /dev/null +++ b/UniSky/Controls/UniformGrid/UniformGrid.Properties.cs @@ -0,0 +1,162 @@ +// 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.Windows.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Data; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// The UniformGrid spaces items evenly. + /// + public partial class UniformGrid + { + /// + /// Determines if this element in the grid participates in the auto-layout algorithm. + /// + public static readonly DependencyProperty AutoLayoutProperty = + DependencyProperty.RegisterAttached( + "AutoLayout", + typeof(bool?), + typeof(UniformGrid), + new PropertyMetadata(null)); + + /// + /// Sets the AutoLayout Attached Property Value. Used internally to track items which need to be arranged vs. fixed in place. + /// Though it its required to use this property to force an element to the 0, 0 position. + /// + /// + /// A true value indicates this item should be automatically arranged. + public static void SetAutoLayout(FrameworkElement element, bool? value) + { + element.SetValue(AutoLayoutProperty, value); + } + + /// + /// Gets the AutoLayout Attached Property Value. Used internally to track items which need to be arranged vs. fixed in place. + /// + /// + /// A true value indicates this item should be automatically arranged. + public static bool? GetAutoLayout(FrameworkElement element) + { + return (bool?)element.GetValue(AutoLayoutProperty); + } + + /// + /// Sets the AutoLayout Attached Property Value. Used internally to track items which need to be arranged vs. fixed in place. + /// + /// + /// A true value indicates this item should be automatically arranged. + internal static void SetAutoLayout(ColumnDefinition element, bool? value) + { + element.SetValue(AutoLayoutProperty, value); + } + + /// + /// Gets the AutoLayout Attached Property Value. Used internally to track items which need to be arranged vs. fixed in place. + /// + /// + /// A true value indicates this item should be automatically arranged. + internal static bool? GetAutoLayout(ColumnDefinition element) + { + return (bool?)element.GetValue(AutoLayoutProperty); + } + + /// + /// Sets the AutoLayout Attached Property Value. Used internally to track items which need to be arranged vs. fixed in place. + /// + /// + /// A true value indicates this item should be automatically arranged. + internal static void SetAutoLayout(RowDefinition element, bool? value) + { + element.SetValue(AutoLayoutProperty, value); + } + + /// + /// Gets the AutoLayout Attached Property Value. Used internally to track items which need to be arranged vs. fixed in place. + /// + /// + /// A true value indicates this item should be automatically arranged. + internal static bool? GetAutoLayout(RowDefinition element) + { + return (bool?)element.GetValue(AutoLayoutProperty); + } + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty ColumnsProperty = + DependencyProperty.Register(nameof(Columns), typeof(int), typeof(UniformGrid), new PropertyMetadata(0, OnPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty FirstColumnProperty = + DependencyProperty.Register(nameof(FirstColumn), typeof(int), typeof(UniformGrid), new PropertyMetadata(0, OnPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(UniformGrid), new PropertyMetadata(Orientation.Horizontal, OnPropertyChanged)); + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty RowsProperty = + DependencyProperty.Register(nameof(Rows), typeof(int), typeof(UniformGrid), new PropertyMetadata(0, OnPropertyChanged)); + + private static void OnPropertyChanged(DependencyObject d, object newValue) + { + var self = d as UniformGrid; + + if (self != null) + { + self.InvalidateMeasure(); + } + } + + /// + /// Gets or sets the number of columns in the UniformGrid. + /// + public int Columns + { + get { return (int)GetValue(ColumnsProperty); } + set { SetValue(ColumnsProperty, value); } + } + + /// + /// Gets or sets the starting column offset for the first row of the UniformGrid. + /// + public int FirstColumn + { + get { return (int)GetValue(FirstColumnProperty); } + set { SetValue(FirstColumnProperty, value); } + } + + /// + /// Gets or sets the orientation of the grid. When , + /// will transpose the layout of automatically arranged items such that they start from + /// top to bottom then based on . + /// Defaults to . + /// + public Orientation Orientation + { + get { return (Orientation)GetValue(OrientationProperty); } + set { SetValue(OrientationProperty, value); } + } + + /// + /// Gets or sets the number of rows in the UniformGrid. + /// + public int Rows + { + get { return (int)GetValue(RowsProperty); } + set { SetValue(RowsProperty, value); } + } + } +} \ No newline at end of file diff --git a/UniSky/Controls/UniformGrid/UniformGrid.cs b/UniSky/Controls/UniformGrid/UniformGrid.cs new file mode 100644 index 0000000..4113338 --- /dev/null +++ b/UniSky/Controls/UniformGrid/UniformGrid.cs @@ -0,0 +1,181 @@ +// 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.Collections.Generic; +using System.Linq; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.UI.Controls +{ + /// + /// The UniformGrid control presents information within a Grid with even spacing. + /// + public partial class UniformGrid : Grid + { + // Internal list we use to keep track of items that we don't have space to layout. + private List _overflow = new List(); + + /// + /// The instance in use, if any. + /// + private TakenSpotsReferenceHolder _spotref; + + /// + protected override Size MeasureOverride(Size availableSize) + { + // Get all Visible FrameworkElement Children + var visible = Children.Where(item => item.Visibility != Visibility.Collapsed && item is FrameworkElement).Select(item => item as FrameworkElement).ToArray(); + +#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly + var (rows, columns) = GetDimensions(visible, Rows, Columns, FirstColumn); +#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly + + // Now that we know size, setup automatic rows/columns + // to utilize Grid for UniformGrid behavior. + // We also interleave any specified rows/columns with fixed sizes. + SetupRowDefinitions(rows); + SetupColumnDefinitions(columns); + + TakenSpotsReferenceHolder spotref; + + // If the last spot holder matches the size currently in use, just reset + // that instance and reuse it to avoid allocating a new bit array. + if (_spotref != null && _spotref.Height == rows && _spotref.Width == columns) + { + spotref = _spotref; + + spotref.Reset(); + } + else + { + spotref = _spotref = new TakenSpotsReferenceHolder(rows, columns); + } + + // Figure out which children we should automatically layout and where available openings are. + foreach (var child in visible) + { + var row = GetRow(child); + var col = GetColumn(child); + var rowspan = GetRowSpan(child); + var colspan = GetColumnSpan(child); + + // If an element needs to be forced in the 0, 0 position, + // they should manually set UniformGrid.AutoLayout to False for that element. + if ((row == 0 && col == 0 && GetAutoLayout(child) == null) || + GetAutoLayout(child) == true) + { + SetAutoLayout(child, true); + } + else + { + SetAutoLayout(child, false); + + spotref.Fill(true, row, col, colspan, rowspan); + } + } + + // Setup available size with our known dimensions now. + // UniformGrid expands size based on largest singular item. + double columnSpacingSize = 0; + double rowSpacingSize = 0; + + //columnSpacingSize = ColumnSpacing * (columns - 1); + //rowSpacingSize = RowSpacing * (rows - 1); + + Size childSize = new Size( + (availableSize.Width - columnSpacingSize) / columns, + (availableSize.Height - rowSpacingSize) / rows); + + double maxWidth = 0.0; + double maxHeight = 0.0; + + // Set Grid Row/Col for every child with autolayout = true + // Backwards with FlowDirection + var freespots = GetFreeSpot(spotref, FirstColumn, Orientation == Orientation.Vertical).GetEnumerator(); + foreach (var child in visible) + { + // Set location if we're in charge + if (GetAutoLayout(child) == true) + { + if (freespots.MoveNext()) + { +#pragma warning disable SA1009 // Closing parenthesis must be followed by a space. +#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly + var (row, column) = freespots.Current; +#pragma warning restore SA1008 // Opening parenthesis must be spaced correctly +#pragma warning restore SA1009 // Closing parenthesis must be followed by a space. + + SetRow(child, row); + SetColumn(child, column); + + var rowspan = GetRowSpan(child); + var colspan = GetColumnSpan(child); + + if (rowspan > 1 || colspan > 1) + { + // TODO: Need to tie this into iterator + spotref.Fill(true, row, column, colspan, rowspan); + } + } + else + { + // We've run out of spots as the developer has + // most likely given us a fixed size and too many elements + // Therefore, tell this element it has no size and move on. + child.Measure(Size.Empty); + + _overflow.Add(child); + + continue; + } + } + else if (GetRow(child) < 0 || GetRow(child) >= rows || + GetColumn(child) < 0 || GetColumn(child) >= columns) + { + // A child is specifying a location, but that location is outside + // of our grid space, so we should hide it instead. + child.Measure(Size.Empty); + + _overflow.Add(child); + + continue; + } + + // Get measurement for max child + child.Measure(childSize); + + maxWidth = Math.Max(child.DesiredSize.Width, maxWidth); + maxHeight = Math.Max(child.DesiredSize.Height, maxHeight); + } + + // Return our desired size based on the largest child we found, our dimensions, and spacing. + var desiredSize = new Size((maxWidth * columns) + columnSpacingSize, (maxHeight * rows) + rowSpacingSize); + + // Required to perform regular grid measurement, but ignore result. + base.MeasureOverride(desiredSize); + + return desiredSize; + } + + /// + protected override Size ArrangeOverride(Size finalSize) + { + // Have grid to the bulk of our heavy lifting. + var size = base.ArrangeOverride(finalSize); + + // Make sure all overflown elements have no size. + foreach (var child in _overflow) + { + child.Arrange(default); + } + + _overflow = new List(); // Reset for next time. + + return size; + } + } +} \ No newline at end of file diff --git a/UniSky/Helpers/Composition/BirdAnimation.cs b/UniSky/Helpers/Composition/BirdAnimation.cs index d225f5a..8c97ec6 100644 --- a/UniSky/Helpers/Composition/BirdAnimation.cs +++ b/UniSky/Helpers/Composition/BirdAnimation.cs @@ -1,5 +1,6 @@ using System; using System.Numerics; +using Microsoft.Toolkit.Uwp.UI.Animations.Behaviors; using Microsoft.Toolkit.Uwp.UI.Media.Geometry; using Windows.Foundation; using Windows.Foundation.Metadata; @@ -11,38 +12,13 @@ namespace UniSky.Helpers.Composition; internal static class BirdAnimation { - public static void RunBirdAnimation(FrameworkElement frame) + public static void RunBirdAnimation(FrameworkElement extendedSplash, FrameworkElement frame) { // TODO: fallback to a shape visual - if (!ApiInformation.IsMethodPresent(typeof(Compositor).FullName, "CreateGeometricClip")) - return; - - var appLogoPath = (string)Application.Current.Resources["BlueSkyLogoPath"]; - var appLogoWidth = (int)Application.Current.Resources["BlueSkyLogoWidth"]; - var appLogoHeight = (int)Application.Current.Resources["BlueSkyLogoHeight"]; - - var size = new Size(frame.ActualWidth, frame.ActualHeight); var frameVisual = ElementCompositionPreview.GetElementVisual(frame); - - var initialScale = (float)(1.5 * Math.Min(size.Width / 620, 1.0)); - - var scale = MathF.Max((float)size.Width / 30, (float)size.Height / 12); - var offsetX = (float)(size.Width / 2.0); - var offsetY = (float)(size.Height / 2.0); + var splashVisual = ElementCompositionPreview.GetElementVisual(extendedSplash); var compositor = frameVisual.Compositor; - var clip = compositor.CreateGeometricClip(appLogoPath); - clip.ViewBox = compositor.CreateViewBox(); - clip.ViewBox.Size = new Vector2(appLogoWidth, appLogoHeight); - clip.ViewBox.Stretch = CompositionStretch.None; - clip.AnchorPoint = new Vector2(0.5f, 0.5f); - clip.Scale = new Vector2(initialScale, initialScale); - clip.Offset = new Vector2(offsetX, offsetY); - - frameVisual.AnchorPoint = new Vector2(0.5f); - frameVisual.Offset = new Vector3((float)(size.Width / 2), (float)(size.Height / 2), 0); - frameVisual.Clip = clip; - var batch = compositor.CreateScopedBatch(CompositionBatchTypes.Animation); batch.Completed += (o, ev) => { @@ -51,34 +27,68 @@ public static void RunBirdAnimation(FrameworkElement frame) frameVisual.Offset = Vector3.Zero; }; - var ease = compositor.CreateCubicBezierEasingFunction(new Vector2(0.7f, 0), new Vector2(0.84f, 0)); - var ease2 = compositor.CreateCubicBezierEasingFunction(new Vector2(0, 0.55f), new Vector2(0.45f, 1)); - - var group = compositor.CreateAnimationGroup(); - - var scaleAnimation = compositor.CreateVector2KeyFrameAnimation(); - scaleAnimation.InsertKeyFrame(1.0f, new Vector2(scale, scale), ease); - scaleAnimation.DelayTime = TimeSpan.FromSeconds(0.15); - scaleAnimation.Duration = TimeSpan.FromSeconds(0.15); - scaleAnimation.Target = "Scale"; - group.Add(scaleAnimation); - - var offsetAnimation = compositor.CreateVector2KeyFrameAnimation(); - offsetAnimation.InsertKeyFrame(1.0f, new Vector2(offsetX, offsetY - (6 * scale)), ease); - offsetAnimation.DelayTime = TimeSpan.FromSeconds(0.15); - offsetAnimation.Duration = TimeSpan.FromSeconds(0.15); - offsetAnimation.Target = "Offset"; - group.Add(offsetAnimation); - - var scaleAnimation2 = compositor.CreateVector3KeyFrameAnimation(); - scaleAnimation2.InsertKeyFrame(0.5f, new Vector3(1.1f), ease); - scaleAnimation2.InsertKeyFrame(1.0f, new Vector3(1.0f), ease2); - scaleAnimation2.DelayTime = TimeSpan.FromSeconds(0.15); - scaleAnimation2.Duration = TimeSpan.FromSeconds(0.3); - scaleAnimation2.Target = "Scale"; - - clip.StartAnimationGroup(group); - frameVisual.StartAnimation(scaleAnimation2.Target, scaleAnimation2); + var splashHideAnimation = compositor.CreateScalarKeyFrameAnimation(); + splashHideAnimation.InsertKeyFrame(0.0f, 1.0f); + splashHideAnimation.InsertKeyFrame(1.0f, 0.0f); + splashHideAnimation.DelayTime = TimeSpan.FromSeconds(0); + splashHideAnimation.Duration = TimeSpan.FromSeconds(0.15); + + if (ApiInformation.IsMethodPresent(typeof(Compositor).FullName, "CreateGeometricClip")) + { + var appLogoPath = (string)Application.Current.Resources["BlueSkyLogoPath"]; + var appLogoWidth = (int)Application.Current.Resources["BlueSkyLogoWidth"]; + var appLogoHeight = (int)Application.Current.Resources["BlueSkyLogoHeight"]; + + var size = new Size(frame.ActualWidth, frame.ActualHeight); + + var initialScale = (float)(1.5 * Math.Min(size.Width / 620, 1.0)); + + var scale = MathF.Max((float)size.Width / 30, (float)size.Height / 12); + var offsetX = (float)(size.Width / 2.0); + var offsetY = (float)(size.Height / 2.0); + + var clip = compositor.CreateGeometricClip(appLogoPath); + clip.ViewBox = compositor.CreateViewBox(); + clip.ViewBox.Size = new Vector2(appLogoWidth, appLogoHeight); + clip.ViewBox.Stretch = CompositionStretch.None; + clip.AnchorPoint = new Vector2(0.5f, 0.5f); + clip.Scale = new Vector2(initialScale, initialScale); + clip.Offset = new Vector2(offsetX, offsetY); + + frameVisual.AnchorPoint = new Vector2(0.5f); + frameVisual.Offset = new Vector3((float)(size.Width / 2), (float)(size.Height / 2), 0); + frameVisual.Clip = clip; + + var ease = compositor.CreateCubicBezierEasingFunction(new Vector2(0.7f, 0), new Vector2(0.84f, 0)); + var ease2 = compositor.CreateCubicBezierEasingFunction(new Vector2(0, 0.55f), new Vector2(0.45f, 1)); + var group = compositor.CreateAnimationGroup(); + + var scaleAnimation = compositor.CreateVector2KeyFrameAnimation(); + scaleAnimation.InsertKeyFrame(1.0f, new Vector2(scale, scale), ease); + scaleAnimation.DelayTime = TimeSpan.FromSeconds(0.15); + scaleAnimation.Duration = TimeSpan.FromSeconds(0.15); + scaleAnimation.Target = "Scale"; + group.Add(scaleAnimation); + + var offsetAnimation = compositor.CreateVector2KeyFrameAnimation(); + offsetAnimation.InsertKeyFrame(1.0f, new Vector2(offsetX, offsetY - (6 * scale)), ease); + offsetAnimation.DelayTime = TimeSpan.FromSeconds(0.15); + offsetAnimation.Duration = TimeSpan.FromSeconds(0.15); + offsetAnimation.Target = "Offset"; + group.Add(offsetAnimation); + + var scaleAnimation2 = compositor.CreateVector3KeyFrameAnimation(); + scaleAnimation2.InsertKeyFrame(0.5f, new Vector3(1.1f), ease); + scaleAnimation2.InsertKeyFrame(1.0f, new Vector3(1.0f), ease2); + scaleAnimation2.DelayTime = TimeSpan.FromSeconds(0.15); + scaleAnimation2.Duration = TimeSpan.FromSeconds(0.3); + scaleAnimation2.Target = "Scale"; + + clip.StartAnimationGroup(group); + frameVisual.StartAnimation(scaleAnimation2.Target, scaleAnimation2); + } + + splashVisual.StartAnimation("Opacity", splashHideAnimation); batch.End(); } diff --git a/UniSky/Package.appxmanifest b/UniSky/Package.appxmanifest index 6e5fca9..2f268f9 100644 --- a/UniSky/Package.appxmanifest +++ b/UniSky/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.0.196.0" /> @@ -47,7 +47,7 @@ - + diff --git a/UniSky/Pages/FeedsPage.xaml b/UniSky/Pages/FeedsPage.xaml index 0b95ae7..32fa3f8 100644 --- a/UniSky/Pages/FeedsPage.xaml +++ b/UniSky/Pages/FeedsPage.xaml @@ -10,14 +10,14 @@ xmlns:viewmodels="using:UniSky.ViewModels" xmlns:mux="using:Microsoft.UI.Xaml.Controls" xmlns:feeds="using:UniSky.ViewModels.Feeds" - xmlns:extensions="using:UniSky.Extensions" + xmlns:extensions="using:UniSky.Extensions" xmlns:templates="using:UniSky.Templates" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=viewmodels:FeedsViewModel}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" NavigationCacheMode="Enabled" w1803:KeyboardAcceleratorPlacementMode="Hidden"> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + IsSettingsVisible="False" + MenuItemsSource="{x:Bind ViewModel.MenuItems}" + FooterMenuItemsSource="{x:Bind ViewModel.FooterMenuItems}" + SelectedItem="{x:Bind ViewModel.SelectedMenuItem, Mode=TwoWay}"> + + + + + + + + + + + - + + + + + + + - - - + + + + + @@ -191,14 +143,11 @@ MaxWidth="600" BorderBrush="{ThemeResource SystemControlRevealSeparatorBrush}" ui:UIElementExtensions.ClipToBounds="True"> - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + +