Compare commits
43 Commits
Canary-1.2
...
cb0b781704
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb0b781704 | ||
|
|
e93672554a | ||
|
|
ddc00cf2d8 | ||
|
|
05b4bd8c61 | ||
|
|
b9beb76e9b | ||
|
|
20111a651c | ||
|
|
659e580f4d | ||
|
|
e6339ac950 | ||
|
|
6b402847d2 | ||
|
|
3b06f4cb78 | ||
|
|
cdf60eecc0 | ||
|
|
1219f329c1 | ||
|
|
53c44b2e1d | ||
|
|
d5527f87cb | ||
|
|
fc75dbc8b8 | ||
|
|
04361b864a | ||
|
|
ef5177f050 | ||
|
|
7271e7050d | ||
|
|
28bcb85c31 | ||
|
|
1ca4484148 | ||
|
|
056f56bc70 | ||
|
|
da0d2e1b70 | ||
|
|
db08498a89 | ||
|
|
7a43dcb513 | ||
|
|
f2329d0e8a | ||
|
|
3aa7ed661d | ||
|
|
6e824e44b8 | ||
|
|
a964bf8f68 | ||
|
|
89e4d287d6 | ||
|
|
f34745a66c | ||
|
|
d5b7851c9b | ||
|
|
1b7032b589 | ||
|
|
e097ea71ff | ||
|
|
299f2144c8 | ||
|
|
33e3ba9ff2 | ||
|
|
9dc36646c1 | ||
|
|
8eea75a6e8 | ||
|
|
57fbcc7aed | ||
|
|
d1c15f3562 | ||
|
|
0423fad7ff | ||
|
|
1951fe0077 | ||
|
|
7fd5a63a5d | ||
|
|
a0594e8169 |
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -12,7 +12,8 @@ env:
|
|||||||
RYUJINX_BASE_VERSION: "1.2"
|
RYUJINX_BASE_VERSION: "1.2"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
|
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Stable-Releases"
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -33,7 +34,7 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
github.rest.git.createRef({
|
github.rest.git.createRef({
|
||||||
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}",
|
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}",
|
||||||
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}",
|
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}",
|
||||||
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
|
||||||
sha: context.sha
|
sha: context.sha
|
||||||
})
|
})
|
||||||
@@ -52,7 +53,7 @@ jobs:
|
|||||||
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
@@ -92,7 +93,7 @@ jobs:
|
|||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ jobs:
|
|||||||
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
|
||||||
omitBodyDuringUpdate: true
|
omitBodyDuringUpdate: true
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
replacesArtifacts: true
|
replacesArtifacts: true
|
||||||
@@ -222,7 +223,7 @@ jobs:
|
|||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<table align="center">
|
<table align="center">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" width="25%">
|
<td align="center" width="25%">
|
||||||
<img src="https://raw.githubusercontent.com/GreemDev/ryuassets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
|
<img src="https://raw.githubusercontent.com/Ryubing/Assets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
|
||||||
</td>
|
</td>
|
||||||
<td align="center" width="75%">
|
<td align="center" width="75%">
|
||||||
|
|
||||||
# Ryujinx
|
# Ryujinx
|
||||||
|
|
||||||
[](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml)
|
[](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml)
|
||||||
[](https://github.com/Ryubing/Ryujinx/releases/latest)
|
[](https://github.com/Ryubing/Ryujinx/releases/latest)
|
||||||
<br>
|
<br>
|
||||||
[](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml)
|
[](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml)
|
||||||
[](https://github.com/Ryubing/Canary-Releases/releases/latest)
|
[](https://github.com/Ryubing/Canary-Releases/releases/latest)
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controller name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controller's Type
|
/// Controller's Type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ namespace Ryujinx.Common
|
|||||||
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
|
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
|
||||||
IsCanaryBuild
|
IsCanaryBuild
|
||||||
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
||||||
: $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}";
|
: GetChangelogForVersion(newVersion);
|
||||||
|
|
||||||
public static string GetChangelogForVersion(Version version) =>
|
public static string GetChangelogForVersion(Version version) =>
|
||||||
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}";
|
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/{version}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Classes="globalConfigMarker"/>
|
Classes="globalConfigMarker"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
</Border>
|
</Border>
|
||||||
</Design.PreviewWith>
|
</Design.PreviewWith>
|
||||||
<Style Selector="DropDownButton">
|
<Style Selector="DropDownButton">
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -269,6 +269,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Version = InputConfig.CurrentVersion,
|
Version = InputConfig.CurrentVersion,
|
||||||
Backend = InputBackendType.WindowKeyboard,
|
Backend = InputBackendType.WindowKeyboard,
|
||||||
Id = "0",
|
Id = "0",
|
||||||
|
Name = "Keyboard",
|
||||||
PlayerIndex = PlayerIndex.Player1,
|
PlayerIndex = PlayerIndex.Player1,
|
||||||
ControllerType = ControllerType.ProController,
|
ControllerType = ControllerType.ProController,
|
||||||
LeftJoycon = new LeftJoyconCommonConfig<Key>
|
LeftJoycon = new LeftJoyconCommonConfig<Key>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
public float StrongRumble { get; set; }
|
public float StrongRumble { get; set; }
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
public ControllerType ControllerType { get; set; }
|
public ControllerType ControllerType { get; set; }
|
||||||
public PlayerIndex PlayerIndex { get; set; }
|
public PlayerIndex PlayerIndex { get; set; }
|
||||||
|
|
||||||
@@ -111,6 +113,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
Id = config.Id;
|
Id = config.Id;
|
||||||
|
Name = config.Name;
|
||||||
ControllerType = config.ControllerType;
|
ControllerType = config.ControllerType;
|
||||||
PlayerIndex = config.PlayerIndex;
|
PlayerIndex = config.PlayerIndex;
|
||||||
|
|
||||||
@@ -201,6 +204,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
StandardControllerInputConfig config = new()
|
StandardControllerInputConfig config = new()
|
||||||
{
|
{
|
||||||
Id = Id,
|
Id = Id,
|
||||||
|
Name = Name,
|
||||||
Backend = InputBackendType.GamepadSDL2,
|
Backend = InputBackendType.GamepadSDL2,
|
||||||
PlayerIndex = PlayerIndex,
|
PlayerIndex = PlayerIndex,
|
||||||
ControllerType = ControllerType,
|
ControllerType = ControllerType,
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
|||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models.Input
|
namespace Ryujinx.Ava.UI.Models.Input
|
||||||
{
|
{
|
||||||
public partial class KeyboardInputConfig : BaseModel
|
public partial class KeyboardInputConfig : BaseModel
|
||||||
{
|
{
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
public ControllerType ControllerType { get; set; }
|
public ControllerType ControllerType { get; set; }
|
||||||
public PlayerIndex PlayerIndex { get; set; }
|
public PlayerIndex PlayerIndex { get; set; }
|
||||||
|
|
||||||
@@ -53,6 +55,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
Id = config.Id;
|
Id = config.Id;
|
||||||
|
Name = config.Name;
|
||||||
ControllerType = config.ControllerType;
|
ControllerType = config.ControllerType;
|
||||||
PlayerIndex = config.PlayerIndex;
|
PlayerIndex = config.PlayerIndex;
|
||||||
|
|
||||||
@@ -100,6 +103,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
StandardKeyboardInputConfig config = new()
|
StandardKeyboardInputConfig config = new()
|
||||||
{
|
{
|
||||||
Id = Id,
|
Id = Id,
|
||||||
|
Name = Name,
|
||||||
Backend = InputBackendType.WindowKeyboard,
|
Backend = InputBackendType.WindowKeyboard,
|
||||||
PlayerIndex = PlayerIndex,
|
PlayerIndex = PlayerIndex,
|
||||||
ControllerType = ControllerType,
|
ControllerType = ControllerType,
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/assets/amiibo/Amiibo.json"));
|
HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://raw.githubusercontent.com/Ryubing/Nfc/refs/heads/main/tags.json"));
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
@@ -451,7 +451,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await _httpClient.GetAsync($"https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/assets/amiibo/Amiibo.json");
|
HttpResponseMessage response = await _httpClient.GetAsync("https://raw.githubusercontent.com/Ryubing/Nfc/refs/heads/main/tags.json");
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -91,18 +91,21 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async void ShowMotionConfig()
|
public async void ShowMotionConfig()
|
||||||
{
|
{
|
||||||
await MotionInputView.Show(this);
|
await MotionInputView.Show(this);
|
||||||
|
ParentModel.IsModified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ShowRumbleConfig()
|
public async void ShowRumbleConfig()
|
||||||
{
|
{
|
||||||
await RumbleInputView.Show(this);
|
await RumbleInputView.Show(this);
|
||||||
|
ParentModel.IsModified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ShowLedConfig()
|
public async void ShowLedConfig()
|
||||||
{
|
{
|
||||||
await LedInputView.Show(this);
|
await LedInputView.Show(this);
|
||||||
|
ParentModel.IsModified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnParentModelChanged()
|
public void OnParentModelChanged()
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
private int _device;
|
private int _device;
|
||||||
private object _configViewModel;
|
private object _configViewModel;
|
||||||
[ObservableProperty] private string _profileName;
|
[ObservableProperty] private string _profileName;
|
||||||
|
[ObservableProperty] private bool _notificationIsVisible; // Automatically call the NotificationView property with OnPropertyChanged()
|
||||||
|
[ObservableProperty] private string _notificationText; // Automatically call the NotificationText property with OnPropertyChanged()
|
||||||
private bool _isLoaded;
|
private bool _isLoaded;
|
||||||
|
|
||||||
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
@@ -88,13 +90,40 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
public bool IsKeyboard => !IsController;
|
public bool IsKeyboard => !IsController;
|
||||||
public bool IsRight { get; set; }
|
public bool IsRight { get; set; }
|
||||||
public bool IsLeft { get; set; }
|
public bool IsLeft { get; set; }
|
||||||
|
public string RevertDeviceId { get; set; }
|
||||||
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
|
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
|
||||||
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
|
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
|
||||||
|
|
||||||
public bool IsModified { get; set; }
|
public bool _isChangeTrackingActive;
|
||||||
|
|
||||||
|
public bool _isModified;
|
||||||
|
|
||||||
|
public bool IsModified
|
||||||
|
{
|
||||||
|
get => _isModified;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isModified = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public event Action NotifyChangesEvent;
|
public event Action NotifyChangesEvent;
|
||||||
|
|
||||||
|
public string _profileChoose;
|
||||||
|
public string ProfileChoose
|
||||||
|
{
|
||||||
|
get => _profileChoose;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// When you select a profile, the settings from the profile will be applied.
|
||||||
|
// To save the settings, you still need to click the apply button
|
||||||
|
_profileChoose = value;
|
||||||
|
LoadProfile();
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public object ConfigViewModel
|
public object ConfigViewModel
|
||||||
{
|
{
|
||||||
get => _configViewModel;
|
get => _configViewModel;
|
||||||
@@ -120,14 +149,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (IsModified)
|
if (IsModified)
|
||||||
{
|
{
|
||||||
|
|
||||||
_playerIdChoose = value;
|
_playerIdChoose = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsModified = false;
|
IsModified = false;
|
||||||
_playerId = value;
|
_playerId = value;
|
||||||
|
_isChangeTrackingActive = false;
|
||||||
|
|
||||||
if (!Enum.IsDefined<PlayerIndex>(_playerId))
|
if (!Enum.IsDefined<PlayerIndex>(_playerId))
|
||||||
{
|
{
|
||||||
@@ -135,13 +164,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
|
|
||||||
}
|
}
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
|
|
||||||
LoadConfiguration();
|
LoadConfiguration();
|
||||||
LoadDevice();
|
LoadDevice();
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
|
|
||||||
|
RevertDeviceId = Devices[Device].Id;
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
|
_isChangeTrackingActive = true;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,6 +180,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
get => _controller;
|
get => _controller;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
MarkAsChanged();
|
||||||
|
|
||||||
_controller = value;
|
_controller = value;
|
||||||
|
|
||||||
if (_controller == -1)
|
if (_controller == -1)
|
||||||
@@ -185,11 +216,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
IsLeft = false;
|
IsLeft = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadInputDriver();
|
LoadInputDriver();
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
NotifyChanges();
|
NotifyChanges();
|
||||||
}
|
}
|
||||||
@@ -229,6 +260,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
get => _device;
|
get => _device;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
MarkAsChanged();
|
||||||
|
|
||||||
_device = value < 0 ? 0 : value;
|
_device = value < 0 ? 0 : value;
|
||||||
|
|
||||||
if (_device >= Devices.Count)
|
if (_device >= Devices.Count)
|
||||||
@@ -248,11 +281,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FindPairedDeviceInConfigFile();
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
NotifyChanges();
|
NotifyChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public InputConfig Config { get; set; }
|
public InputConfig Config { get; set; }
|
||||||
|
|
||||||
public InputViewModel(UserControl owner) : this()
|
public InputViewModel(UserControl owner) : this()
|
||||||
@@ -274,6 +309,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
|
|
||||||
PlayerId = PlayerIndex.Player1;
|
PlayerId = PlayerIndex.Player1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isChangeTrackingActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputViewModel()
|
public InputViewModel()
|
||||||
@@ -311,8 +348,50 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
{
|
{
|
||||||
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick);
|
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig), VisualStick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FindPairedDeviceInConfigFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FindPairedDeviceInConfigFile()
|
||||||
|
{
|
||||||
|
// This function allows you to output a message about the device configuration found in the file
|
||||||
|
// NOTE: if the configuration is found, we display the message "Waiting for controller connection",
|
||||||
|
// but only if the id gamepad belongs to the selected player
|
||||||
|
NotificationIsVisible = Config != null && Devices.FirstOrDefault(d => d.Id == Config.Id).Id != Config.Id && Config.PlayerIndex == PlayerId;
|
||||||
|
if (NotificationIsVisible)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Config.Name))
|
||||||
|
{
|
||||||
|
NotificationText = $"{LocaleManager.Instance[LocaleKeys.ControllerSettingsWaitingConnectDevice].Format("No information", Config.Id)}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NotificationText = $"{LocaleManager.Instance[LocaleKeys.ControllerSettingsWaitingConnectDevice].Format(Config.Name, Config.Id)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void MarkAsChanged()
|
||||||
|
{
|
||||||
|
//If tracking is active, then allow changing the modifier
|
||||||
|
if (!IsModified && _isChangeTrackingActive)
|
||||||
|
{
|
||||||
|
RevertDeviceId = Devices[Device].Id; // Remember the device to undo changes
|
||||||
|
IsModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void UnlinkDevice()
|
||||||
|
{
|
||||||
|
// "Disabled" mode is available after unbinding the device
|
||||||
|
// NOTE: the IsModified flag to be able to apply the settings.
|
||||||
|
NotificationIsVisible = false;
|
||||||
|
IsModified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void LoadDevice()
|
public void LoadDevice()
|
||||||
{
|
{
|
||||||
if (Config == null || Config.Backend == InputBackendType.Invalid)
|
if (Config == null || Config.Backend == InputBackendType.Invalid)
|
||||||
@@ -378,14 +457,34 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleOnGamepadDisconnected(string id)
|
private async void HandleOnGamepadDisconnected(string id)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(LoadDevices);
|
_isChangeTrackingActive = false; // Disable configuration change tracking
|
||||||
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
LoadDevices();
|
||||||
|
|
||||||
|
IsModified = true;
|
||||||
|
RevertChanges();
|
||||||
|
FindPairedDeviceInConfigFile();
|
||||||
|
|
||||||
|
_isChangeTrackingActive = true; // Enable configuration change tracking
|
||||||
|
return System.Threading.Tasks.Task.CompletedTask;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleOnGamepadConnected(string id)
|
private async void HandleOnGamepadConnected(string id)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(LoadDevices);
|
_isChangeTrackingActive = false; // Disable configuration change tracking
|
||||||
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
LoadDevices();
|
||||||
|
|
||||||
|
IsModified = true;
|
||||||
|
RevertChanges();
|
||||||
|
|
||||||
|
_isChangeTrackingActive = true;// Enable configuration change tracking
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCurrentGamepadId()
|
private string GetCurrentGamepadId()
|
||||||
@@ -558,12 +657,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
if (activeDevice.Type == DeviceType.Keyboard)
|
if (activeDevice.Type == DeviceType.Keyboard)
|
||||||
{
|
{
|
||||||
string id = activeDevice.Id;
|
string id = activeDevice.Id;
|
||||||
|
string name = activeDevice.Name;
|
||||||
|
|
||||||
config = new StandardKeyboardInputConfig
|
config = new StandardKeyboardInputConfig
|
||||||
{
|
{
|
||||||
Version = InputConfig.CurrentVersion,
|
Version = InputConfig.CurrentVersion,
|
||||||
Backend = InputBackendType.WindowKeyboard,
|
Backend = InputBackendType.WindowKeyboard,
|
||||||
Id = id,
|
Id = id,
|
||||||
|
Name = name,
|
||||||
ControllerType = ControllerType.ProController,
|
ControllerType = ControllerType.ProController,
|
||||||
LeftJoycon = new LeftJoyconCommonConfig<Key>
|
LeftJoycon = new LeftJoyconCommonConfig<Key>
|
||||||
{
|
{
|
||||||
@@ -613,12 +714,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
bool isNintendoStyle = Devices.ToList().FirstOrDefault(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
|
bool isNintendoStyle = Devices.ToList().FirstOrDefault(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
|
||||||
|
|
||||||
string id = activeDevice.Id.Split(" ")[0];
|
string id = activeDevice.Id.Split(" ")[0];
|
||||||
|
string name = activeDevice.Name;
|
||||||
|
|
||||||
config = new StandardControllerInputConfig
|
config = new StandardControllerInputConfig
|
||||||
{
|
{
|
||||||
Version = InputConfig.CurrentVersion,
|
Version = InputConfig.CurrentVersion,
|
||||||
Backend = InputBackendType.GamepadSDL2,
|
Backend = InputBackendType.GamepadSDL2,
|
||||||
Id = id,
|
Id = id,
|
||||||
|
Name = name,
|
||||||
ControllerType = ControllerType.ProController,
|
ControllerType = ControllerType.ProController,
|
||||||
DeadzoneLeft = 0.1f,
|
DeadzoneLeft = 0.1f,
|
||||||
DeadzoneRight = 0.1f,
|
DeadzoneRight = 0.1f,
|
||||||
@@ -688,6 +791,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadProfileButton()
|
||||||
|
{
|
||||||
|
LoadProfile();
|
||||||
|
IsModified = true;
|
||||||
|
}
|
||||||
|
|
||||||
public async void LoadProfile()
|
public async void LoadProfile()
|
||||||
{
|
{
|
||||||
if (Device == 0)
|
if (Device == 0)
|
||||||
@@ -739,9 +848,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
{
|
{
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
|
|
||||||
|
config.Id = Config.Id; // Set current device id instead of changing device(independent profiles)
|
||||||
|
|
||||||
LoadConfiguration(config);
|
LoadConfiguration(config);
|
||||||
|
|
||||||
LoadDevice();
|
//LoadDevice(); This line of code hard-links profiles to controllers, the commented line allows profiles to be applied to all controllers
|
||||||
|
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
|
|
||||||
@@ -751,54 +862,58 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
|
|
||||||
public async void SaveProfile()
|
public async void SaveProfile()
|
||||||
{
|
{
|
||||||
if (Device == 0)
|
|
||||||
{
|
if (Device == 0)
|
||||||
return;
|
{
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
|
||||||
|
|
||||||
if (ConfigViewModel == null)
|
return;
|
||||||
{
|
}
|
||||||
return;
|
else
|
||||||
}
|
{
|
||||||
|
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
|
||||||
|
|
||||||
if (ProfileName == LocaleManager.Instance[LocaleKeys.ControllerSettingsProfileDefault])
|
if (validFileName)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileDefaultProfileOverwriteErrorMessage]);
|
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
|
||||||
|
|
||||||
return;
|
InputConfig config = null;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
|
|
||||||
|
|
||||||
if (validFileName)
|
if (IsKeyboard)
|
||||||
{
|
{
|
||||||
string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
|
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
|
||||||
|
}
|
||||||
|
else if (IsController)
|
||||||
|
{
|
||||||
|
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
|
||||||
|
}
|
||||||
|
|
||||||
InputConfig config = null;
|
config.ControllerType = Controllers[_controller].Type;
|
||||||
|
|
||||||
if (IsKeyboard)
|
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
|
||||||
{
|
|
||||||
config = (ConfigViewModel as KeyboardInputViewModel).Config.GetConfig();
|
|
||||||
}
|
|
||||||
else if (IsController)
|
|
||||||
{
|
|
||||||
config = (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
config.ControllerType = Controllers[_controller].Type;
|
await File.WriteAllTextAsync(path, jsonString);
|
||||||
|
|
||||||
string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
|
LoadProfiles();
|
||||||
|
|
||||||
await File.WriteAllTextAsync(path, jsonString);
|
ProfileChoose = ProfileName; // Show new profile
|
||||||
|
}
|
||||||
LoadProfiles();
|
else
|
||||||
}
|
{
|
||||||
else
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
|
||||||
{
|
}
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void RemoveProfile()
|
public async void RemoveProfile()
|
||||||
@@ -825,14 +940,33 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
|
|
||||||
|
ProfileChoose = ProfilesList[0].ToString(); // Show default profile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RevertChanges()
|
||||||
|
{
|
||||||
|
Device = Devices.ToList().FindIndex(d => d.Id == RevertDeviceId);
|
||||||
|
LoadDevice();
|
||||||
|
LoadConfiguration();
|
||||||
|
OnPropertyChanged();
|
||||||
|
IsModified = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!IsModified)
|
||||||
|
{
|
||||||
|
return; //If the input settings were not touched, then do nothing
|
||||||
|
}
|
||||||
|
|
||||||
IsModified = false;
|
IsModified = false;
|
||||||
|
|
||||||
List<InputConfig> newConfig = [];
|
RevertDeviceId = Devices[Device].Id; // Remember selected device after saving
|
||||||
|
|
||||||
|
List <InputConfig> newConfig = [];
|
||||||
|
|
||||||
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
|
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
|
||||||
|
|
||||||
@@ -862,6 +996,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
: (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
|
: (ConfigViewModel as ControllerInputViewModel).Config.GetConfig();
|
||||||
config.ControllerType = Controllers[_controller].Type;
|
config.ControllerType = Controllers[_controller].Type;
|
||||||
config.PlayerIndex = _playerId;
|
config.PlayerIndex = _playerId;
|
||||||
|
config.Name = device.Name;
|
||||||
|
|
||||||
int i = newConfig.FindIndex(x => x.PlayerIndex == PlayerId);
|
int i = newConfig.FindIndex(x => x.PlayerIndex == PlayerId);
|
||||||
if (i == -1)
|
if (i == -1)
|
||||||
|
|||||||
@@ -64,8 +64,9 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!float.IsNaN(_changeSlider) && _changeSlider != (float)check.Value)
|
if (!float.IsNaN(_changeSlider) && _changeSlider != (float)check.Value)
|
||||||
{
|
{
|
||||||
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
|
FlagInputConfigChanged();
|
||||||
|
|
||||||
_changeSlider = (float)check.Value;
|
_changeSlider = (float)check.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +76,8 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
{
|
{
|
||||||
if (sender is CheckBox { IsPointerOver: true })
|
if (sender is CheckBox { IsPointerOver: true })
|
||||||
{
|
{
|
||||||
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
|
FlagInputConfigChanged();
|
||||||
|
|
||||||
_currentAssigner?.Cancel();
|
_currentAssigner?.Cancel();
|
||||||
_currentAssigner = null;
|
_currentAssigner = null;
|
||||||
}
|
}
|
||||||
@@ -102,7 +104,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
this.Focus(NavigationMethod.Pointer);
|
this.Focus(NavigationMethod.Pointer);
|
||||||
|
|
||||||
PointerPressed += MouseClick;
|
PointerPressed += MouseClick;
|
||||||
|
|
||||||
ControllerInputViewModel viewModel = (DataContext as ControllerInputViewModel);
|
ControllerInputViewModel viewModel = (DataContext as ControllerInputViewModel);
|
||||||
|
|
||||||
IKeyboard keyboard =
|
IKeyboard keyboard =
|
||||||
@@ -115,7 +117,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
if (e.ButtonValue.HasValue)
|
if (e.ButtonValue.HasValue)
|
||||||
{
|
{
|
||||||
Button buttonValue = e.ButtonValue.Value;
|
Button buttonValue = e.ButtonValue.Value;
|
||||||
viewModel.ParentModel.IsModified = true;
|
FlagInputConfigChanged();
|
||||||
|
|
||||||
switch (button.Name)
|
switch (button.Name)
|
||||||
{
|
{
|
||||||
@@ -209,6 +211,11 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FlagInputConfigChanged()
|
||||||
|
{
|
||||||
|
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void MouseClick(object sender, PointerPressedEventArgs e)
|
private void MouseClick(object sender, PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
||||||
@@ -232,7 +239,6 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
{
|
{
|
||||||
gamepad?.ClearLed();
|
gamepad?.ClearLed();
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentAssigner?.Cancel();
|
_currentAssigner?.Cancel();
|
||||||
_currentAssigner = null;
|
_currentAssigner = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,13 +41,20 @@
|
|||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="2"
|
Margin="2"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center" ColumnDefinitions="Auto,*">
|
VerticalAlignment="Center" ColumnDefinitions="Auto,*,Auto">
|
||||||
<TextBlock
|
<StackPanel
|
||||||
|
Orientation="Vertical"
|
||||||
Margin="5,0,10,0"
|
Margin="5,0,10,0"
|
||||||
Width="90"
|
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{ext:Locale ControllerSettingsPlayer}" />
|
Width="90">
|
||||||
|
<TextBlock
|
||||||
|
Text="{ext:Locale ControllerSettingsPlayer}" />
|
||||||
|
<TextBlock
|
||||||
|
Classes="pending"
|
||||||
|
Text ="{ext:Locale ControllerSettingsModifiedNotification}"
|
||||||
|
IsVisible="{Binding IsModified}"/>
|
||||||
|
</StackPanel>
|
||||||
<ComboBox
|
<ComboBox
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Name="PlayerIndexBox"
|
Name="PlayerIndexBox"
|
||||||
@@ -62,6 +69,18 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
<Button
|
||||||
|
Grid.Column="2"
|
||||||
|
MinWidth="0"
|
||||||
|
Margin="5,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="{ext:Locale ControllerSettingsCancelCurrentChangesToolTip}"
|
||||||
|
Command="{Binding RevertChanges}">
|
||||||
|
<ui:SymbolIcon
|
||||||
|
Symbol="Undo"
|
||||||
|
FontSize="15"
|
||||||
|
Height="20" />
|
||||||
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<!-- Profile Selection -->
|
<!-- Profile Selection -->
|
||||||
<Grid
|
<Grid
|
||||||
@@ -81,7 +100,8 @@
|
|||||||
Name="ProfileBox"
|
Name="ProfileBox"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
SelectedIndex="0"
|
SelectedItem="{Binding ProfileChoose, Mode=TwoWay}"
|
||||||
|
SelectionChanged="ComboBox_SelectionChanged"
|
||||||
ItemsSource="{Binding ProfilesList}"
|
ItemsSource="{Binding ProfilesList}"
|
||||||
Text="{Binding ProfileName, Mode=TwoWay}" />
|
Text="{Binding ProfileName, Mode=TwoWay}" />
|
||||||
<Button
|
<Button
|
||||||
@@ -90,7 +110,7 @@
|
|||||||
Margin="5,0,0,0"
|
Margin="5,0,0,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
ToolTip.Tip="{ext:Locale ControllerSettingsLoadProfileToolTip}"
|
ToolTip.Tip="{ext:Locale ControllerSettingsLoadProfileToolTip}"
|
||||||
Command="{Binding LoadProfile}">
|
Command="{Binding LoadProfileButton}">
|
||||||
<ui:SymbolIcon
|
<ui:SymbolIcon
|
||||||
Symbol="View"
|
Symbol="View"
|
||||||
FontSize="15"
|
FontSize="15"
|
||||||
@@ -148,7 +168,7 @@
|
|||||||
MinWidth="0"
|
MinWidth="0"
|
||||||
Margin="5,0,0,0"
|
Margin="5,0,0,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Command="{Binding LoadDevices}">
|
Command="{Binding LoadDevice}">
|
||||||
<ui:SymbolIcon
|
<ui:SymbolIcon
|
||||||
Symbol="Refresh"
|
Symbol="Refresh"
|
||||||
FontSize="15"
|
FontSize="15"
|
||||||
@@ -181,15 +201,37 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ContentControl Content="{Binding ConfigViewModel}" IsVisible="{Binding ShowSettings}">
|
<ContentControl IsVisible="{Binding NotificationIsVisible}">
|
||||||
<ContentControl.DataTemplates>
|
<ContentControl.Content>
|
||||||
<DataTemplate DataType="viewModels:ControllerInputViewModel">
|
|
||||||
<views:ControllerInputView />
|
<StackPanel>
|
||||||
</DataTemplate>
|
<TextBlock
|
||||||
<DataTemplate DataType="viewModels:KeyboardInputViewModel">
|
Margin="5,20,0,0"
|
||||||
<views:KeyboardInputView />
|
Text="{Binding NotificationText}" />
|
||||||
</DataTemplate>
|
<Button
|
||||||
</ContentControl.DataTemplates>
|
MinWidth="0"
|
||||||
|
Width="90"
|
||||||
|
Height="27"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding UnlinkDevice}">
|
||||||
|
<TextBlock
|
||||||
|
Text="{ext:Locale ControllerSettingsUnlink}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Center" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</ContentControl.Content>
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
|
<ContentControl Content="{Binding ConfigViewModel}" IsVisible="{Binding ShowSettings}">
|
||||||
|
<ContentControl.DataTemplates>
|
||||||
|
<DataTemplate DataType="viewModels:ControllerInputViewModel">
|
||||||
|
<views:ControllerInputView />
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="viewModels:KeyboardInputViewModel">
|
||||||
|
<views:KeyboardInputView />
|
||||||
|
</DataTemplate>
|
||||||
|
</ContentControl.DataTemplates>
|
||||||
|
</ContentControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
using Ryujinx.Ava.UI.Controls;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
@@ -62,14 +63,23 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewModel.PlayerId = ViewModel.PlayerIdChoose;
|
|
||||||
|
|
||||||
ViewModel.IsModified = false;
|
ViewModel.IsModified = false;
|
||||||
}
|
ViewModel.PlayerId = ViewModel.PlayerIdChoose;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is FAComboBox faComboBox)
|
||||||
|
{
|
||||||
|
faComboBox.IsDropDownOpen = false;
|
||||||
|
ViewModel.IsModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
ViewModel.Dispose();
|
ViewModel.Dispose();
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.Assigner;
|
using Ryujinx.Input.Assigner;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
using Button = Ryujinx.Input.Button;
|
using Button = Ryujinx.Input.Button;
|
||||||
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
||||||
|
|
||||||
@@ -186,11 +188,63 @@ namespace Ryujinx.Ava.UI.Views.Input
|
|||||||
{
|
{
|
||||||
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
||||||
|
|
||||||
|
bool shouldRemoveBinding = e.GetCurrentPoint(this).Properties.IsRightButtonPressed;
|
||||||
|
|
||||||
|
if (shouldRemoveBinding)
|
||||||
|
{
|
||||||
|
DeleteBind();
|
||||||
|
}
|
||||||
|
|
||||||
_currentAssigner?.Cancel(shouldUnbind);
|
_currentAssigner?.Cancel(shouldUnbind);
|
||||||
|
|
||||||
PointerPressed -= MouseClick;
|
PointerPressed -= MouseClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DeleteBind()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_currentAssigner != null)
|
||||||
|
{
|
||||||
|
Dictionary<string, Action> buttonActions = new Dictionary<string, Action>
|
||||||
|
{
|
||||||
|
{ "ButtonZl", () => ViewModel.Config.ButtonZl = Key.Unbound },
|
||||||
|
{ "ButtonL", () => ViewModel.Config.ButtonL = Key.Unbound },
|
||||||
|
{ "ButtonMinus", () => ViewModel.Config.ButtonMinus = Key.Unbound },
|
||||||
|
{ "LeftStickButton", () => ViewModel.Config.LeftStickButton = Key.Unbound },
|
||||||
|
{ "LeftStickUp", () => ViewModel.Config.LeftStickUp = Key.Unbound },
|
||||||
|
{ "LeftStickDown", () => ViewModel.Config.LeftStickDown = Key.Unbound },
|
||||||
|
{ "LeftStickRight", () => ViewModel.Config.LeftStickRight = Key.Unbound },
|
||||||
|
{ "LeftStickLeft", () => ViewModel.Config.LeftStickLeft = Key.Unbound },
|
||||||
|
{ "DpadUp", () => ViewModel.Config.DpadUp = Key.Unbound },
|
||||||
|
{ "DpadDown", () => ViewModel.Config.DpadDown = Key.Unbound },
|
||||||
|
{ "DpadLeft", () => ViewModel.Config.DpadLeft = Key.Unbound },
|
||||||
|
{ "DpadRight", () => ViewModel.Config.DpadRight = Key.Unbound },
|
||||||
|
{ "LeftButtonSr", () => ViewModel.Config.LeftButtonSr = Key.Unbound },
|
||||||
|
{ "LeftButtonSl", () => ViewModel.Config.LeftButtonSl = Key.Unbound },
|
||||||
|
{ "RightButtonSr", () => ViewModel.Config.RightButtonSr = Key.Unbound },
|
||||||
|
{ "RightButtonSl", () => ViewModel.Config.RightButtonSl = Key.Unbound },
|
||||||
|
{ "ButtonZr", () => ViewModel.Config.ButtonZr = Key.Unbound },
|
||||||
|
{ "ButtonR", () => ViewModel.Config.ButtonR = Key.Unbound },
|
||||||
|
{ "ButtonPlus", () => ViewModel.Config.ButtonPlus = Key.Unbound },
|
||||||
|
{ "ButtonA", () => ViewModel.Config.ButtonA = Key.Unbound },
|
||||||
|
{ "ButtonB", () => ViewModel.Config.ButtonB = Key.Unbound },
|
||||||
|
{ "ButtonX", () => ViewModel.Config.ButtonX = Key.Unbound },
|
||||||
|
{ "ButtonY", () => ViewModel.Config.ButtonY = Key.Unbound },
|
||||||
|
{ "RightStickButton", () => ViewModel.Config.RightStickButton = Key.Unbound },
|
||||||
|
{ "RightStickUp", () => ViewModel.Config.RightStickUp = Key.Unbound },
|
||||||
|
{ "RightStickDown", () => ViewModel.Config.RightStickDown = Key.Unbound },
|
||||||
|
{ "RightStickRight", () => ViewModel.Config.RightStickRight = Key.Unbound },
|
||||||
|
{ "RightStickLeft", () => ViewModel.Config.RightStickLeft = Key.Unbound }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
ViewModel.ParentModel.IsModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnDetachedFromVisualTree(e);
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ using Ryujinx.Ava.UI.Helpers;
|
|||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.Assigner;
|
using Ryujinx.Input.Assigner;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
using Button = Ryujinx.Input.Button;
|
using Button = Ryujinx.Input.Button;
|
||||||
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
using Key = Ryujinx.Common.Configuration.Hid.Key;
|
||||||
|
|
||||||
@@ -48,12 +50,47 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
private void MouseClick(object sender, PointerPressedEventArgs e)
|
private void MouseClick(object sender, PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
|
||||||
|
bool shouldRemoveBinding = e.GetCurrentPoint(this).Properties.IsRightButtonPressed;
|
||||||
|
|
||||||
|
if (shouldRemoveBinding)
|
||||||
|
{
|
||||||
|
DeleteBind();
|
||||||
|
}
|
||||||
|
|
||||||
_currentAssigner?.Cancel(shouldUnbind);
|
_currentAssigner?.Cancel(shouldUnbind);
|
||||||
|
|
||||||
PointerPressed -= MouseClick;
|
PointerPressed -= MouseClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DeleteBind()
|
||||||
|
{
|
||||||
|
if (DataContext is not SettingsViewModel viewModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_currentAssigner != null)
|
||||||
|
{
|
||||||
|
Dictionary<string, Action> buttonActions = new Dictionary<string, Action>
|
||||||
|
{
|
||||||
|
{ "ToggleVSyncMode", () => viewModel.KeyboardHotkey.ToggleVSyncMode = Key.Unbound },
|
||||||
|
{ "Screenshot", () => viewModel.KeyboardHotkey.Screenshot = Key.Unbound },
|
||||||
|
{ "ShowUI", () => viewModel.KeyboardHotkey.ShowUI = Key.Unbound },
|
||||||
|
{ "Pause", () => viewModel.KeyboardHotkey.Pause = Key.Unbound },
|
||||||
|
{ "ToggleMute", () => viewModel.KeyboardHotkey.ToggleMute = Key.Unbound },
|
||||||
|
{ "ResScaleUp", () => viewModel.KeyboardHotkey.ResScaleUp = Key.Unbound },
|
||||||
|
{ "ResScaleDown", () => viewModel.KeyboardHotkey.ResScaleDown = Key.Unbound },
|
||||||
|
{ "VolumeUp", () => viewModel.KeyboardHotkey.VolumeUp = Key.Unbound },
|
||||||
|
{ "VolumeDown", () => viewModel.KeyboardHotkey.VolumeDown = Key.Unbound },
|
||||||
|
{ "CustomVSyncIntervalIncrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalIncrement = Key.Unbound },
|
||||||
|
{ "CustomVSyncIntervalDecrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = Key.Unbound }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
|
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is ToggleButton button)
|
if (sender is ToggleButton button)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
<DataTemplate DataType="{x:Type systems:CompatibilityEntry}">
|
<DataTemplate DataType="{x:Type systems:CompatibilityEntry}">
|
||||||
<Grid MinWidth="800"
|
<Grid MinWidth="800"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
ColumnDefinitions="*,Auto,Auto,*"
|
ColumnDefinitions="Auto,Auto,Auto,*"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
ToolTip.Tip="{Binding LocalizedLastUpdated}">
|
ToolTip.Tip="{Binding LocalizedLastUpdated}">
|
||||||
<TextBlock Grid.Column="0"
|
<TextBlock Grid.Column="0"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||||
Width="1100"
|
Width="1100"
|
||||||
Height="910"
|
Height="910"
|
||||||
MinWidth="800"
|
MinWidth="1037"
|
||||||
MinHeight="480"
|
MinHeight="480"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
x:DataType="viewModels:SettingsViewModel"
|
x:DataType="viewModels:SettingsViewModel"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||||
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
|
||||||
Width="1100"
|
Width="1100"
|
||||||
MinWidth="800"
|
MinWidth="844"
|
||||||
MinHeight="480"
|
MinHeight="480"
|
||||||
Title="{ext:WindowTitle Settings}"
|
Title="{ext:WindowTitle Settings}"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
|
|||||||
Reference in New Issue
Block a user