Compare commits

..

2 Commits

Author SHA1 Message Date
qhy040404
3b86783493 migrate to TokenizingTextBox 2024-02-23 18:08:30 +08:00
qhy040404
e3adc2e595 finish suggestion methods 2024-02-23 14:10:09 +08:00
31 changed files with 420 additions and 464 deletions

View File

@@ -41,12 +41,14 @@ internal class ScopedPage : Page
/// 应当在 InitializeComponent() 前调用
/// </summary>
/// <typeparam name="TViewModel">视图模型类型</typeparam>
protected void InitializeWith<TViewModel>()
protected TViewModel InitializeWith<TViewModel>()
where TViewModel : class, IViewModel
{
IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService<TViewModel>();
viewModel.CancellationToken = viewCancellationTokenSource.Token;
DataContext = viewModel;
return (TViewModel)viewModel;
}
/// <inheritdoc/>

View File

@@ -5,6 +5,12 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoColorTextSyntax : MiHoYoXmlElementSyntax
{
public MiHoYoColorTextSyntax(MiHoYoColorKind colorKind, string text, int start, int end)
: base(MiHoYoSyntaxKind.ColorText, text, start, end)
{
ColorKind = colorKind;
}
public MiHoYoColorTextSyntax(MiHoYoColorKind colorKind, string text, in TextPosition position)
: base(MiHoYoSyntaxKind.ColorText, text, position)
{
@@ -39,5 +45,5 @@ internal sealed class MiHoYoColorTextSyntax : MiHoYoXmlElementSyntax
}
}
public ReadOnlySpan<char> ColorSpan { get => Text.AsSpan(ColorPosition); }
public ReadOnlySpan<char> ColorSpan { get => Text.AsSpan()[ColorPosition.Start..ColorPosition.End]; }
}

View File

@@ -1,16 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoInterpolatedContentTextSyntax : MiHoYoInterpolatedTextSyntax
{
public MiHoYoInterpolatedContentTextSyntax(MiHoYoInterpolationKind interpolationKind, string text, in TextPosition position)
: base(interpolationKind, text, position)
{
}
public TextPosition ContentPosition { get; }
public ReadOnlySpan<char> ContentSpan => Text.AsSpan(ContentPosition);
}

View File

@@ -1,32 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal class MiHoYoInterpolatedTextSyntax : MiHoYoSyntaxNode
{
public MiHoYoInterpolatedTextSyntax(MiHoYoInterpolationKind interpolationKind, string text, in TextPosition position)
: base(MiHoYoSyntaxKind.InterpolatedText, text, position)
{
InterpolationKind = interpolationKind;
}
public MiHoYoInterpolationKind InterpolationKind { get; }
}
internal enum MiHoYoInterpolationKind
{
None,
Female,
LayoutMobile,
LayoutPC,
LayoutPS,
Male,
MateAvatarSexProInfoMaleFemalePronounBoyGirlD,
MateAvatarSexProInfoMaleFemalePronounBoyGirlFirst,
Nickname,
NonBreakSpace,
PlayerAvatarSexProInfoMaleFemalePronounHeShe,
RealNameId1,
RealNameId1HostOnlyTrue,
}

View File

@@ -5,6 +5,11 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoItalicTextSyntax : MiHoYoXmlElementSyntax
{
public MiHoYoItalicTextSyntax(string text, int start, int end)
: base(MiHoYoSyntaxKind.ItalicText, text, start, end)
{
}
public MiHoYoItalicTextSyntax(string text, in TextPosition position)
: base(MiHoYoSyntaxKind.ItalicText, text, position)
{

View File

@@ -5,6 +5,11 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal sealed class MiHoYoPlainTextSyntax : MiHoYoSyntaxNode
{
public MiHoYoPlainTextSyntax(string text, int start, int end)
: base(MiHoYoSyntaxKind.PlainText, text, start, end)
{
}
public MiHoYoPlainTextSyntax(string text, in TextPosition position)
: base(MiHoYoSyntaxKind.PlainText, text, position)
{

View File

@@ -9,8 +9,4 @@ internal sealed class MiHoYoRootSyntax : MiHoYoSyntaxNode
: base(MiHoYoSyntaxKind.Root, text, start, end)
{
}
public bool IsInterpolated { get => Text.AsSpan()[0] is '#'; }
public TextPosition ContentPosition { get => IsInterpolated ? new(Position.Start + 1, Position.End) : Position; }
}

View File

@@ -8,7 +8,6 @@ internal enum MiHoYoSyntaxKind
None,
Root,
PlainText,
InterpolatedText,
ColorText,
ItalicText,
}

View File

@@ -7,6 +7,8 @@ internal sealed class MiHoYoSyntaxTree
{
public MiHoYoSyntaxNode Root { get; set; } = default!;
public string Text { get; set; } = default!;
public static MiHoYoSyntaxTree Parse(string text)
{
MiHoYoRootSyntax root = new(text, 0, text.Length);
@@ -14,6 +16,7 @@ internal sealed class MiHoYoSyntaxTree
MiHoYoSyntaxTree tree = new()
{
Text = text,
Root = root,
};
@@ -24,50 +27,43 @@ internal sealed class MiHoYoSyntaxTree
{
TextPosition contentPosition = syntax switch
{
MiHoYoRootSyntax rootSyntax => rootSyntax.ContentPosition,
MiHoYoXmlElementSyntax xmlSyntax => xmlSyntax.ContentPosition,
_ => syntax.Position,
};
ReadOnlySpan<char> contentSpan = text.AsSpan(contentPosition);
ReadOnlySpan<char> contentSpan = text.AsSpan().Slice(contentPosition.Start, contentPosition.Length);
int endOfProcessedAtContent = 0;
while (true)
{
if (endOfProcessedAtContent >= contentSpan.Length)
{
return;
break;
}
// <tag>some text</tag>
// ↑ ↑ ↑ ↑
// | | | right closing
// | | right opening
// | left closing
// left opening
int indexOfXmlLeftOpeningAtUnprocessedContent = contentSpan[endOfProcessedAtContent..].IndexOf('<');
// End of content
if (indexOfXmlLeftOpeningAtUnprocessedContent < 0)
{
TextPosition positionAtText = new(contentPosition.Start + endOfProcessedAtContent, contentPosition.End);
ParseInterpolatedOrPlainText(text, syntax, positionAtText);
return;
TextPosition position = new(contentPosition.Start + endOfProcessedAtContent, contentPosition.End);
MiHoYoPlainTextSyntax plainText = new(text, position);
syntax.Children.Add(plainText);
break;
}
// Text between xml elements
// We have plain text between xml elements
if (indexOfXmlLeftOpeningAtUnprocessedContent > 0)
{
TextPosition position = new(0, indexOfXmlLeftOpeningAtUnprocessedContent);
TextPosition positionAtContent = position >> endOfProcessedAtContent;
TextPosition positionAtText = positionAtContent >> contentPosition.Start;
ParseInterpolatedOrPlainText(text, syntax, positionAtText);
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
syntax.Children.Add(plainText);
endOfProcessedAtContent = positionAtContent.End;
continue;
}
// Peek the next character after left opening
// Peek the next character after '<'
int indexOfXmlLeftOpeningAtContent = endOfProcessedAtContent + indexOfXmlLeftOpeningAtUnprocessedContent;
switch (contentSpan[indexOfXmlLeftOpeningAtContent + 1])
{
@@ -83,8 +79,8 @@ internal sealed class MiHoYoSyntaxTree
};
TextPosition position = new(0, endOfXmlColorRightClosingAtUnprocessedContent);
TextPosition positionAtContent = position >> endOfProcessedAtContent;
TextPosition positionAtText = positionAtContent >> contentPosition.Start;
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
MiHoYoColorTextSyntax colorText = new(colorKind, text, positionAtText);
ParseComponents(text, colorText);
@@ -98,8 +94,8 @@ internal sealed class MiHoYoSyntaxTree
int endOfXmlItalicRightClosingAtUnprocessedContent = EndOfXmlClosing(contentSpan[indexOfXmlLeftOpeningAtContent..], out _);
TextPosition position = new(0, endOfXmlItalicRightClosingAtUnprocessedContent);
TextPosition positionAtContent = position >> endOfProcessedAtContent;
TextPosition positionAtText = positionAtContent >> contentPosition.Start;
TextPosition positionAtContent = position.RightShift(endOfProcessedAtContent);
TextPosition positionAtText = positionAtContent.RightShift(contentPosition.Start);
MiHoYoItalicTextSyntax italicText = new(text, positionAtText);
ParseComponents(text, italicText);
@@ -111,174 +107,6 @@ internal sealed class MiHoYoSyntaxTree
}
}
private static void ParseInterpolatedOrPlainText(string text, MiHoYoSyntaxNode syntax, in TextPosition spanPosition)
{
if (text.AsSpan()[0] is not '#')
{
MiHoYoPlainTextSyntax plainText = new(text, spanPosition);
syntax.Children.Add(plainText);
return;
}
ReadOnlySpan<char> span = text.AsSpan(spanPosition);
int endOfProcessedAtSpan = 0;
while (true)
{
if (endOfProcessedAtSpan >= span.Length)
{
return;
}
int indexOfOpeningAtUnprocessedSpan = span[endOfProcessedAtSpan..].IndexOf('{');
// End of content
if (indexOfOpeningAtUnprocessedSpan < 0)
{
TextPosition positionAtText = new(spanPosition.Start + endOfProcessedAtSpan, spanPosition.End);
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
syntax.Children.Add(plainText);
break;
}
// Text between interpolation
if (indexOfOpeningAtUnprocessedSpan > 0)
{
TextPosition position = new(0, indexOfOpeningAtUnprocessedSpan);
TextPosition positionAtSpan = position >> endOfProcessedAtSpan;
TextPosition positionAtText = positionAtSpan >> spanPosition.Start;
MiHoYoPlainTextSyntax plainText = new(text, positionAtText);
syntax.Children.Add(plainText);
endOfProcessedAtSpan = positionAtSpan.End;
continue;
}
// Peek the next character after left opening
int indexOfOpeningAtSpan = endOfProcessedAtSpan + indexOfOpeningAtUnprocessedSpan;
int endOfClosingAtUnprocessedSpan = span[indexOfOpeningAtSpan..].IndexOf('}') + 1;
TextPosition position2 = new(0, endOfClosingAtUnprocessedSpan);
TextPosition positionAtSpan2 = position2 >> endOfProcessedAtSpan;
TextPosition positionAtText2 = positionAtSpan2 >> spanPosition.Start;
switch (span[indexOfOpeningAtSpan + 1])
{
// {F#(.*?)}
case 'F':
{
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(MiHoYoInterpolationKind.Female, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {LAYOUT_MOBILE#(.+?)}
// {LAYOUT_PC#(.+?)}
// {LAYOUT_PS#(.+?)}
case 'L':
{
MiHoYoInterpolationKind kind = span[indexOfOpeningAtSpan + 9] switch
{
'O' => MiHoYoInterpolationKind.LayoutMobile,
'P' => MiHoYoInterpolationKind.LayoutPC,
'S' => MiHoYoInterpolationKind.LayoutPS,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {MATEAVATAR#SEXPRO[INFO_MALE_PRONOUN_BOYD|INFO_FEMALE_PRONOUN_GIRLD]}
// {MATEAVATAR#SEXPRO[INFO_MALE_PRONOUN_BOYFIRST|INFO_FEMALE_PRONOUN_GIRLFIRST]}
// {M#(.*?)}
case 'M':
{
switch (span[indexOfOpeningAtSpan + 2])
{
case 'A':
{
MiHoYoInterpolationKind kind = endOfClosingAtUnprocessedSpan switch
{
69 => MiHoYoInterpolationKind.MateAvatarSexProInfoMaleFemalePronounBoyGirlD,
77 => MiHoYoInterpolationKind.MateAvatarSexProInfoMaleFemalePronounBoyGirlFirst,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedTextSyntax textSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(textSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
case '#':
{
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(MiHoYoInterpolationKind.Male, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
default:
throw Must.NeverHappen($"Unexpected span content {span}");
}
break;
}
// {NICKNAME}
// {NON_BREAK_SPACE}
case 'N':
{
MiHoYoInterpolationKind kind = span[indexOfOpeningAtSpan + 2] switch
{
'I' => MiHoYoInterpolationKind.Nickname,
'O' => MiHoYoInterpolationKind.NonBreakSpace,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedTextSyntax textSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(textSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {PLAYERAVATAR#SEXPRO[INFO_MALE_PRONOUN_HE|INFO_FEMALE_PRONOUN_SHE]}
case 'P':
{
MiHoYoInterpolatedContentTextSyntax contentTextSyntax = new(MiHoYoInterpolationKind.PlayerAvatarSexProInfoMaleFemalePronounHeShe, text, positionAtText2);
syntax.Children.Add(contentTextSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
// {REALNAME[ID(1)|HOSTONLY(true)]}
// {REALNAME[ID(1)]}
case 'R':
{
MiHoYoInterpolationKind kind = endOfClosingAtUnprocessedSpan switch
{
32 => MiHoYoInterpolationKind.RealNameId1HostOnlyTrue,
17 => MiHoYoInterpolationKind.RealNameId1,
_ => throw Must.NeverHappen($"Unexpected span content {span}"),
};
MiHoYoInterpolatedTextSyntax textSyntax = new(kind, text, positionAtText2);
syntax.Children.Add(textSyntax);
endOfProcessedAtSpan += endOfClosingAtUnprocessedSpan;
break;
}
default:
throw Must.NeverHappen($"Unexpected span content {span}");
}
}
}
private static int EndOfXmlClosing(in ReadOnlySpan<char> span, out int endOfLeftClosing)
{
endOfLeftClosing = 0;

View File

@@ -5,6 +5,11 @@ namespace Snap.Hutao.Control.Text.Syntax.MiHoYo;
internal abstract class MiHoYoXmlElementSyntax : MiHoYoSyntaxNode
{
public MiHoYoXmlElementSyntax(MiHoYoSyntaxKind kind, string text, int start, int end)
: base(kind, text, start, end)
{
}
public MiHoYoXmlElementSyntax(MiHoYoSyntaxKind kind, string text, in TextPosition position)
: base(kind, text, position)
{

View File

@@ -22,13 +22,13 @@ internal readonly struct TextPosition
get => End - Start;
}
public static TextPosition operator <<(TextPosition position, int offset)
public TextPosition LeftShift(int offset)
{
return new(position.Start - offset, position.End - offset);
return new(Start - offset, End - offset);
}
public static TextPosition operator >>(TextPosition position, int offset)
public TextPosition RightShift(int offset)
{
return new(position.Start + offset, position.End + offset);
return new(Start + offset, End + offset);
}
}

View File

@@ -1,12 +0,0 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Control.Text.Syntax;
internal static class TextPositionExtension
{
public static ReadOnlySpan<char> AsSpan(this string? text, TextPosition position)
{
return text.AsSpan(position.Start, position.Length);
}
}

View File

@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Snap.Hutao.Win32.Foundation;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WinRT;
@@ -34,7 +33,7 @@ public static partial class Program
}
[LibraryImport("Microsoft.WindowsAppRuntime.dll", EntryPoint = "WindowsAppRuntime_EnsureIsLoaded")]
private static partial HRESULT WindowsAppRuntimeEnsureIsLoaded();
private static partial int WindowsAppRuntimeEnsureIsLoaded();
[LibraryImport("Microsoft.ui.xaml.dll")]
private static partial void XamlCheckProcessRequirements();

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -198,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Select Account to Launch</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;Prince&lt;/color&gt;/&lt;color=#FFB6C1&gt;Princess&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;I&lt;/color&gt;/&lt;color=#FFB6C1&gt;I&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -198,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Pilih akun untuk memulai</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -198,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>アカウントを選択して開始します</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -198,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>계정 선택 및 시작</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -198,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Selecionar conta para iniciar</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -201,10 +201,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>选择账号并启动</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -198,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>Выберите учетную запись для запуска</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;Принц&lt;/color&gt;/&lt;color=#FFB6C1&gt;Принцесса&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;I&lt;/color&gt;/&lt;color=#FFB6C1&gt;I&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -60,45 +60,45 @@
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -198,10 +198,10 @@
<data name="LaunchGameTitle" xml:space="preserve">
<value>選擇帳號並啟動</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlD" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;王子&lt;/color&gt;/&lt;color=#FFB6C1&gt;公主&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameMateAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<data name="MetadataSpecialNameMetaAvatarSexProInfoPronounBoyGirlFirst" xml:space="preserve">
<value>&lt;color=#1E90FF&gt;我&lt;/color&gt;/&lt;color=#FFB6C1&gt;我&lt;/color&gt;</value>
</data>
<data name="MetadataSpecialNameNickname" xml:space="preserve">

View File

@@ -298,6 +298,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.0.240109" />
<PackageReference Include="CommunityToolkit.WinUI.Notifications" Version="7.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />

View File

@@ -7,7 +7,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
xmlns:shc="using:Snap.Hutao.Control"
xmlns:shcb="using:Snap.Hutao.Control.Behavior"
xmlns:shci="using:Snap.Hutao.Control.Image"
@@ -73,6 +72,10 @@
</shct:DescriptionTextBlock>
</DataTemplate>
<DataTemplate x:Key="SuggestionTemplate">
<TextBlock VerticalAlignment="Center" Text="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="AvatarListTemplate">
<Grid>
<Grid.ColumnDefinitions>
@@ -265,22 +268,20 @@
</CommandBar.Content>
<!--<AppBarButton Icon="{shcm:FontIcon Glyph=&#xE946;}" Label="搜索提示"/>-->
<AppBarElementContainer>
<AutoSuggestBox
Width="240"
Height="36"
Margin="6,6,6,0"
<cwc:TokenizingTextBox
x:Name="AvatarSuggestBox"
Width="520"
Margin="6,0,6,0"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
ItemsSource="{Binding FilterTokens, Mode=TwoWay}"
MaximumTokens="5"
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiAvatarAutoSuggestBoxPlaceHolder}"
QueryIcon="{shcm:FontIcon Glyph=&#xE721;}"
Style="{StaticResource DefaultAutoSuggestBoxStyle}"
Text="{Binding FilterText, Mode=TwoWay}">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="QuerySubmitted">
<mxic:InvokeCommandAction Command="{Binding FilterCommand}" CommandParameter="{Binding FilterText}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</AutoSuggestBox>
QueryIcon="{cw:FontIconSource Glyph=&#xE721;}"
SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
SuggestedItemsSource="{Binding AvailableQueries}"
Text="{Binding FilterToken, Mode=TwoWay}"
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
</AppBarElementContainer>
<AppBarButton
Command="{Binding CultivateCommand}"

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control;
using Snap.Hutao.ViewModel.Wiki;
@@ -17,7 +19,19 @@ internal sealed partial class WikiAvatarPage : ScopedPage
/// </summary>
public WikiAvatarPage()
{
InitializeWith<WikiAvatarViewModel>();
WikiAvatarViewModel viewModel = InitializeWith<WikiAvatarViewModel>();
InitializeComponent();
viewModel.Initialize(new TokenizingTextBoxAccessor(AvatarSuggestBox));
}
private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
{
public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
{
TokenizingTextBox = tokenizingTextBox;
}
public TokenizingTextBox TokenizingTextBox { get; private set; }
}
}

View File

@@ -32,6 +32,10 @@
Source="{Binding Converter={StaticResource PropertyDescriptor}}"/>
</DataTemplate>
<DataTemplate x:Key="SuggestionTemplate">
<TextBlock VerticalAlignment="Center" Text="{Binding}"/>
</DataTemplate>
<DataTemplate x:Key="WeaponListTemplate">
<Grid>
<Grid.ColumnDefinitions>
@@ -131,21 +135,21 @@
LocalSettingKeySuffixForCurrent="WikiWeaponPage.Weapons"/>
</CommandBar.Content>
<AppBarElementContainer>
<AutoSuggestBox
Width="240"
Height="36"
Margin="16,6,6,0"
<cwc:TokenizingTextBox
x:Name="WeaponSuggestBox"
Width="520"
Margin="16,0,6,0"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
ItemTemplate="{StaticResource SuggestionTemplate}"
ItemsSource="{Binding FilterTokens, Mode=TwoWay}"
MaximumTokens="5"
PlaceholderText="{shcm:ResourceString Name=ViewPageWiKiWeaponAutoSuggestBoxPlaceHolder}"
QueryIcon="{shcm:FontIcon Glyph=&#xE721;}"
Text="{Binding FilterText, Mode=TwoWay}">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="QuerySubmitted">
<mxic:InvokeCommandAction Command="{Binding FilterCommand}" CommandParameter="{Binding FilterText}"/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
</AutoSuggestBox>
QueryIcon="{cw:FontIconSource Glyph=&#xE721;}"
SuggestedItemTemplate="{StaticResource SuggestionTemplate}"
SuggestedItemsSource="{Binding AvailableQueries}"
Text="{Binding FilterToken, Mode=TwoWay}"
TokenItemTemplate="{StaticResource SuggestionTemplate}"/>
</AppBarElementContainer>
<AppBarButton
Command="{Binding CultivateCommand}"

View File

@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Controls;
using Snap.Hutao.Control;
using Snap.Hutao.ViewModel.Wiki;
@@ -17,7 +18,19 @@ internal sealed partial class WikiWeaponPage : ScopedPage
/// </summary>
public WikiWeaponPage()
{
InitializeWith<WikiWeaponViewModel>();
WikiWeaponViewModel viewModel = InitializeWith<WikiWeaponViewModel>();
InitializeComponent();
viewModel.Initialize(new TokenizingTextBoxAccessor(WeaponSuggestBox));
}
private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
{
public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
{
TokenizingTextBox = tokenizingTextBox;
}
public TokenizingTextBox TokenizingTextBox { get; private set; }
}
}

View File

@@ -377,36 +377,31 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
[Command("SearchAchievementCommand")]
private void UpdateAchievementsFilterBySearch(string? search)
{
if (Achievements is null)
if (Achievements is not null)
{
return;
SetProperty(ref selectedAchievementGoal, null);
if (!string.IsNullOrEmpty(search))
{
if (uint.TryParse(search, out uint achievementId))
{
Achievements.Filter = view => view.Inner.Id == achievementId;
return;
}
if (VersionRegex().IsMatch(search))
{
Achievements.Filter = view => view.Inner.Version == search;
return;
}
Achievements.Filter = view =>
{
return view.Inner.Title.Contains(search, StringComparison.CurrentCultureIgnoreCase)
|| view.Inner.Description.Contains(search, StringComparison.CurrentCultureIgnoreCase);
};
}
}
SetProperty(ref selectedAchievementGoal, null, nameof(SelectedAchievementGoal));
if (string.IsNullOrEmpty(search))
{
Achievements.Filter = default;
return;
}
if (uint.TryParse(search, out uint achievementId))
{
Achievements.Filter = view => view.Inner.Id == achievementId;
return;
}
if (VersionRegex().IsMatch(search))
{
Achievements.Filter = view => view.Inner.Version == search;
return;
}
Achievements.Filter = view =>
{
return view.Inner.Title.Contains(search, StringComparison.CurrentCultureIgnoreCase)
|| view.Inner.Description.Contains(search, StringComparison.CurrentCultureIgnoreCase);
};
}
private void UpdateAchievementsFinishPercent()

View File

@@ -0,0 +1,12 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Controls;
using Snap.Hutao.Control;
namespace Snap.Hutao.ViewModel.Wiki;
internal interface ITokenizingTextBoxAccessor : IXamlElementAccessor
{
TokenizingTextBox TokenizingTextBox { get; }
}

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.ViewModel.Wiki;
internal interface IWikiViewModelInitialization
{
void Initialize(ITokenizingTextBoxAccessor accessor);
}

View File

@@ -1,11 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Collection.AdvancedCollectionView;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Item;
@@ -17,6 +20,8 @@ using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Response;
using System.Collections.Frozen;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
@@ -32,7 +37,7 @@ namespace Snap.Hutao.ViewModel.Wiki;
[HighQuality]
[ConstructorGenerated]
[Injection(InjectAs.Scoped)]
internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWikiViewModelInitialization
{
private readonly IContentDialogFactory contentDialogFactory;
private readonly ICultivationService cultivationService;
@@ -45,10 +50,12 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
private AdvancedCollectionView<Avatar>? avatars;
private Avatar? selected;
private string? filterText;
private ObservableCollection<string>? filterTokens;
private string? filterToken;
private BaseValueInfo? baseValueInfo;
private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelAvatarCurveMap;
private List<Promote>? promotes;
private FrozenSet<string> availableQueries;
/// <summary>
/// 角色列表
@@ -75,9 +82,21 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); }
/// <summary>
/// 筛选文本
/// 保存的筛选标志
/// </summary>
public string? FilterText { get => filterText; set => SetProperty(ref filterText, value); }
public ObservableCollection<string>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
public FrozenSet<string>? AvailableQueries { get => availableQueries; }
public void Initialize(ITokenizingTextBoxAccessor accessor)
{
accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
}
protected override async ValueTask<bool> InitializeUIAsync()
{
@@ -101,6 +120,18 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
await taskContext.SwitchToMainThreadAsync();
Avatars = new(list, true);
Selected = Avatars.View.ElementAtOrDefault(0);
FilterTokens = [];
availableQueries = FrozenSet.ToFrozenSet<string>(
[
.. avatars.Select(a => a.Name),
.. IntrinsicFrozen.AssociationTypes,
.. IntrinsicFrozen.BodyTypes,
.. IntrinsicFrozen.ElementNames,
.. IntrinsicFrozen.ItemQualities,
.. IntrinsicFrozen.WeaponTypes,
]);
return true;
}
@@ -202,21 +233,53 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap, avatarPromoteMap);
}
[Command("FilterCommand")]
private void ApplyFilter(string? input)
private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (Avatars is null)
{
return;
}
if (string.IsNullOrWhiteSpace(input))
if (string.IsNullOrWhiteSpace(FilterToken))
{
return;
}
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
}
}
private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion is not null)
{
return;
}
ApplyFilter();
}
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
}
private void ApplyFilter()
{
if (Avatars is null)
{
return;
}
if (FilterTokens.IsNullOrEmpty())
{
Avatars.Filter = default!;
return;
}
Avatars.Filter = AvatarFilter.Compile(input);
Avatars.Filter = AvatarFilter.Compile(string.Join(' ', FilterTokens));
if (Selected is not null && Avatars.Contains(Selected))
{

View File

@@ -1,11 +1,14 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Collection.AdvancedCollectionView;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Item;
using Snap.Hutao.Model.Metadata.Weapon;
@@ -17,6 +20,7 @@ using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Response;
using System.Collections.Frozen;
using System.Runtime.InteropServices;
using CalculateAvatarPromotionDelta = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.AvatarPromotionDelta;
using CalculateClient = Snap.Hutao.Web.Hoyolab.Takumi.Event.Calculate.CalculateClient;
@@ -29,7 +33,7 @@ namespace Snap.Hutao.ViewModel.Wiki;
/// </summary>
[ConstructorGenerated]
[Injection(InjectAs.Scoped)]
internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWikiViewModelInitialization
{
private readonly IContentDialogFactory contentDialogFactory;
private readonly CalculateClient calculateClient;
@@ -42,10 +46,12 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
private AdvancedCollectionView<Weapon>? weapons;
private Weapon? selected;
private string? filterText;
private List<string>? filterTokens;
private string? filterToken;
private BaseValueInfo? baseValueInfo;
private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelWeaponCurveMap;
private List<Promote>? promotes;
private FrozenSet<string> availableQueries;
/// <summary>
/// 角色列表
@@ -72,9 +78,21 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
public BaseValueInfo? BaseValueInfo { get => baseValueInfo; set => SetProperty(ref baseValueInfo, value); }
/// <summary>
/// 筛选文本
/// 保存的筛选标志
/// </summary>
public string? FilterText { get => filterText; set => SetProperty(ref filterText, value); }
public List<string>? FilterTokens { get => filterTokens; set => SetProperty(ref filterTokens, value); }
public string? FilterToken { get => filterToken; set => SetProperty(ref filterToken, value); }
public FrozenSet<string> AvailableQueries { get => availableQueries; }
public void Initialize(ITokenizingTextBoxAccessor accessor)
{
accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
}
/// <inheritdoc/>
protected override async Task OpenUIAsync()
@@ -98,6 +116,15 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
Weapons = new(list, true);
Selected = Weapons.View.ElementAtOrDefault(0);
FilterTokens = [];
availableQueries = FrozenSet.ToFrozenSet(
[
.. weapons.Select(w => w.Name),
.. IntrinsicFrozen.ItemQualities,
.. IntrinsicFrozen.FightProperties,
.. IntrinsicFrozen.WeaponTypes,
]);
}
}
@@ -187,21 +214,53 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
BaseValueInfo = new(weapon.MaxLevel, propertyCurveValues, levelWeaponCurveMap, weaponPromoteMap);
}
[Command("FilterCommand")]
private void ApplyFilter(string? input)
private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (Weapons is null)
{
return;
}
if (string.IsNullOrWhiteSpace(input))
if (string.IsNullOrWhiteSpace(FilterToken))
{
return;
}
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
}
}
private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion is not null)
{
return;
}
ApplyFilter();
}
private void OnTokenItemModified(TokenizingTextBox sender, object args)
{
ApplyFilter();
}
private void ApplyFilter()
{
if (Weapons is null)
{
return;
}
if (FilterTokens.IsNullOrEmpty())
{
Weapons.Filter = default!;
return;
}
Weapons.Filter = WeaponFilter.Compile(input);
Weapons.Filter = WeaponFilter.Compile(string.Join(' ', FilterTokens));
if (Selected is not null && Weapons.Contains(Selected))
{