diff --git a/CRSim/App.xaml b/CRSim/App.xaml index 2ac0292..65368e3 100644 --- a/CRSim/App.xaml +++ b/CRSim/App.xaml @@ -9,6 +9,7 @@ + diff --git a/CRSim/Assets/CRSimIcon.png b/CRSim/Assets/CRSimIcon.png index d12d2c6..2d62627 100644 Binary files a/CRSim/Assets/CRSimIcon.png and b/CRSim/Assets/CRSimIcon.png differ diff --git a/CRSim/Assets/HomeHeaderTiles/CRSim-Icon.ico b/CRSim/Assets/HomeHeaderTiles/CRSim-Icon.ico deleted file mode 100644 index 55fe937..0000000 Binary files a/CRSim/Assets/HomeHeaderTiles/CRSim-Icon.ico and /dev/null differ diff --git a/CRSim/Assets/HomeHeaderTiles/GalleryHeaderImage.jpg b/CRSim/Assets/HomeHeaderTiles/GalleryHeaderImage.jpg new file mode 100644 index 0000000..c3e66fc Binary files /dev/null and b/CRSim/Assets/HomeHeaderTiles/GalleryHeaderImage.jpg differ diff --git a/CRSim/CRSim.csproj b/CRSim/CRSim.csproj index 7fb4aaf..98e2b15 100644 --- a/CRSim/CRSim.csproj +++ b/CRSim/CRSim.csproj @@ -14,22 +14,12 @@ None preview - - - - - - - - - - - - - + + PreserveNewest + @@ -51,6 +41,7 @@ + @@ -68,13 +59,18 @@ - - - - - - - + + Designer + + + Designer + + + Designer + + + Designer + Designer @@ -113,18 +109,24 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + True \ - - MSBuild:Compile - - - MSBuild:Compile - diff --git a/CRSim/Controls/HomePageHeader.xaml b/CRSim/Controls/HomePageHeader.xaml new file mode 100644 index 0000000..40d2096 --- /dev/null +++ b/CRSim/Controls/HomePageHeader.xaml @@ -0,0 +1,142 @@ + + + + + M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z + + + + + + + + 0.9 + + + + + + + + + + 0.8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CRSim/Controls/HomePageHeader.xaml.cs b/CRSim/Controls/HomePageHeader.xaml.cs new file mode 100644 index 0000000..55f9013 --- /dev/null +++ b/CRSim/Controls/HomePageHeader.xaml.cs @@ -0,0 +1,10 @@ +namespace CRSim.Controls; + +public sealed partial class HomePageHeader : UserControl +{ + + public HomePageHeader() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/CRSim/Controls/HorizontalScrollContainer.xaml b/CRSim/Controls/HorizontalScrollContainer.xaml new file mode 100644 index 0000000..4887b9e --- /dev/null +++ b/CRSim/Controls/HorizontalScrollContainer.xaml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CRSim/Controls/HorizontalScrollContainer.xaml.cs b/CRSim/Controls/HorizontalScrollContainer.xaml.cs new file mode 100644 index 0000000..c97cd63 --- /dev/null +++ b/CRSim/Controls/HorizontalScrollContainer.xaml.cs @@ -0,0 +1,72 @@ +namespace CRSim.Controls; + +public sealed partial class HorizontalScrollContainer : UserControl +{ + public HorizontalScrollContainer() + { + this.InitializeComponent(); + } + + public object Source + { + get => (object)GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } + + public static readonly DependencyProperty SourceProperty = + DependencyProperty.Register(nameof(Source), typeof(object), typeof(HorizontalScrollContainer), new PropertyMetadata(null)); + + private void Scroller_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e) + { + if (e.FinalView.HorizontalOffset < 1) + { + ScrollBackBtn.Visibility = Visibility.Collapsed; + } + else if (e.FinalView.HorizontalOffset > 1) + { + ScrollBackBtn.Visibility = Visibility.Visible; + } + + if (e.FinalView.HorizontalOffset > scroller.ScrollableWidth - 1) + { + ScrollForwardBtn.Visibility = Visibility.Collapsed; + } + else if (e.FinalView.HorizontalOffset < scroller.ScrollableWidth - 1) + { + ScrollForwardBtn.Visibility = Visibility.Visible; + } + } + + private void ScrollBackBtn_Click(object sender, RoutedEventArgs e) + { + scroller.ChangeView(scroller.HorizontalOffset - scroller.ViewportWidth, null, null); + + // Manually focus to ScrollForwardBtn since this button disappears after scrolling to the end. + ScrollForwardBtn.Focus(FocusState.Programmatic); + } + + private void ScrollForwardBtn_Click(object sender, RoutedEventArgs e) + { + scroller.ChangeView(scroller.HorizontalOffset + scroller.ViewportWidth, null, null); + + // Manually focus to ScrollBackBtn since this button disappears after scrolling to the end. + ScrollBackBtn.Focus(FocusState.Programmatic); + } + + private void Scroller_SizeChanged(object sender, SizeChangedEventArgs e) + { + UpdateScrollButtonsVisibility(); + } + + private void UpdateScrollButtonsVisibility() + { + if (scroller.ScrollableWidth > 0) + { + ScrollForwardBtn.Visibility = Visibility.Visible; + } + else + { + ScrollForwardBtn.Visibility = Visibility.Collapsed; + } + } +} \ No newline at end of file diff --git a/CRSim/Controls/OpacityMaskView.xaml b/CRSim/Controls/OpacityMaskView.xaml new file mode 100644 index 0000000..7251e4f --- /dev/null +++ b/CRSim/Controls/OpacityMaskView.xaml @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/CRSim/Controls/OpacityMaskView.xaml.cs b/CRSim/Controls/OpacityMaskView.xaml.cs new file mode 100644 index 0000000..36c338f --- /dev/null +++ b/CRSim/Controls/OpacityMaskView.xaml.cs @@ -0,0 +1,85 @@ +using Microsoft.UI.Composition; +using Microsoft.UI.Xaml.Hosting; +using Microsoft.UI.Xaml.Media; +using System.Numerics; + +namespace CRSim.Controls; + +[TemplatePart(Name = RootGridTemplateName, Type = typeof(Grid))] +[TemplatePart(Name = MaskContainerTemplateName, Type = typeof(Border))] +[TemplatePart(Name = ContentPresenterTemplateName, Type = typeof(ContentPresenter))] +public partial class OpacityMaskView : ContentControl +{ + + public static readonly DependencyProperty OpacityMaskProperty = + DependencyProperty.Register(nameof(OpacityMask), typeof(UIElement), typeof(OpacityMaskView), new PropertyMetadata(null, OnOpacityMaskChanged)); + + private const string ContentPresenterTemplateName = "PART_ContentPresenter"; + private const string MaskContainerTemplateName = "PART_MaskContainer"; + private const string RootGridTemplateName = "PART_RootGrid"; + + private readonly Compositor _compositor = CompositionTarget.GetCompositorForCurrentThread(); + private CompositionBrush? _mask; + private CompositionMaskBrush? _maskBrush; + + public OpacityMaskView() + { + DefaultStyleKey = typeof(OpacityMaskView); + } + + public UIElement? OpacityMask + { + get => (UIElement?)GetValue(OpacityMaskProperty); + set => SetValue(OpacityMaskProperty, value); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + Grid rootGrid = (Grid)GetTemplateChild(RootGridTemplateName); + ContentPresenter contentPresenter = (ContentPresenter)GetTemplateChild(ContentPresenterTemplateName); + Border maskContainer = (Border)GetTemplateChild(MaskContainerTemplateName); + + _maskBrush = _compositor.CreateMaskBrush(); + _maskBrush.Source = GetVisualBrush(contentPresenter); + _mask = GetVisualBrush(maskContainer); + _maskBrush.Mask = OpacityMask is null ? null : _mask; + + SpriteVisual redirectVisual = _compositor.CreateSpriteVisual(); + redirectVisual.RelativeSizeAdjustment = Vector2.One; + redirectVisual.Brush = _maskBrush; + ElementCompositionPreview.SetElementChildVisual(rootGrid, redirectVisual); + } + + private static CompositionBrush GetVisualBrush(UIElement element) + { + Visual visual = ElementCompositionPreview.GetElementVisual(element); + + Compositor compositor = visual.Compositor; + + CompositionVisualSurface visualSurface = compositor.CreateVisualSurface(); + visualSurface.SourceVisual = visual; + ExpressionAnimation sourceSizeAnimation = compositor.CreateExpressionAnimation($"{nameof(visual)}.Size"); + sourceSizeAnimation.SetReferenceParameter(nameof(visual), visual); + visualSurface.StartAnimation(nameof(visualSurface.SourceSize), sourceSizeAnimation); + + CompositionSurfaceBrush brush = compositor.CreateSurfaceBrush(visualSurface); + + visual.Opacity = 0; + + return brush; + } + + private static void OnOpacityMaskChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + OpacityMaskView self = (OpacityMaskView)d; + if (self._maskBrush is not { } maskBrush) + { + return; + } + + UIElement? opacityMask = (UIElement?)e.NewValue; + maskBrush.Mask = opacityMask is null ? null : self._mask; + } +} \ No newline at end of file diff --git a/CRSim/Controls/Tile.xaml b/CRSim/Controls/Tile.xaml new file mode 100644 index 0000000..ea28410 --- /dev/null +++ b/CRSim/Controls/Tile.xaml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CRSim/Controls/Tile.xaml.cs b/CRSim/Controls/Tile.xaml.cs new file mode 100644 index 0000000..ace5801 --- /dev/null +++ b/CRSim/Controls/Tile.xaml.cs @@ -0,0 +1,49 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace CRSim.Controls; + +public sealed partial class Tile : UserControl +{ + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register(nameof(Title), typeof(string), typeof(Tile), new PropertyMetadata(null)); + + public string Description + { + get { return (string)GetValue(DescriptionProperty); } + set { SetValue(DescriptionProperty, value); } + } + + public static readonly DependencyProperty DescriptionProperty = + DependencyProperty.Register(nameof(Description), typeof(string), typeof(Tile), new PropertyMetadata(null)); + + public object Source + { + get { return (object)GetValue(SourceProperty); } + set { SetValue(SourceProperty, value); } + } + + public static readonly DependencyProperty SourceProperty = + DependencyProperty.Register(nameof(Source), typeof(object), typeof(Tile), new PropertyMetadata(null)); + + public string Link + { + get { return (string)GetValue(LinkProperty); } + set { SetValue(LinkProperty, value); } + } + + public static readonly DependencyProperty LinkProperty = + DependencyProperty.Register(nameof(Link), typeof(string), typeof(Tile), new PropertyMetadata(null)); + + + public Tile() + { + this.InitializeComponent(); + } +} \ No newline at end of file diff --git a/CRSim/ViewModels/DashboardPageViewModel.cs b/CRSim/ViewModels/DashboardPageViewModel.cs index 65f345c..5410ba3 100644 --- a/CRSim/ViewModels/DashboardPageViewModel.cs +++ b/CRSim/ViewModels/DashboardPageViewModel.cs @@ -3,9 +3,29 @@ namespace CRSim.ViewModels; public partial class DashboardPageViewModel : ObservableObject { + [ObservableProperty] + public partial string? UpdateMessage { get; set; } = null; public StyleManager StyleManager { get; } - public DashboardPageViewModel(StyleManager styleManager) + private readonly INetworkService _networkService; + private readonly ISettingsService _settingsService; + public DashboardPageViewModel(StyleManager styleManager,INetworkService networkService,ISettingsService settingsService) { StyleManager = styleManager; + _networkService = networkService; + _settingsService = settingsService; + InitializeAsync(); + } + private async void InitializeAsync() + { + var url = _settingsService.GetSettings().ApiUri.Contains("47") + ? "https://api.github.com/repos/denglihong2007/CRSim/releases/latest" + : "https://crsim.com.cn/api/version"; + + var updateInfo = await _networkService.GetUpdateAsync(url); + + if (updateInfo is not null && updateInfo.Name != Assembly.GetExecutingAssembly().GetName().Version.ToString()) + { + UpdateMessage = $"有新版本 {updateInfo.Name} 可用,请前往“设置”下载安装更新!"; + } } } \ No newline at end of file diff --git a/CRSim/Views/DashboardPage.xaml b/CRSim/Views/DashboardPage.xaml index 9098e92..3d93ae5 100644 --- a/CRSim/Views/DashboardPage.xaml +++ b/CRSim/Views/DashboardPage.xaml @@ -6,32 +6,68 @@ xmlns:local="using:CRSim.Views" xmlns:winui="using:CommunityToolkit.WinUI" xmlns:screensim="using:CRSim.ScreenSimulator.Views" + xmlns:animations="using:CommunityToolkit.WinUI.Animations" + xmlns:localcontrols="using:CRSim.Controls" + xmlns:converts="using:CRSim.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - - - - - - - - - - - - - - -