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">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+