5 Commits
7 changed files with 131 additions and 36 deletions

No files matched your search

+6 -5
View File
@@ -1,3 +1,4 @@
using ADOFAIRenderer.Localization;
using ADOFAIRenderer.UI; using ADOFAIRenderer.UI;
using UnityEngine; using UnityEngine;
@@ -45,18 +46,18 @@ internal class FirstUpdateHook : MonoBehaviour
var currentVersion = driverVer.Value.ToString("F2"); var currentVersion = driverVer.Value.ToString("F2");
CommonWarningDialogUI.ShowDialog( CommonWarningDialogUI.ShowDialog(
"NVIDIA Graphics Driver Update Recommended", LocalizationManager.Get("ui.driver_warning.title"),
$"Currently detected NVIDIA graphics driver version: <b><color=#ff5555>{currentVersion}</color></b>\n\nDriver version <b><color=#55ff55>610.00 or higher</color></b> is required to render video using hardware acceleration (NVENC).\n\nIf the driver version is lower, encoding may fail or fallback to software (CPU) encoding, significantly slowing down rendering. Please update your graphics driver.", LocalizationManager.Get("ui.driver_warning.body", currentVersion),
new DialogButton("Update Driver", () => new DialogButton(LocalizationManager.Get("ui.driver_warning.btn_update"), () =>
{ {
Application.OpenURL("https://www.nvidia.com/Download/index.aspx"); Application.OpenURL("https://www.nvidia.com/Download/index.aspx");
Application.Quit(); Application.Quit();
}, new Color(0.0f, 0.8f, 1.0f, 1.0f)), }, new Color(0.0f, 0.8f, 1.0f, 1.0f)),
new DialogButton("Disable Feature", () => new DialogButton(LocalizationManager.Get("ui.driver_warning.btn_disable"), () =>
{ {
RenderMod.IsFeatureDisabled = true; RenderMod.IsFeatureDisabled = true;
}), }),
new DialogButton("Continue Anyway", () => new DialogButton(LocalizationManager.Get("ui.driver_warning.btn_continue"), () =>
{ {
// ignored // ignored
}) })
+5 -1
View File
@@ -116,7 +116,11 @@ public static class PresetManager
return Path.Combine(root, "UserData", "ADOFAIRenderer", "Presets"); return Path.Combine(root, "UserData", "ADOFAIRenderer", "Presets");
} }
private static string GetSafeFileName(string name) /// <summary>
/// File name (without extension) of the preset file for the given preset name: every character that is invalid
/// in a file name is replaced with '_'. Different preset names can map to the same file name (e.g. "a/b" and "a_b").
/// </summary>
internal static string GetSafeFileName(string name)
{ {
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, '_')); return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, '_'));
} }
+24 -1
View File
@@ -45,10 +45,33 @@
"ui.render_config.random_seed": "Random Seed (Optional)", "ui.render_config.random_seed": "Random Seed (Optional)",
"ui.render_config.validation_title": "Invalid Settings", "ui.render_config.validation_title": "Invalid Settings",
"ui.render_config.validation_error": "Please check your input values:\n- Width and Height must be integers > 0\n- Render FPS and Video FPS must be > 0\n- Delay After End must be >= 0", "ui.render_config.validation_error": "Please check your input values:\n- Width and Height must be integers > 0\n- Render FPS and Video FPS must be > 0\n- Delay After End must be >= 0",
"ui.render_config.validation_even_size": "Render width and height must be even numbers (current: {0} x {1}).",
"ui.render_config.invalid_path": "Output directory path is invalid:\n{0}", "ui.render_config.invalid_path": "Output directory path is invalid:\n{0}",
"ui.render_config.planet_colors": "Planet Colors (Optional)", "ui.render_config.planet_colors": "Planet Colors (Optional)",
"ui.render_config.fire_planet": "Fire Planet", "ui.render_config.fire_planet": "Fire Planet",
"ui.render_config.ice_planet": "Ice Planet", "ui.render_config.ice_planet": "Ice Planet",
"ui.render_config.wind_planet": "Wind Planet", "ui.render_config.wind_planet": "Wind Planet",
"ui.render_config.expired": "(Expired)" "ui.render_config.expired": "(Expired)",
"ui.ffmpeg_download.title": "FFmpeg Library Installation Required",
"ui.ffmpeg_download.desc_required": "FFmpeg shared libraries are required to render video.",
"ui.ffmpeg_download.desc_mac_build": "For macOS, reliable public builds are unavailable, so FFmpeg 8.1 must be built manually.",
"ui.ffmpeg_download.desc_mac_open": "Click the button below to open the official FFmpeg website.",
"ui.ffmpeg_download.desc_download": "Click the button below to automatically download and install from the internet.",
"ui.ffmpeg_download.desc_size": "<color=#aaaaaa><size=13>(Download size: ~70MB, extracted to UserLibs folder)</size></color>",
"ui.ffmpeg_download.btn_download": "Download & Install",
"ui.ffmpeg_download.btn_open_site": "Open Website",
"ui.ffmpeg_download.btn_cancel": "Cancel",
"ui.ffmpeg_download.btn_retry": "Retry",
"ui.ffmpeg_download.status_starting": "Starting download...",
"ui.ffmpeg_download.status_downloading": "Downloading... ({0}%)",
"ui.ffmpeg_download.status_extracting": "Extracting and copying library files...",
"ui.ffmpeg_download.progress_extracting": "Extracting...",
"ui.ffmpeg_download.status_complete": "FFmpeg installation complete!",
"ui.ffmpeg_download.status_cancelled": "Download was cancelled.",
"ui.ffmpeg_download.status_failed": "Installation failed: {0}",
"ui.driver_warning.title": "NVIDIA Graphics Driver Update Recommended",
"ui.driver_warning.body": "Currently detected NVIDIA graphics driver version: <b><color=#ff5555>{0}</color></b>\n\nDriver version <b><color=#55ff55>610.00 or higher</color></b> is required to render video using hardware acceleration (NVENC).\n\nIf the driver version is lower, encoding may fail or fallback to software (CPU) encoding, significantly slowing down rendering. Please update your graphics driver.",
"ui.driver_warning.btn_update": "Update Driver",
"ui.driver_warning.btn_disable": "Disable Feature",
"ui.driver_warning.btn_continue": "Continue Anyway"
} }
+24 -1
View File
@@ -45,10 +45,33 @@
"ui.render_config.random_seed": "랜덤 시드 (선택 사항)", "ui.render_config.random_seed": "랜덤 시드 (선택 사항)",
"ui.render_config.validation_title": "설정 오류", "ui.render_config.validation_title": "설정 오류",
"ui.render_config.validation_error": "입력값을 확인해 주세요:\n- 너비와 높이는 0보다 큰 정수여야 합니다.\n- 렌더 FPS와 영상 FPS는 0보다 커야 합니다.\n- 종료 후 대기 시간은 0 이상이어야 합니다.", "ui.render_config.validation_error": "입력값을 확인해 주세요:\n- 너비와 높이는 0보다 큰 정수여야 합니다.\n- 렌더 FPS와 영상 FPS는 0보다 커야 합니다.\n- 종료 후 대기 시간은 0 이상이어야 합니다.",
"ui.render_config.validation_even_size": "렌더 해상도의 가로와 세로는 짝수여야 해요. (현재: {0} x {1})",
"ui.render_config.invalid_path": "출력 디렉토리 경로가 올바르지 않습니다:\n{0}", "ui.render_config.invalid_path": "출력 디렉토리 경로가 올바르지 않습니다:\n{0}",
"ui.render_config.planet_colors": "행성 색상 설정 (선택 사항)", "ui.render_config.planet_colors": "행성 색상 설정 (선택 사항)",
"ui.render_config.fire_planet": "불 행성", "ui.render_config.fire_planet": "불 행성",
"ui.render_config.ice_planet": "얼음 행성", "ui.render_config.ice_planet": "얼음 행성",
"ui.render_config.wind_planet": "바람 행성", "ui.render_config.wind_planet": "바람 행성",
"ui.render_config.expired": "(만료됨)" "ui.render_config.expired": "(만료됨)",
"ui.ffmpeg_download.title": "FFmpeg 라이브러리 설치 필요",
"ui.ffmpeg_download.desc_required": "영상을 렌더링하려면 FFmpeg 공유 라이브러리가 필요해요.",
"ui.ffmpeg_download.desc_mac_build": "macOS는 신뢰할 수 있는 공개 빌드가 없어서 FFmpeg 8.1을 직접 빌드해야 해요.",
"ui.ffmpeg_download.desc_mac_open": "아래 버튼을 누르면 FFmpeg 공식 웹사이트가 열려요.",
"ui.ffmpeg_download.desc_download": "아래 버튼을 누르면 인터넷에서 자동으로 내려받아 설치해요.",
"ui.ffmpeg_download.desc_size": "<color=#aaaaaa><size=13>(다운로드 크기: 약 70MB, UserLibs 폴더에 압축 해제)</size></color>",
"ui.ffmpeg_download.btn_download": "다운로드 및 설치",
"ui.ffmpeg_download.btn_open_site": "웹사이트 열기",
"ui.ffmpeg_download.btn_cancel": "취소",
"ui.ffmpeg_download.btn_retry": "다시 시도",
"ui.ffmpeg_download.status_starting": "다운로드를 시작하는 중...",
"ui.ffmpeg_download.status_downloading": "다운로드 중... ({0}%)",
"ui.ffmpeg_download.status_extracting": "라이브러리 파일을 압축 해제하고 복사하는 중...",
"ui.ffmpeg_download.progress_extracting": "압축 해제 중...",
"ui.ffmpeg_download.status_complete": "FFmpeg 설치가 끝났어요!",
"ui.ffmpeg_download.status_cancelled": "다운로드가 취소되었어요.",
"ui.ffmpeg_download.status_failed": "설치에 실패했어요: {0}",
"ui.driver_warning.title": "NVIDIA 그래픽 드라이버 업데이트 권장",
"ui.driver_warning.body": "현재 감지된 NVIDIA 그래픽 드라이버 버전: <b><color=#ff5555>{0}</color></b>\n\n하드웨어 가속(NVENC)으로 영상을 렌더링하려면 드라이버 버전 <b><color=#55ff55>610.00 이상</color></b>이 필요해요.\n\n드라이버 버전이 더 낮으면 인코딩이 실패하거나 소프트웨어(CPU) 인코딩으로 바뀌어 렌더링이 훨씬 느려질 수 있어요. 그래픽 드라이버를 업데이트해 주세요.",
"ui.driver_warning.btn_update": "드라이버 업데이트",
"ui.driver_warning.btn_disable": "기능 끄기",
"ui.driver_warning.btn_continue": "그래도 계속"
} }
+17 -16
View File
@@ -1,4 +1,5 @@
using System; using System;
using ADOFAIRenderer.Localization;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Net.Http; using System.Net.Http;
@@ -128,7 +129,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
_window = O5Window.Create(ctx, bgPanelObj.transform, new O5WindowOptions _window = O5Window.Create(ctx, bgPanelObj.transform, new O5WindowOptions
{ {
Id = "RendererMod_FFmpegDownloadDialog", Id = "RendererMod_FFmpegDownloadDialog",
Title = "FFmpeg Library Installation Required", Title = LocalizationManager.Get("ui.ffmpeg_download.title"),
Size = new Vector2(580, 270), Size = new Vector2(580, 270),
Draggable = true, Draggable = true,
Resizable = false, Resizable = false,
@@ -150,16 +151,16 @@ internal class FFmpegDownloadUI : MonoBehaviour
// 3. Description text // 3. Description text
var descText = O5Factory.ControlText(ctx, content, 18f, noPad: true); var descText = O5Factory.ControlText(ctx, content, 18f, noPad: true);
var descStrBuilder = new StringBuilder(); var descStrBuilder = new StringBuilder();
descStrBuilder.AppendLine("FFmpeg shared libraries are required to render video."); descStrBuilder.AppendLine(LocalizationManager.Get("ui.ffmpeg_download.desc_required"));
if (Application.platform == RuntimePlatform.OSXPlayer) if (Application.platform == RuntimePlatform.OSXPlayer)
{ {
descStrBuilder.AppendLine("For macOS, reliable public builds are unavailable, so FFmpeg 8.1 must be built manually."); descStrBuilder.AppendLine(LocalizationManager.Get("ui.ffmpeg_download.desc_mac_build"));
descStrBuilder.AppendLine("Click the button below to open the official FFmpeg website."); descStrBuilder.AppendLine(LocalizationManager.Get("ui.ffmpeg_download.desc_mac_open"));
} }
else else
{ {
descStrBuilder.AppendLine("Click the button below to automatically download and install from the internet."); descStrBuilder.AppendLine(LocalizationManager.Get("ui.ffmpeg_download.desc_download"));
descStrBuilder.AppendLine("<color=#aaaaaa><size=13>(Download size: ~70MB, extracted to UserLibs folder)</size></color>"); descStrBuilder.AppendLine(LocalizationManager.Get("ui.ffmpeg_download.desc_size"));
} }
descText.text = descStrBuilder.ToString(); descText.text = descStrBuilder.ToString();
descText.alignment = TextAlignmentOptions.Center; descText.alignment = TextAlignmentOptions.Center;
@@ -196,7 +197,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
ctx: ctx, ctx: ctx,
parent: _buttonContainerObj.transform, parent: _buttonContainerObj.transform,
onClick: onDownloadClick, onClick: onDownloadClick,
text: Application.platform != RuntimePlatform.OSXPlayer ? "Download & Install" : "Open Website", text: LocalizationManager.Get(Application.platform != RuntimePlatform.OSXPlayer ? "ui.ffmpeg_download.btn_download" : "ui.ffmpeg_download.btn_open_site"),
id: "btn_ffmpeg_download", id: "btn_ffmpeg_download",
height: 40f height: 40f
); );
@@ -211,7 +212,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
ctx: ctx, ctx: ctx,
parent: _buttonContainerObj.transform, parent: _buttonContainerObj.transform,
onClick: CancelProcess, onClick: CancelProcess,
text: "Cancel", text: LocalizationManager.Get("ui.ffmpeg_download.btn_cancel"),
id: "btn_ffmpeg_cancel", id: "btn_ffmpeg_cancel",
height: 40f height: 40f
); );
@@ -341,7 +342,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
string tempZipPath = ""; string tempZipPath = "";
try try
{ {
UpdateStatus("Starting download...", new Color(0.8f, 0.8f, 0.8f)); UpdateStatus(LocalizationManager.Get("ui.ffmpeg_download.status_starting"), new Color(0.8f, 0.8f, 0.8f));
UpdateProgress(0f, "0%"); UpdateProgress(0f, "0%");
var userLibsDir = MelonEnvironment.UserLibsDirectory; var userLibsDir = MelonEnvironment.UserLibsDirectory;
@@ -361,19 +362,19 @@ internal class FFmpegDownloadUI : MonoBehaviour
var progressReporter = new Progress<float>(percent => var progressReporter = new Progress<float>(percent =>
{ {
UpdateProgress(percent, $"{percent * 100f:F1}%"); UpdateProgress(percent, $"{percent * 100f:F1}%");
UpdateStatus($"Downloading... ({(percent * 100f):F1}%)", new Color(0f, 0.8f, 1f)); UpdateStatus(LocalizationManager.Get("ui.ffmpeg_download.status_downloading", $"{percent * 100f:F1}"), new Color(0f, 0.8f, 1f));
}); });
await DownloadFileAsync(DownloadUrl, tempZipPath, progressReporter, _cts.Token); await DownloadFileAsync(DownloadUrl, tempZipPath, progressReporter, _cts.Token);
// Extract after download complete // Extract after download complete
UpdateStatus("Extracting and copying library files...", new Color(0.9f, 0.9f, 0.2f)); UpdateStatus(LocalizationManager.Get("ui.ffmpeg_download.status_extracting"), new Color(0.9f, 0.9f, 0.2f));
UpdateProgress(0.99f, "Extracting..."); UpdateProgress(0.99f, LocalizationManager.Get("ui.ffmpeg_download.progress_extracting"));
await ExtractDllsAsync(tempZipPath, userLibsDir); await ExtractDllsAsync(tempZipPath, userLibsDir);
// Update completion status // Update completion status
UpdateStatus("FFmpeg installation complete!", new Color(0.2f, 0.9f, 0.2f)); UpdateStatus(LocalizationManager.Get("ui.ffmpeg_download.status_complete"), new Color(0.2f, 0.9f, 0.2f));
UpdateProgress(1f, "100%"); UpdateProgress(1f, "100%");
await Task.Delay(1000); await Task.Delay(1000);
@@ -395,12 +396,12 @@ internal class FFmpegDownloadUI : MonoBehaviour
// ignored // ignored
} }
} }
UpdateStatus("Download was cancelled.", new Color(0.9f, 0.5f, 0.2f)); UpdateStatus(LocalizationManager.Get("ui.ffmpeg_download.status_cancelled"), new Color(0.9f, 0.5f, 0.2f));
} }
catch (Exception ex) catch (Exception ex)
{ {
Melon<RenderMod>.Logger.Error($"Error during FFmpeg download/installation: {ex}"); Melon<RenderMod>.Logger.Error($"Error during FFmpeg download/installation: {ex}");
UpdateStatus($"Installation failed: {ex.Message}", new Color(1f, 0.3f, 0.3f)); UpdateStatus(LocalizationManager.Get("ui.ffmpeg_download.status_failed", ex.Message), new Color(1f, 0.3f, 0.3f));
// Reset buttons on failure // Reset buttons on failure
ExecuteOnMainThread(() => ExecuteOnMainThread(() =>
@@ -410,7 +411,7 @@ internal class FFmpegDownloadUI : MonoBehaviour
var downloadBtnText = _buttonContainerObj.transform.Find("DownloadButton/DownloadText")?.GetComponent<TextMeshProUGUI>(); var downloadBtnText = _buttonContainerObj.transform.Find("DownloadButton/DownloadText")?.GetComponent<TextMeshProUGUI>();
if (downloadBtnText) if (downloadBtnText)
{ {
downloadBtnText.text = "Retry"; downloadBtnText.text = LocalizationManager.Get("ui.ffmpeg_download.btn_retry");
} }
}); });
} }
+7
View File
@@ -94,8 +94,15 @@ internal class RendererUIManager : MonoBehaviour
); );
_windowManager = O5WindowManager.Create(_context, transform, "RendererMod_Canvas"); _windowManager = O5WindowManager.Create(_context, transform, "RendererMod_Canvas");
// The O5Kit canvas is created with sortingOrder 32767. It is lowered so that the dialogs of
// CommonWarningDialogUI (32766) are drawn above the windows of this manager.
var windowCanvas = transform.Find("RendererMod_Canvas")?.GetComponent<Canvas>();
if (windowCanvas != null) windowCanvas.sortingOrder = WindowCanvasSortingOrder;
} }
private const int WindowCanvasSortingOrder = 32000;
private void Update() { private void Update() {
O5Object.TickAll(); O5Object.TickAll();
Context.Tooltip.Tick(); Context.Tooltip.Tick();
+48 -12
View File
@@ -39,7 +39,7 @@ internal static class RenderConfigWindow
/// </summary> /// </summary>
private static RenderConfig? _uiConfig; private static RenderConfig? _uiConfig;
private static RenderConfig UiConfig => _uiConfig ??= new RenderConfig(); private static RenderConfig UIConfig => _uiConfig ??= new RenderConfig();
// Controls cache for validation & sync // Controls cache for validation & sync
private static O5Slider? _renderFpsSlider; private static O5Slider? _renderFpsSlider;
@@ -120,7 +120,7 @@ internal static class RenderConfigWindow
ReloadPresetDropdown(GlobalSettings.Current.LastPresetName); ReloadPresetDropdown(GlobalSettings.Current.LastPresetName);
} }
var config = UiConfig; var config = UIConfig;
ApplyConfigToUI(config); ApplyConfigToUI(config);
SetWindowActive(true); SetWindowActive(true);
@@ -133,7 +133,7 @@ internal static class RenderConfigWindow
var (_, scrollContent, _) = var (_, scrollContent, _) =
O5Factory.ScrollView(ctx, content, spacing: 10f, padding: 8f); O5Factory.ScrollView(ctx, content, spacing: 10f, padding: 8f);
var config = UiConfig; var config = UIConfig;
EnsureDefaultConfig(config); EnsureDefaultConfig(config);
double[] renderFpsSnap = { 23.976, 24, 29.97, 30, 59.94, 60, 144, double[] renderFpsSnap = { 23.976, 24, 29.97, 30, 59.94, 60, 144,
@@ -843,9 +843,17 @@ internal static class RenderConfigWindow
private static void TryGoRender() private static void TryGoRender()
{ {
var config = UiConfig; var config = UIConfig;
SyncUItoConfig(config); SyncUItoConfig(config);
// The encoders use 4:2:0 chroma subsampling, which needs an even width and height.
if (config.RenderWidth % 2 != 0 || config.RenderHeight % 2 != 0)
{
ShowValidationError(LocalizationManager.Get(
"ui.render_config.validation_even_size", config.RenderWidth, config.RenderHeight));
return;
}
// Sync Output Directory // Sync Output Directory
if (_outputDirInput != null) if (_outputDirInput != null)
{ {
@@ -959,6 +967,13 @@ internal static class RenderConfigWindow
} }
} }
/// <summary>
/// Whether the text is the Custom label of any language. A preset with such a name could not be told apart
/// from the Custom entry of the dropdown after a language change.
/// </summary>
private static bool IsCustomPresetLabel(string text) =>
text == CustomPresetLabel || text == "(Modified)" || text == "(수정됨)";
private static string CustomPresetLabel => LocalizationManager.Get("ui.render_config.preset_custom"); private static string CustomPresetLabel => LocalizationManager.Get("ui.render_config.preset_custom");
private static void MarkConfigModified() private static void MarkConfigModified()
@@ -1010,7 +1025,7 @@ internal static class RenderConfigWindow
SyncUItoConfig(config); SyncUItoConfig(config);
var name = _presetNameInput?.Value.Trim(); var name = _presetNameInput?.Value.Trim();
if (string.IsNullOrWhiteSpace(name) || name == CustomPresetLabel) if (string.IsNullOrWhiteSpace(name) || IsCustomPresetLabel(name))
{ {
CommonWarningDialogUI.ShowDialog( CommonWarningDialogUI.ShowDialog(
LocalizationManager.Get("ui.render_config.validation_title"), LocalizationManager.Get("ui.render_config.validation_title"),
@@ -1023,12 +1038,16 @@ internal static class RenderConfigWindow
bool isCurrentPreset = !string.IsNullOrEmpty(_lastLoadedPresetName) && bool isCurrentPreset = !string.IsNullOrEmpty(_lastLoadedPresetName) &&
string.Equals(_lastLoadedPresetName, name, StringComparison.OrdinalIgnoreCase); string.Equals(_lastLoadedPresetName, name, StringComparison.OrdinalIgnoreCase);
var existing = PresetManager.GetPresets().FirstOrDefault(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)); // A preset with another name can share the file of this name (PresetManager.GetSafeFileName), e.g. "a/b" and "a_b".
var fileName = PresetManager.GetSafeFileName(name);
var existing = PresetManager.GetPresets().FirstOrDefault(p =>
string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase) ||
string.Equals(PresetManager.GetSafeFileName(p.Name), fileName, StringComparison.OrdinalIgnoreCase));
if (existing != null && !isCurrentPreset) if (existing != null && !isCurrentPreset)
{ {
CommonWarningDialogUI.ShowDialog( CommonWarningDialogUI.ShowDialog(
LocalizationManager.Get("ui.render_config.preset_overwrite_title"), LocalizationManager.Get("ui.render_config.preset_overwrite_title"),
LocalizationManager.Get("ui.render_config.preset_overwrite_desc", name), LocalizationManager.Get("ui.render_config.preset_overwrite_desc", existing.Name),
new DialogButton( new DialogButton(
LocalizationManager.Get("ui.render_config.confirm"), LocalizationManager.Get("ui.render_config.confirm"),
() => DoSavePreset(name, config) () => DoSavePreset(name, config)
@@ -1188,14 +1207,31 @@ internal static class RenderConfigWindow
private static void OnLanguageChanged(SystemLanguage language) private static void OnLanguageChanged(SystemLanguage language)
{ {
if (_configWindow == null) return; if (_configWindow == null || _presetDropdown == null) return;
if (_presetDropdown != null) // The preset names are not localized, only the Custom label is. A dropdown value that is not a preset name
// is the Custom label (of the previous language), which is shown only while the values are modified.
var names = PresetManager.GetPresets().Select(p => p.Name).ToList();
if (names.Count == 0)
{ {
var cur = _presetDropdown.Value; _presetDropdown.SetValues(new List<string> { CustomPresetLabel });
var isCustom = cur == "(Modified)" || cur == "(수정됨)" || cur == CustomPresetLabel; _presetDropdown.Set(CustomPresetLabel, false);
ReloadPresetDropdown(isCustom ? CustomPresetLabel : cur); UpdateDeleteButtonState();
return;
} }
var current = _presetDropdown.Value;
var isModified = string.IsNullOrEmpty(current) || !names.Contains(current);
_presetDropdown.SetValues(names);
if (isModified)
{
MarkConfigModified();
return;
}
_presetDropdown.Set(current, false);
UpdateDeleteButtonState();
} }
private static string GetVersionText() private static string GetVersionText()