Files
MaaAssistantArknights/src/MaaWpfGui/ViewModels/UI/SettingsViewModel.cs
H2O_MERO a752f4b193 feat:为设置添加了搜索支持 (#16833)
* fix:修复了问题 只能在#nullable 注释上下文内的代码中使用可为 null 的引用类型的注释

* chore: Apply suggestion from @sourcery-ai[bot]

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* chore: 搜索框放到导航栏上方

* chore: UpdateSourceTrigger

* rft: 通过附加属性实现

* rft: 实时遍历可视树,结束搜索时还原展开状态

* perf: 雷霆边框

* chore: 等待界面更新后再搜索

---------

Co-authored-by: uye <99072975+ABA2396@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
2026-05-28 12:08:07 +08:00

1207 lines
36 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// <copyright file="SettingsViewModel.cs" company="MaaAssistantArknights">
// Part of the MaaWpfGui project, maintained by the MaaAssistantArknights team (Maa Team)
// Copyright (C) 2021-2025 MaaAssistantArknights Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License v3.0 only as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
// </copyright>
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using HandyControl.Controls;
using HandyControl.Data;
using JetBrains.Annotations;
using MaaWpfGui.Configuration.Factory;
using MaaWpfGui.Configuration.Single.MaaTask;
using MaaWpfGui.Constants;
using MaaWpfGui.Constants.Enums;
using MaaWpfGui.Extensions;
using MaaWpfGui.Helper;
using MaaWpfGui.Main;
using MaaWpfGui.Models;
using MaaWpfGui.Services.HotKeys;
using MaaWpfGui.States;
using MaaWpfGui.Utilities.ValueType;
using MaaWpfGui.ViewModels.Items;
using MaaWpfGui.ViewModels.UserControl.Settings;
using Newtonsoft.Json;
using Serilog;
using Stylet;
using ComboBox = System.Windows.Controls.ComboBox;
using Timer = System.Timers.Timer;
namespace MaaWpfGui.ViewModels.UI;
/// <summary>
/// The view model of settings.
/// </summary>
public class SettingsViewModel : Screen
{
private readonly RunningState _runningState;
private static readonly ILogger _logger = Log.ForContext<SettingsViewModel>();
/// <summary>
/// Gets the visibility of task setting views.
/// </summary>
public TaskSettingVisibilityInfo TaskSettingVisibilities { get; } = TaskSettingVisibilityInfo.Instance;
#region Model
/// <summary>
/// Gets 游戏设置 model
/// </summary>
public static GameSettingsUserControlModel GameSettings { get; } = GameSettingsUserControlModel.Instance;
/// <summary>
/// Gets 连接设置 model
/// </summary>
public static ConnectSettingsUserControlModel ConnectSettings { get; } = ConnectSettingsUserControlModel.Instance;
/// <summary>
/// Gets 启动设置 model
/// </summary>
public static StartSettingsUserControlModel StartSettings { get; } = StartSettingsUserControlModel.Instance;
/// <summary>
/// Gets 界面设置 model
/// </summary>
public static GuiSettingsUserControlModel GuiSettings { get; } = GuiSettingsUserControlModel.Instance;
/// <summary>
/// Gets 背景设置 model
/// </summary>
public static BackgroundSettingsUserControlModel BackgroundSettings { get; } = BackgroundSettingsUserControlModel.Instance;
/// <summary>
/// Gets 定时设置 model
/// </summary>
public static TimerSettingsUserControlModel TimerSettings { get; } = TimerSettingsUserControlModel.Instance;
/// <summary>
/// Gets 远程控制 model
/// </summary>
public static RemoteControlUserControlModel RemoteControlSettings { get; } = RemoteControlUserControlModel.Instance;
/// <summary>
/// Gets 软件更新 model
/// </summary>
public static VersionUpdateSettingsUserControlModel VersionUpdateSettings { get; } = VersionUpdateSettingsUserControlModel.Instance;
/// <summary>
/// Gets 外部通知 model
/// </summary>
public static ExternalNotificationSettingsUserControlModel ExternalNotificationSettings { get; } = ExternalNotificationSettingsUserControlModel.Instance;
/// <summary>
/// Gets 性能设置 model
/// </summary>
public static PerformanceUserControlModel PerformanceSettings { get; } = PerformanceUserControlModel.Instance;
/// <summary>
/// Gets 问题反馈 model
/// </summary>
public static IssueReportUserControlModel IssueReportSettings { get; } = IssueReportUserControlModel.Instance;
/// <summary>
/// Gets 成就 model
/// </summary>
public static AchievementSettingsUserControlModel AchievementSettings { get; } = AchievementSettingsUserControlModel.Instance;
#endregion Model
/// <summary>
/// Initializes a new instance of the <see cref="SettingsViewModel"/> class.
/// </summary>
public SettingsViewModel()
{
DisplayName = LocalizationHelper.GetString("Settings");
Init();
HangoverEnd();
_runningState = RunningState.Instance;
_runningState.StateChanged += (_, e) => {
Idle = e.NewState.Idle;
// Inited = e.Inited;
// Stopping = e.Stopping;
};
}
#region Init
private bool _idle;
/// <summary>
/// Gets or sets a value indicating whether it is idle.
/// </summary>
public bool Idle
{
get => _idle;
set => SetAndNotify(ref _idle, value);
}
private void Init()
{
InitSettings();
TaskQueueViewModel.RoguelikeTask.InitRoguelike();
InitConfiguration();
InitUiSettings();
InitConnectConfig();
InitVersionUpdate();
}
public SettingItemViewModel GetSettingItemByKey(string key)
{
return Settings.First(s => s.Key == key);
}
public SettingItemViewModel SwitchConfigurationSetting => GetSettingItemByKey("SwitchConfiguration");
public SettingItemViewModel ScheduleSettingsSetting => GetSettingItemByKey("ScheduleSettings");
public SettingItemViewModel PerformanceSettingsSetting => GetSettingItemByKey("PerformanceSettings");
public SettingItemViewModel GameSettingsSetting => GetSettingItemByKey("GameSettings");
public SettingItemViewModel ConnectionSettingsSetting => GetSettingItemByKey("ConnectionSettings");
public SettingItemViewModel StartupSettingsSetting => GetSettingItemByKey("StartupSettings");
public SettingItemViewModel RemoteControlSettingsSetting => GetSettingItemByKey("RemoteControlSettings");
public SettingItemViewModel UiSettingsSetting => GetSettingItemByKey("UiSettings");
public SettingItemViewModel BackgroundSettingsSetting => GetSettingItemByKey("BackgroundSettings");
public SettingItemViewModel ExternalNotificationSettingsSetting => GetSettingItemByKey("ExternalNotificationSettings");
public SettingItemViewModel HotKeySettingsSetting => GetSettingItemByKey("HotKeySettings");
public SettingItemViewModel AchievementSettingsSetting => GetSettingItemByKey("AchievementSettings");
public SettingItemViewModel UpdateSettingsSetting => GetSettingItemByKey("UpdateSettings");
public SettingItemViewModel IssueReportSetting => GetSettingItemByKey("IssueReport");
public SettingItemViewModel AboutUsSetting => GetSettingItemByKey("AboutUs");
private void InitSettings()
{
List<string> keyList =
[
"SwitchConfiguration",
"ScheduleSettings",
"PerformanceSettings",
"GameSettings",
"ConnectionSettings",
"StartupSettings",
"RemoteControlSettings",
"UiSettings",
"BackgroundSettings",
"ExternalNotificationSettings",
"HotKeySettings",
"AchievementSettings",
"UpdateSettings",
"IssueReport",
"AboutUs",
];
var tempOrderList = new List<SettingItemViewModel?>(new SettingItemViewModel[keyList.Count]);
var nonOrderList = new List<SettingItemViewModel?>();
foreach (var key in keyList)
{
int order = ConfigurationHelper.GetSettingOrder(key, -1);
var item = new SettingItemViewModel(key, LocalizationHelper.GetString(key), -1);
if (order < 0 || order >= tempOrderList.Count || tempOrderList[order] != null)
{
nonOrderList.Add(item);
}
else
{
item.Value = order;
tempOrderList[order] = item;
}
}
int fillIndex = 0;
foreach (var item in nonOrderList.OfType<SettingItemViewModel>())
{
while (fillIndex < tempOrderList.Count && tempOrderList[fillIndex] != null)
{
fillIndex++;
}
if (fillIndex < tempOrderList.Count)
{
item.Value = fillIndex;
tempOrderList[fillIndex] = item;
ConfigurationHelper.SetSettingOrder(item.Key, fillIndex);
}
}
Settings = [.. tempOrderList.OfType<SettingItemViewModel>()];
Settings.CollectionChanged += Settings_CollectionChanged;
}
private void Settings_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs? e)
{
Execute.OnUIThread(() => {
if (e?.Action == NotifyCollectionChangedAction.Move)
{
AchievementTrackerHelper.Instance.Unlock(AchievementIds.SortingMaster);
}
for (int i = 0; i < Settings.Count; i++)
{
var item = Settings[i];
if (item.Value == i)
{
continue;
}
item.Value = i;
ConfigurationHelper.SetSettingOrder(item.Key, i);
}
OnSettingItemValueChanged();
});
}
private void OnSettingItemValueChanged()
{
Application.Current.Dispatcher.InvokeAsync(() => {
RefreshDividerOffsetsRequested?.Invoke(this, EventArgs.Empty);
}, System.Windows.Threading.DispatcherPriority.Loaded);
}
public event EventHandler? RefreshDividerOffsetsRequested;
private ObservableCollection<SettingItemViewModel> _settings = [];
private string _searchText = string.Empty;
public string SearchText
{
get => _searchText;
set
{
if (SetAndNotify(ref _searchText, value))
{
SearchRequested?.Invoke(this, value);
}
}
}
public event EventHandler<string>? SearchRequested;
public ObservableCollection<SettingItemViewModel> Settings
{
get => _settings;
set => SetAndNotify(ref _settings, value);
}
private void InitConfiguration()
{
var configurations = new ObservableCollection<CombinedData>();
foreach (var conf in ConfigurationHelper.GetConfigurationList())
{
configurations.Add(new CombinedData { Display = conf, Value = conf });
}
ConfigurationList = configurations;
}
private void InitUiSettings()
{
var languageList = (from pair in LocalizationHelper.SupportedLanguages
where pair.Key != PallasLangKey || Cheers
select new CombinedData { Display = pair.Value, Value = pair.Key })
.ToList();
GuiSettings.LanguageList = languageList;
GuiSettings.SwitchDarkMode();
}
private void InitConnectConfig()
{
var addressListJson = ConfigurationHelper.GetValue(ConfigurationKeys.AddressHistory, string.Empty);
if (!string.IsNullOrEmpty(addressListJson))
{
ConnectSettings.ConnectAddressHistory = JsonConvert.DeserializeObject<ObservableCollection<string>>(addressListJson) ?? [];
}
}
private void InitVersionUpdate()
{
if (VersionUpdateSettings is { VersionType: VersionUpdateSettingsUserControlModel.UpdateVersionType.Nightly, AllowNightlyUpdates: false })
{
VersionUpdateSettings.VersionType = VersionUpdateSettingsUserControlModel.UpdateVersionType.Beta;
}
}
#endregion Init
#region EasterEggs
/// <summary>
/// The Pallas language key.
/// </summary>
public const string PallasLangKey = "pallas";
private bool _cheers = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.Cheers, false);
/// <summary>
/// Gets or sets a value indicating whether to cheer.
/// </summary>
public bool Cheers
{
get => _cheers;
set {
if (_cheers == value)
{
return;
}
SetAndNotify(ref _cheers, value);
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.Cheers, value.ToString());
if (_cheers)
{
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.Localization, PallasLangKey);
}
}
}
private bool _hangover = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.Hangover, false);
/// <summary>
/// Gets or sets a value indicating whether to hangover.
/// </summary>
public bool Hangover
{
get => _hangover;
set {
SetAndNotify(ref _hangover, value);
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.Hangover, value.ToString());
}
}
private string _lastBuyWineTime = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.LastBuyWineTime, DateTime.UtcNow.ToYjDate().AddDays(-1).ToFormattedString());
public string LastBuyWineTime
{
get => _lastBuyWineTime;
set {
SetAndNotify(ref _lastBuyWineTime, value);
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.LastBuyWineTime, value);
}
}
public void HangoverEnd()
{
if (!Hangover)
{
return;
}
Hangover = false;
MessageBoxHelper.Show(
LocalizationHelper.GetString("Hangover"),
LocalizationHelper.GetString("Burping"),
iconKey: "HangoverGeometry",
iconBrushKey: "PallasBrush");
Bootstrapper.ShutdownAndRestartWithoutArgs();
}
public void Sober()
{
if (!Cheers || GuiSettings.Language != PallasLangKey)
{
return;
}
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.Localization, SoberLanguage);
Hangover = true;
Cheers = false;
}
private string _soberLanguage = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.SoberLanguage, LocalizationHelper.DefaultLanguage);
public string SoberLanguage
{
get => _soberLanguage;
set {
SetAndNotify(ref _soberLanguage, value);
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.SoberLanguage, value);
}
}
/// <summary>
/// Did you buy wine?
/// </summary>
/// <returns>The answer.</returns>
public bool DidYouBuyWine()
{
var now = DateTime.UtcNow.ToYjDate();
if (now == DateTime.ParseExact(LastBuyWineTime.Replace('-', '/'), "yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture))
{
return false;
}
// if (now.IsAprilFoolsDay())
// {
// return true;
// }
string[] wineList = ["酒", "liquor", "drink", "wine", "beer", "술", "🍷", "🍸", "🍺", "🍻", "🥃", "🍶"];
foreach (var task in ConfigFactory.CurrentConfig.TaskQueue.OfType<MallTask>())
{
if (wineList.Any(task.FirstList.Contains))
{
return true;
}
}
return false;
}
#endregion EasterEggs
#region HotKey
/// <summary>
/// Gets or sets the hotkey: ShowGui.
/// </summary>
public static MaaHotKey? HotKeyShowGui
{
get => Instances.MaaHotKeyManager.GetOrNull(MaaHotKeyAction.ShowGui);
set => SetHotKey(MaaHotKeyAction.ShowGui, value);
}
/// <summary>
/// Gets or sets the hotkey: LinkStart.
/// </summary>
public static MaaHotKey? HotKeyLinkStart
{
get => Instances.MaaHotKeyManager.GetOrNull(MaaHotKeyAction.LinkStart);
set => SetHotKey(MaaHotKeyAction.LinkStart, value);
}
private static void SetHotKey(MaaHotKeyAction action, MaaHotKey? value)
{
if (value != null)
{
Instances.MaaHotKeyManager.TryRegister(action, value);
}
else
{
Instances.MaaHotKeyManager.UnRegister(action);
}
}
#endregion HotKey
#region
public ObservableCollection<CombinedData> ConfigurationList { get; set; } = [];
private string? _currentConfiguration = ConfigurationHelper.GetCurrentConfiguration();
public string? CurrentConfiguration
{
get => _currentConfiguration;
set {
if (string.IsNullOrEmpty(value) || value == _currentConfiguration)
{
return;
}
var previousConfiguration = _currentConfiguration;
bool ret = ConfigurationHelper.SwitchConfiguration(value);
ret &= ConfigFactory.SwitchConfig(value);
if (!ret)
{
if (!string.IsNullOrEmpty(previousConfiguration))
{
ConfigurationHelper.SwitchConfiguration(previousConfiguration);
ConfigFactory.SwitchConfig(previousConfiguration);
}
return;
}
SetAndNotify(ref _currentConfiguration, value);
Bootstrapper.ShutdownAndRestartWithoutArgs();
}
}
private string _newConfigurationName = string.Empty;
public string NewConfigurationName
{
get => _newConfigurationName;
set => SetAndNotify(ref _newConfigurationName, value);
}
// UI 绑定的方法
[UsedImplicitly]
public void AddConfiguration()
{
if (string.IsNullOrEmpty(NewConfigurationName))
{
NewConfigurationName = DateTime.Now.ToString("yy/MM/dd HH:mm:ss");
}
bool existsInHelper = ConfigurationHelper.ConfigurationExists(NewConfigurationName);
bool existsInFactory = ConfigFactory.ConfigurationExists(NewConfigurationName);
// 两边都已存在,提示并返回
if (existsInHelper && existsInFactory)
{
Growl.Info(new GrowlInfo {
IsCustom = true,
Message = LocalizationHelper.GetStringFormat("ConfigExists", NewConfigurationName),
IconKey = "HangoverGeometry",
IconBrushKey = "PallasBrush",
});
return;
}
// 至少有一侧存在(两边都存在的情况已在上方 return清理残余配置以便重新添加
if (existsInHelper)
{
ConfigurationHelper.DeleteConfiguration(NewConfigurationName);
}
if (existsInFactory)
{
ConfigFactory.DeleteConfiguration(NewConfigurationName);
}
// 两边都不存在,执行添加
bool helperAdded = ConfigurationHelper.AddConfiguration(NewConfigurationName, CurrentConfiguration);
bool factoryAdded = ConfigFactory.AddConfiguration(NewConfigurationName, CurrentConfiguration);
if (!helperAdded || !factoryAdded)
{
// 任一侧添加失败,回滚另一侧
if (helperAdded) { ConfigurationHelper.DeleteConfiguration(NewConfigurationName); }
if (factoryAdded) { ConfigFactory.DeleteConfiguration(NewConfigurationName); }
Growl.Info(new GrowlInfo {
IsCustom = true,
Message = LocalizationHelper.GetStringFormat("ConfigExists", NewConfigurationName),
IconKey = "HangoverGeometry",
IconBrushKey = "PallasBrush",
});
return;
}
ConfigurationList.Add(new CombinedData { Display = NewConfigurationName, Value = NewConfigurationName });
// 配置数量大于 1 时,标题栏显示配置名
UpdateWindowTitle();
Growl.Info(new GrowlInfo {
IsCustom = true,
Message = LocalizationHelper.GetStringFormat("AddConfigSuccess", NewConfigurationName),
IconKey = "HangoverGeometry",
IconBrushKey = "PallasBrush",
});
}
// UI 绑定的方法
[UsedImplicitly]
public void DeleteConfiguration(CombinedData delete)
{
if (ConfigurationHelper.DeleteConfiguration(delete.Display) && ConfigFactory.DeleteConfiguration(delete.Display))
{
ConfigurationList.Remove(delete);
if (ConfigurationList.Count <= 1)
{
UpdateWindowTitle();
}
}
}
#endregion
#region SettingsGuide
public static int GuideMaxStep => 7;
private int _guideStepIndex = ConfigurationHelper.GetValue(ConfigurationKeys.GuideStepIndex, 0);
public int GuideStepIndex
{
get => _guideStepIndex;
set {
SetAndNotify(ref _guideStepIndex, value);
ConfigurationHelper.SetValue(ConfigurationKeys.GuideStepIndex, value.ToString());
}
}
private string _guideTransitionMode = "Bottom2Top";
public string GuideTransitionMode
{
get => _guideTransitionMode;
set => SetAndNotify(ref _guideTransitionMode, value);
}
// UI 绑定的方法
[UsedImplicitly]
public void NextGuide(StepBar stepBar)
{
GuideTransitionMode = "Bottom2Top";
stepBar.Next();
}
// UI 绑定的方法
[UsedImplicitly]
public void PrevGuide(StepBar stepBar)
{
GuideTransitionMode = "Top2Bottom";
stepBar.Prev();
}
// UI 绑定的方法
[UsedImplicitly]
public void DoneGuide()
{
TaskSettingVisibilities.Guide = false;
GuideStepIndex = GuideMaxStep;
}
// UI 绑定的方法
[UsedImplicitly]
public void RestartGuide()
{
GuideStepIndex = 0;
TaskSettingVisibilities.Guide = true;
var result = MessageBoxHelper.Show(
LocalizationHelper.GetString("RestartGuidePrompt"),
LocalizationHelper.GetString("Tip"),
MessageBoxButton.OKCancel,
MessageBoxImage.Question);
if (result == MessageBoxResult.OK)
{
Bootstrapper.ShutdownAndRestartWithoutArgs();
}
}
#endregion SettingsGuide
#region
private enum NotifyType
{
None,
SelectedIndex,
ScrollOffset,
}
private NotifyType _notifySource = NotifyType.None;
private Timer? _resetNotifyTimer;
private void ResetNotifySource()
{
if (_resetNotifyTimer != null)
{
_resetNotifyTimer.Stop();
_resetNotifyTimer.Close();
}
_resetNotifyTimer = new Timer(20);
_resetNotifyTimer.Elapsed += (_, _) => {
_notifySource = NotifyType.None;
};
_resetNotifyTimer.AutoReset = false;
_resetNotifyTimer.Enabled = true;
_resetNotifyTimer.Start();
}
/// <summary>
/// Gets or sets the height of scroll viewport.
/// </summary>
public double ScrollViewportHeight { get; set; }
/// <summary>
/// Gets or sets the extent height of scroll.
/// </summary>
public double ScrollExtentHeight { get; set; }
private List<double> _dividerVerticalOffsetList = [];
/// <summary>
/// Gets or sets the list of divider vertical offset.
/// </summary>
public List<double> DividerVerticalOffsetList
{
get => _dividerVerticalOffsetList;
set {
if (_dividerVerticalOffsetList == value)
{
return;
}
_dividerVerticalOffsetList = value;
SetAndNotify(ref _dividerVerticalOffsetList, value);
}
}
private int _selectedIndex;
/// <summary>
/// Gets or sets the index selected.
/// </summary>
public int SelectedIndex
{
get => _selectedIndex;
set {
if (_selectedIndex == value)
{
return;
}
if (value < 0 || value > DividerVerticalOffsetList.Count)
{
return;
}
switch (_notifySource)
{
case NotifyType.None:
_notifySource = NotifyType.SelectedIndex;
SetAndNotify(ref _selectedIndex, value);
ScrollOffset = DividerVerticalOffsetList[value];
ResetNotifySource();
break;
case NotifyType.ScrollOffset:
SetAndNotify(ref _selectedIndex, value);
break;
case NotifyType.SelectedIndex:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private double _scrollOffset;
/// <summary>
/// Gets or sets the scroll offset.
/// </summary>
public double ScrollOffset
{
get => _scrollOffset;
set {
if (!AllowScrollOffsetChange)
{
return;
}
switch (_notifySource)
{
case NotifyType.None:
_notifySource = NotifyType.ScrollOffset;
SetAndNotify(ref _scrollOffset, value);
// 设置 ListBox SelectedIndex 为当前 ScrollOffset 索引
if (DividerVerticalOffsetList.Count > 0)
{
// 滚动条滚动到底部,返回最后一个 Divider 索引
if (value + ScrollViewportHeight >= ScrollExtentHeight)
{
SelectedIndex = DividerVerticalOffsetList.Count - 1;
ResetNotifySource();
break;
}
// 根据出当前 ScrollOffset 选出最后一个在可视范围的 Divider 索引
var dividerSelect = DividerVerticalOffsetList.Select((n, i) => (
dividerAppeared: value >= n,
index: i));
var index = dividerSelect.LastOrDefault(n => n.dividerAppeared).index;
SelectedIndex = index;
}
ResetNotifySource();
break;
case NotifyType.SelectedIndex:
SetAndNotify(ref _scrollOffset, value);
break;
case NotifyType.ScrollOffset:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public bool AllowScrollOffsetChange { get; set; } = true;
#endregion
#region
public bool IsSwitchConfigurationExpanded
{
get => ConfigFactory.Root.GUI.ExpanderSwitchConfiguration;
set
{
ConfigFactory.Root.GUI.ExpanderSwitchConfiguration = value;
NotifyOfPropertyChange();
}
}
public bool IsScheduleSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderScheduleSettings;
set
{
ConfigFactory.Root.GUI.ExpanderScheduleSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsPerformanceSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderPerformanceSettings;
set
{
ConfigFactory.Root.GUI.ExpanderPerformanceSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsGameSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderGameSettings;
set
{
ConfigFactory.Root.GUI.ExpanderGameSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsConnectionSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderConnectionSettings;
set
{
ConfigFactory.Root.GUI.ExpanderConnectionSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsStartupSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderStartupSettings;
set
{
ConfigFactory.Root.GUI.ExpanderStartupSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsRemoteControlSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderRemoteControlSettings;
set
{
ConfigFactory.Root.GUI.ExpanderRemoteControlSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsUiSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderUiSettings;
set
{
ConfigFactory.Root.GUI.ExpanderUiSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsBackgroundSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderBackgroundSettings;
set
{
ConfigFactory.Root.GUI.ExpanderBackgroundSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsExternalNotificationSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderExternalNotificationSettings;
set
{
ConfigFactory.Root.GUI.ExpanderExternalNotificationSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsHotKeySettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderHotKeySettings;
set
{
ConfigFactory.Root.GUI.ExpanderHotKeySettings = value;
NotifyOfPropertyChange();
}
}
public bool IsAchievementSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderAchievementSettings;
set
{
ConfigFactory.Root.GUI.ExpanderAchievementSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsUpdateSettingsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderUpdateSettings;
set
{
ConfigFactory.Root.GUI.ExpanderUpdateSettings = value;
NotifyOfPropertyChange();
}
}
public bool IsIssueReportExpanded
{
get => ConfigFactory.Root.GUI.ExpanderIssueReport;
set
{
ConfigFactory.Root.GUI.ExpanderIssueReport = value;
NotifyOfPropertyChange();
}
}
public bool IsAboutUsExpanded
{
get => ConfigFactory.Root.GUI.ExpanderAboutUs;
set
{
ConfigFactory.Root.GUI.ExpanderAboutUs = value;
NotifyOfPropertyChange();
}
}
#endregion
/// <summary>
/// Requires the user to restart to apply settings.
/// </summary>
/// <param name="isYostarEN">Whether to include the YostarEN resolution tip.</param>
public static void AskRestartToApplySettings(bool isYostarEN = false)
{
var resolutionTip = isYostarEN ? "\n" + LocalizationHelper.GetString("SwitchResolutionTip") : string.Empty;
var result = MessageBoxHelper.Show(
LocalizationHelper.GetString("PromptRestartForSettingsChange") + resolutionTip,
LocalizationHelper.GetString("Tip"),
MessageBoxButton.OKCancel,
MessageBoxImage.Question);
if (result == MessageBoxResult.OK)
{
Bootstrapper.ShutdownAndRestartWithoutArgs();
}
}
/// <summary>
/// Make comboBox searchable
/// UI 绑定的方法
/// </summary>
/// <param name="sender">Event sender</param>
/// <param name="e">Event args</param>
[UsedImplicitly]
public static void MakeComboBoxSearchable(object sender, EventArgs e)
{
(sender as ComboBox)?.MakeComboBoxSearchable();
}
private bool _isCheckingAnnouncement = false;
public bool IsCheckingAnnouncement
{
get => _isCheckingAnnouncement;
set {
SetAndNotify(ref _isCheckingAnnouncement, value);
}
}
// UI 绑定的方法
[UsedImplicitly]
public async Task CheckAndDownloadAnnouncement()
{
if (IsCheckingAnnouncement)
{
return;
}
IsCheckingAnnouncement = true;
try
{
if (Instances.AnnouncementDialogViewModel.View is System.Windows.Window window)
{
if (window.WindowState == WindowState.Minimized)
{
window.WindowState = WindowState.Normal;
}
window.Activate();
}
else
{
Instances.WindowManager.ShowWindow(Instances.AnnouncementDialogViewModel);
}
await Instances.AnnouncementDialogViewModel.CheckAndDownloadAnnouncement();
}
finally
{
IsCheckingAnnouncement = false;
}
}
/// <summary>
/// 标题栏显示模拟器名称和IP端口。
/// </summary>
public void UpdateWindowTitle()
{
var rvm = (RootViewModel)this.Parent;
var newVersionFoundInfo = VersionUpdateSettings.NewVersionFoundInfo;
var uiVersion = VersionUpdateSettingsUserControlModel.UiVersion;
var startupUpdateCheck = VersionUpdateSettings.StartupUpdateCheck;
var isDebug = Instances.VersionUpdateDialogViewModel.IsDebugVersion();
if (newVersionFoundInfo != uiVersion && !isDebug && !string.IsNullOrEmpty(newVersionFoundInfo) && startupUpdateCheck)
{
rvm.WindowVersionUpdateInfo = $"{newVersionFoundInfo}".Trim();
}
rvm.WindowResourceUpdateInfo = VersionUpdateSettings.NewResourceFoundInfo;
string prefix = ConfigurationHelper.GetValue(ConfigurationKeys.WindowTitlePrefix, string.Empty);
if (!string.IsNullOrEmpty(prefix))
{
prefix += " - ";
}
List<string> windowTitleSelectShowList = [.. GuiSettings.WindowTitleSelectShowList
.Cast<KeyValuePair<string, string>>().Select(pair => pair.Key)];
string currentConfiguration = string.Empty;
string connectConfigName = string.Empty;
string connectAddress = string.Empty;
string clientName = string.Empty;
foreach (var select in windowTitleSelectShowList)
{
switch (select)
{
case "1": // 配置名
if (ConfigurationList.Count > 1)
{
currentConfiguration = $" ({CurrentConfiguration})";
}
break;
case "2": // 连接模式
foreach (var data in ConnectSettings.ConnectConfigList.Where(data => data.Value == ConnectSettings.ConnectConfig))
{
connectConfigName = $" - {data.Display}";
}
break;
case "3": // 端口地址
connectAddress = $" ({ConnectSettings.ConnectAddress})".Replace("127.0.0.1:", string.Empty).Replace("localhost:", string.Empty);
break;
case "4": // 客户端类型
clientName = $" - {ClientName}";
break;
}
}
string resourceVersionDisplay = !string.IsNullOrEmpty(VersionUpdateSettings.ResourceVersion)
? $" - {LocalizationHelper.FormatVersion(VersionUpdateSettings.ResourceVersion, VersionUpdateSettings.ResourceDateTime)}"
: string.Empty;
string uiVersionDisplay = LocalizationHelper.FormatVersion(uiVersion, VersionUpdateSettingsUserControlModel.BuildDateTime);
string adminTag = Bootstrapper.IsAdministratorWithUac() ? $" ({LocalizationHelper.GetString("Administrator")})" : string.Empty;
rvm.WindowTitle = $"{prefix}MAA{adminTag}{currentConfiguration} - {uiVersionDisplay}{resourceVersionDisplay}{connectConfigName}{connectAddress}{clientName}";
}
/// <summary>
/// Gets the client type.
/// </summary>
private string ClientName
{
get {
foreach (var item in GameSettings.ClientTypeList.Where(item => item.Value == GameSettings.ClientType))
{
return item.Display;
}
return "Unknown Client";
}
}
private static readonly Dictionary<string, string> _serverMapping = new()
{
{ ClientType.Official, "CN" },
{ ClientType.Bilibili, "CN" },
{ ClientType.EN, "US" },
{ ClientType.JP, "JP" },
{ ClientType.KR, "KR" },
{ ClientType.Txwy, "ZH_TW" },
};
/// <summary>
/// Gets the server type.
/// </summary>
public string ServerType => _serverMapping[GameSettings.ClientType];
}