// // 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 // #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; /// /// The view model of settings. /// public class SettingsViewModel : Screen { private readonly RunningState _runningState; private static readonly ILogger _logger = Log.ForContext(); /// /// Gets the visibility of task setting views. /// public TaskSettingVisibilityInfo TaskSettingVisibilities { get; } = TaskSettingVisibilityInfo.Instance; #region 设置界面 Model /// /// Gets 游戏设置 model /// public static GameSettingsUserControlModel GameSettings { get; } = GameSettingsUserControlModel.Instance; /// /// Gets 连接设置 model /// public static ConnectSettingsUserControlModel ConnectSettings { get; } = ConnectSettingsUserControlModel.Instance; /// /// Gets 启动设置 model /// public static StartSettingsUserControlModel StartSettings { get; } = StartSettingsUserControlModel.Instance; /// /// Gets 界面设置 model /// public static GuiSettingsUserControlModel GuiSettings { get; } = GuiSettingsUserControlModel.Instance; /// /// Gets 背景设置 model /// public static BackgroundSettingsUserControlModel BackgroundSettings { get; } = BackgroundSettingsUserControlModel.Instance; /// /// Gets 定时设置 model /// public static TimerSettingsUserControlModel TimerSettings { get; } = TimerSettingsUserControlModel.Instance; /// /// Gets 远程控制 model /// public static RemoteControlUserControlModel RemoteControlSettings { get; } = RemoteControlUserControlModel.Instance; /// /// Gets 软件更新 model /// public static VersionUpdateSettingsUserControlModel VersionUpdateSettings { get; } = VersionUpdateSettingsUserControlModel.Instance; /// /// Gets 外部通知 model /// public static ExternalNotificationSettingsUserControlModel ExternalNotificationSettings { get; } = ExternalNotificationSettingsUserControlModel.Instance; /// /// Gets 性能设置 model /// public static PerformanceUserControlModel PerformanceSettings { get; } = PerformanceUserControlModel.Instance; /// /// Gets 问题反馈 model /// public static IssueReportUserControlModel IssueReportSettings { get; } = IssueReportUserControlModel.Instance; /// /// Gets 成就 model /// public static AchievementSettingsUserControlModel AchievementSettings { get; } = AchievementSettingsUserControlModel.Instance; #endregion 设置界面 Model /// /// Initializes a new instance of the class. /// 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; /// /// Gets or sets a value indicating whether it is idle. /// 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 keyList = [ "SwitchConfiguration", "ScheduleSettings", "PerformanceSettings", "GameSettings", "ConnectionSettings", "StartupSettings", "RemoteControlSettings", "UiSettings", "BackgroundSettings", "ExternalNotificationSettings", "HotKeySettings", "AchievementSettings", "UpdateSettings", "IssueReport", "AboutUs", ]; var tempOrderList = new List(new SettingItemViewModel[keyList.Count]); var nonOrderList = new List(); 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()) { 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()]; 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 _settings = []; private string _searchText = string.Empty; public string SearchText { get => _searchText; set { if (SetAndNotify(ref _searchText, value)) { SearchRequested?.Invoke(this, value); } } } public event EventHandler? SearchRequested; public ObservableCollection Settings { get => _settings; set => SetAndNotify(ref _settings, value); } private void InitConfiguration() { var configurations = new ObservableCollection(); 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>(addressListJson) ?? []; } } private void InitVersionUpdate() { if (VersionUpdateSettings is { VersionType: VersionUpdateSettingsUserControlModel.UpdateVersionType.Nightly, AllowNightlyUpdates: false }) { VersionUpdateSettings.VersionType = VersionUpdateSettingsUserControlModel.UpdateVersionType.Beta; } } #endregion Init #region EasterEggs /// /// The Pallas language key. /// public const string PallasLangKey = "pallas"; private bool _cheers = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.Cheers, false); /// /// Gets or sets a value indicating whether to cheer. /// 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); /// /// Gets or sets a value indicating whether to hangover. /// 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); } } /// /// Did you buy wine? /// /// The answer. 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()) { if (wineList.Any(task.FirstList.Contains)) { return true; } } return false; } #endregion EasterEggs #region HotKey /// /// Gets or sets the hotkey: ShowGui. /// public static MaaHotKey? HotKeyShowGui { get => Instances.MaaHotKeyManager.GetOrNull(MaaHotKeyAction.ShowGui); set => SetHotKey(MaaHotKeyAction.ShowGui, value); } /// /// Gets or sets the hotkey: LinkStart. /// 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 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(); } /// /// Gets or sets the height of scroll viewport. /// public double ScrollViewportHeight { get; set; } /// /// Gets or sets the extent height of scroll. /// public double ScrollExtentHeight { get; set; } private List _dividerVerticalOffsetList = []; /// /// Gets or sets the list of divider vertical offset. /// public List DividerVerticalOffsetList { get => _dividerVerticalOffsetList; set { if (_dividerVerticalOffsetList == value) { return; } _dividerVerticalOffsetList = value; SetAndNotify(ref _dividerVerticalOffsetList, value); } } private int _selectedIndex; /// /// Gets or sets the index selected. /// 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; /// /// Gets or sets the scroll offset. /// 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 折叠框展开状态 /// /// Requires the user to restart to apply settings. /// /// Whether to include the YostarEN resolution tip. 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(); } } /// /// Make comboBox searchable /// UI 绑定的方法 /// /// Event sender /// Event args [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; } } /// /// 标题栏显示模拟器名称和IP端口。 /// 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 windowTitleSelectShowList = [.. GuiSettings.WindowTitleSelectShowList .Cast>().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}"; } /// /// Gets the client type. /// 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 _serverMapping = new() { { ClientType.Official, "CN" }, { ClientType.Bilibili, "CN" }, { ClientType.EN, "US" }, { ClientType.JP, "JP" }, { ClientType.KR, "KR" }, { ClientType.Txwy, "ZH_TW" }, }; /// /// Gets the server type. /// public string ServerType => _serverMapping[GameSettings.ClientType]; }