mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23f3e5df77 | ||
|
|
4a027a8d3f | ||
|
|
80459708a7 | ||
|
|
650b67bea0 | ||
|
|
18b3d23b1c | ||
|
|
915b1aae32 | ||
|
|
71a1bdc173 | ||
|
|
810f8704e6 | ||
|
|
0165c03ae6 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,9 +4,6 @@
|
|||||||
|
|
||||||
.vs/
|
.vs/
|
||||||
|
|
||||||
src/Snap.Hutao/SettingsUI/bin
|
|
||||||
src/Snap.Hutao/SettingsUI/obj
|
|
||||||
|
|
||||||
src/Snap.Hutao/Snap.Hutao/bin/
|
src/Snap.Hutao/Snap.Hutao/bin/
|
||||||
src/Snap.Hutao/Snap.Hutao/obj/
|
src/Snap.Hutao/Snap.Hutao/obj/
|
||||||
src/Snap.Hutao/Snap.Hutao/Snap.Hutao_TemporaryKey.pfx
|
src/Snap.Hutao/Snap.Hutao/Snap.Hutao_TemporaryKey.pfx
|
||||||
|
|||||||
@@ -28,4 +28,5 @@
|
|||||||
* [microsoft/vs-threading](https://github.com/microsoft/vs-threading)
|
* [microsoft/vs-threading](https://github.com/microsoft/vs-threading)
|
||||||
* [microsoft/vs-validation](https://github.com/microsoft/vs-validation)
|
* [microsoft/vs-validation](https://github.com/microsoft/vs-validation)
|
||||||
* [microsoft/WindowsAppSDK](https://github.com/microsoft/WindowsAppSDK)
|
* [microsoft/WindowsAppSDK](https://github.com/microsoft/WindowsAppSDK)
|
||||||
* [microsoft/microsoft-ui-xaml](https://github.com/microsoft/microsoft-ui-xaml)
|
* [microsoft/microsoft-ui-xaml](https://github.com/microsoft/microsoft-ui-xaml)
|
||||||
|
* [WinUICommunity/SettingsUI](https://github.com/WinUICommunity/SettingsUI)
|
||||||
@@ -17,6 +17,16 @@ trigger:
|
|||||||
- azure-pipelines.yml
|
- azure-pipelines.yml
|
||||||
- .github/ISSUE_TEMPLATE/*.yml
|
- .github/ISSUE_TEMPLATE/*.yml
|
||||||
- .github/workflows/*.yml
|
- .github/workflows/*.yml
|
||||||
|
pr:
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- README.md
|
||||||
|
- azure-pipelines.yml
|
||||||
|
- .github/ISSUE_TEMPLATE/*.yml
|
||||||
|
- .github/workflows/*.yml
|
||||||
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
@@ -28,7 +38,7 @@ variables:
|
|||||||
project: $(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj'
|
project: $(Build.SourcesDirectory)/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj'
|
||||||
buildPlatform: 'x64'
|
buildPlatform: 'x64'
|
||||||
buildConfiguration: 'Release'
|
buildConfiguration: 'Release'
|
||||||
build_date: $[ format('{0:yyyy}.{0:MM}.{0:dd}', pipeline.startTime) ]
|
build_date: $[ format('{0:yyyy}.{0:M}.{0:d}', pipeline.startTime) ]
|
||||||
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -134,6 +144,7 @@ steps:
|
|||||||
secureFile: 'Snap.Hutao.CI.cer'
|
secureFile: 'Snap.Hutao.CI.cer'
|
||||||
|
|
||||||
- task: GitHubRelease@1
|
- task: GitHubRelease@1
|
||||||
|
condition: or(eq(variables['Build.Reason'], 'Manual'), eq(variables['Build.Reason'], 'IndividualCI'), eq(variables['Build.Reason'], 'BatchedCI'))
|
||||||
inputs:
|
inputs:
|
||||||
gitHubConnection: 'github.com_Masterain'
|
gitHubConnection: 'github.com_Masterain'
|
||||||
repositoryName: 'DGP-Studio/Snap.Hutao'
|
repositoryName: 'DGP-Studio/Snap.Hutao'
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Automation;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace SettingsUI.Controls;
|
|
||||||
|
|
||||||
public class CheckBoxWithDescriptionControl : CheckBox
|
|
||||||
{
|
|
||||||
private readonly CheckBoxWithDescriptionControl _checkBoxSubTextControl;
|
|
||||||
|
|
||||||
public CheckBoxWithDescriptionControl()
|
|
||||||
{
|
|
||||||
_checkBoxSubTextControl = this;
|
|
||||||
Loaded += CheckBoxSubTextControl_Loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate()
|
|
||||||
{
|
|
||||||
Update();
|
|
||||||
base.OnApplyTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(Header))
|
|
||||||
{
|
|
||||||
AutomationProperties.SetName(this, Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckBoxSubTextControl_Loaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
StackPanel panel = new() { Orientation = Orientation.Vertical };
|
|
||||||
|
|
||||||
// Add text box only if the description is not empty. Required for additional plugin options.
|
|
||||||
if (!string.IsNullOrWhiteSpace(Description))
|
|
||||||
{
|
|
||||||
panel.Children.Add(new TextBlock() { Margin = new Thickness(0, 10, 0, 0), Text = Header });
|
|
||||||
//Style = (Style)Application.Current.Resources["SecondaryIsEnabledTextBlockStyle"]
|
|
||||||
panel.Children.Add(new IsEnabledTextBlock() { Text = Description });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
panel.Children.Add(new TextBlock() { Margin = new Thickness(0, 0, 0, 0), Text = Header });
|
|
||||||
}
|
|
||||||
|
|
||||||
_checkBoxSubTextControl.Content = panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
|
|
||||||
"Header",
|
|
||||||
typeof(string),
|
|
||||||
typeof(CheckBoxWithDescriptionControl),
|
|
||||||
new PropertyMetadata(default(string)));
|
|
||||||
|
|
||||||
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
|
|
||||||
"Description",
|
|
||||||
typeof(object),
|
|
||||||
typeof(CheckBoxWithDescriptionControl),
|
|
||||||
new PropertyMetadata(default(string)));
|
|
||||||
|
|
||||||
[Localizable(true)]
|
|
||||||
public string Header
|
|
||||||
{
|
|
||||||
get => (string)GetValue(HeaderProperty);
|
|
||||||
set => SetValue(HeaderProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Localizable(true)]
|
|
||||||
public string Description
|
|
||||||
{
|
|
||||||
get => (string)GetValue(DescriptionProperty);
|
|
||||||
set => SetValue(DescriptionProperty, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace SettingsUI.Controls;
|
|
||||||
|
|
||||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
|
||||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
|
||||||
public class IsEnabledTextBlock : Control
|
|
||||||
{
|
|
||||||
public IsEnabledTextBlock()
|
|
||||||
{
|
|
||||||
DefaultStyleKey = typeof(IsEnabledTextBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate()
|
|
||||||
{
|
|
||||||
IsEnabledChanged -= IsEnabledTextBlock_IsEnabledChanged;
|
|
||||||
SetEnabledState();
|
|
||||||
IsEnabledChanged += IsEnabledTextBlock_IsEnabledChanged;
|
|
||||||
base.OnApplyTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
|
|
||||||
"Text",
|
|
||||||
typeof(string),
|
|
||||||
typeof(IsEnabledTextBlock),
|
|
||||||
null);
|
|
||||||
|
|
||||||
[Localizable(true)]
|
|
||||||
public string Text
|
|
||||||
{
|
|
||||||
get => (string)GetValue(TextProperty);
|
|
||||||
set => SetValue(TextProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void IsEnabledTextBlock_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
SetEnabledState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetEnabledState()
|
|
||||||
{
|
|
||||||
VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:local="using:SettingsUI.Controls">
|
|
||||||
|
|
||||||
<Style TargetType="local:IsEnabledTextBlock">
|
|
||||||
<Setter Property="Foreground" Value="{ThemeResource DefaultTextForegroundThemeBrush}" />
|
|
||||||
<Setter Property="IsTabStop" Value="False" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="local:IsEnabledTextBlock">
|
|
||||||
<Grid>
|
|
||||||
<VisualStateManager.VisualStateGroups>
|
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
|
||||||
<VisualState x:Name="Normal"/>
|
|
||||||
<VisualState x:Name="Disabled">
|
|
||||||
<VisualState.Setters>
|
|
||||||
<Setter Target="Label.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
|
||||||
</VisualState.Setters>
|
|
||||||
</VisualState>
|
|
||||||
</VisualStateGroup>
|
|
||||||
</VisualStateManager.VisualStateGroups>
|
|
||||||
<TextBlock x:Name="Label"
|
|
||||||
FontSize="{TemplateBinding FontSize}"
|
|
||||||
FontWeight="{TemplateBinding FontWeight}"
|
|
||||||
FontFamily="{TemplateBinding FontFamily}"
|
|
||||||
Foreground="{TemplateBinding Foreground}"
|
|
||||||
TextWrapping="WrapWholeWords"
|
|
||||||
Text="{TemplateBinding Text}" />
|
|
||||||
</Grid>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
<Style TargetType="local:IsEnabledTextBlock" x:Key="SecondaryIsEnabledTextBlockStyle">
|
|
||||||
<Setter Property="Foreground" Value="{ThemeResource TextFillColorSecondaryBrush}" />
|
|
||||||
<Setter Property="FontSize" Value="{StaticResource SecondaryTextFontSize}"/>
|
|
||||||
</Style>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Automation;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace SettingsUI.Controls;
|
|
||||||
|
|
||||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
|
||||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
|
||||||
[TemplatePart(Name = PartIconPresenter, Type = typeof(ContentPresenter))]
|
|
||||||
[TemplatePart(Name = PartDescriptionPresenter, Type = typeof(ContentPresenter))]
|
|
||||||
public class Setting : ContentControl
|
|
||||||
{
|
|
||||||
private const string PartIconPresenter = "IconPresenter";
|
|
||||||
private const string PartDescriptionPresenter = "DescriptionPresenter";
|
|
||||||
private ContentPresenter? _iconPresenter;
|
|
||||||
private ContentPresenter? _descriptionPresenter;
|
|
||||||
private Setting? _setting;
|
|
||||||
|
|
||||||
public Setting()
|
|
||||||
{
|
|
||||||
DefaultStyleKey = typeof(Setting);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
|
|
||||||
"Header",
|
|
||||||
typeof(string),
|
|
||||||
typeof(Setting),
|
|
||||||
new PropertyMetadata(default(string), OnHeaderChanged));
|
|
||||||
|
|
||||||
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
|
|
||||||
"Description",
|
|
||||||
typeof(object),
|
|
||||||
typeof(Setting),
|
|
||||||
new PropertyMetadata(null, OnDescriptionChanged));
|
|
||||||
|
|
||||||
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
|
|
||||||
"Icon",
|
|
||||||
typeof(object),
|
|
||||||
typeof(Setting),
|
|
||||||
new PropertyMetadata(default(string), OnIconChanged));
|
|
||||||
|
|
||||||
public static readonly DependencyProperty ActionContentProperty = DependencyProperty.Register(
|
|
||||||
"ActionContent",
|
|
||||||
typeof(object),
|
|
||||||
typeof(Setting),
|
|
||||||
null);
|
|
||||||
|
|
||||||
[Localizable(true)]
|
|
||||||
public string Header
|
|
||||||
{
|
|
||||||
get => (string)GetValue(HeaderProperty);
|
|
||||||
set => SetValue(HeaderProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Localizable(true)]
|
|
||||||
public object Description
|
|
||||||
{
|
|
||||||
get => GetValue(DescriptionProperty);
|
|
||||||
set => SetValue(DescriptionProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Icon
|
|
||||||
{
|
|
||||||
get => GetValue(IconProperty);
|
|
||||||
set => SetValue(IconProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public object ActionContent
|
|
||||||
{
|
|
||||||
get => GetValue(ActionContentProperty);
|
|
||||||
set => SetValue(ActionContentProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate()
|
|
||||||
{
|
|
||||||
IsEnabledChanged -= Setting_IsEnabledChanged;
|
|
||||||
_setting = this;
|
|
||||||
_iconPresenter = (ContentPresenter)_setting.GetTemplateChild(PartIconPresenter);
|
|
||||||
_descriptionPresenter = (ContentPresenter)_setting.GetTemplateChild(PartDescriptionPresenter);
|
|
||||||
Update();
|
|
||||||
SetEnabledState();
|
|
||||||
IsEnabledChanged += Setting_IsEnabledChanged;
|
|
||||||
base.OnApplyTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
((Setting)d).Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
((Setting)d).Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
((Setting)d).Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Setting_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
SetEnabledState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetEnabledState()
|
|
||||||
{
|
|
||||||
VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
if (_setting == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_setting.ActionContent != null)
|
|
||||||
{
|
|
||||||
if (_setting.ActionContent.GetType() != typeof(Button))
|
|
||||||
{
|
|
||||||
// We do not want to override the default AutomationProperties.Name of a button. Its Content property already describes what it does.
|
|
||||||
if (!string.IsNullOrEmpty(_setting.Header))
|
|
||||||
{
|
|
||||||
AutomationProperties.SetName((UIElement)_setting.ActionContent, _setting.Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_setting._iconPresenter != null)
|
|
||||||
{
|
|
||||||
if (_setting.Icon == null)
|
|
||||||
{
|
|
||||||
_setting._iconPresenter.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_setting._iconPresenter.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_setting.Description == null)
|
|
||||||
{
|
|
||||||
_setting._descriptionPresenter!.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_setting._descriptionPresenter!.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
<ResourceDictionary
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:controls="using:SettingsUI.Controls">
|
|
||||||
|
|
||||||
<Style TargetType="controls:Setting">
|
|
||||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
|
||||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundBrush}" />
|
|
||||||
<Setter Property="BorderThickness" Value="{ThemeResource CardBorderThickness}" />
|
|
||||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}" />
|
|
||||||
<Setter Property="IsTabStop" Value="False" />
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="Padding" Value="16" />
|
|
||||||
<Setter Property="Margin" Value="0,0,0,0"/>
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="controls:Setting">
|
|
||||||
<Grid x:Name="RootGrid"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
Padding="{TemplateBinding Padding}"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
MinHeight="48">
|
|
||||||
<VisualStateManager.VisualStateGroups>
|
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
|
||||||
<VisualState x:Name="Normal"/>
|
|
||||||
<VisualState x:Name="Disabled">
|
|
||||||
<VisualState.Setters>
|
|
||||||
<Setter Target="HeaderPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
|
||||||
<Setter Target="DescriptionPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
|
||||||
<Setter Target="IconPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
|
||||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
|
||||||
</VisualState.Setters>
|
|
||||||
</VisualState>
|
|
||||||
</VisualStateGroup>
|
|
||||||
</VisualStateManager.VisualStateGroups>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<!-- Icon -->
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
<!-- Header and subtitle -->
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<!-- Action control -->
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<ContentPresenter x:Name="IconPresenter"
|
|
||||||
Content="{TemplateBinding Icon}"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
FontSize="20"
|
|
||||||
IsTextScaleFactorEnabled="False"
|
|
||||||
Margin="2,0,18,0"
|
|
||||||
MaxWidth="20"
|
|
||||||
AutomationProperties.AccessibilityView="Raw"
|
|
||||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
|
||||||
Foreground="{ThemeResource CardPrimaryForegroundBrush}"
|
|
||||||
VerticalAlignment="Center"/>
|
|
||||||
|
|
||||||
<StackPanel
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Margin="0,0,16,0">
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
x:Name="HeaderPresenter"
|
|
||||||
Text="{TemplateBinding Header}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Foreground="{ThemeResource CardPrimaryForegroundBrush}" />
|
|
||||||
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="DescriptionPresenter"
|
|
||||||
Content="{TemplateBinding Description}"
|
|
||||||
FontSize="{StaticResource SecondaryTextFontSize}"
|
|
||||||
TextWrapping="WrapWholeWords"
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<ContentPresenter.Resources>
|
|
||||||
<Style TargetType="TextBlock" BasedOn="{StaticResource CaptionTextBlockStyle}">
|
|
||||||
<Style.Setters>
|
|
||||||
<Setter Property="TextWrapping" Value="WrapWholeWords"/>
|
|
||||||
</Style.Setters>
|
|
||||||
</Style>
|
|
||||||
<Style TargetType="HyperlinkButton" BasedOn="{StaticResource TextButtonStyle}">
|
|
||||||
<Style.Setters>
|
|
||||||
<Setter Property="FontSize" Value="12"/>
|
|
||||||
<Setter Property="Padding" Value="0,0,0,0"/>
|
|
||||||
</Style.Setters>
|
|
||||||
</Style>
|
|
||||||
</ContentPresenter.Resources>
|
|
||||||
</ContentPresenter>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="ContentPresenter"
|
|
||||||
Content="{TemplateBinding ActionContent}"
|
|
||||||
Grid.Column="2"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
HorizontalAlignment="Right" />
|
|
||||||
</Grid>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Automation;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
|
|
||||||
namespace SettingsUI.Controls;
|
|
||||||
|
|
||||||
public partial class SettingExpander : Expander
|
|
||||||
{
|
|
||||||
public SettingExpander()
|
|
||||||
{
|
|
||||||
DefaultStyleKey = typeof(Expander);
|
|
||||||
Style = (Style)Application.Current.Resources["SettingExpanderStyle"];
|
|
||||||
RegisterPropertyChangedCallback(Expander.HeaderProperty, OnHeaderChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnHeaderChanged(DependencyObject d, DependencyProperty dp)
|
|
||||||
{
|
|
||||||
SettingExpander self = (SettingExpander)d;
|
|
||||||
if (self.Header != null)
|
|
||||||
{
|
|
||||||
if (self.Header.GetType() == typeof(Setting))
|
|
||||||
{
|
|
||||||
Setting selfSetting = (Setting)self.Header;
|
|
||||||
selfSetting.Style = (Style)Application.Current.Resources["ExpanderHeaderSettingStyle"];
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(selfSetting.Header))
|
|
||||||
{
|
|
||||||
AutomationProperties.SetName(self, selfSetting.Header);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Automation.Peers;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace SettingsUI.Controls;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a control that can contain multiple settings (or other) controls
|
|
||||||
/// </summary>
|
|
||||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
|
||||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
|
||||||
[TemplatePart(Name = PartDescriptionPresenter, Type = typeof(ContentPresenter))]
|
|
||||||
public partial class SettingsGroup : ItemsControl
|
|
||||||
{
|
|
||||||
private const string PartDescriptionPresenter = "DescriptionPresenter";
|
|
||||||
private ContentPresenter? _descriptionPresenter;
|
|
||||||
private SettingsGroup? _settingsGroup;
|
|
||||||
|
|
||||||
public SettingsGroup()
|
|
||||||
{
|
|
||||||
DefaultStyleKey = typeof(SettingsGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Localizable(true)]
|
|
||||||
public string Header
|
|
||||||
{
|
|
||||||
get => (string)GetValue(HeaderProperty);
|
|
||||||
set => SetValue(HeaderProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
|
|
||||||
"Header",
|
|
||||||
typeof(string),
|
|
||||||
typeof(SettingsGroup),
|
|
||||||
new PropertyMetadata(default(string)));
|
|
||||||
|
|
||||||
[Localizable(true)]
|
|
||||||
public object Description
|
|
||||||
{
|
|
||||||
get => GetValue(DescriptionProperty);
|
|
||||||
set => SetValue(DescriptionProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
|
|
||||||
"Description",
|
|
||||||
typeof(object),
|
|
||||||
typeof(SettingsGroup),
|
|
||||||
new PropertyMetadata(null, OnDescriptionChanged));
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate()
|
|
||||||
{
|
|
||||||
IsEnabledChanged -= SettingsGroup_IsEnabledChanged;
|
|
||||||
_settingsGroup = this;
|
|
||||||
_descriptionPresenter = (ContentPresenter)_settingsGroup.GetTemplateChild(PartDescriptionPresenter);
|
|
||||||
SetEnabledState();
|
|
||||||
IsEnabledChanged += SettingsGroup_IsEnabledChanged;
|
|
||||||
base.OnApplyTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnDescriptionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
((SettingsGroup)d).Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SettingsGroup_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
|
|
||||||
{
|
|
||||||
SetEnabledState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetEnabledState()
|
|
||||||
{
|
|
||||||
VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
if (_settingsGroup == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_settingsGroup.Description == null)
|
|
||||||
{
|
|
||||||
_settingsGroup._descriptionPresenter!.Visibility = Visibility.Collapsed;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_settingsGroup._descriptionPresenter!.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override AutomationPeer OnCreateAutomationPeer()
|
|
||||||
{
|
|
||||||
return new SettingsGroupAutomationPeer(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<ResourceDictionary
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:controls="using:SettingsUI.Controls">
|
|
||||||
|
|
||||||
<Style TargetType="controls:SettingsGroup">
|
|
||||||
<Setter Property="ItemsPanel">
|
|
||||||
<Setter.Value>
|
|
||||||
<ItemsPanelTemplate>
|
|
||||||
<StackPanel Orientation="Vertical"
|
|
||||||
Spacing="2"/>
|
|
||||||
</ItemsPanelTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
<Setter Property="IsTabStop" Value="False" />
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="controls:SettingsGroup">
|
|
||||||
<Grid HorizontalAlignment="Stretch">
|
|
||||||
<VisualStateManager.VisualStateGroups>
|
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
|
||||||
<VisualState x:Name="Normal"/>
|
|
||||||
<VisualState x:Name="Disabled">
|
|
||||||
<VisualState.Setters>
|
|
||||||
<Setter Target="HeaderPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
|
||||||
<Setter Target="DescriptionPresenter.Foreground" Value="{ThemeResource TextFillColorDisabledBrush}" />
|
|
||||||
</VisualState.Setters>
|
|
||||||
</VisualState>
|
|
||||||
</VisualStateGroup>
|
|
||||||
</VisualStateManager.VisualStateGroups>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock x:Name="HeaderPresenter"
|
|
||||||
Text="{TemplateBinding Header}"
|
|
||||||
Grid.Row="0"
|
|
||||||
Style="{ThemeResource BodyStrongTextBlockStyle}"
|
|
||||||
Margin="1,32,0,0"
|
|
||||||
AutomationProperties.HeadingLevel="Level2"/>
|
|
||||||
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="DescriptionPresenter"
|
|
||||||
Content="{TemplateBinding Description}"
|
|
||||||
TextWrapping="WrapWholeWords"
|
|
||||||
Margin="1,4,0,0"
|
|
||||||
Grid.Row="1"
|
|
||||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
|
||||||
<ContentPresenter.Resources>
|
|
||||||
<Style TargetType="TextBlock" BasedOn="{StaticResource CaptionTextBlockStyle}">
|
|
||||||
<Style.Setters>
|
|
||||||
<Setter Property="TextWrapping" Value="WrapWholeWords"/>
|
|
||||||
</Style.Setters>
|
|
||||||
</Style>
|
|
||||||
<Style TargetType="HyperlinkButton" BasedOn="{StaticResource TextButtonStyle}">
|
|
||||||
<Style.Setters>
|
|
||||||
<Setter Property="Padding" Value="0,0,0,0"/>
|
|
||||||
</Style.Setters>
|
|
||||||
</Style>
|
|
||||||
</ContentPresenter.Resources>
|
|
||||||
</ContentPresenter>
|
|
||||||
<ItemsPresenter Grid.Row="2" Margin="0,8,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// Copyright (c) Microsoft Corporation
|
|
||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
||||||
// See the LICENSE file in the project root for more information.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml.Automation.Peers;
|
|
||||||
|
|
||||||
namespace SettingsUI.Controls;
|
|
||||||
|
|
||||||
public class SettingsGroupAutomationPeer : FrameworkElementAutomationPeer
|
|
||||||
{
|
|
||||||
public SettingsGroupAutomationPeer(SettingsGroup owner)
|
|
||||||
: base(owner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string GetNameCore()
|
|
||||||
{
|
|
||||||
SettingsGroup? selectedSettingsGroup = (SettingsGroup)Owner;
|
|
||||||
return selectedSettingsGroup.Header;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net7.0-windows10.0.18362.0</TargetFrameworks>
|
|
||||||
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
|
||||||
<RootNamespace>SettingsUI</RootNamespace>
|
|
||||||
<Platforms>x64</Platforms>
|
|
||||||
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
|
|
||||||
<UseWinUI>true</UseWinUI>
|
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Page Update="Controls\IsEnabledTextBlock\IsEnabledTextBlock.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Controls\SettingsGroup\SettingsGroup.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Controls\Setting\Setting.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Styles\Button.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Styles\Common.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Styles\TextBlock.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Themes\Colors.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Themes\Generic.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Themes\SettingsExpanderStyles.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
<Page Update="Themes\SettingsUI.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
</Page>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,700 +0,0 @@
|
|||||||
<ResourceDictionary
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:contract7NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,7)"
|
|
||||||
xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)">
|
|
||||||
|
|
||||||
<Style
|
|
||||||
x:Key="SettingButtonStyle"
|
|
||||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
|
||||||
TargetType="Button">
|
|
||||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
|
||||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
|
||||||
<Setter Property="Padding" Value="16,5,16,6"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="HyperlinkButtonStyle" TargetType="HyperlinkButton">
|
|
||||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="TextButtonStyle" TargetType="ButtonBase">
|
|
||||||
<Setter Property="Background" Value="{ThemeResource HyperlinkButtonBackground}"/>
|
|
||||||
<Setter Property="Foreground" Value="{ThemeResource HyperlinkButtonForeground}"/>
|
|
||||||
<Setter Property="MinWidth" Value="0"/>
|
|
||||||
<Setter Property="MinHeight" Value="0"/>
|
|
||||||
<Setter Property="Margin" Value="0"/>
|
|
||||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="ButtonBase">
|
|
||||||
<Grid
|
|
||||||
Margin="{TemplateBinding Padding}"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
CornerRadius="4">
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="Text"
|
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
|
||||||
Content="{TemplateBinding Content}"
|
|
||||||
FontWeight="SemiBold"/>
|
|
||||||
|
|
||||||
<VisualStateManager.VisualStateGroups>
|
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
|
||||||
<VisualState x:Name="Normal"/>
|
|
||||||
|
|
||||||
<VisualState x:Name="PointerOver">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundPointerOver}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPointerOver}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="BorderBrush">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPointerOver}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
|
|
||||||
<VisualState x:Name="Pressed">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundPressed}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPressed}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="BorderBrush">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPressed}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
|
|
||||||
<VisualState x:Name="Disabled">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="BorderBrush">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
|
|
||||||
</VisualStateGroup>
|
|
||||||
|
|
||||||
</VisualStateManager.VisualStateGroups>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- This style overrides the default style so that all ToggleSwitches are right aligned, with the label on the left -->
|
|
||||||
<Style x:Key="ToggleSwitchSettingStyle" TargetType="ToggleSwitch">
|
|
||||||
<Setter Property="Foreground" Value="{ThemeResource ToggleSwitchContentForeground}"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
|
||||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Right"/>
|
|
||||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
|
|
||||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
|
|
||||||
<Setter Property="ManipulationMode" Value="System,TranslateX"/>
|
|
||||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
|
|
||||||
|
|
||||||
<Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3"/>
|
|
||||||
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="ToggleSwitch">
|
|
||||||
<Grid
|
|
||||||
contract7Present:CornerRadius="{TemplateBinding CornerRadius}"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="HeaderContentPresenter"
|
|
||||||
Grid.Row="0"
|
|
||||||
Margin="{ThemeResource ToggleSwitchTopHeaderMargin}"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
x:DeferLoadStrategy="Lazy"
|
|
||||||
AutomationProperties.AccessibilityView="Raw"
|
|
||||||
Content="{TemplateBinding Header}"
|
|
||||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
|
||||||
Foreground="{ThemeResource ToggleSwitchHeaderForeground}"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
Visibility="Collapsed"/>
|
|
||||||
<Grid
|
|
||||||
Grid.Row="1"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Top">
|
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="{ThemeResource ToggleSwitchPreContentMargin}"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="{ThemeResource ToggleSwitchPostContentMargin}"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="12" MaxWidth="12"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Grid
|
|
||||||
x:Name="SwitchAreaGrid"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Margin="0,5"
|
|
||||||
contract7NotPresent:CornerRadius="{StaticResource ControlCornerRadius}"
|
|
||||||
contract7Present:CornerRadius="{TemplateBinding CornerRadius}"
|
|
||||||
Background="{ThemeResource ToggleSwitchContainerBackground}"
|
|
||||||
Control.IsTemplateFocusTarget="True"/>
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="OffContentPresenter"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.Column="0"
|
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
|
||||||
AutomationProperties.AccessibilityView="Raw"
|
|
||||||
Content="{TemplateBinding OffContent}"
|
|
||||||
ContentTemplate="{TemplateBinding OffContentTemplate}"
|
|
||||||
Foreground="{TemplateBinding Foreground}"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Opacity="0"/>
|
|
||||||
<ContentPresenter
|
|
||||||
x:Name="OnContentPresenter"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.Column="0"
|
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
|
||||||
AutomationProperties.AccessibilityView="Raw"
|
|
||||||
Content="{TemplateBinding OnContent}"
|
|
||||||
ContentTemplate="{TemplateBinding OnContentTemplate}"
|
|
||||||
Foreground="{TemplateBinding Foreground}"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Opacity="0"/>
|
|
||||||
<Rectangle
|
|
||||||
x:Name="OuterBorder"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="40"
|
|
||||||
Height="20"
|
|
||||||
Fill="{ThemeResource ToggleSwitchFillOff}"
|
|
||||||
RadiusX="10"
|
|
||||||
RadiusY="10"
|
|
||||||
Stroke="{ThemeResource ToggleSwitchStrokeOff}"
|
|
||||||
StrokeThickness="{ThemeResource ToggleSwitchOuterBorderStrokeThickness}"/>
|
|
||||||
<Rectangle
|
|
||||||
x:Name="SwitchKnobBounds"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="40"
|
|
||||||
Height="20"
|
|
||||||
Fill="{ThemeResource ToggleSwitchFillOn}"
|
|
||||||
Opacity="0"
|
|
||||||
RadiusX="10"
|
|
||||||
RadiusY="10"
|
|
||||||
Stroke="{ThemeResource ToggleSwitchStrokeOn}"
|
|
||||||
StrokeThickness="{ThemeResource ToggleSwitchOnStrokeThickness}"/>
|
|
||||||
<Grid
|
|
||||||
x:Name="SwitchKnob"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="20"
|
|
||||||
Height="20"
|
|
||||||
HorizontalAlignment="Left">
|
|
||||||
<Border
|
|
||||||
x:Name="SwitchKnobOn"
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="12"
|
|
||||||
Height="12"
|
|
||||||
Margin="0,0,1,0"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
contract7Present:BackgroundSizing="OuterBorderEdge"
|
|
||||||
Background="{ThemeResource ToggleSwitchKnobFillOn}"
|
|
||||||
BorderBrush="{ThemeResource ToggleSwitchKnobStrokeOn}"
|
|
||||||
CornerRadius="7"
|
|
||||||
Opacity="0"
|
|
||||||
RenderTransformOrigin="0.5, 0.5">
|
|
||||||
<Border.RenderTransform>
|
|
||||||
<CompositeTransform/>
|
|
||||||
</Border.RenderTransform>
|
|
||||||
</Border>
|
|
||||||
<Rectangle
|
|
||||||
x:Name="SwitchKnobOff"
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="12"
|
|
||||||
Height="12"
|
|
||||||
Margin="-1,0,0,0"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Fill="{ThemeResource ToggleSwitchKnobFillOff}"
|
|
||||||
RadiusX="7"
|
|
||||||
RadiusY="7"
|
|
||||||
RenderTransformOrigin="0.5, 0.5">
|
|
||||||
<Rectangle.RenderTransform>
|
|
||||||
<CompositeTransform/>
|
|
||||||
</Rectangle.RenderTransform>
|
|
||||||
</Rectangle>
|
|
||||||
<Grid.RenderTransform>
|
|
||||||
<TranslateTransform x:Name="KnobTranslateTransform"/>
|
|
||||||
</Grid.RenderTransform>
|
|
||||||
</Grid>
|
|
||||||
<Thumb
|
|
||||||
x:Name="SwitchThumb"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
AutomationProperties.AccessibilityView="Raw">
|
|
||||||
<Thumb.Template>
|
|
||||||
<ControlTemplate TargetType="Thumb">
|
|
||||||
<Rectangle Fill="Transparent"/>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Thumb.Template>
|
|
||||||
</Thumb>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<VisualStateManager.VisualStateGroups>
|
|
||||||
<VisualStateGroup x:Name="CommonStates">
|
|
||||||
<VisualState x:Name="Normal">
|
|
||||||
<Storyboard>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Stroke">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOff}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOff}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOff}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOn}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOn}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOn}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContainerBackground}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
<VisualState x:Name="PointerOver">
|
|
||||||
<Storyboard>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffPointerOver}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffPointerOver}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffPointerOver}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnPointerOver}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnPointerOver}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnPointerOver}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundPointerOver}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="14"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="14"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="14"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="14"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
<VisualState x:Name="Pressed">
|
|
||||||
|
|
||||||
<VisualState.Setters>
|
|
||||||
<Setter Target="SwitchKnobOn.HorizontalAlignment" Value="Right"/>
|
|
||||||
<Setter Target="SwitchKnobOn.Margin" Value="0,0,3,0"/>
|
|
||||||
<Setter Target="SwitchKnobOff.HorizontalAlignment" Value="Left"/>
|
|
||||||
<Setter Target="SwitchKnobOff.Margin" Value="3,0,0,0"/>
|
|
||||||
</VisualState.Setters>
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffPressed}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffPressed}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnPressed}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnPressed}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffPressed}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnPressed}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundPressed}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="17"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="14"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="17"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlFasterAnimationDuration}"
|
|
||||||
Value="14"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
<VisualState x:Name="Disabled">
|
|
||||||
<Storyboard>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchHeaderForegroundDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter" Storyboard.TargetProperty="Foreground">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContentForegroundDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter" Storyboard.TargetProperty="Foreground">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchContentForegroundDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Stroke).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchStrokeOffDisabled}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchFillOffDisabled}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchFillOnDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Stroke">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchStrokeOnDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Fill">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOffDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Background">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleSwitchKnobFillOnDisabled}"/>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
<ColorAnimationUsingKeyFrames Storyboard.TargetName="SwitchAreaGrid" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
|
|
||||||
<LinearColorKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="{ThemeResource ToggleSwitchContainerBackgroundDisabled}"/>
|
|
||||||
</ColorAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOn"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Width">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames
|
|
||||||
EnableDependentAnimation="True"
|
|
||||||
Storyboard.TargetName="SwitchKnobOff"
|
|
||||||
Storyboard.TargetProperty="Height">
|
|
||||||
<SplineDoubleKeyFrame
|
|
||||||
KeySpline="{StaticResource ControlFastOutSlowInKeySpline}"
|
|
||||||
KeyTime="{StaticResource ControlNormalAnimationDuration}"
|
|
||||||
Value="12"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
|
|
||||||
</VisualStateGroup>
|
|
||||||
<VisualStateGroup x:Name="ToggleStates">
|
|
||||||
|
|
||||||
<VisualStateGroup.Transitions>
|
|
||||||
<VisualTransition
|
|
||||||
x:Name="DraggingToOnTransition"
|
|
||||||
GeneratedDuration="0"
|
|
||||||
From="Dragging"
|
|
||||||
To="On">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOnOffset}" TargetName="SwitchKnob"/>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualTransition>
|
|
||||||
<VisualTransition
|
|
||||||
x:Name="OnToDraggingTransition"
|
|
||||||
GeneratedDuration="0"
|
|
||||||
From="On"
|
|
||||||
To="Dragging">
|
|
||||||
<Storyboard>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="0" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="0" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="0" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualTransition>
|
|
||||||
<VisualTransition
|
|
||||||
x:Name="DraggingToOffTransition"
|
|
||||||
GeneratedDuration="0"
|
|
||||||
From="Dragging"
|
|
||||||
To="Off">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobCurrentToOffOffset}" TargetName="SwitchKnob"/>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualTransition>
|
|
||||||
<VisualTransition
|
|
||||||
x:Name="OnToOffTransition"
|
|
||||||
GeneratedDuration="0"
|
|
||||||
From="On"
|
|
||||||
To="Off">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOnToOffOffset}" TargetName="SwitchKnob"/>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualTransition>
|
|
||||||
<VisualTransition
|
|
||||||
x:Name="OffToOnTransition"
|
|
||||||
GeneratedDuration="0"
|
|
||||||
From="Off"
|
|
||||||
To="On">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<RepositionThemeAnimation FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.KnobOffToOnOffset}" TargetName="SwitchKnob"/>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualTransition>
|
|
||||||
</VisualStateGroup.Transitions>
|
|
||||||
<VisualState x:Name="Dragging"/>
|
|
||||||
<VisualState x:Name="Off"/>
|
|
||||||
<VisualState x:Name="On">
|
|
||||||
<Storyboard>
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetName="KnobTranslateTransform"
|
|
||||||
Storyboard.TargetProperty="X"
|
|
||||||
To="20"
|
|
||||||
Duration="0"/>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobBounds" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="OuterBorder" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOn" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="1"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchKnobOff" Storyboard.TargetProperty="Opacity">
|
|
||||||
<LinearDoubleKeyFrame KeyTime="{StaticResource ControlFasterAnimationDuration}" Value="0"/>
|
|
||||||
</DoubleAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
|
|
||||||
</VisualStateGroup>
|
|
||||||
<VisualStateGroup x:Name="ContentStates">
|
|
||||||
<VisualState x:Name="OffContent">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetName="OffContentPresenter"
|
|
||||||
Storyboard.TargetProperty="Opacity"
|
|
||||||
To="1"
|
|
||||||
Duration="0"/>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OffContentPresenter" Storyboard.TargetProperty="IsHitTestVisible">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0">
|
|
||||||
<DiscreteObjectKeyFrame.Value>
|
|
||||||
<x:Boolean>True</x:Boolean>
|
|
||||||
</DiscreteObjectKeyFrame.Value>
|
|
||||||
</DiscreteObjectKeyFrame>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
<VisualState x:Name="OnContent">
|
|
||||||
|
|
||||||
<Storyboard>
|
|
||||||
<DoubleAnimation
|
|
||||||
Storyboard.TargetName="OnContentPresenter"
|
|
||||||
Storyboard.TargetProperty="Opacity"
|
|
||||||
To="1"
|
|
||||||
Duration="0"/>
|
|
||||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OnContentPresenter" Storyboard.TargetProperty="IsHitTestVisible">
|
|
||||||
<DiscreteObjectKeyFrame KeyTime="0">
|
|
||||||
<DiscreteObjectKeyFrame.Value>
|
|
||||||
<x:Boolean>True</x:Boolean>
|
|
||||||
</DiscreteObjectKeyFrame.Value>
|
|
||||||
</DiscreteObjectKeyFrame>
|
|
||||||
</ObjectAnimationUsingKeyFrames>
|
|
||||||
</Storyboard>
|
|
||||||
</VisualState>
|
|
||||||
|
|
||||||
</VisualStateGroup>
|
|
||||||
|
|
||||||
</VisualStateManager.VisualStateGroups>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<ResourceDictionary
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:controls="using:SettingsUI.Controls">
|
|
||||||
<Style x:Key="ListViewItemSettingStyle" TargetType="ListViewItem">
|
|
||||||
<Setter Property="Margin" Value="0,0,0,2"/>
|
|
||||||
<Setter Property="Padding" Value="0,0,0,0"/>
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style BasedOn="{StaticResource DefaultCheckBoxStyle}" TargetType="controls:CheckBoxWithDescriptionControl"/>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
<Style x:Key="OobeSubtitleStyle" TargetType="TextBlock">
|
|
||||||
<Setter Property="Margin" Value="0,16,0,0"/>
|
|
||||||
<Setter Property="Foreground" Value="{ThemeResource DefaultTextForegroundThemeBrush}"/>
|
|
||||||
<Setter Property="AutomationProperties.HeadingLevel" Value="Level3"/>
|
|
||||||
<Setter Property="FontFamily" Value="XamlAutoFontFamily"/>
|
|
||||||
<Setter Property="FontSize" Value="{StaticResource BodyTextBlockFontSize}"/>
|
|
||||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
|
||||||
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
|
||||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
|
||||||
<Setter Property="LineStackingStrategy" Value="MaxHeight"/>
|
|
||||||
<Setter Property="TextLineBounds" Value="Full"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<x:Double x:Key="SecondaryTextFontSize">12</x:Double>
|
|
||||||
<Style x:Key="SecondaryTextStyle" TargetType="TextBlock">
|
|
||||||
<Setter Property="FontSize" Value="{StaticResource SecondaryTextFontSize}"/>
|
|
||||||
<Setter Property="Foreground" Value="{ThemeResource TextFillColorSecondaryBrush}"/>
|
|
||||||
</Style>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
|
||||||
<ResourceDictionary x:Key="Dark">
|
|
||||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="CardBackgroundFillColorDefaultBrush"/>
|
|
||||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush"/>
|
|
||||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="TextFillColorPrimaryBrush"/>
|
|
||||||
<SolidColorBrush x:Key="InfoBarInformationalSeverityBackgroundBrush" Color="#FF34424d"/>
|
|
||||||
<Color x:Key="InfoBarInformationalSeverityIconBackground">#FF5fb2f2</Color>
|
|
||||||
<Thickness x:Key="CardBorderThickness">1</Thickness>
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
||||||
<ResourceDictionary x:Key="Light">
|
|
||||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="CardBackgroundFillColorDefaultBrush"/>
|
|
||||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush"/>
|
|
||||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="TextFillColorPrimaryBrush"/>
|
|
||||||
<SolidColorBrush x:Key="InfoBarInformationalSeverityBackgroundBrush" Color="#FFd3e7f7"/>
|
|
||||||
<Color x:Key="InfoBarInformationalSeverityIconBackground">#FF0063b1</Color>
|
|
||||||
<Thickness x:Key="CardBorderThickness">1</Thickness>
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
||||||
<ResourceDictionary x:Key="HighContrast">
|
|
||||||
<StaticResource x:Key="CardBackgroundBrush" ResourceKey="SystemColorButtonFaceColorBrush"/>
|
|
||||||
<StaticResource x:Key="CardBorderBrush" ResourceKey="SystemColorButtonTextColorBrush"/>
|
|
||||||
<StaticResource x:Key="CardPrimaryForegroundBrush" ResourceKey="SystemColorButtonTextColorBrush"/>
|
|
||||||
<SolidColorBrush x:Key="InfoBarInformationalSeverityBackgroundBrush" Color="#FF34424d"/>
|
|
||||||
<Color x:Key="InfoBarInformationalSeverityIconBackground">#FF5fb2f2</Color>
|
|
||||||
<Thickness x:Key="CardBorderThickness">2</Thickness>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</ResourceDictionary.ThemeDictionaries>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<ResourceDictionary
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
|
||||||
<ResourceDictionary Source="ms-appx:///SettingsUI/Controls/Setting/Setting.xaml" />
|
|
||||||
<ResourceDictionary Source="ms-appx:///SettingsUI/Controls/SettingsGroup/SettingsGroup.xaml" />
|
|
||||||
<ResourceDictionary Source="ms-appx:///SettingsUI/Controls/IsEnabledTextBlock/IsEnabledTextBlock.xaml" />
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<ResourceDictionary
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:controls="using:SettingsUI.Controls">
|
|
||||||
|
|
||||||
<!-- Thickness -->
|
|
||||||
<Thickness x:Key="ExpanderContentPadding">0</Thickness>
|
|
||||||
<Thickness x:Key="ExpanderSettingMargin">56, 8, 40, 8</Thickness>
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="ExpanderChevronPointerOverBackground">Transparent</SolidColorBrush>
|
|
||||||
|
|
||||||
<!-- Styles -->
|
|
||||||
<!-- Setting used in a Expander header -->
|
|
||||||
<Style x:Key="ExpanderHeaderSettingStyle" TargetType="controls:Setting">
|
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
|
||||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
|
||||||
<Setter Property="Padding" Value="0,14,0,14"/>
|
|
||||||
<Setter Property="Margin" Value="0"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Thickness x:Key="ExpanderChevronMargin">0,0,8,0</Thickness>
|
|
||||||
|
|
||||||
<!-- Setting used in a Expander header -->
|
|
||||||
<Style x:Key="ExpanderContentSettingStyle" TargetType="controls:Setting">
|
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
|
||||||
<Setter Property="BorderThickness" Value="0,1,0,0"/>
|
|
||||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
|
||||||
<Setter Property="CornerRadius" Value="0"/>
|
|
||||||
<Setter Property="Padding" Value="{StaticResource ExpanderSettingMargin}"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<!-- Setting expander style -->
|
|
||||||
<Style x:Key="SettingExpanderStyle" TargetType="Expander">
|
|
||||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundBrush}"/>
|
|
||||||
<Setter Property="BorderThickness" Value="{ThemeResource CardBorderThickness}"/>
|
|
||||||
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="ExpanderSeparatorStyle" TargetType="Rectangle">
|
|
||||||
<Setter Property="Height" Value="1"/>
|
|
||||||
<Setter Property="Stroke" Value="{ThemeResource CardBorderBrush}"/>
|
|
||||||
</Style>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
|
||||||
<ResourceDictionary Source="../Styles/Common.xaml"/>
|
|
||||||
<ResourceDictionary Source="../Styles/TextBlock.xaml"/>
|
|
||||||
<ResourceDictionary Source="../Styles/Button.xaml"/>
|
|
||||||
<ResourceDictionary Source="../Themes/Colors.xaml"/>
|
|
||||||
<ResourceDictionary Source="../Themes/SettingsExpanderStyles.xaml"/>
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
|
||||||
|
|
||||||
<x:Double x:Key="SettingActionControlMinWidth">240</x:Double>
|
|
||||||
</ResourceDictionary>
|
|
||||||
@@ -10,8 +10,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SettingsUI", "SettingsUI\SettingsUI.csproj", "{DCA5678C-896E-49FB-97A7-5A504A5CFF17}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration", "Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj", "{8B96721E-5604-47D2-9B72-06FEBAD0CE00}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration", "Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj", "{8B96721E-5604-47D2-9B72-06FEBAD0CE00}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Installer", "Snap.Hutao.Installer\Snap.Hutao.Installer.csproj", "{CEC01691-F65E-4874-9AE2-F571369A7631}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.Installer", "Snap.Hutao.Installer\Snap.Hutao.Installer.csproj", "{CEC01691-F65E-4874-9AE2-F571369A7631}"
|
||||||
@@ -52,22 +50,6 @@ Global
|
|||||||
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.ActiveCfg = Release|x86
|
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.ActiveCfg = Release|x86
|
||||||
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Build.0 = Release|x86
|
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Build.0 = Release|x86
|
||||||
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Deploy.0 = Release|x86
|
{AAAB7CF0-F299-49B8-BDB4-4C320B3EC2C7}.Release|x86.Deploy.0 = Release|x86
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|arm64.ActiveCfg = Debug|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|arm64.Build.0 = Debug|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|arm64.ActiveCfg = Release|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|arm64.Build.0 = Release|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|x64.Build.0 = Release|x64
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{DCA5678C-896E-49FB-97A7-5A504A5CFF17}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|arm64.ActiveCfg = Debug|Any CPU
|
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Debug|arm64.ActiveCfg = Debug|Any CPU
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<muxc:XamlControlsResources/>
|
<muxc:XamlControlsResources/>
|
||||||
<ResourceDictionary Source="ms-appx:///SettingsUI/Themes/SettingsUI.xaml"/>
|
<ResourceDictionary Source="ms-appx:///SettingsUI/Themes/Generic.xaml"/>
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<ResourceDictionary.ThemeDictionaries>
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
@@ -42,6 +42,23 @@
|
|||||||
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
|
<CornerRadius x:Key="CompatCornerRadiusRight">0,6,6,0</CornerRadius>
|
||||||
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
<CornerRadius x:Key="CompatCornerRadiusBottom">0,0,6,6</CornerRadius>
|
||||||
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
|
<CornerRadius x:Key="CompatCornerRadiusSmall">2</CornerRadius>
|
||||||
|
<!-- OpenPaneLength -->
|
||||||
|
<x:Double x:Key="CompatSplitViewOpenPaneLength">212</x:Double>
|
||||||
|
<x:Double x:Key="CompatSplitViewOpenPaneLength2">252</x:Double>
|
||||||
|
<GridLength x:Key="CompatGridLength2">252</GridLength>
|
||||||
|
<!-- Uris -->
|
||||||
|
<x:String x:Key="DocumentLink_MhyAccountSwitch">https://hut.ao/features/mhy-account-switch.html#%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96-cookie</x:String>
|
||||||
|
<x:String x:Key="DocumentLink_BugReport">https://hut.ao/statements/bug-report.html</x:String>
|
||||||
|
<x:String x:Key="HolographicHat_GetToken_Release">https://github.com/HolographicHat/GetToken/releases/latest</x:String>
|
||||||
|
|
||||||
|
<x:String x:Key="UI_ItemIcon_None">https://static.snapgenshin.com/Bg/UI_ItemIcon_None.png</x:String>
|
||||||
|
<x:String x:Key="UI_ImgSign_ItemIcon">https://static.snapgenshin.com/Bg/UI_ImgSign_ItemIcon.png</x:String>
|
||||||
|
<x:String x:Key="UI_AvatarIcon_Costume_Card">https://static.snapgenshin.com/AvatarCard/UI_AvatarIcon_Costume_Card.png</x:String>
|
||||||
|
<x:String x:Key="UI_EmotionIcon25">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon25.png</x:String>
|
||||||
|
<x:String x:Key="UI_EmotionIcon71">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon71.png</x:String>
|
||||||
|
<x:String x:Key="UI_EmotionIcon250">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon250.png</x:String>
|
||||||
|
<x:String x:Key="UI_EmotionIcon272">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon272.png</x:String>
|
||||||
|
<x:String x:Key="UI_EmotionIcon293">https://static.snapgenshin.com/EmotionIcon/UI_EmotionIcon293.png</x:String>
|
||||||
<!-- Converters -->
|
<!-- Converters -->
|
||||||
<cwuc:BoolNegationConverter x:Key="BoolNegationConverter"/>
|
<cwuc:BoolNegationConverter x:Key="BoolNegationConverter"/>
|
||||||
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
<cwuc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
|
||||||
@@ -52,6 +69,7 @@
|
|||||||
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
<shmmc:AvatarSideIconConverter x:Key="AvatarSideIconConverter"/>
|
||||||
<shmmc:DescParamDescriptor x:Key="DescParamDescriptor"/>
|
<shmmc:DescParamDescriptor x:Key="DescParamDescriptor"/>
|
||||||
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
|
<shmmc:ElementNameIconConverter x:Key="ElementNameIconConverter"/>
|
||||||
|
<shmmc:EmotionIconConverter x:Key="EmotionIconConverter"/>
|
||||||
<shmmc:EquipIconConverter x:Key="EquipIconConverter"/>
|
<shmmc:EquipIconConverter x:Key="EquipIconConverter"/>
|
||||||
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
|
<shmmc:GachaAvatarImgConverter x:Key="GachaAvatarImgConverter"/>
|
||||||
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
|
<shmmc:GachaAvatarIconConverter x:Key="GachaAvatarIconConverter"/>
|
||||||
@@ -61,8 +79,16 @@
|
|||||||
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
|
<shmmc:QualityColorConverter x:Key="QualityColorConverter"/>
|
||||||
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
|
<shmmc:WeaponTypeIconConverter x:Key="WeaponTypeIconConverter"/>
|
||||||
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
<shvc:BoolToVisibilityRevertConverter x:Key="BoolToVisibilityRevertConverter"/>
|
||||||
|
<shvc:EmptyCollectionToBoolConverter x:Key="EmptyCollectionToBoolConverter"/>
|
||||||
|
<shvc:EmptyCollectionToBoolRevertConverter x:Key="EmptyCollectionToBoolRevertConverter"/>
|
||||||
|
<shvc:EmptyCollectionToVisibilityConverter x:Key="EmptyCollectionToVisibilityConverter"/>
|
||||||
|
<shvc:EmptyCollectionToVisibilityRevertConverter x:Key="EmptyCollectionToVisibilityRevertConverter"/>
|
||||||
|
<shvc:EmptyObjectToBoolConverter x:Key="EmptyObjectToBoolConverter"/>
|
||||||
|
<shvc:EmptyObjectToBoolRevertConverter x:Key="EmptyObjectToBoolRevertConverter"/>
|
||||||
<shvc:EmptyObjectToVisibilityConverter x:Key="EmptyObjectToVisibilityConverter"/>
|
<shvc:EmptyObjectToVisibilityConverter x:Key="EmptyObjectToVisibilityConverter"/>
|
||||||
|
<shvc:EmptyObjectToVisibilityRevertConverter x:Key="EmptyObjectToVisibilityRevertConverter"/>
|
||||||
|
<shvc:Int32ToVisibilityConverter x:Key="Int32ToVisibilityConverter"/>
|
||||||
|
<shvc:Int32ToVisibilityRevertConverter x:Key="Int32ToVisibilityRevertConverter"/>
|
||||||
<!-- Styles -->
|
<!-- Styles -->
|
||||||
<Style
|
<Style
|
||||||
x:Key="LargeGridViewItemStyle"
|
x:Key="LargeGridViewItemStyle"
|
||||||
@@ -70,7 +96,16 @@
|
|||||||
TargetType="GridViewItem">
|
TargetType="GridViewItem">
|
||||||
<Setter Property="Margin" Value="0,0,12,12"/>
|
<Setter Property="Margin" Value="0,0,12,12"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style
|
||||||
|
x:Key="SettingButtonStyle"
|
||||||
|
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||||
|
TargetType="Button">
|
||||||
|
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}"/>
|
||||||
|
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}"/>
|
||||||
|
<Setter Property="Padding" Value="16,6,16,6"/>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||||
|
</Style>
|
||||||
<Style x:Key="BorderCardStyle" TargetType="Border">
|
<Style x:Key="BorderCardStyle" TargetType="Border">
|
||||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}"/>
|
||||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
|
||||||
@@ -81,7 +116,6 @@
|
|||||||
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
|
<ItemsPanelTemplate x:Key="ItemsStackPanelTemplate">
|
||||||
<ItemsStackPanel/>
|
<ItemsStackPanel/>
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
|
|
||||||
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
|
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
|
||||||
<StackPanel Orientation="Horizontal"/>
|
<StackPanel Orientation="Horizontal"/>
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public partial class App : Application
|
|||||||
ToastNotificationManagerCompat.OnActivated += Activation.NotificationActivate;
|
ToastNotificationManagerCompat.OnActivated += Activation.NotificationActivate;
|
||||||
|
|
||||||
logger.LogInformation(EventIds.CommonLog, "Snap Hutao | {name} : {version}", CoreEnvironment.FamilyName, CoreEnvironment.Version);
|
logger.LogInformation(EventIds.CommonLog, "Snap Hutao | {name} : {version}", CoreEnvironment.FamilyName, CoreEnvironment.Version);
|
||||||
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.TemporaryFolder.Path);
|
logger.LogInformation(EventIds.CommonLog, "Cache folder : {folder}", ApplicationData.Current.LocalCacheFolder.Path);
|
||||||
|
|
||||||
JumpListHelper.ConfigureAsync().SafeForget(logger);
|
JumpListHelper.ConfigureAsync().SafeForget(logger);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Snap.Hutao.Context.FileSystem.Location;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.FileSystem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 文件系统上下文
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TLocation">路径位置类型</typeparam>
|
|
||||||
internal abstract class FileSystemContext
|
|
||||||
{
|
|
||||||
private readonly IFileSystemLocation location;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化文件系统上下文
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="location">指定的文件系统位置</param>
|
|
||||||
public FileSystemContext(IFileSystemLocation location)
|
|
||||||
{
|
|
||||||
this.location = location;
|
|
||||||
EnsureDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建文件,若已存在文件,则不会创建
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">文件</param>
|
|
||||||
public void CreateFileOrIgnore(string file)
|
|
||||||
{
|
|
||||||
file = Locate(file);
|
|
||||||
if (!File.Exists(file))
|
|
||||||
{
|
|
||||||
File.Create(file).Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建文件夹,若已存在文件,则不会创建
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">文件夹</param>
|
|
||||||
public void CreateFolderOrIgnore(string folder)
|
|
||||||
{
|
|
||||||
folder = Locate(folder);
|
|
||||||
if (!Directory.Exists(folder))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(folder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 尝试删除文件夹
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">文件夹</param>
|
|
||||||
public void DeleteFolderOrIgnore(string folder)
|
|
||||||
{
|
|
||||||
folder = Locate(folder);
|
|
||||||
if (Directory.Exists(folder))
|
|
||||||
{
|
|
||||||
Directory.Delete(folder, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查文件是否存在
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">文件名称</param>
|
|
||||||
/// <returns>是否存在</returns>
|
|
||||||
public bool FileExists(string file)
|
|
||||||
{
|
|
||||||
return File.Exists(Locate(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查文件是否存在
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">文件夹名称</param>
|
|
||||||
/// <param name="file">文件名称</param>
|
|
||||||
/// <returns>是否存在</returns>
|
|
||||||
public bool FileExists(string folder, string file)
|
|
||||||
{
|
|
||||||
return File.Exists(Locate(folder, file));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查文件是否存在
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">文件夹名称</param>
|
|
||||||
/// <returns>是否存在</returns>
|
|
||||||
public bool FolderExists(string folder)
|
|
||||||
{
|
|
||||||
return Directory.Exists(Locate(folder));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 定位根目录中的文件或文件夹
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileOrFolder">文件或文件夹</param>
|
|
||||||
/// <returns>绝对路径</returns>
|
|
||||||
public string Locate(string fileOrFolder)
|
|
||||||
{
|
|
||||||
return Path.GetFullPath(fileOrFolder, location.GetPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 定位根目录下子文件夹中的文件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">文件夹</param>
|
|
||||||
/// <param name="file">文件</param>
|
|
||||||
/// <returns>绝对路径</returns>
|
|
||||||
public string Locate(string folder, string file)
|
|
||||||
{
|
|
||||||
return Path.GetFullPath(Path.Combine(folder, file), location.GetPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将文件移动到指定的子目录
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">文件</param>
|
|
||||||
/// <param name="folder">文件夹</param>
|
|
||||||
/// <param name="overwrite">是否覆盖</param>
|
|
||||||
/// <returns>是否成功 当文件不存在时会失败</returns>
|
|
||||||
public bool MoveToFolderOrIgnore(string file, string folder, bool overwrite = true)
|
|
||||||
{
|
|
||||||
string target = Locate(folder, file);
|
|
||||||
file = Locate(file);
|
|
||||||
|
|
||||||
if (File.Exists(file))
|
|
||||||
{
|
|
||||||
File.Move(file, target, overwrite);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 等效于 <see cref="File.OpenRead(string)"/> ,但路径经过解析
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">文件名</param>
|
|
||||||
/// <returns>文件流</returns>
|
|
||||||
public FileStream OpenRead(string file)
|
|
||||||
{
|
|
||||||
return File.OpenRead(Locate(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 等效于 <see cref="File.Create(string)"/> ,但路径经过解析
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">文件名</param>
|
|
||||||
/// <returns>文件流</returns>
|
|
||||||
public FileStream Create(string file)
|
|
||||||
{
|
|
||||||
return File.Create(Locate(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查根目录
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>是否创建了路径</returns>
|
|
||||||
private bool EnsureDirectory()
|
|
||||||
{
|
|
||||||
string folder = location.GetPath();
|
|
||||||
if (!Directory.Exists(folder))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(folder);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.FileSystem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 我的文档上下文
|
|
||||||
/// </summary>
|
|
||||||
[Injection(InjectAs.Transient)]
|
|
||||||
internal class HutaoContext : FileSystemContext
|
|
||||||
{
|
|
||||||
/// <inheritdoc cref="FileSystemContext"/>
|
|
||||||
public HutaoContext(Location.HutaoLocation myDocument)
|
|
||||||
: base(myDocument)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using System.IO;
|
|
||||||
using Windows.ApplicationModel;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.FileSystem.Location;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 我的文档位置
|
|
||||||
/// </summary>
|
|
||||||
[Injection(InjectAs.Transient)]
|
|
||||||
internal class HutaoLocation : IFileSystemLocation
|
|
||||||
{
|
|
||||||
private string? path;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetPath()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
|
||||||
|
|
||||||
// 将测试版与正式版的文件目录分离
|
|
||||||
string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao";
|
|
||||||
path = Path.GetFullPath(Path.Combine(myDocument, folderName));
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.FileSystem.Location;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 文件系统位置
|
|
||||||
/// </summary>
|
|
||||||
public interface IFileSystemLocation
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取路径
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>路径</returns>
|
|
||||||
string GetPath();
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.FileSystem.Location;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 我的文档位置
|
|
||||||
/// </summary>
|
|
||||||
[Injection(InjectAs.Transient)]
|
|
||||||
internal class Metadata : IFileSystemLocation
|
|
||||||
{
|
|
||||||
private string? path;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string GetPath()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
|
||||||
path = Path.GetFullPath(Path.Combine(myDocument, "Hutao", "Metadata"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Snap.Hutao.Context.FileSystem.Location;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.FileSystem;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 元数据上下文
|
|
||||||
/// </summary>
|
|
||||||
[Injection(InjectAs.Transient)]
|
|
||||||
internal class MetadataContext : FileSystemContext
|
|
||||||
{
|
|
||||||
/// <inheritdoc cref="FileSystemContext"/>
|
|
||||||
public MetadataContext(Metadata metadata)
|
|
||||||
: base(metadata)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Extension;
|
namespace Snap.Hutao.Control.Extension;
|
||||||
@@ -11,19 +10,6 @@ namespace Snap.Hutao.Control.Extension;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class ContentDialogExtensions
|
internal static class ContentDialogExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 针对窗口进行初始化
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="contentDialog">对话框</param>
|
|
||||||
/// <param name="window">窗口</param>
|
|
||||||
/// <returns>初始化完成的对话框</returns>
|
|
||||||
public static ContentDialog InitializeWithWindow(this ContentDialog contentDialog, Window window)
|
|
||||||
{
|
|
||||||
contentDialog.XamlRoot = window.Content.XamlRoot;
|
|
||||||
|
|
||||||
return contentDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 阻止用户交互
|
/// 阻止用户交互
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using Microsoft.UI.Xaml.Media.Imaging;
|
|||||||
using Snap.Hutao.Core.Caching;
|
using Snap.Hutao.Core.Caching;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Windows.Storage;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Image;
|
namespace Snap.Hutao.Control.Image;
|
||||||
|
|
||||||
@@ -23,6 +22,7 @@ public class CachedImage : ImageEx
|
|||||||
{
|
{
|
||||||
IsCacheEnabled = true;
|
IsCacheEnabled = true;
|
||||||
EnableLazyLoading = true;
|
EnableLazyLoading = true;
|
||||||
|
LazyLoadingThreshold = 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -33,18 +33,18 @@ public class CachedImage : ImageEx
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Verify.Operation(imageUri.Host != string.Empty, "无效的Uri");
|
Verify.Operation(imageUri.Host != string.Empty, "无效的Uri");
|
||||||
StorageFile file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true);
|
string file = await imageCache.GetFileFromCacheAsync(imageUri).ConfigureAwait(true);
|
||||||
|
|
||||||
// check token state to determine whether the operation should be canceled.
|
// check token state to determine whether the operation should be canceled.
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// BitmapImage initialize with a uri will increase image quality and loading speed.
|
// BitmapImage initialize with a uri will increase image quality and loading speed.
|
||||||
return new BitmapImage(new(file.Path));
|
return new BitmapImage(new(file));
|
||||||
}
|
}
|
||||||
catch (COMException)
|
catch (COMException)
|
||||||
{
|
{
|
||||||
// The image is corrupted, remove it.
|
// The image is corrupted, remove it.
|
||||||
await imageCache.RemoveAsync(imageUri.Enumerate()).ConfigureAwait(false);
|
imageCache.Remove(imageUri.Enumerate());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
|
|||||||
@@ -152,19 +152,17 @@ internal static class CompositionExtensions
|
|||||||
/// 创建一个线性渐变画刷
|
/// 创建一个线性渐变画刷
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="compositor">合成器</param>
|
/// <param name="compositor">合成器</param>
|
||||||
/// <param name="start">起点</param>
|
/// <param name="direction">方向</param>
|
||||||
/// <param name="end">终点</param>
|
|
||||||
/// <param name="stops">锚点</param>
|
/// <param name="stops">锚点</param>
|
||||||
/// <returns>线性渐变画刷</returns>
|
/// <returns>线性渐变画刷</returns>
|
||||||
public static CompositionLinearGradientBrush CompositeLinearGradientBrush(
|
public static CompositionLinearGradientBrush CompositeLinearGradientBrush(
|
||||||
this Compositor compositor,
|
this Compositor compositor,
|
||||||
Vector2 start,
|
GradientDirection direction,
|
||||||
Vector2 end,
|
|
||||||
params GradientStop[] stops)
|
params GradientStop[] stops)
|
||||||
{
|
{
|
||||||
CompositionLinearGradientBrush brush = compositor.CreateLinearGradientBrush();
|
CompositionLinearGradientBrush brush = compositor.CreateLinearGradientBrush();
|
||||||
brush.StartPoint = start;
|
brush.StartPoint = GetStartPointOfDirection(direction);
|
||||||
brush.EndPoint = end;
|
brush.EndPoint = GetEndPointOfDirection(direction);
|
||||||
|
|
||||||
foreach (GradientStop stop in stops)
|
foreach (GradientStop stop in stops)
|
||||||
{
|
{
|
||||||
@@ -193,5 +191,31 @@ internal static class CompositionExtensions
|
|||||||
return brush;
|
return brush;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Vector2 GetStartPointOfDirection(GradientDirection direction)
|
||||||
|
{
|
||||||
|
return direction switch
|
||||||
|
{
|
||||||
|
GradientDirection.BottomToTop => Vector2.UnitY,
|
||||||
|
GradientDirection.LeftBottomToRightTop => Vector2.UnitY,
|
||||||
|
GradientDirection.RightBottomToLeftTop => Vector2.One,
|
||||||
|
GradientDirection.RightToLeft => Vector2.UnitX,
|
||||||
|
GradientDirection.RightTopToLeftBottom => Vector2.UnitX,
|
||||||
|
_ => Vector2.Zero,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector2 GetEndPointOfDirection(GradientDirection direction)
|
||||||
|
{
|
||||||
|
return direction switch
|
||||||
|
{
|
||||||
|
GradientDirection.LeftBottomToRightTop => Vector2.UnitX,
|
||||||
|
GradientDirection.LeftToRight => Vector2.UnitX,
|
||||||
|
GradientDirection.LeftTopToRightBottom => Vector2.One,
|
||||||
|
GradientDirection.RightTopToLeftBottom => Vector2.UnitY,
|
||||||
|
GradientDirection.TopToBottom => Vector2.UnitY,
|
||||||
|
_ => Vector2.Zero,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public record struct GradientStop(float Offset, Windows.UI.Color Color);
|
public record struct GradientStop(float Offset, Windows.UI.Color Color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ using Microsoft.UI.Xaml.Media;
|
|||||||
using Snap.Hutao.Core.Caching;
|
using Snap.Hutao.Core.Caching;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Windows.Storage;
|
|
||||||
using Windows.Storage.Streams;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Image;
|
namespace Snap.Hutao.Control.Image;
|
||||||
|
|
||||||
@@ -60,15 +59,16 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步加载图像表面
|
/// 异步加载图像表面
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="storageFile">文件</param>
|
/// <param name="file">文件</param>
|
||||||
/// <param name="token">取消令牌</param>
|
/// <param name="token">取消令牌</param>
|
||||||
/// <returns>加载的图像表面</returns>
|
/// <returns>加载的图像表面</returns>
|
||||||
protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
protected virtual async Task<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token)
|
||||||
{
|
{
|
||||||
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token).ConfigureAwait(true))
|
TaskCompletionSource loadCompleteTaskSource = new();
|
||||||
{
|
LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(new(file));
|
||||||
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
surface.LoadCompleted += (s, e) => loadCompleteTaskSource.TrySetResult();
|
||||||
}
|
await loadCompleteTaskSource.Task.ConfigureAwait(true);
|
||||||
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -130,7 +130,7 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StorageFile storageFile = await imageCache.GetFileFromCacheAsync(uri).ConfigureAwait(true);
|
string storageFile = await imageCache.GetFileFromCacheAsync(uri).ConfigureAwait(true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -138,7 +138,11 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
|
|||||||
}
|
}
|
||||||
catch (COMException)
|
catch (COMException)
|
||||||
{
|
{
|
||||||
await imageCache.RemoveAsync(uri.Enumerate()).ConfigureAwait(true);
|
imageCache.Remove(uri.Enumerate());
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
imageCache.Remove(uri.Enumerate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Composition;
|
using Microsoft.UI.Composition;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using System.Numerics;
|
using System.IO;
|
||||||
using Windows.Graphics.Imaging;
|
using Windows.Graphics.Imaging;
|
||||||
using Windows.Storage;
|
|
||||||
using Windows.Storage.Streams;
|
using Windows.Storage.Streams;
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Image;
|
namespace Snap.Hutao.Control.Image;
|
||||||
@@ -16,8 +16,29 @@ namespace Snap.Hutao.Control.Image;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Gradient : CompositionImage
|
public class Gradient : CompositionImage
|
||||||
{
|
{
|
||||||
|
private static readonly DependencyProperty BackgroundDirectionProperty = Property<Gradient>.Depend(nameof(BackgroundDirection), GradientDirection.TopToBottom);
|
||||||
|
private static readonly DependencyProperty ForegroundDirectionProperty = Property<Gradient>.Depend(nameof(ForegroundDirection), GradientDirection.TopToBottom);
|
||||||
|
|
||||||
private double imageAspectRatio;
|
private double imageAspectRatio;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 背景方向
|
||||||
|
/// </summary>
|
||||||
|
public GradientDirection BackgroundDirection
|
||||||
|
{
|
||||||
|
get => (GradientDirection)GetValue(BackgroundDirectionProperty);
|
||||||
|
set => SetValue(BackgroundDirectionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 前景方向
|
||||||
|
/// </summary>
|
||||||
|
public GradientDirection ForegroundDirection
|
||||||
|
{
|
||||||
|
get => (GradientDirection)GetValue(ForegroundDirectionProperty);
|
||||||
|
set => SetValue(ForegroundDirectionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void OnUpdateVisual(SpriteVisual spriteVisual)
|
protected override void OnUpdateVisual(SpriteVisual spriteVisual)
|
||||||
{
|
{
|
||||||
@@ -29,15 +50,22 @@ public class Gradient : CompositionImage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
|
protected override async Task<LoadedImageSurface> LoadImageSurfaceAsync(string file, CancellationToken token)
|
||||||
{
|
{
|
||||||
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token).ConfigureAwait(true))
|
using (FileStream fileStream = new(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||||
{
|
{
|
||||||
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream).AsTask(token).ConfigureAwait(true);
|
using (IRandomAccessStream imageStream = fileStream.AsRandomAccessStream())
|
||||||
imageAspectRatio = decoder.PixelWidth / (double)decoder.PixelHeight;
|
{
|
||||||
|
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
|
||||||
return LoadedImageSurface.StartLoadFromStream(imageStream);
|
imageAspectRatio = decoder.PixelWidth / (double)decoder.PixelHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskCompletionSource loadCompleteTaskSource = new();
|
||||||
|
LoadedImageSurface surface = LoadedImageSurface.StartLoadFromUri(new(file));
|
||||||
|
surface.LoadCompleted += (s, e) => loadCompleteTaskSource.TrySetResult();
|
||||||
|
await loadCompleteTaskSource.Task.ConfigureAwait(true);
|
||||||
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -45,8 +73,8 @@ public class Gradient : CompositionImage
|
|||||||
{
|
{
|
||||||
CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.UniformToFill, vRatio: 0f);
|
CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.UniformToFill, vRatio: 0f);
|
||||||
|
|
||||||
CompositionLinearGradientBrush backgroundBrush = compositor.CompositeLinearGradientBrush(new(1f, 0), Vector2.UnitY, new(0, Colors.White), new(1, Colors.Black));
|
CompositionLinearGradientBrush backgroundBrush = compositor.CompositeLinearGradientBrush(BackgroundDirection, new(0, Colors.White), new(1, Colors.Black));
|
||||||
CompositionLinearGradientBrush foregroundBrush = compositor.CompositeLinearGradientBrush(Vector2.Zero, Vector2.UnitY, new(0, Colors.White), new(0.95f, Colors.Black));
|
CompositionLinearGradientBrush foregroundBrush = compositor.CompositeLinearGradientBrush(ForegroundDirection, new(0, Colors.White), new(1, Colors.Black));
|
||||||
|
|
||||||
CompositionEffectBrush gradientEffectBrush = compositor.CompositeBlendEffectBrush(backgroundBrush, foregroundBrush);
|
CompositionEffectBrush gradientEffectBrush = compositor.CompositeBlendEffectBrush(backgroundBrush, foregroundBrush);
|
||||||
CompositionEffectBrush opacityMaskEffectBrush = compositor.CompositeLuminanceToAlphaEffectBrush(gradientEffectBrush);
|
CompositionEffectBrush opacityMaskEffectBrush = compositor.CompositeLuminanceToAlphaEffectBrush(gradientEffectBrush);
|
||||||
|
|||||||
50
src/Snap.Hutao/Snap.Hutao/Control/Image/GradientDirection.cs
Normal file
50
src/Snap.Hutao/Snap.Hutao/Control/Image/GradientDirection.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Control.Image;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渐变方向
|
||||||
|
/// </summary>
|
||||||
|
public enum GradientDirection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 下到上
|
||||||
|
/// </summary>
|
||||||
|
BottomToTop,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 左下到右上
|
||||||
|
/// </summary>
|
||||||
|
LeftBottomToRightTop,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 左到右
|
||||||
|
/// </summary>
|
||||||
|
LeftToRight,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 左上到右下
|
||||||
|
/// </summary>
|
||||||
|
LeftTopToRightBottom,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 右下到左上
|
||||||
|
/// </summary>
|
||||||
|
RightBottomToLeftTop,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 右到左
|
||||||
|
/// </summary>
|
||||||
|
RightToLeft,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 右上到左下
|
||||||
|
/// </summary>
|
||||||
|
RightTopToLeftBottom,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上到下
|
||||||
|
/// </summary>
|
||||||
|
TopToBottom,
|
||||||
|
}
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml.Markup;
|
|
||||||
using Snap.Hutao.Extension;
|
|
||||||
using Snap.Hutao.Localization;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Markup;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 国际化拓展
|
|
||||||
/// </summary>
|
|
||||||
[MarkupExtensionReturnType(ReturnType = typeof(string))]
|
|
||||||
internal class I18NExtension : MarkupExtension
|
|
||||||
{
|
|
||||||
private static readonly ITranslation Translation;
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, Type> TranslationMap = new()
|
|
||||||
{
|
|
||||||
["zh-CN"] = typeof(LanguagezhCN),
|
|
||||||
};
|
|
||||||
|
|
||||||
static I18NExtension()
|
|
||||||
{
|
|
||||||
string currentName = CultureInfo.CurrentUICulture.Name;
|
|
||||||
Type? languageType = TranslationMap.GetValueOrDefault2(currentName, typeof(LanguagezhCN));
|
|
||||||
Translation = (ITranslation)Activator.CreateInstance(languageType!)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造默认的国际化拓展
|
|
||||||
/// </summary>
|
|
||||||
public I18NExtension()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构造默认的国际化拓展
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">键</param>
|
|
||||||
public I18NExtension(string key)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 键名称
|
|
||||||
/// </summary>
|
|
||||||
public string Key { get; set; } = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取字符串
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">键</param>
|
|
||||||
/// <returns>翻译的字符串</returns>
|
|
||||||
internal static string Get(string key)
|
|
||||||
{
|
|
||||||
return Translation[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override object ProvideValue()
|
|
||||||
{
|
|
||||||
return Translation[Key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Markup;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 国际化帮助类
|
|
||||||
/// WinUI 3 目前存在部分页面无法使用 MarkupExtension 的问题
|
|
||||||
/// 使用 此帮助类 绕过限制
|
|
||||||
/// </summary>
|
|
||||||
internal class I18NHelper
|
|
||||||
{
|
|
||||||
private static readonly DependencyProperty TranslationProperty = Property<I18NHelper>.Attach("Translation", string.Empty, OnKeyChanged);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取键
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">对象</param>
|
|
||||||
/// <returns>值</returns>
|
|
||||||
public static string GetTranslation(DependencyObject obj)
|
|
||||||
{
|
|
||||||
return (string)obj.GetValue(TranslationProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置键
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">对象</param>
|
|
||||||
/// <param name="value">值</param>
|
|
||||||
public static void SetTranslation(DependencyObject obj, string value)
|
|
||||||
{
|
|
||||||
string tarnslation = I18NExtension.Get(value);
|
|
||||||
obj.SetValue(TranslationProperty, tarnslation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnKeyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arg)
|
|
||||||
{
|
|
||||||
string translation = I18NExtension.Get(arg.NewValue.ToString() ?? string.Empty);
|
|
||||||
|
|
||||||
if (obj is AppBarButton appBarButton)
|
|
||||||
{
|
|
||||||
appBarButton.Label = translation;
|
|
||||||
}
|
|
||||||
else if (obj is AppBarToggleButton appBarToggleButton)
|
|
||||||
{
|
|
||||||
appBarToggleButton.Label = translation;
|
|
||||||
}
|
|
||||||
else if (obj is AutoSuggestBox autoSuggestBox)
|
|
||||||
{
|
|
||||||
autoSuggestBox.PlaceholderText = translation;
|
|
||||||
}
|
|
||||||
else if (obj is ContentControl contentControl)
|
|
||||||
{
|
|
||||||
contentControl.Content = translation;
|
|
||||||
}
|
|
||||||
else if (obj is MenuFlyoutItem menuFlyoutItem)
|
|
||||||
{
|
|
||||||
menuFlyoutItem.Text = translation;
|
|
||||||
}
|
|
||||||
else if (obj is TextBlock textBlock)
|
|
||||||
{
|
|
||||||
textBlock.Text = translation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.UI.Xaml.Markup;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Markup;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Uri扩展
|
|
||||||
/// </summary>
|
|
||||||
[MarkupExtensionReturnType(ReturnType = typeof(Uri))]
|
|
||||||
public sealed class UriExtension : MarkupExtension
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 构造一个新的Uri扩展
|
|
||||||
/// </summary>
|
|
||||||
public UriExtension()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 地址
|
|
||||||
/// </summary>
|
|
||||||
public string? Value { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override object ProvideValue()
|
|
||||||
{
|
|
||||||
return new Uri(Value ?? string.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,12 +5,13 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||||
|
Loaded="OnRootControlLoaded"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<SplitButton
|
<SplitButton
|
||||||
|
Name="RootSplitButton"
|
||||||
Padding="0,6"
|
Padding="0,6"
|
||||||
Click="SplitButtonClick"
|
Click="SplitButtonClick">
|
||||||
Loaded="SplitButtonLoaded">
|
|
||||||
<SplitButton.Content>
|
<SplitButton.Content>
|
||||||
<FontIcon Name="IconPresenter" Glyph=""/>
|
<FontIcon Name="IconPresenter" Glyph=""/>
|
||||||
</SplitButton.Content>
|
</SplitButton.Content>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao.Control.Panel;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed partial class PanelSelector : UserControl
|
public sealed partial class PanelSelector : UserControl
|
||||||
{
|
{
|
||||||
private static readonly DependencyProperty CurrentProperty = Property<PanelSelector>.Depend(nameof(Current), "List");
|
private static readonly DependencyProperty CurrentProperty = Property<PanelSelector>.Depend(nameof(Current), "List", OnCurrentChanged);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的面板选择器
|
/// 构造一个新的面板选择器
|
||||||
@@ -30,51 +30,60 @@ public sealed partial class PanelSelector : UserControl
|
|||||||
set => SetValue(CurrentProperty, value);
|
set => SetValue(CurrentProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SplitButtonLoaded(object sender, RoutedEventArgs e)
|
private static void OnCurrentChanged(PanelSelector sender, string current)
|
||||||
{
|
{
|
||||||
MenuFlyout menuFlyout = (MenuFlyout)((SplitButton)sender).Flyout;
|
MenuFlyout menuFlyout = (MenuFlyout)sender.RootSplitButton.Flyout;
|
||||||
((RadioMenuFlyoutItem)menuFlyout.Items[0]).IsChecked = true;
|
RadioMenuFlyoutItem targetItem = menuFlyout.Items
|
||||||
|
.Cast<RadioMenuFlyoutItem>()
|
||||||
|
.Single(i => (string)i.Tag == current);
|
||||||
|
targetItem.IsChecked = true;
|
||||||
|
sender.IconPresenter.Glyph = ((FontIcon)targetItem.Icon).Glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnCurrentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||||
|
{
|
||||||
|
OnCurrentChanged((PanelSelector)obj, (string)args.NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRootControlLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// because the GroupName shares in global
|
||||||
|
// we have to impl a control scoped GroupName.
|
||||||
|
PanelSelector selector = (PanelSelector)sender;
|
||||||
|
MenuFlyout menuFlyout = (MenuFlyout)selector.RootSplitButton.Flyout;
|
||||||
|
int hash = GetHashCode();
|
||||||
|
foreach (RadioMenuFlyoutItem item in menuFlyout.Items.Cast<RadioMenuFlyoutItem>())
|
||||||
|
{
|
||||||
|
item.GroupName = $"PanelSelector{hash}Group";
|
||||||
|
}
|
||||||
|
|
||||||
|
OnCurrentChanged(selector, Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SplitButtonClick(SplitButton sender, SplitButtonClickEventArgs args)
|
private void SplitButtonClick(SplitButton sender, SplitButtonClickEventArgs args)
|
||||||
{
|
{
|
||||||
MenuFlyout menuFlyout = (MenuFlyout)sender.Flyout;
|
MenuFlyout menuFlyout = (MenuFlyout)sender.Flyout;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (; i < menuFlyout.Items.Count; i++)
|
for (; i < menuFlyout.Items.Count; i++)
|
||||||
{
|
{
|
||||||
RadioMenuFlyoutItem current = (RadioMenuFlyoutItem)menuFlyout.Items[i];
|
if ((string)menuFlyout.Items[i].Tag == Current)
|
||||||
if (current.IsChecked)
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
++i;
|
||||||
|
i %= menuFlyout.Items.Count; // move the count index to 0
|
||||||
if (i > menuFlyout.Items.Count)
|
|
||||||
{
|
|
||||||
i = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == menuFlyout.Items.Count)
|
|
||||||
{
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
RadioMenuFlyoutItem item = (RadioMenuFlyoutItem)menuFlyout.Items[i];
|
RadioMenuFlyoutItem item = (RadioMenuFlyoutItem)menuFlyout.Items[i];
|
||||||
item.IsChecked = true;
|
item.IsChecked = true;
|
||||||
UpdateState(item);
|
Current = (string)item.Tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RadioMenuFlyoutItemClick(object sender, RoutedEventArgs e)
|
private void RadioMenuFlyoutItemClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
RadioMenuFlyoutItem item = (RadioMenuFlyoutItem)sender;
|
RadioMenuFlyoutItem item = (RadioMenuFlyoutItem)sender;
|
||||||
UpdateState(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateState(RadioMenuFlyoutItem item)
|
|
||||||
{
|
|
||||||
Current = (string)item.Tag;
|
Current = (string)item.Tag;
|
||||||
IconPresenter.Glyph = ((FontIcon)item.Icon).Glyph;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Abstraction;
|
||||||
|
|
||||||
|
[SuppressMessage("", "SA1600")]
|
||||||
|
public abstract class DisposableObject : IDisposable
|
||||||
|
{
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!IsDisposed)
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
Dispose(isDisposing: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
IsDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void VerifyNotDisposed()
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(GetType().FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Windows.Storage;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Caching;
|
namespace Snap.Hutao.Core.Caching;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -12,23 +10,20 @@ namespace Snap.Hutao.Core.Caching;
|
|||||||
internal interface IImageCache
|
internal interface IImageCache
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the StorageFile containing cached item for given Uri
|
/// Gets the file path containing cached item for given Uri
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="uri">Uri of the item.</param>
|
/// <param name="uri">Uri of the item.</param>
|
||||||
/// <returns>a StorageFile</returns>
|
/// <returns>a string path</returns>
|
||||||
Task<StorageFile> GetFileFromCacheAsync(Uri uri);
|
Task<string> GetFileFromCacheAsync(Uri uri);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removed items based on uri list passed
|
/// Removed items based on uri list passed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="uriForCachedItems">Enumerable uri list</param>
|
/// <param name="uriForCachedItems">Enumerable uri list</param>
|
||||||
/// <returns>awaitable Task</returns>
|
void Remove(IEnumerable<Uri> uriForCachedItems);
|
||||||
Task RemoveAsync(IEnumerable<Uri> uriForCachedItems);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes cached files that have expired
|
/// Removes invalid cached files
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="duration">Optional timespan to compute whether file has expired or not. If no value is supplied, <see cref="CacheDuration"/> is used.</param>
|
void RemoveInvalid();
|
||||||
/// <returns>awaitable task</returns>
|
|
||||||
Task RemoveExpiredAsync(TimeSpan? duration = null);
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.Caching;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图像缓存 文件路径操作
|
||||||
|
/// </summary>
|
||||||
|
internal interface IImageCacheFilePathOperation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 从分类与文件名获取文件路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="category">分类</param>
|
||||||
|
/// <param name="fileName">文件名</param>
|
||||||
|
/// <returns>文件路径</returns>
|
||||||
|
string GetFilePathFromCategoryAndFileName(string category, string fileName);
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ using System.Net.Http;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using Windows.Storage.FileProperties;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Caching;
|
namespace Snap.Hutao.Core.Caching;
|
||||||
|
|
||||||
@@ -20,11 +19,10 @@ namespace Snap.Hutao.Core.Caching;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Injection(InjectAs.Singleton, typeof(IImageCache))]
|
[Injection(InjectAs.Singleton, typeof(IImageCache))]
|
||||||
[HttpClient(HttpClientConfigration.Default)]
|
[HttpClient(HttpClientConfigration.Default)]
|
||||||
[PrimaryHttpMessageHandler(MaxConnectionsPerServer = 16)]
|
[PrimaryHttpMessageHandler(MaxConnectionsPerServer = 8)]
|
||||||
[SuppressMessage("", "CA1001")]
|
public class ImageCache : IImageCache, IImageCacheFilePathOperation
|
||||||
public class ImageCache : IImageCache
|
|
||||||
{
|
{
|
||||||
private const string DateAccessedProperty = "System.DateAccessed";
|
private const string CacheFolderName = nameof(ImageCache);
|
||||||
|
|
||||||
private static readonly ImmutableDictionary<int, TimeSpan> RetryCountToDelay = new Dictionary<int, TimeSpan>()
|
private static readonly ImmutableDictionary<int, TimeSpan> RetryCountToDelay = new Dictionary<int, TimeSpan>()
|
||||||
{
|
{
|
||||||
@@ -36,17 +34,13 @@ public class ImageCache : IImageCache
|
|||||||
[5] = TimeSpan.FromSeconds(64),
|
[5] = TimeSpan.FromSeconds(64),
|
||||||
}.ToImmutableDictionary();
|
}.ToImmutableDictionary();
|
||||||
|
|
||||||
private readonly List<string> extendedPropertyNames = new() { DateAccessedProperty };
|
|
||||||
|
|
||||||
private readonly SemaphoreSlim cacheFolderSemaphore = new(1);
|
|
||||||
private readonly ILogger logger;
|
private readonly ILogger logger;
|
||||||
|
|
||||||
// violate di rule
|
// violate di rule
|
||||||
private readonly HttpClient httpClient;
|
private readonly HttpClient httpClient;
|
||||||
|
|
||||||
private StorageFolder? baseFolder;
|
private string? baseFolder;
|
||||||
private string? cacheFolderName;
|
private string? cacheFolder;
|
||||||
private StorageFolder? cacheFolder;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ImageCache"/> class.
|
/// Initializes a new instance of the <see cref="ImageCache"/> class.
|
||||||
@@ -57,115 +51,84 @@ public class ImageCache : IImageCache
|
|||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
httpClient = httpClientFactory.CreateClient(nameof(ImageCache));
|
httpClient = httpClientFactory.CreateClient(nameof(ImageCache));
|
||||||
|
|
||||||
CacheDuration = TimeSpan.FromDays(30);
|
|
||||||
RetryCount = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets or sets the life duration of every cache entry.
|
public void RemoveInvalid()
|
||||||
/// </summary>
|
|
||||||
public TimeSpan CacheDuration { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of retries trying to ensure the file is cached.
|
|
||||||
/// </summary>
|
|
||||||
public uint RetryCount { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears all files in the cache
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>awaitable task</returns>
|
|
||||||
public async Task ClearAsync()
|
|
||||||
{
|
{
|
||||||
StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
|
string folder = GetCacheFolder();
|
||||||
IReadOnlyList<StorageFile> files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
|
string[] files = Directory.GetFiles(folder);
|
||||||
|
|
||||||
await RemoveAsync(files).ConfigureAwait(false);
|
List<string> filesToDelete = new();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
foreach (string file in files)
|
||||||
/// Removes cached files that have expired
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="duration">Optional timespan to compute whether file has expired or not. If no value is supplied, <see cref="CacheDuration"/> is used.</param>
|
|
||||||
/// <returns>awaitable task</returns>
|
|
||||||
public async Task RemoveExpiredAsync(TimeSpan? duration = null)
|
|
||||||
{
|
|
||||||
TimeSpan expiryDuration = duration ?? CacheDuration;
|
|
||||||
|
|
||||||
StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
|
|
||||||
IReadOnlyList<StorageFile> files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
|
|
||||||
|
|
||||||
List<StorageFile> filesToDelete = new();
|
|
||||||
|
|
||||||
foreach (StorageFile file in files)
|
|
||||||
{
|
{
|
||||||
if (file == null)
|
if (IsFileInvalid(file, false))
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await IsFileOutOfDateAsync(file, expiryDuration, false).ConfigureAwait(false))
|
|
||||||
{
|
{
|
||||||
filesToDelete.Add(file);
|
filesToDelete.Add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await RemoveAsync(filesToDelete).ConfigureAwait(false);
|
RemoveInternal(filesToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Removed items based on uri list passed
|
public void Remove(IEnumerable<Uri> uriForCachedItems)
|
||||||
/// </summary>
|
|
||||||
/// <param name="uriForCachedItems">Enumerable uri list</param>
|
|
||||||
/// <returns>awaitable Task</returns>
|
|
||||||
public async Task RemoveAsync(IEnumerable<Uri> uriForCachedItems)
|
|
||||||
{
|
{
|
||||||
if (uriForCachedItems == null || !uriForCachedItems.Any())
|
if (uriForCachedItems == null || !uriForCachedItems.Any())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
|
string folder = GetCacheFolder();
|
||||||
IReadOnlyList<StorageFile> files = await folder.GetFilesAsync().AsTask().ConfigureAwait(false);
|
string[] files = Directory.GetFiles(folder);
|
||||||
|
|
||||||
List<StorageFile> filesToDelete = new();
|
List<string> filesToDelete = new();
|
||||||
|
|
||||||
Dictionary<string, StorageFile> cachedFiles = files.ToDictionary(file => file.Name);
|
|
||||||
|
|
||||||
foreach (Uri uri in uriForCachedItems)
|
foreach (Uri uri in uriForCachedItems)
|
||||||
{
|
{
|
||||||
string fileName = GetCacheFileName(uri);
|
string filePath = Path.Combine(folder, GetCacheFileName(uri));
|
||||||
if (cachedFiles.TryGetValue(fileName, out StorageFile? file))
|
if (files.Contains(filePath))
|
||||||
{
|
{
|
||||||
filesToDelete.Add(file);
|
filesToDelete.Add(filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await RemoveAsync(filesToDelete).ConfigureAwait(false);
|
RemoveInternal(filesToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets the StorageFile containing cached item for given Uri
|
public async Task<string> GetFileFromCacheAsync(Uri uri)
|
||||||
/// </summary>
|
|
||||||
/// <param name="uri">Uri of the item.</param>
|
|
||||||
/// <returns>a StorageFile</returns>
|
|
||||||
public async Task<StorageFile> GetFileFromCacheAsync(Uri uri)
|
|
||||||
{
|
{
|
||||||
StorageFolder folder = await GetCacheFolderAsync().ConfigureAwait(false);
|
string filePath = Path.Combine(GetCacheFolder(), GetCacheFileName(uri));
|
||||||
|
|
||||||
string fileName = GetCacheFileName(uri);
|
if (!File.Exists(filePath) || new FileInfo(filePath).Length == 0)
|
||||||
|
|
||||||
IStorageItem? item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (item == null || (await item.GetBasicPropertiesAsync()).Size == 0)
|
|
||||||
{
|
{
|
||||||
StorageFile baseFile = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
|
await DownloadFileAsync(uri, filePath).ConfigureAwait(false);
|
||||||
await DownloadFileAsync(uri, baseFile).ConfigureAwait(false);
|
|
||||||
item = await folder.TryGetItemAsync(fileName).AsTask().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Must.NotNull((item as StorageFile)!);
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string GetFilePathFromCategoryAndFileName(string category, string fileName)
|
||||||
|
{
|
||||||
|
Uri dummyUri = new(Web.HutaoEndpoints.StaticFile(category, fileName));
|
||||||
|
return Path.Combine(GetCacheFolder(), GetCacheFileName(dummyUri));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RemoveInternal(IEnumerable<string> filePaths)
|
||||||
|
{
|
||||||
|
foreach (string filePath in filePaths)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(filePath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetCacheFileName(Uri uri)
|
private static string GetCacheFileName(Uri uri)
|
||||||
@@ -176,48 +139,19 @@ public class ImageCache : IImageCache
|
|||||||
return System.Convert.ToHexString(hash);
|
return System.Convert.ToHexString(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private static bool IsFileInvalid(string file, bool treatNullFileAsInvalid = true)
|
||||||
/// Override-able method that checks whether file is valid or not.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="file">storage file</param>
|
|
||||||
/// <param name="duration">cache duration</param>
|
|
||||||
/// <param name="treatNullFileAsOutOfDate">option to mark uninitialized file as expired</param>
|
|
||||||
/// <returns>bool indicate whether file has expired or not</returns>
|
|
||||||
private async Task<bool> IsFileOutOfDateAsync(StorageFile file, TimeSpan duration, bool treatNullFileAsOutOfDate = true)
|
|
||||||
{
|
{
|
||||||
if (file == null)
|
if (!File.Exists(file))
|
||||||
{
|
{
|
||||||
return treatNullFileAsOutOfDate;
|
return treatNullFileAsInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get extended properties.
|
// Get extended properties.
|
||||||
IDictionary<string, object> extraProperties = await file.Properties
|
FileInfo fileInfo = new(file);
|
||||||
.RetrievePropertiesAsync(extendedPropertyNames)
|
return fileInfo.Length == 0;
|
||||||
.AsTask()
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
// Get date-accessed property.
|
|
||||||
object? propValue = extraProperties[DateAccessedProperty];
|
|
||||||
|
|
||||||
if (propValue != null)
|
|
||||||
{
|
|
||||||
DateTimeOffset? lastAccess = propValue as DateTimeOffset?;
|
|
||||||
|
|
||||||
if (lastAccess.HasValue)
|
|
||||||
{
|
|
||||||
return DateTime.Now.Subtract(lastAccess.Value.DateTime) > duration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicProperties properties = await file
|
|
||||||
.GetBasicPropertiesAsync()
|
|
||||||
.AsTask()
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
return properties.Size == 0 || DateTime.Now.Subtract(properties.DateModified.DateTime) > duration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadFileAsync(Uri uri, StorageFile baseFile)
|
private async Task DownloadFileAsync(Uri uri, string baseFile)
|
||||||
{
|
{
|
||||||
logger.LogInformation(EventIds.FileCaching, "Begin downloading for {uri}", uri);
|
logger.LogInformation(EventIds.FileCaching, "Begin downloading for {uri}", uri);
|
||||||
|
|
||||||
@@ -230,18 +164,23 @@ public class ImageCache : IImageCache
|
|||||||
{
|
{
|
||||||
using (Stream httpStream = await message.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
using (Stream httpStream = await message.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
using (FileStream fileStream = File.Create(baseFile.Path))
|
using (FileStream fileStream = File.Create(baseFile))
|
||||||
{
|
{
|
||||||
await httpStream.CopyToAsync(fileStream).ConfigureAwait(false);
|
await httpStream.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (message.StatusCode == HttpStatusCode.NotFound)
|
||||||
|
{
|
||||||
|
// directly goto https://static.hut.ao
|
||||||
|
retryCount = 3;
|
||||||
|
}
|
||||||
else if (message.StatusCode == HttpStatusCode.TooManyRequests)
|
else if (message.StatusCode == HttpStatusCode.TooManyRequests)
|
||||||
{
|
{
|
||||||
retryCount++;
|
retryCount++;
|
||||||
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
|
TimeSpan delay = message.Headers.RetryAfter?.Delta ?? RetryCountToDelay[retryCount];
|
||||||
logger.LogInformation("Retry after {delay}.", delay);
|
logger.LogInformation("Retry {uri} after {delay}.", uri, delay);
|
||||||
await Task.Delay(delay).ConfigureAwait(false);
|
await Task.Delay(delay).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -252,61 +191,20 @@ public class ImageCache : IImageCache
|
|||||||
|
|
||||||
if (retryCount == 3)
|
if (retryCount == 3)
|
||||||
{
|
{
|
||||||
uri = new UriBuilder(uri) { Host = "static.hut.ao", }.Uri;
|
uri = new UriBuilder(uri) { Host = Web.HutaoEndpoints.StaticHutao, }.Uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private string GetCacheFolder()
|
||||||
/// Initializes with default values if user has not initialized explicitly
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>awaitable task</returns>
|
|
||||||
private async Task InitializeInternalAsync()
|
|
||||||
{
|
|
||||||
if (cacheFolder != null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (await cacheFolderSemaphore.EnterAsync().ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
baseFolder ??= ApplicationData.Current.TemporaryFolder;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(cacheFolderName))
|
|
||||||
{
|
|
||||||
cacheFolderName = GetType().Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheFolder = await baseFolder
|
|
||||||
.CreateFolderAsync(cacheFolderName, CreationCollisionOption.OpenIfExists)
|
|
||||||
.AsTask()
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<StorageFolder> GetCacheFolderAsync()
|
|
||||||
{
|
{
|
||||||
if (cacheFolder == null)
|
if (cacheFolder == null)
|
||||||
{
|
{
|
||||||
await InitializeInternalAsync().ConfigureAwait(false);
|
baseFolder ??= ApplicationData.Current.LocalCacheFolder.Path;
|
||||||
|
DirectoryInfo info = Directory.CreateDirectory(Path.Combine(baseFolder, CacheFolderName));
|
||||||
|
cacheFolder = info.FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Must.NotNull(cacheFolder!);
|
return cacheFolder!;
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RemoveAsync(IEnumerable<StorageFile> files)
|
|
||||||
{
|
|
||||||
foreach (StorageFile file in files)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
logger.LogInformation(EventIds.CacheRemoveFile, "Removing file {file}", file.Path);
|
|
||||||
await file.DeleteAsync().AsTask().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
logger.LogError(EventIds.CacheException, "Failed to delete file: {file}", file.Path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,9 @@ using Microsoft.Win32;
|
|||||||
using Snap.Hutao.Core.Convert;
|
using Snap.Hutao.Core.Convert;
|
||||||
using Snap.Hutao.Core.Json;
|
using Snap.Hutao.Core.Json;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.IO;
|
||||||
using System.Text.Json.Serialization.Metadata;
|
using System.Text.Json.Serialization.Metadata;
|
||||||
using Windows.ApplicationModel;
|
using Windows.ApplicationModel;
|
||||||
|
|
||||||
@@ -28,7 +31,20 @@ internal static class CoreEnvironment
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 米游社 Rpc 版本
|
/// 米游社 Rpc 版本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string HoyolabXrpcVersion = "2.42.1";
|
public const string HoyolabXrpcVersion = "2.43.1";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 盐
|
||||||
|
/// </summary>
|
||||||
|
// https://github.com/UIGF-org/Hoyolab.Salt
|
||||||
|
public static readonly ImmutableDictionary<string, string> DynamicSecrets = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
[nameof(SaltType.K2)] = "ODzG1Jrn6zebX19VRmaJwjFI2CDvBUGq",
|
||||||
|
[nameof(SaltType.LK2)] = "V1PYbXKQY7ysdx3MNCcNbsE1LtY2QZpW",
|
||||||
|
[nameof(SaltType.X4)] = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs",
|
||||||
|
[nameof(SaltType.X6)] = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||||
|
[nameof(SaltType.PROD)] = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS",
|
||||||
|
}.ToImmutableDictionary();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标准UA
|
/// 标准UA
|
||||||
@@ -55,6 +71,11 @@ internal static class CoreEnvironment
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string FamilyName;
|
public static readonly string FamilyName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据文件夹
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string DataFolder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 默认的Json序列化选项
|
/// 默认的Json序列化选项
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -78,6 +99,7 @@ internal static class CoreEnvironment
|
|||||||
|
|
||||||
static CoreEnvironment()
|
static CoreEnvironment()
|
||||||
{
|
{
|
||||||
|
DataFolder = GetDocumentsHutaoPath();
|
||||||
Version = Package.Current.Id.Version.ToVersion();
|
Version = Package.Current.Id.Version.ToVersion();
|
||||||
FamilyName = Package.Current.Id.FamilyName;
|
FamilyName = Package.Current.Id.FamilyName;
|
||||||
CommonUA = $"Snap Hutao/{Version}";
|
CommonUA = $"Snap Hutao/{Version}";
|
||||||
@@ -93,4 +115,19 @@ internal static class CoreEnvironment
|
|||||||
object? machineGuid = Registry.GetValue(CryptographyKey, MachineGuidValue, userName);
|
object? machineGuid = Registry.GetValue(CryptographyKey, MachineGuidValue, userName);
|
||||||
return Md5Convert.ToHexString($"{userName}{machineGuid}");
|
return Md5Convert.ToHexString($"{userName}{machineGuid}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetDocumentsHutaoPath()
|
||||||
|
{
|
||||||
|
string myDocument = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||||
|
#if RELEASE
|
||||||
|
// 将测试版与正式版的文件目录分离
|
||||||
|
string folderName = Package.Current.PublisherDisplayName == "DGP Studio CI" ? "HutaoAlpha" : "Hutao";
|
||||||
|
#else
|
||||||
|
// 使得迁移能正常生成
|
||||||
|
string folderName = "Hutao";
|
||||||
|
#endif
|
||||||
|
string path = Path.GetFullPath(Path.Combine(myDocument, folderName));
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,7 @@
|
|||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
using Snap.Hutao.Context.FileSystem;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.DependencyInjection;
|
namespace Snap.Hutao.Core.DependencyInjection;
|
||||||
@@ -31,9 +30,7 @@ internal static class IocConfiguration
|
|||||||
/// <returns>可继续操作的集合</returns>
|
/// <returns>可继续操作的集合</returns>
|
||||||
public static IServiceCollection AddDatebase(this IServiceCollection services)
|
public static IServiceCollection AddDatebase(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
HutaoContext myDocument = new(new());
|
string dbFile = System.IO.Path.Combine(CoreEnvironment.DataFolder, "Userdata.db");
|
||||||
|
|
||||||
string dbFile = myDocument.Locate("Userdata.db");
|
|
||||||
string sqlConnectionString = $"Data Source={dbFile}";
|
string sqlConnectionString = $"Data Source={dbFile}";
|
||||||
|
|
||||||
// temporarily create a context
|
// temporarily create a context
|
||||||
|
|||||||
311
src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsJob.cs
Normal file
311
src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsJob.cs
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Snap.Hutao.Core.Abstraction;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Windows.Win32;
|
||||||
|
using Windows.Win32.Foundation;
|
||||||
|
using Windows.Win32.Networking.BackgroundIntelligentTransferService;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.IO.Bits;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BITS Job
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("", "SA1600")]
|
||||||
|
internal class BitsJob : DisposableObject, IBackgroundCopyCallback
|
||||||
|
{
|
||||||
|
private const uint BitsEngineNoProgressTimeout = 120;
|
||||||
|
private const int MaxResumeAttempts = 10;
|
||||||
|
|
||||||
|
private readonly string displayName;
|
||||||
|
private readonly ILogger<BitsJob> log;
|
||||||
|
private readonly object lockObj = new();
|
||||||
|
|
||||||
|
private IBackgroundCopyJob? nativeJob;
|
||||||
|
private System.Exception? jobException;
|
||||||
|
private BG_JOB_PROGRESS progress;
|
||||||
|
private BG_JOB_STATE state;
|
||||||
|
private bool isJobComplete;
|
||||||
|
private int resumeAttempts;
|
||||||
|
|
||||||
|
private BitsJob(IServiceProvider serviceProvider, string displayName, IBackgroundCopyJob job)
|
||||||
|
{
|
||||||
|
this.displayName = displayName;
|
||||||
|
nativeJob = job;
|
||||||
|
log = serviceProvider.GetRequiredService<ILogger<BitsJob>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HRESULT ErrorCode { get; private set; }
|
||||||
|
|
||||||
|
public static BitsJob CreateJob(IServiceProvider serviceProvider, IBackgroundCopyManager backgroundCopyManager, Uri uri, string filePath)
|
||||||
|
{
|
||||||
|
ILogger<BitsJob> service = serviceProvider.GetRequiredService<ILogger<BitsJob>>();
|
||||||
|
string text = $"BitsDownloadJob - {uri}";
|
||||||
|
IBackgroundCopyJob ppJob;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
backgroundCopyManager.CreateJob(text, BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out Guid _, out ppJob);
|
||||||
|
ppJob.SetNotifyFlags(11u);
|
||||||
|
ppJob.SetNoProgressTimeout(BitsEngineNoProgressTimeout);
|
||||||
|
ppJob.SetPriority(BG_JOB_PRIORITY.BG_JOB_PRIORITY_FOREGROUND);
|
||||||
|
ppJob.SetProxySettings(BG_JOB_PROXY_USAGE.BG_JOB_PROXY_USAGE_AUTODETECT, null, null);
|
||||||
|
}
|
||||||
|
catch (COMException ex)
|
||||||
|
{
|
||||||
|
service.LogInformation("Failed to create job. {message}", ex.Message);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitsJob bitsJob = new(serviceProvider, text, ppJob);
|
||||||
|
bitsJob.InitJob(uri.AbsoluteUri, filePath);
|
||||||
|
return bitsJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JobTransferred(IBackgroundCopyJob job)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateProgress();
|
||||||
|
UpdateJobState();
|
||||||
|
CompleteOrCancel();
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
log.LogInformation("Failed to job transfer: {message}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JobError(IBackgroundCopyJob job, IBackgroundCopyError error)
|
||||||
|
{
|
||||||
|
IBackgroundCopyError error2 = error;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log.LogInformation("Failed job: {message}", displayName);
|
||||||
|
UpdateJobState();
|
||||||
|
BG_ERROR_CONTEXT errorContext = BG_ERROR_CONTEXT.BG_ERROR_CONTEXT_NONE;
|
||||||
|
HRESULT returnCode = new(0);
|
||||||
|
|
||||||
|
Invoke(() => error2.GetError(out errorContext, out returnCode), "GetError", throwOnFailure: false);
|
||||||
|
ErrorCode = returnCode;
|
||||||
|
jobException = new IOException(string.Format("Error context: {0}, Error code: {1}", errorContext, returnCode));
|
||||||
|
CompleteOrCancel();
|
||||||
|
log.LogInformation(jobException, "Job Exception:");
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
log?.LogInformation("Failed to handle job error: {message}", ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JobModification(IBackgroundCopyJob job, uint reserved)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateJobState();
|
||||||
|
if (state == BG_JOB_STATE.BG_JOB_STATE_TRANSIENT_ERROR)
|
||||||
|
{
|
||||||
|
HRESULT errorCode = GetErrorCode(job);
|
||||||
|
if (errorCode == -2145844944)
|
||||||
|
{
|
||||||
|
ErrorCode = errorCode;
|
||||||
|
CompleteOrCancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resumeAttempts++;
|
||||||
|
if (resumeAttempts <= MaxResumeAttempts)
|
||||||
|
{
|
||||||
|
Resume();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.LogInformation("Max resume attempts for job '{name}' exceeded. Canceling.", displayName);
|
||||||
|
CompleteOrCancel();
|
||||||
|
}
|
||||||
|
else if (IsProgressingState(state))
|
||||||
|
{
|
||||||
|
UpdateProgress();
|
||||||
|
}
|
||||||
|
else if (state == BG_JOB_STATE.BG_JOB_STATE_CANCELLED || state == BG_JOB_STATE.BG_JOB_STATE_ERROR)
|
||||||
|
{
|
||||||
|
CompleteOrCancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
log.LogInformation(ex, "message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
log.LogInformation("Canceling job {name}", displayName);
|
||||||
|
lock (lockObj)
|
||||||
|
{
|
||||||
|
if (!isJobComplete)
|
||||||
|
{
|
||||||
|
Invoke(() => nativeJob?.Cancel(), "Bits Cancel");
|
||||||
|
jobException = new OperationCanceledException();
|
||||||
|
isJobComplete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WaitForCompletion(Action<ProgressUpdateStatus> callback, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.Register(Cancel);
|
||||||
|
int noProgressSeconds = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateJobState();
|
||||||
|
while (IsProgressingState(state) || state == BG_JOB_STATE.BG_JOB_STATE_QUEUED)
|
||||||
|
{
|
||||||
|
if (noProgressSeconds > BitsEngineNoProgressTimeout)
|
||||||
|
{
|
||||||
|
jobException = new TimeoutException($"Timeout reached for job {displayName} whilst in state {state}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
UpdateJobState();
|
||||||
|
UpdateProgress();
|
||||||
|
|
||||||
|
if (state is BG_JOB_STATE.BG_JOB_STATE_TRANSFERRING or BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED or BG_JOB_STATE.BG_JOB_STATE_ACKNOWLEDGED)
|
||||||
|
{
|
||||||
|
noProgressSeconds = 0;
|
||||||
|
callback(new ProgressUpdateStatus((long)progress.BytesTransferred, (long)progress.BytesTotal));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh every seconds.
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
++noProgressSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
cancellationTokenRegistration.Dispose();
|
||||||
|
CompleteOrCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jobException != null)
|
||||||
|
{
|
||||||
|
throw jobException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
UpdateJobState();
|
||||||
|
CompleteOrCancel();
|
||||||
|
nativeJob = null;
|
||||||
|
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsProgressingState(BG_JOB_STATE state)
|
||||||
|
{
|
||||||
|
if (state != BG_JOB_STATE.BG_JOB_STATE_CONNECTING && state != BG_JOB_STATE.BG_JOB_STATE_TRANSIENT_ERROR)
|
||||||
|
{
|
||||||
|
return state == BG_JOB_STATE.BG_JOB_STATE_TRANSFERRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CompleteOrCancel()
|
||||||
|
{
|
||||||
|
if (isJobComplete)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (lockObj)
|
||||||
|
{
|
||||||
|
if (isJobComplete)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED)
|
||||||
|
{
|
||||||
|
log.LogInformation("Completing job '{name}'.", displayName);
|
||||||
|
Invoke(() => nativeJob?.Complete(), "Bits Complete");
|
||||||
|
while (state == BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED)
|
||||||
|
{
|
||||||
|
Thread.Sleep(50);
|
||||||
|
UpdateJobState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.LogInformation("Canceling job '{name}'.", displayName);
|
||||||
|
Invoke(() => nativeJob?.Cancel(), "Bits Cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
isJobComplete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateJobState()
|
||||||
|
{
|
||||||
|
if (nativeJob is IBackgroundCopyJob job)
|
||||||
|
{
|
||||||
|
Invoke(() => job.GetState(out state), "GetState");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateProgress()
|
||||||
|
{
|
||||||
|
if (!isJobComplete)
|
||||||
|
{
|
||||||
|
Invoke(() => nativeJob?.GetProgress(out progress), "GetProgress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Resume()
|
||||||
|
{
|
||||||
|
Invoke(() => nativeJob?.Resume(), "Bits Resume");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Invoke(Action action, string displayName, bool throwOnFailure = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
log.LogInformation("{name} failed. {exception}", displayName, ex);
|
||||||
|
if (throwOnFailure)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitJob(string remoteUrl, string filePath)
|
||||||
|
{
|
||||||
|
nativeJob?.AddFile(remoteUrl, filePath);
|
||||||
|
nativeJob?.SetNotifyInterface(this);
|
||||||
|
Resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HRESULT GetErrorCode(IBackgroundCopyJob job)
|
||||||
|
{
|
||||||
|
IBackgroundCopyJob job2 = job;
|
||||||
|
IBackgroundCopyError? error = null;
|
||||||
|
|
||||||
|
Invoke(() => job2.GetError(out error), "GetError", false);
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
HRESULT returnCode = new(0);
|
||||||
|
Invoke(() => error.GetError(out _, out returnCode), "GetError", false);
|
||||||
|
return returnCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsManager.cs
Normal file
77
src/Snap.Hutao/Snap.Hutao/Core/IO/Bits/BitsManager.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Windows.Win32.Networking.BackgroundIntelligentTransferService;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.IO.Bits;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// BITS 管理器
|
||||||
|
/// </summary>
|
||||||
|
[Injection(InjectAs.Singleton)]
|
||||||
|
internal class BitsManager
|
||||||
|
{
|
||||||
|
private readonly Lazy<IBackgroundCopyManager> lazyBackgroundCopyManager = new(() => (IBackgroundCopyManager)new BackgroundCopyManager());
|
||||||
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
private readonly ILogger<BitsManager> logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的 BITS 管理器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceProvider">服务提供器</param>
|
||||||
|
public BitsManager(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
this.serviceProvider = serviceProvider;
|
||||||
|
logger = serviceProvider.GetRequiredService<ILogger<BitsManager>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步下载文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uri">文件uri</param>
|
||||||
|
/// <param name="progress">进度</param>
|
||||||
|
/// <param name="token">取消令牌</param>
|
||||||
|
/// <returns>是否下载成功,以及创建的文件</returns>
|
||||||
|
public async Task<ValueResult<bool, TempFile>> DownloadAsync(Uri uri, IProgress<ProgressUpdateStatus> progress, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
TempFile tempFile = new(true);
|
||||||
|
bool result = await Task.Run(() => DownloadCore(uri, tempFile.Path, progress.Report, token), token).ConfigureAwait(false);
|
||||||
|
return new(result, tempFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DownloadCore(Uri uri, string tempFile, Action<ProgressUpdateStatus> progress, CancellationToken token)
|
||||||
|
{
|
||||||
|
IBackgroundCopyManager value;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = lazyBackgroundCopyManager.Value;
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
logger?.LogWarning("BITS download engine not supported: {message}", ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (BitsJob bitsJob = BitsJob.CreateJob(serviceProvider, value, uri, tempFile))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bitsJob.WaitForCompletion(progress, token);
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
logger?.LogWarning(ex, "BITS download failed:");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitsJob.ErrorCode != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Core.IO.Bits;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 进度更新状态
|
||||||
|
/// </summary>
|
||||||
|
public class ProgressUpdateStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的进度更新状态
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bytesRead">接收字节数</param>
|
||||||
|
/// <param name="totalBytes">总字节数</param>
|
||||||
|
public ProgressUpdateStatus(long bytesRead, long totalBytes)
|
||||||
|
{
|
||||||
|
BytesRead = bytesRead;
|
||||||
|
TotalBytes = totalBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 接收字节数
|
||||||
|
/// </summary>
|
||||||
|
public long BytesRead { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总字节数
|
||||||
|
/// </summary>
|
||||||
|
public long TotalBytes { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{BytesRead}/{TotalBytes}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,14 +8,20 @@ namespace Snap.Hutao.Core.IO;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 封装一个临时文件
|
/// 封装一个临时文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class TemporaryFile : IDisposable
|
internal sealed class TempFile : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个新的临时文件
|
/// 构造一个新的临时文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TemporaryFile()
|
/// <param name="delete">是否在创建时删除文件</param>
|
||||||
|
public TempFile(bool delete = false)
|
||||||
{
|
{
|
||||||
Path = System.IO.Path.GetTempFileName();
|
Path = System.IO.Path.GetTempFileName();
|
||||||
|
|
||||||
|
if (delete)
|
||||||
|
{
|
||||||
|
File.Delete(Path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -28,9 +34,9 @@ internal sealed class TemporaryFile : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="file">源文件</param>
|
/// <param name="file">源文件</param>
|
||||||
/// <returns>临时文件</returns>
|
/// <returns>临时文件</returns>
|
||||||
public static TemporaryFile? CreateFromFileCopy(string file)
|
public static TempFile? CreateFromFileCopy(string file)
|
||||||
{
|
{
|
||||||
TemporaryFile temporaryFile = new();
|
TempFile temporaryFile = new();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Copy(file, temporaryFile.Path, true);
|
File.Copy(file, temporaryFile.Path, true);
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
using CommunityToolkit.WinUI.Notifications;
|
using CommunityToolkit.WinUI.Notifications;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Service.DailyNote;
|
using Snap.Hutao.Service.DailyNote;
|
||||||
@@ -123,6 +124,9 @@ internal static class Activation
|
|||||||
{
|
{
|
||||||
case "":
|
case "":
|
||||||
{
|
{
|
||||||
|
// Increase launch times
|
||||||
|
LocalSetting.Set(SettingKeys.LaunchTimes, LocalSetting.Get(SettingKeys.LaunchTimes, 0) + 1);
|
||||||
|
|
||||||
await WaitMainWindowAsync().ConfigureAwait(false);
|
await WaitMainWindowAsync().ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
using Snap.Hutao.Context.FileSystem;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
@@ -61,8 +60,8 @@ public sealed class LogEntryQueue : IDisposable
|
|||||||
|
|
||||||
private static LogDbContext InitializeDbContext()
|
private static LogDbContext InitializeDbContext()
|
||||||
{
|
{
|
||||||
HutaoContext myDocument = new(new());
|
string logDbName = System.IO.Path.Combine(CoreEnvironment.DataFolder, "Log.db");
|
||||||
LogDbContext logDbContext = LogDbContext.Create($"Data Source={myDocument.Locate("Log.db")}");
|
LogDbContext logDbContext = LogDbContext.Create($"Data Source={logDbName}");
|
||||||
if (logDbContext.Database.GetPendingMigrations().Any())
|
if (logDbContext.Database.GetPendingMigrations().Any())
|
||||||
{
|
{
|
||||||
Debug.WriteLine("[Debug] Performing LogDbContext Migrations");
|
Debug.WriteLine("[Debug] Performing LogDbContext Migrations");
|
||||||
@@ -70,7 +69,7 @@ public sealed class LogEntryQueue : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only raw sql can pass
|
// only raw sql can pass
|
||||||
logDbContext.Database.ExecuteSqlRaw("DELETE FROM logs WHERE Exception IS NULL");
|
logDbContext.Logs.Where(log => log.Exception == null).ExecuteDelete();
|
||||||
return logDbContext;
|
return logDbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,4 +17,19 @@ internal static class SettingKeys
|
|||||||
/// 导航侧栏是否展开
|
/// 导航侧栏是否展开
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string IsNavPaneOpen = "IsNavPaneOpen";
|
public const string IsNavPaneOpen = "IsNavPaneOpen";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动次数
|
||||||
|
/// </summary>
|
||||||
|
public const string LaunchTimes = "LaunchTimes";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静态资源合约V1
|
||||||
|
/// </summary>
|
||||||
|
public const string StaticResourceV1Contract = "StaticResourceV1Contract";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静态资源合约V2 成就图标与物品图标
|
||||||
|
/// </summary>
|
||||||
|
public const string StaticResourceV2Contract = "StaticResourceV2Contract";
|
||||||
}
|
}
|
||||||
@@ -5,9 +5,9 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.UI.Composition;
|
using Microsoft.UI.Composition;
|
||||||
using Microsoft.UI.Composition.SystemBackdrops;
|
using Microsoft.UI.Composition.SystemBackdrops;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Snap.Hutao.Context.Database;
|
|
||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Windows.System;
|
using Windows.System;
|
||||||
using WinRT;
|
using WinRT;
|
||||||
|
|||||||
@@ -77,4 +77,32 @@ public static partial class EnumerableExtension
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Enumerable.ToDictionary{TSource, TKey}(IEnumerable{TSource}, Func{TSource, TKey})"/>
|
||||||
|
public static Dictionary<TKey, TSource> ToDictionaryOverride<TKey, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
Dictionary<TKey, TSource> dictionary = new();
|
||||||
|
|
||||||
|
foreach (TSource value in source)
|
||||||
|
{
|
||||||
|
dictionary[keySelector(value)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="Enumerable.ToDictionary{TSource, TKey, TElement}(IEnumerable{TSource}, Func{TSource, TKey}, Func{TSource, TElement})"/>
|
||||||
|
public static Dictionary<TKey, TValue> ToDictionaryOverride<TKey, TValue, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TValue> valueSelector)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
Dictionary<TKey, TValue> dictionary = new();
|
||||||
|
|
||||||
|
foreach (TSource value in source)
|
||||||
|
{
|
||||||
|
dictionary[keySelector(value)] = valueSelector(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.Extension;
|
namespace Snap.Hutao.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -8,26 +10,6 @@ namespace Snap.Hutao.Extension;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class EnumerableExtension
|
public static partial class EnumerableExtension
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 计数
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TSource">源类型</typeparam>
|
|
||||||
/// <typeparam name="TKey">计数的键类型</typeparam>
|
|
||||||
/// <param name="source">源</param>
|
|
||||||
/// <param name="keySelector">键选择器</param>
|
|
||||||
/// <returns>计数表</returns>
|
|
||||||
public static IEnumerable<KeyValuePair<TKey, int>> CountBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
|
||||||
where TKey : notnull, IEquatable<TKey>
|
|
||||||
{
|
|
||||||
CounterInt32<TKey> counter = new();
|
|
||||||
foreach (TSource item in source)
|
|
||||||
{
|
|
||||||
counter.Increase(keySelector(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 如果传入集合不为空则原路返回,
|
/// 如果传入集合不为空则原路返回,
|
||||||
/// 如果传入集合为空返回一个集合的空集
|
/// 如果传入集合为空返回一个集合的空集
|
||||||
@@ -64,56 +46,14 @@ public static partial class EnumerableExtension
|
|||||||
return source.FirstOrDefault(predicate) ?? source.FirstOrDefault();
|
return source.FirstOrDefault(predicate) ?? source.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="Enumerable.ToDictionary{TSource, TKey}(IEnumerable{TSource}, Func{TSource, TKey})"/>
|
|
||||||
public static Dictionary<TKey, TSource> ToDictionaryOverride<TKey, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
Dictionary<TKey, TSource> dictionary = new();
|
|
||||||
|
|
||||||
foreach (TSource value in source)
|
|
||||||
{
|
|
||||||
dictionary[keySelector(value)] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Enumerable.ToDictionary{TSource, TKey, TElement}(IEnumerable{TSource}, Func{TSource, TKey}, Func{TSource, TElement})"/>
|
|
||||||
public static Dictionary<TKey, TValue> ToDictionaryOverride<TKey, TValue, TSource>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TValue> valueSelector)
|
|
||||||
where TKey : notnull
|
|
||||||
{
|
|
||||||
Dictionary<TKey, TValue> dictionary = new();
|
|
||||||
|
|
||||||
foreach (TSource value in source)
|
|
||||||
{
|
|
||||||
dictionary[keySelector(value)] = valueSelector(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示一个对 <see cref="TItem"/> 类型的计数器
|
/// 转换到 <see cref="ObservableCollection{T}"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TItem">待计数的类型</typeparam>
|
/// <typeparam name="T">类型</typeparam>
|
||||||
private class CounterInt32<TItem> : Dictionary<TItem, int>
|
/// <param name="source">源</param>
|
||||||
where TItem : notnull, IEquatable<TItem>
|
/// <returns><see cref="ObservableCollection{T}"/></returns>
|
||||||
|
public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> source)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return new ObservableCollection<T>(source);
|
||||||
/// 增加计数器
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">物品</param>
|
|
||||||
public void Increase(TItem? item)
|
|
||||||
{
|
|
||||||
if (item != null)
|
|
||||||
{
|
|
||||||
if (!ContainsKey(item))
|
|
||||||
{
|
|
||||||
this[item] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this[item] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Factory.Abstraction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内容对话框工厂
|
||||||
|
/// </summary>
|
||||||
|
internal interface IContentDialogFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 创建一个新的内容对话框,用于确认
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">标题</param>
|
||||||
|
/// <param name="content">内容</param>
|
||||||
|
/// <returns>内容对话框</returns>
|
||||||
|
ContentDialog CreateForConfirm(string title, string content);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建一个新的内容对话框,用于确认或取消
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">标题</param>
|
||||||
|
/// <param name="content">内容</param>
|
||||||
|
/// <param name="defaultButton">默认按钮</param>
|
||||||
|
/// <returns>内容对话框</returns>
|
||||||
|
ContentDialog CreateForConfirmCancel(string title, string content, ContentDialogButton defaultButton = ContentDialogButton.Close);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建一个新的内容对话框,用于提示未知的进度
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">标题</param>
|
||||||
|
/// <returns>内容对话框</returns>
|
||||||
|
ContentDialog CreateForIndeterminateProgress(string title);
|
||||||
|
}
|
||||||
67
src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
Normal file
67
src/Snap.Hutao/Snap.Hutao/Factory/ContentDialogFactory.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Snap.Hutao.Factory.Abstraction;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Factory;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IContentDialogFactory"/>
|
||||||
|
[Injection(InjectAs.Transient, typeof(IContentDialogFactory))]
|
||||||
|
internal class ContentDialogFactory : IContentDialogFactory
|
||||||
|
{
|
||||||
|
private readonly MainWindow mainWindow;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的内容对话框工厂
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mainWindow">主窗体</param>
|
||||||
|
public ContentDialogFactory(MainWindow mainWindow)
|
||||||
|
{
|
||||||
|
this.mainWindow = mainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ContentDialog CreateForConfirm(string title, string content)
|
||||||
|
{
|
||||||
|
ContentDialog dialog = new()
|
||||||
|
{
|
||||||
|
XamlRoot = mainWindow.Content.XamlRoot,
|
||||||
|
Title = title,
|
||||||
|
Content = content,
|
||||||
|
DefaultButton = ContentDialogButton.Primary,
|
||||||
|
PrimaryButtonText = "确认",
|
||||||
|
};
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ContentDialog CreateForConfirmCancel(string title, string content, ContentDialogButton defaultButton = ContentDialogButton.Close)
|
||||||
|
{
|
||||||
|
ContentDialog dialog = new()
|
||||||
|
{
|
||||||
|
XamlRoot = mainWindow.Content.XamlRoot,
|
||||||
|
Title = title,
|
||||||
|
Content = content,
|
||||||
|
DefaultButton = defaultButton,
|
||||||
|
PrimaryButtonText = "确认",
|
||||||
|
CloseButtonText = "取消",
|
||||||
|
};
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ContentDialog CreateForIndeterminateProgress(string title)
|
||||||
|
{
|
||||||
|
ContentDialog dialog = new()
|
||||||
|
{
|
||||||
|
XamlRoot = mainWindow.Content.XamlRoot,
|
||||||
|
Title = title,
|
||||||
|
Content = new ProgressBar() { IsIndeterminate = true },
|
||||||
|
};
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Localization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 翻译
|
|
||||||
/// </summary>
|
|
||||||
internal interface ITranslation
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取对应键的值
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">键</param>
|
|
||||||
/// <returns>对应的值</returns>
|
|
||||||
string this[string key] { get; }
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Localization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 中文翻译 zh-CN
|
|
||||||
/// </summary>
|
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
|
|
||||||
internal class LanguagezhCN : ITranslation
|
|
||||||
{
|
|
||||||
private readonly Dictionary<string, string> translations = new()
|
|
||||||
{
|
|
||||||
["AppName"] = "胡桃",
|
|
||||||
|
|
||||||
["NavigationViewItem_Activity"] = "活动",
|
|
||||||
["NavigationViewItem_Achievement"] = "成就",
|
|
||||||
["NavigationViewItem_Wiki_Avatar"] = "角色",
|
|
||||||
["NavigationViewItem_GachaLog"] = "祈愿记录",
|
|
||||||
|
|
||||||
["UserPanel_Account"] = "账号",
|
|
||||||
["UserPanel_Add_Account"] = "添加新账号",
|
|
||||||
["UserPanel_GameRole"] = "角色",
|
|
||||||
|
|
||||||
["Achievement_Search_PlaceHolder"] = "搜索成就名称,描述或编号",
|
|
||||||
["Achievement_Create_Archive"] = "创建新存档",
|
|
||||||
["Achievement_Delete_Archive"] = "删除当前存档",
|
|
||||||
["Achievement_Import"] = "导入",
|
|
||||||
["Achievement_Import_From_Clipboard"] = "从剪贴板导入",
|
|
||||||
["Achievement_Import_From_File"] = "从 UIAF 文件导入",
|
|
||||||
["Achievement_IncompleteItemFirst"] = "优先未完成",
|
|
||||||
|
|
||||||
["Wiki_Avatar_Filter"] = "筛选",
|
|
||||||
["Wiki_Avatar_Filter_Element"] = "元素",
|
|
||||||
["Wiki_Avatar_Filter_Association"] = "所属",
|
|
||||||
["Wiki_Avatar_Filter_Weapon"] = "武器",
|
|
||||||
["Wiki_Avatar_Filter_Quality"] = "星级",
|
|
||||||
["Wiki_Avatar_Filter_Body"] = "体型",
|
|
||||||
["Wiki_Avatar_Fetter_Native"] = "所属",
|
|
||||||
["Wiki_Avatar_Fetter_Constellation"] = "命之座",
|
|
||||||
["Wiki_Avatar_Fetter_Birth"] = "生日",
|
|
||||||
["Wiki_Avatar_Fetter_CvChinese"] = "汉语 CV",
|
|
||||||
["Wiki_Avatar_Fetter_CvJapanese"] = "日语 CV",
|
|
||||||
["Wiki_Avatar_Fetter_CvEnglish"] = "英语 CV",
|
|
||||||
["Wiki_Avatar_Fetter_CvKorean"] = "韩语 CV",
|
|
||||||
["Wiki_Avatar_Subtitle_Skill"] = "天赋",
|
|
||||||
["Wiki_Avatar_Subtitle_Talent"] = "命之座",
|
|
||||||
["Wiki_Avatar_Subtitle_Other"] = "其他",
|
|
||||||
["Wiki_Avatar_Expander_Costumes"] = "衣装",
|
|
||||||
["Wiki_Avatar_Expander_Fetters"] = "资料",
|
|
||||||
["Wiki_Avatar_Expander_FetterStories"] = "故事",
|
|
||||||
|
|
||||||
["DescParamComboBox_Level"] = "等级",
|
|
||||||
|
|
||||||
["GachaLog_Refresh"] = "刷新",
|
|
||||||
["GachaLog_Refresh_WebCache"] = "从缓存刷新",
|
|
||||||
["GachaLog_Refresh_ManualInput"] = "手动输入Url",
|
|
||||||
["GachaLog_Refresh_Aggressive"] = "全量刷新",
|
|
||||||
["GachaLog_Import"] = "导入",
|
|
||||||
["GachaLog_Import_UIGFJ"] = "从 UIGF Json 文件导入",
|
|
||||||
["GachaLog_Import_UIGFW"] = "从 UIGF Excel 文件导入",
|
|
||||||
["GachaLog_Export"] = "导出",
|
|
||||||
["GachaLog_Export_UIGFJ"] = "导出到 UIGF Json 文件",
|
|
||||||
["GachaLog_Export_UIGFW"] = "导出到 UIGF Excel 文件",
|
|
||||||
["GachaLog_PivotItem_Summary"] = "总览",
|
|
||||||
["GachaLog_PivotItem_History"] = "历史",
|
|
||||||
["GachaLog_PivotItem_Avatar"] = "角色",
|
|
||||||
["GachaLog_PivotItem_Weapon"] = "武器",
|
|
||||||
|
|
||||||
["StatisticsCard_Guarantee"] = "保底",
|
|
||||||
["StatisticsCard_Up"] = "保底",
|
|
||||||
["StatisticsCard_Pull"] = "抽",
|
|
||||||
["StatisticsCard_Orange"] = "五星",
|
|
||||||
["StatisticsCard_Purple"] = "四星",
|
|
||||||
["StatisticsCard_Blue"] = "三星",
|
|
||||||
["StatisticsCard_OrangeAverage"] = "五星平均抽数",
|
|
||||||
["StatisticsCard_UpOrangeAverage"] = "UP 平均抽数",
|
|
||||||
|
|
||||||
["Setting_Group_AboutHutao"] = "关于 胡桃",
|
|
||||||
["Setting_HutaoIcon_Description_Part1"] = "胡桃 图标由 ",
|
|
||||||
["Setting_HutaoIcon_Description_Part2"] = "纸绘,并由 ",
|
|
||||||
["Setting_HutaoIcon_Description_Part3"] = " 后期处理后,授权使用。",
|
|
||||||
["Setting_Feedback_Header"] = "反馈",
|
|
||||||
["Setting_Feedback_Description"] = "只处理在 Github 上反馈的问题",
|
|
||||||
["Setting_Feedback_Hyperlink"] = "只处理在 Github 上反馈的问题",
|
|
||||||
["Setting_UpdateCheck_Header"] = "检查更新",
|
|
||||||
["Setting_UpdateCheck_Description"] = "根本没有检查更新选项",
|
|
||||||
["Setting_UpdateCheck_Info"] = "都说了没有了",
|
|
||||||
["Setting_Group_Experimental"] = "测试功能",
|
|
||||||
["Setting_DataFolder_Header"] = "打开 数据 文件夹",
|
|
||||||
["Setting_DataFolder_Description"] = "用户数据/日志/元数据在此处存放",
|
|
||||||
["Setting_DataFolder_Action"] = "打开",
|
|
||||||
["Setting_CacheFolder_Header"] = "打开 缓存 文件夹",
|
|
||||||
["Setting_CacheFolder_Description"] = "图片缓存在此处存放",
|
|
||||||
["Setting_CacheFolder_Action"] = "打开",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public string this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (translations.TryGetValue(key, out string? result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
x:Class="Snap.Hutao.MainWindow"
|
x:Class="Snap.Hutao.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Controls"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:shv="using:Snap.Hutao.View"
|
xmlns:shv="using:Snap.Hutao.View"
|
||||||
@@ -13,6 +14,19 @@
|
|||||||
Height="44"
|
Height="44"
|
||||||
Margin="48,0,0,0"/>
|
Margin="48,0,0,0"/>
|
||||||
|
|
||||||
<shv:MainView/>
|
<cwuc:SwitchPresenter x:Name="ContentSwitchPresenter">
|
||||||
|
<cwuc:Case>
|
||||||
|
<cwuc:Case.Value>
|
||||||
|
<x:Boolean>False</x:Boolean>
|
||||||
|
</cwuc:Case.Value>
|
||||||
|
<shv:MainView/>
|
||||||
|
</cwuc:Case>
|
||||||
|
<cwuc:Case>
|
||||||
|
<cwuc:Case.Value>
|
||||||
|
<x:Boolean>True</x:Boolean>
|
||||||
|
</cwuc:Case.Value>
|
||||||
|
<shv:WelcomeView/>
|
||||||
|
</cwuc:Case>
|
||||||
|
</cwuc:SwitchPresenter>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Core.Windowing;
|
using Snap.Hutao.Core.Windowing;
|
||||||
|
using Snap.Hutao.Message;
|
||||||
using Windows.Graphics;
|
using Windows.Graphics;
|
||||||
using Windows.Win32.UI.WindowsAndMessaging;
|
using Windows.Win32.UI.WindowsAndMessaging;
|
||||||
|
|
||||||
@@ -13,7 +16,7 @@ namespace Snap.Hutao;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Injection(InjectAs.Singleton)]
|
[Injection(InjectAs.Singleton)]
|
||||||
[SuppressMessage("", "CA1001")]
|
[SuppressMessage("", "CA1001")]
|
||||||
public sealed partial class MainWindow : Window, IExtendedWindowSource
|
public sealed partial class MainWindow : Window, IExtendedWindowSource, IRecipient<WelcomeStateCompleteMessage>
|
||||||
{
|
{
|
||||||
private const int MinWidth = 848;
|
private const int MinWidth = 848;
|
||||||
private const int MinHeight = 524;
|
private const int MinHeight = 524;
|
||||||
@@ -27,6 +30,14 @@ public sealed partial class MainWindow : Window, IExtendedWindowSource
|
|||||||
ExtendedWindow<MainWindow>.Initialize(this);
|
ExtendedWindow<MainWindow>.Initialize(this);
|
||||||
IsPresent = true;
|
IsPresent = true;
|
||||||
Closed += (s, e) => IsPresent = false;
|
Closed += (s, e) => IsPresent = false;
|
||||||
|
|
||||||
|
Ioc.Default.GetRequiredService<IMessenger>().Register(this);
|
||||||
|
|
||||||
|
// Query the StaticResourceV1Contract & StaticResourceV2Contract.
|
||||||
|
// If not complete we should present the welcome view.
|
||||||
|
ContentSwitchPresenter.Value =
|
||||||
|
!LocalSetting.Get(SettingKeys.StaticResourceV1Contract, false)
|
||||||
|
|| (!LocalSetting.Get(SettingKeys.StaticResourceV2Contract, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -49,4 +60,10 @@ public sealed partial class MainWindow : Window, IExtendedWindowSource
|
|||||||
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
|
pInfo->ptMinTrackSize.X = (int)Math.Max(MinWidth * scalingFactor, pInfo->ptMinTrackSize.X);
|
||||||
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
|
pInfo->ptMinTrackSize.Y = (int)Math.Max(MinHeight * scalingFactor, pInfo->ptMinTrackSize.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Receive(WelcomeStateCompleteMessage message)
|
||||||
|
{
|
||||||
|
ContentSwitchPresenter.Value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ namespace Snap.Hutao.Message;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用户切换消息
|
/// 用户切换消息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
|
|
||||||
internal class UserChangedMessage : ValueChangedMessage<User>
|
internal class UserChangedMessage : ValueChangedMessage<User>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,6 @@ namespace Snap.Hutao.Message;
|
|||||||
/// 值变化消息
|
/// 值变化消息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TValue">值的类型</typeparam>
|
/// <typeparam name="TValue">值的类型</typeparam>
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
|
|
||||||
internal abstract class ValueChangedMessage<TValue>
|
internal abstract class ValueChangedMessage<TValue>
|
||||||
where TValue : class
|
where TValue : class
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Message;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 欢迎状态完成消息
|
||||||
|
/// </summary>
|
||||||
|
public class WelcomeStateCompleteMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
537
src/Snap.Hutao/Snap.Hutao/Migrations/20221231104727_SpiralAbyssEntry.Designer.cs
generated
Normal file
537
src/Snap.Hutao/Snap.Hutao/Migrations/20221231104727_SpiralAbyssEntry.Designer.cs
generated
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20221231104727_SpiralAbyssEntry")]
|
||||||
|
partial class SpiralAbyssEntry
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.1");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("ArchiveId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Current")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Time")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ArchiveId");
|
||||||
|
|
||||||
|
b.ToTable("achievements");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSelected")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("achievement_archives");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Info")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("avatar_infos");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProjectId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
|
b.ToTable("cultivate_entries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("EntryId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsFinished")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("EntryId");
|
||||||
|
|
||||||
|
b.ToTable("cultivate_items");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateProject", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("AttachedUid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSelected")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("cultivate_projects");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DailyNote")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("DailyTaskNotify")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ExpeditionNotify")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("HomeCoinNotifyThreshold")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ResinNotifySuppressed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ResinNotifyThreshold")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowInHomeWidget")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("TransformerNotify")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("TransformerNotifySuppressed")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("daily_notes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSelected")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("gacha_archives");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid>("ArchiveId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("GachaType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("QueryType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Time")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ArchiveId");
|
||||||
|
|
||||||
|
b.ToTable("gacha_items");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("AttachUid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MihoyoSDK")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("game_accounts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<uint>("Count")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProjectId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
|
b.ToTable("inventory_items");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("AppendPropIdList")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Level")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MainPropId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProjectId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
|
b.ToTable("inventory_reliquaries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ItemId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Level")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("ProjectId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("PromoteLevel")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ProjectId");
|
||||||
|
|
||||||
|
b.ToTable("inventory_weapons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.ObjectCacheEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("ExpireTime")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Key");
|
||||||
|
|
||||||
|
b.ToTable("object_cache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Key");
|
||||||
|
|
||||||
|
b.ToTable("settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ScheduleId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SpiralAbyss")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("spiral_abysses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Aid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CookieToken")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSelected")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Ltoken")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Mid")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Stoken")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ArchiveId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Archive");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProjectId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Project");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.CultivateItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.CultivateEntry", "Entry")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EntryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Entry");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive")
|
||||||
|
.WithMany("Items")
|
||||||
|
.HasForeignKey("ArchiveId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Archive");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProjectId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Project");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryReliquary", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProjectId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Project");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.InventoryWeapon", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Snap.Hutao.Model.Entity.CultivateProject", "Project")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProjectId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Project");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Items");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class SpiralAbyssEntry : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "spiral_abysses",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
InnerId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
ScheduleId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Uid = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
SpiralAbyss = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_spiral_abysses", x => x.InnerId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "spiral_abysses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using System;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
@@ -385,6 +385,28 @@ namespace Snap.Hutao.Migrations
|
|||||||
b.ToTable("settings");
|
b.ToTable("settings");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.SpiralAbyssEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("InnerId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ScheduleId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SpiralAbyss")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Uid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("InnerId");
|
||||||
|
|
||||||
|
b.ToTable("spiral_abysses");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("InnerId")
|
b.Property<Guid>("InnerId")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Snap.Hutao.Context.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Intrinsic;
|
||||||
|
using Snap.Hutao.Model.Primitive;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色
|
||||||
|
/// </summary>
|
||||||
|
public class Avatar
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="avatarId">角色Id</param>
|
||||||
|
/// <param name="idAvatarMap">Id角色映射</param>
|
||||||
|
public Avatar(AvatarId avatarId, Dictionary<AvatarId, Metadata.Avatar.Avatar> idAvatarMap)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLineIf(!idAvatarMap.ContainsKey(avatarId), avatarId.Value);
|
||||||
|
Metadata.Avatar.Avatar metaAvatar = idAvatarMap[avatarId];
|
||||||
|
Name = metaAvatar.Name;
|
||||||
|
Icon = Metadata.Converter.AvatarIconConverter.IconNameToUri(metaAvatar.Icon);
|
||||||
|
SideIcon = Metadata.Converter.AvatarIconConverter.IconNameToUri(metaAvatar.SideIcon);
|
||||||
|
Quality = metaAvatar.Quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图标
|
||||||
|
/// </summary>
|
||||||
|
public Uri Icon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 侧面图标
|
||||||
|
/// </summary>
|
||||||
|
public Uri SideIcon { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 星级
|
||||||
|
/// </summary>
|
||||||
|
public ItemQuality Quality { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Primitive;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上下半视图
|
||||||
|
/// </summary>
|
||||||
|
public class BattleView
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的上下半视图
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="battle">战斗</param>
|
||||||
|
/// <param name="idAvatarMap">Id角色映射</param>
|
||||||
|
public BattleView(Battle battle, Dictionary<AvatarId, Metadata.Avatar.Avatar> idAvatarMap)
|
||||||
|
{
|
||||||
|
Time = DateTimeOffset.FromUnixTimeSeconds(battle.Timestamp).ToLocalTime().ToString("yyyy.MM.dd HH:mm:ss");
|
||||||
|
Avatars = battle.Avatars.Select(a => new Avatar(a.Id, idAvatarMap)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间
|
||||||
|
/// </summary>
|
||||||
|
public string Time { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 角色
|
||||||
|
/// </summary>
|
||||||
|
public List<Avatar> Avatars { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Primitive;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层视图
|
||||||
|
/// </summary>
|
||||||
|
public class FloorView
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的层视图
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="floor">层</param>
|
||||||
|
/// <param name="idAvatarMap">Id角色映射</param>
|
||||||
|
public FloorView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.Floor floor, Dictionary<AvatarId, Metadata.Avatar.Avatar> idAvatarMap)
|
||||||
|
{
|
||||||
|
Index = $"第 {floor.Index} 层";
|
||||||
|
SettleTime = DateTimeOffset.FromUnixTimeSeconds(floor.SettleTime).ToLocalTime().ToString("yyyy.MM.dd HH:mm:ss");
|
||||||
|
Star = floor.Star;
|
||||||
|
Levels = floor.Levels.Select(l => new LevelView(l, idAvatarMap)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层号
|
||||||
|
/// </summary>
|
||||||
|
public string Index { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 时间
|
||||||
|
/// </summary>
|
||||||
|
public string SettleTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 星数
|
||||||
|
/// </summary>
|
||||||
|
public int Star { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 间信息
|
||||||
|
/// </summary>
|
||||||
|
public List<LevelView> Levels { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Primitive;
|
||||||
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 间视图
|
||||||
|
/// </summary>
|
||||||
|
public class LevelView
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的间视图
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level">间</param>
|
||||||
|
/// <param name="idAvatarMap">Id角色映射</param>
|
||||||
|
public LevelView(Level level, Dictionary<AvatarId, Metadata.Avatar.Avatar> idAvatarMap)
|
||||||
|
{
|
||||||
|
Index = $"第 {level.Index} 间";
|
||||||
|
Star = level.Star;
|
||||||
|
Battles = level.Battles.Select(b => new BattleView(b, idAvatarMap)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 间号
|
||||||
|
/// </summary>
|
||||||
|
public string Index { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 星数
|
||||||
|
/// </summary>
|
||||||
|
public int Star { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上下半
|
||||||
|
/// </summary>
|
||||||
|
public List<BattleView> Battles { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Primitive;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排行角色
|
||||||
|
/// </summary>
|
||||||
|
public class RankAvatar : Avatar
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
/// <param name="avatarId">角色Id</param>
|
||||||
|
/// <param name="idAvatarMap">Id角色映射</param>
|
||||||
|
public RankAvatar(int value, AvatarId avatarId, Dictionary<AvatarId, Metadata.Avatar.Avatar> idAvatarMap)
|
||||||
|
: base(avatarId, idAvatarMap)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Value { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Model.Primitive;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Binding.SpiralAbyss;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深渊视图
|
||||||
|
/// </summary>
|
||||||
|
public class SpiralAbyssView
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 构造一个新的深渊视图
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="spiralAbyss">深渊信息</param>
|
||||||
|
/// <param name="idAvatarMap">Id角色映射</param>
|
||||||
|
public SpiralAbyssView(Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss spiralAbyss, Dictionary<AvatarId, Metadata.Avatar.Avatar> idAvatarMap)
|
||||||
|
{
|
||||||
|
Schedule = $"第 {spiralAbyss.ScheduleId} 期";
|
||||||
|
TotalBattleTimes = spiralAbyss.TotalBattleTimes;
|
||||||
|
TotalStar = spiralAbyss.TotalStar;
|
||||||
|
MaxFloor = spiralAbyss.MaxFloor;
|
||||||
|
Reveals = spiralAbyss.RevealRank.Select(r => new RankAvatar(r.Value, r.AvatarId, idAvatarMap)).ToList();
|
||||||
|
Defeat = spiralAbyss.DefeatRank.Select(r => new RankAvatar(r.Value, r.AvatarId, idAvatarMap)).SingleOrDefault();
|
||||||
|
Damage = spiralAbyss.DamageRank.Select(r => new RankAvatar(r.Value, r.AvatarId, idAvatarMap)).SingleOrDefault();
|
||||||
|
TakeDamage = spiralAbyss.TakeDamageRank.Select(r => new RankAvatar(r.Value, r.AvatarId, idAvatarMap)).SingleOrDefault();
|
||||||
|
NormalSkill = spiralAbyss.NormalSkillRank.Select(r => new RankAvatar(r.Value, r.AvatarId, idAvatarMap)).SingleOrDefault();
|
||||||
|
EnergySkill = spiralAbyss.EnergySkillRank.Select(r => new RankAvatar(r.Value, r.AvatarId, idAvatarMap)).SingleOrDefault();
|
||||||
|
Floors = spiralAbyss.Floors.Select(f => new FloorView(f, idAvatarMap)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 期
|
||||||
|
/// </summary>
|
||||||
|
public string Schedule { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 战斗次数
|
||||||
|
/// </summary>
|
||||||
|
public int TotalBattleTimes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 共获得渊星
|
||||||
|
/// </summary>
|
||||||
|
public int TotalStar { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最深抵达
|
||||||
|
/// </summary>
|
||||||
|
public string MaxFloor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 出战次数
|
||||||
|
/// </summary>
|
||||||
|
public List<RankAvatar> Reveals { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 击破次数
|
||||||
|
/// </summary>
|
||||||
|
public RankAvatar? Defeat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 最强一击
|
||||||
|
/// </summary>
|
||||||
|
public RankAvatar? Damage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 承受伤害
|
||||||
|
/// </summary>
|
||||||
|
public RankAvatar? TakeDamage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素战技
|
||||||
|
/// </summary>
|
||||||
|
public RankAvatar? NormalSkill { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 元素爆发
|
||||||
|
/// </summary>
|
||||||
|
public RankAvatar? EnergySkill { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 层信息
|
||||||
|
/// </summary>
|
||||||
|
public List<FloorView> Floors { get; set; }
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Snap.Hutao.Extension;
|
using Snap.Hutao.Extension;
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
@@ -48,7 +49,15 @@ public class User : ObservableObject
|
|||||||
public UserGameRole? SelectedUserGameRole
|
public UserGameRole? SelectedUserGameRole
|
||||||
{
|
{
|
||||||
get => selectedUserGameRole;
|
get => selectedUserGameRole;
|
||||||
set => SetProperty(ref selectedUserGameRole, value);
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref selectedUserGameRole, value))
|
||||||
|
{
|
||||||
|
Ioc.Default
|
||||||
|
.GetRequiredService<IMessenger>()
|
||||||
|
.Send(new Message.UserChangedMessage() { OldValue = this, NewValue = this });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="EntityUser.IsSelected"/>
|
/// <inheritdoc cref="EntityUser.IsSelected"/>
|
||||||
|
|||||||
@@ -31,4 +31,32 @@ public class UserAndRole
|
|||||||
/// 角色
|
/// 角色
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UserGameRole Role { get; private set; }
|
public UserGameRole Role { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从用户与选中的角色转换
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">角色</param>
|
||||||
|
/// <returns>用户与角色</returns>
|
||||||
|
public static UserAndRole FromUser(User user)
|
||||||
|
{
|
||||||
|
return new UserAndRole(user.Entity, user.SelectedUserGameRole!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试转换到用户与角色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">用户</param>
|
||||||
|
/// <param name="userAndRole">用户与角色</param>
|
||||||
|
/// <returns>是否转换成功</returns>
|
||||||
|
public static bool TryFromUser(User? user, [NotNullWhen(true)]out UserAndRole? userAndRole)
|
||||||
|
{
|
||||||
|
if (user != null && user.SelectedUserGameRole != null)
|
||||||
|
{
|
||||||
|
userAndRole = FromUser(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
userAndRole = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -18,4 +18,4 @@ internal class DailyNoteEntryConfiguration : IEntityTypeConfiguration<DailyNoteE
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasConversion<JsonTextValueConverter<Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote>>();
|
.HasConversion<JsonTextValueConverter<Web.Hoyolab.Takumi.GameRecord.DailyNote.DailyNote>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace Snap.Hutao.Model.Entity.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深渊入口配置
|
||||||
|
/// </summary>
|
||||||
|
internal class SpiralAbyssEntryConfiguration : IEntityTypeConfiguration<SpiralAbyssEntry>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Configure(EntityTypeBuilder<SpiralAbyssEntry> builder)
|
||||||
|
{
|
||||||
|
builder.Property(e => e.SpiralAbyss)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasConversion<JsonTextValueConverter<Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Snap.Hutao.Model.Binding.User;
|
using Snap.Hutao.Model.Binding.User;
|
||||||
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
|
||||||
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
|
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
|
||||||
@@ -13,11 +14,8 @@ namespace Snap.Hutao.Model.Entity;
|
|||||||
/// 实时便笺入口
|
/// 实时便笺入口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Table("daily_notes")]
|
[Table("daily_notes")]
|
||||||
public class DailyNoteEntry : INotifyPropertyChanged
|
public class DailyNoteEntry : ObservableObject
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 内部Id
|
/// 内部Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -130,6 +128,6 @@ public class DailyNoteEntry : INotifyPropertyChanged
|
|||||||
public void UpdateDailyNote(DailyNote? dailyNote)
|
public void UpdateDailyNote(DailyNote? dailyNote)
|
||||||
{
|
{
|
||||||
DailyNote = dailyNote;
|
DailyNote = dailyNote;
|
||||||
PropertyChanged?.Invoke(this, new(nameof(DailyNote)));
|
OnPropertyChanged(nameof(DailyNote));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,9 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Snap.Hutao.Model.Entity;
|
|
||||||
using Snap.Hutao.Model.Entity.Configuration;
|
using Snap.Hutao.Model.Entity.Configuration;
|
||||||
|
|
||||||
namespace Snap.Hutao.Context.Database;
|
namespace Snap.Hutao.Model.Entity.Database;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 应用程序数据库上下文
|
/// 应用程序数据库上下文
|
||||||
@@ -117,6 +116,11 @@ public sealed class AppDbContext : DbContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public DbSet<InventoryReliquary> InventoryReliquaries { get; set; } = default!;
|
public DbSet<InventoryReliquary> InventoryReliquaries { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深渊记录
|
||||||
|
/// </summary>
|
||||||
|
public DbSet<SpiralAbyssEntry> SpiralAbysses { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个临时的应用程序数据库上下文
|
/// 构造一个临时的应用程序数据库上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -139,8 +143,9 @@ public sealed class AppDbContext : DbContext
|
|||||||
{
|
{
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.ApplyConfiguration(new AvatarInfoConfiguration())
|
.ApplyConfiguration(new AvatarInfoConfiguration())
|
||||||
.ApplyConfiguration(new UserConfiguration())
|
|
||||||
.ApplyConfiguration(new DailyNoteEntryConfiguration())
|
.ApplyConfiguration(new DailyNoteEntryConfiguration())
|
||||||
.ApplyConfiguration(new InventoryReliquaryConfiguration());
|
.ApplyConfiguration(new InventoryReliquaryConfiguration())
|
||||||
|
.ApplyConfiguration(new SpiralAbyssEntryConfiguration())
|
||||||
|
.ApplyConfiguration(new UserConfiguration());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user