SHA256
Add Preset Feature to Render Settings
This commit is contained in:
5 files changed
+674
-71
No files matched your search
@@ -68,5 +68,19 @@ namespace ADOFAIRenderer.Data
|
||||
callback(Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the selected enum value and updates the dropdown index.
|
||||
/// </summary>
|
||||
/// <param name="value">The enum value to set.</param>
|
||||
public void SetValue(T value)
|
||||
{
|
||||
if (_dropdown == null) return;
|
||||
int idx = _values.IndexOf(value);
|
||||
if (idx >= 0)
|
||||
{
|
||||
_dropdown.index = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MelonLoader;
|
||||
using MelonLoader.Utils;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ADOFAIRenderer.Data
|
||||
{
|
||||
public class RenderPreset
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public RenderConfig Config { get; set; } = null!;
|
||||
}
|
||||
|
||||
public static class PresetManager
|
||||
{
|
||||
private static string GetPresetsDirectory()
|
||||
{
|
||||
return Path.Combine(MelonEnvironment.GameRootDirectory, "UserData", "ADOFAIRenderer", "Presets");
|
||||
}
|
||||
|
||||
private static string GetSafeFileName(string name)
|
||||
{
|
||||
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, '_'));
|
||||
}
|
||||
|
||||
public static void SavePreset(string name, RenderConfig config)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentException("프리셋 이름은 비어 있을 수 없습니다.");
|
||||
}
|
||||
|
||||
string dir = GetPresetsDirectory();
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
var preset = new RenderPreset
|
||||
{
|
||||
Name = name,
|
||||
Config = config
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(preset, Formatting.Indented);
|
||||
string safeName = GetSafeFileName(name);
|
||||
string filePath = Path.Combine(dir, safeName + ".json");
|
||||
File.WriteAllText(filePath, json);
|
||||
}
|
||||
|
||||
public static List<RenderPreset> GetPresets()
|
||||
{
|
||||
var presets = new List<RenderPreset>();
|
||||
string dir = GetPresetsDirectory();
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
return presets;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string[] files = Directory.GetFiles(dir, "*.json");
|
||||
foreach (string file in files)
|
||||
{
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(file);
|
||||
var preset = JsonConvert.DeserializeObject<RenderPreset>(json);
|
||||
if (preset != null && !string.IsNullOrEmpty(preset.Name) && preset.Config != null)
|
||||
{
|
||||
presets.Add(preset);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MelonLogger.Warning($"프리셋 파일 '{Path.GetFileName(file)}' 로드 중 오류 발생: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MelonLogger.Error($"프리셋 목록을 불러오는 중 오류 발생: {ex.Message}");
|
||||
}
|
||||
|
||||
return presets;
|
||||
}
|
||||
|
||||
public static void DeletePreset(string name)
|
||||
{
|
||||
string dir = GetPresetsDirectory();
|
||||
string safeName = GetSafeFileName(name);
|
||||
string filePath = Path.Combine(dir, safeName + ".json");
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -4,31 +4,31 @@ namespace ADOFAIRenderer.Data
|
||||
{
|
||||
public class RenderConfig
|
||||
{
|
||||
public double RenderFPS;
|
||||
public double VideoFPS;
|
||||
public double RenderFPS { get; set; }
|
||||
public double VideoFPS { get; set; }
|
||||
|
||||
public int RenderWidth;
|
||||
public int RenderHeight;
|
||||
public int RenderWidth { get; set; }
|
||||
public int RenderHeight { get; set; }
|
||||
|
||||
public Codec Codec;
|
||||
public EncodingConfig? EncodingConfig;
|
||||
public Codec Codec { get; set; }
|
||||
public EncodingConfig? EncodingConfig { get; set; }
|
||||
|
||||
public double DelayAfterLevelEnd;
|
||||
public bool ShowJudgementText;
|
||||
public double DelayAfterLevelEnd { get; set; }
|
||||
public bool ShowJudgementText { get; set; }
|
||||
|
||||
public int? RandomSeed = null;
|
||||
public bool ProcessAudio;
|
||||
public int? RandomSeed { get; set; } = null;
|
||||
public bool ProcessAudio { get; set; }
|
||||
}
|
||||
|
||||
public class EncodingConfig
|
||||
{
|
||||
public RateControlMode RateControlMode;
|
||||
public RateControlMode RateControlMode { get; set; }
|
||||
|
||||
public int Bitrate;
|
||||
public int MaxBitrate;
|
||||
public int Bitrate { get; set; }
|
||||
public int MaxBitrate { get; set; }
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public int QP;
|
||||
public double KeyframeInterval;
|
||||
public bool ForceSoftwareEncoder;
|
||||
public int QP { get; set; }
|
||||
public double KeyframeInterval { get; set; }
|
||||
public bool ForceSoftwareEncoder { get; set; }
|
||||
}
|
||||
}
|
||||
+535
-54
@@ -63,6 +63,17 @@ namespace ADOFAIRenderer.UI
|
||||
{
|
||||
Melon<RenderMod>.Logger.Error("Button 'EncodingSettingsButton'을(를) 찾을 수 없습니다.");
|
||||
}
|
||||
|
||||
var presetSettingsButton = _root.Q<Button>("PresetSettingsButton");
|
||||
if (presetSettingsButton != null)
|
||||
{
|
||||
Melon<RenderMod>.Logger.Msg($"[UI] PresetSettingsButton 발견: pickingMode={presetSettingsButton.pickingMode}, enabledSelf={presetSettingsButton.enabledSelf}");
|
||||
presetSettingsButton.RegisterCallback<ClickEvent>(_ => OpenPresetConfig());
|
||||
}
|
||||
else
|
||||
{
|
||||
Melon<RenderMod>.Logger.Error("Button 'PresetSettingsButton'을(를) 찾을 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -94,15 +105,21 @@ namespace ADOFAIRenderer.UI
|
||||
}
|
||||
|
||||
// 1. 전체 화면 컨테이너 생성 및 배경 적용
|
||||
_encodingConfigContainer = new VisualElement { name = "EncodingSettingsContainer" };
|
||||
_encodingConfigContainer.style.position = Position.Absolute;
|
||||
_encodingConfigContainer.style.width = Length.Percent(100);
|
||||
_encodingConfigContainer.style.height = Length.Percent(100);
|
||||
_encodingConfigContainer.style.left = 0;
|
||||
_encodingConfigContainer.style.top = 0;
|
||||
_encodingConfigContainer.style.backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 1.0f));
|
||||
_encodingConfigContainer.style.justifyContent = Justify.Center;
|
||||
_encodingConfigContainer.style.alignItems = Align.Center;
|
||||
_encodingConfigContainer = new VisualElement
|
||||
{
|
||||
name = "EncodingSettingsContainer",
|
||||
style =
|
||||
{
|
||||
position = Position.Absolute,
|
||||
width = Length.Percent(100),
|
||||
height = Length.Percent(100),
|
||||
left = 0,
|
||||
top = 0,
|
||||
backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 1.0f)),
|
||||
justifyContent = Justify.Center,
|
||||
alignItems = Align.Center
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 타이틀 로우
|
||||
var titleRow = CreateRow();
|
||||
@@ -269,10 +286,9 @@ namespace ADOFAIRenderer.UI
|
||||
// 화면 전환 제어
|
||||
void ToggleScreens(bool showEncoding)
|
||||
{
|
||||
foreach (var child in _root.Children())
|
||||
if (_root.childCount > 0)
|
||||
{
|
||||
if (child == _encodingConfigContainer) continue;
|
||||
child.style.display = showEncoding ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
_root[0].style.display = showEncoding ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
}
|
||||
_encodingConfigContainer.style.display = showEncoding ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
@@ -287,42 +303,16 @@ namespace ADOFAIRenderer.UI
|
||||
warningRow.style.display = mode == RateControlMode.CQP && qpField.value == 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
// 로우 생성 헬퍼 (UXML의 ui-row 속성 복제)
|
||||
VisualElement CreateRow()
|
||||
{
|
||||
var row = new VisualElement
|
||||
{
|
||||
style =
|
||||
{
|
||||
flexDirection = FlexDirection.Row,
|
||||
height = 130,
|
||||
maxHeight = 130,
|
||||
minHeight = 80,
|
||||
justifyContent = Justify.Center,
|
||||
alignItems = Align.Center
|
||||
}
|
||||
};
|
||||
row.AddToClassList("ui-row");
|
||||
return row;
|
||||
}
|
||||
|
||||
// 라벨 생성 헬퍼 (우측 정렬 적용하여 인풋과의 인접성 향상)
|
||||
Label CreateFormLabel(string text)
|
||||
{
|
||||
var label = new Label(text)
|
||||
{
|
||||
style =
|
||||
{
|
||||
unityTextAlign = TextAnchor.MiddleRight,
|
||||
color = new StyleColor(Color.white)
|
||||
}
|
||||
};
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryGoRender()
|
||||
{
|
||||
if (!ValidateInputs())
|
||||
{
|
||||
Melon<RenderMod>.Logger.Error("필수 입력 값이 비어 있거나 올바르지 않습니다. 모든 설정을 입력해 주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
var widthField = _root.Q<IntegerField>("RenderWidth");
|
||||
var heightField = _root.Q<IntegerField>("RenderHeight");
|
||||
var randomSeedField = _root.Q<IntegerField>("RandomSeed");
|
||||
@@ -333,16 +323,6 @@ namespace ADOFAIRenderer.UI
|
||||
var showJudgementText = _root.Q<Toggle>("ShowJudgementText");
|
||||
var processAudio = _root.Q<Toggle>("ProcessAudioToggle");
|
||||
|
||||
if (string.IsNullOrEmpty(widthField.text) ||
|
||||
string.IsNullOrEmpty(heightField.text) ||
|
||||
string.IsNullOrEmpty(renderFpsField.text) ||
|
||||
string.IsNullOrEmpty(videoFpsField.text) ||
|
||||
string.IsNullOrEmpty(delayField.text))
|
||||
{
|
||||
Melon<RenderMod>.Logger.Error("필수 입력 값이 비어 있습니다. 모든 설정을 입력해 주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy validated values to Configuration manually
|
||||
var config = RenderManager.Instance.Configuration;
|
||||
config.RenderWidth = widthField.value;
|
||||
@@ -356,6 +336,8 @@ namespace ADOFAIRenderer.UI
|
||||
|
||||
if (!string.IsNullOrEmpty(randomSeedField.text))
|
||||
config.RandomSeed = randomSeedField.value;
|
||||
else
|
||||
config.RandomSeed = null;
|
||||
|
||||
RenderManager.Instance.Configuration.EncodingConfig ??= GenerateEncodingConfig();
|
||||
|
||||
@@ -363,6 +345,505 @@ namespace ADOFAIRenderer.UI
|
||||
RenderManager.Instance.SetupGameScene();
|
||||
}
|
||||
|
||||
private VisualElement? _presetConfigContainer;
|
||||
|
||||
private bool ValidateInputs()
|
||||
{
|
||||
var widthField = _root.Q<IntegerField>("RenderWidth");
|
||||
var heightField = _root.Q<IntegerField>("RenderHeight");
|
||||
var renderFpsField = _root.Q<DoubleField>("RenderFPS");
|
||||
var videoFpsField = _root.Q<DoubleField>("VideoFPS");
|
||||
var delayField = _root.Q<DoubleField>("DelayAfterLevelEnd");
|
||||
|
||||
if (widthField == null || string.IsNullOrEmpty(widthField.text) ||
|
||||
heightField == null || string.IsNullOrEmpty(heightField.text) ||
|
||||
renderFpsField == null || string.IsNullOrEmpty(renderFpsField.text) ||
|
||||
videoFpsField == null || string.IsNullOrEmpty(videoFpsField.text) ||
|
||||
delayField == null || string.IsNullOrEmpty(delayField.text))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (widthField.value <= 0 || heightField.value <= 0 ||
|
||||
renderFpsField.value <= 0 || videoFpsField.value <= 0 ||
|
||||
delayField.value < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ApplyConfigToUI(RenderConfig config)
|
||||
{
|
||||
var widthField = _root.Q<IntegerField>("RenderWidth");
|
||||
var heightField = _root.Q<IntegerField>("RenderHeight");
|
||||
var randomSeedField = _root.Q<IntegerField>("RandomSeed");
|
||||
var renderFpsField = _root.Q<DoubleField>("RenderFPS");
|
||||
var videoFpsField = _root.Q<DoubleField>("VideoFPS");
|
||||
var delayField = _root.Q<DoubleField>("DelayAfterLevelEnd");
|
||||
var showJudgementText = _root.Q<Toggle>("ShowJudgementText");
|
||||
var processAudio = _root.Q<Toggle>("ProcessAudioToggle");
|
||||
|
||||
if (widthField != null) widthField.value = config.RenderWidth;
|
||||
if (heightField != null) heightField.value = config.RenderHeight;
|
||||
if (renderFpsField != null) renderFpsField.value = config.RenderFPS;
|
||||
if (videoFpsField != null) videoFpsField.value = config.VideoFPS;
|
||||
if (delayField != null) delayField.value = config.DelayAfterLevelEnd;
|
||||
if (showJudgementText != null) showJudgementText.value = config.ShowJudgementText;
|
||||
if (processAudio != null) processAudio.value = config.ProcessAudio;
|
||||
|
||||
if (randomSeedField != null)
|
||||
{
|
||||
if (config.RandomSeed.HasValue)
|
||||
{
|
||||
randomSeedField.value = config.RandomSeed.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
randomSeedField.value = 0;
|
||||
var textProperty = typeof(IntegerField).GetProperty("text", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
|
||||
if (textProperty == null)
|
||||
{
|
||||
textProperty = typeof(TextInputBaseField<int>).GetProperty("text", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy);
|
||||
}
|
||||
textProperty?.SetValue(randomSeedField, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
_codecBinder.SetValue(config.Codec);
|
||||
RenderManager.Instance.Configuration = config;
|
||||
}
|
||||
|
||||
private void OpenPresetConfig()
|
||||
{
|
||||
if (_presetConfigContainer != null && _root.Contains(_presetConfigContainer))
|
||||
{
|
||||
_root.Remove(_presetConfigContainer);
|
||||
}
|
||||
|
||||
_presetConfigContainer = new VisualElement { name = "PresetSettingsContainer" };
|
||||
_presetConfigContainer.style.position = Position.Absolute;
|
||||
_presetConfigContainer.style.width = Length.Percent(100);
|
||||
_presetConfigContainer.style.height = Length.Percent(100);
|
||||
_presetConfigContainer.style.left = 0;
|
||||
_presetConfigContainer.style.top = 0;
|
||||
_presetConfigContainer.style.backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 1.0f));
|
||||
_presetConfigContainer.style.justifyContent = Justify.Center;
|
||||
_presetConfigContainer.style.alignItems = Align.Center;
|
||||
|
||||
// Title Row
|
||||
var titleRow = CreateRow();
|
||||
titleRow.style.height = StyleKeyword.Auto;
|
||||
titleRow.style.minHeight = StyleKeyword.Null;
|
||||
titleRow.style.maxHeight = StyleKeyword.Null;
|
||||
var title = new Label("프리셋 설정")
|
||||
{
|
||||
style =
|
||||
{
|
||||
fontSize = 36,
|
||||
unityFontStyleAndWeight = FontStyle.Bold,
|
||||
color = new StyleColor(Color.white),
|
||||
unityTextAlign = TextAnchor.MiddleCenter,
|
||||
width = 820,
|
||||
height = StyleKeyword.Auto
|
||||
}
|
||||
};
|
||||
titleRow.Add(title);
|
||||
_presetConfigContainer.Add(titleRow);
|
||||
|
||||
// ScrollView for Preset List
|
||||
var scrollView = new ScrollView
|
||||
{
|
||||
style =
|
||||
{
|
||||
width = 820,
|
||||
height = 380,
|
||||
backgroundColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f, 0.5f)),
|
||||
borderTopWidth = 1,
|
||||
borderBottomWidth = 1,
|
||||
borderLeftWidth = 1,
|
||||
borderRightWidth = 1,
|
||||
borderTopColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f, 0.8f)),
|
||||
borderBottomColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f, 0.8f)),
|
||||
borderLeftColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f, 0.8f)),
|
||||
borderRightColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f, 0.8f)),
|
||||
borderTopLeftRadius = 5,
|
||||
borderTopRightRadius = 5,
|
||||
borderBottomLeftRadius = 5,
|
||||
borderBottomRightRadius = 5,
|
||||
paddingBottom = 10,
|
||||
paddingTop = 10,
|
||||
paddingLeft = 10,
|
||||
paddingRight = 10,
|
||||
marginBottom = 20
|
||||
}
|
||||
};
|
||||
_presetConfigContainer.Add(scrollView);
|
||||
|
||||
// Style the vertical scrollbar
|
||||
scrollView.verticalScroller.style.width = 10f;
|
||||
|
||||
var tracker = scrollView.verticalScroller.Q<VisualElement>("unity-tracker");
|
||||
if (tracker != null)
|
||||
{
|
||||
tracker.style.backgroundColor = new StyleColor(new Color(0.08f, 0.08f, 0.08f, 0.3f));
|
||||
tracker.style.borderTopWidth = 0f;
|
||||
tracker.style.borderBottomWidth = 0f;
|
||||
tracker.style.borderLeftWidth = 0f;
|
||||
tracker.style.borderRightWidth = 0f;
|
||||
}
|
||||
|
||||
var dragger = scrollView.verticalScroller.Q<VisualElement>("unity-dragger");
|
||||
if (dragger != null)
|
||||
{
|
||||
dragger.style.backgroundColor = new StyleColor(new Color(0.25f, 0.25f, 0.25f, 0.8f));
|
||||
dragger.style.borderTopLeftRadius = 4f;
|
||||
dragger.style.borderTopRightRadius = 4f;
|
||||
dragger.style.borderBottomLeftRadius = 4f;
|
||||
dragger.style.borderBottomRightRadius = 4f;
|
||||
dragger.style.borderTopWidth = 0f;
|
||||
dragger.style.borderBottomWidth = 0f;
|
||||
dragger.style.borderLeftWidth = 0f;
|
||||
dragger.style.borderRightWidth = 0f;
|
||||
}
|
||||
|
||||
var lowButton = scrollView.verticalScroller.Q<VisualElement>("unity-low-button");
|
||||
if (lowButton != null)
|
||||
{
|
||||
lowButton.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
var highButton = scrollView.verticalScroller.Q<VisualElement>("unity-high-button");
|
||||
if (highButton != null)
|
||||
{
|
||||
highButton.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
// Refresh Preset List
|
||||
System.Action refreshPresets = null!;
|
||||
refreshPresets = () =>
|
||||
{
|
||||
scrollView.Clear();
|
||||
var presets = PresetManager.GetPresets();
|
||||
if (presets.Count == 0)
|
||||
{
|
||||
var emptyRow = CreateRow();
|
||||
emptyRow.style.height = StyleKeyword.Auto;
|
||||
emptyRow.style.minHeight = StyleKeyword.Null;
|
||||
emptyRow.style.maxHeight = StyleKeyword.Null;
|
||||
|
||||
var emptyLabel = new Label("저장된 프리셋이 없습니다.")
|
||||
{
|
||||
style =
|
||||
{
|
||||
color = new StyleColor(Color.gray),
|
||||
unityTextAlign = TextAnchor.MiddleCenter,
|
||||
marginTop = 80,
|
||||
fontSize = 20
|
||||
}
|
||||
};
|
||||
emptyRow.Add(emptyLabel);
|
||||
scrollView.Add(emptyRow);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < presets.Count; i++)
|
||||
{
|
||||
var preset = presets[i];
|
||||
var itemRow = new VisualElement
|
||||
{
|
||||
style =
|
||||
{
|
||||
flexDirection = FlexDirection.Row,
|
||||
justifyContent = Justify.SpaceBetween,
|
||||
alignItems = Align.Center,
|
||||
paddingBottom = 5,
|
||||
paddingTop = 5,
|
||||
borderBottomWidth = (i == presets.Count - 1) ? 0 : 1,
|
||||
borderBottomColor = new StyleColor(new Color(0.2f, 0.2f, 0.2f, 0.8f))
|
||||
}
|
||||
};
|
||||
itemRow.AddToClassList("ui-row");
|
||||
|
||||
var nameLabel = new Label(preset.Name)
|
||||
{
|
||||
style =
|
||||
{
|
||||
color = new StyleColor(Color.white),
|
||||
fontSize = 30,
|
||||
flexGrow = 1
|
||||
}
|
||||
};
|
||||
|
||||
var btnContainer = new VisualElement
|
||||
{
|
||||
style = { flexDirection = FlexDirection.Row }
|
||||
};
|
||||
|
||||
var loadBtn = new Button { text = "불러오기" };
|
||||
loadBtn.style.marginRight = 10;
|
||||
loadBtn.style.width = 180f;
|
||||
loadBtn.style.marginTop = 0f;
|
||||
loadBtn.style.marginBottom = 0f;
|
||||
loadBtn.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.22f, 1.0f));
|
||||
loadBtn.style.borderTopColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
loadBtn.style.borderBottomColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
loadBtn.style.borderLeftColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
loadBtn.style.borderRightColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
loadBtn.style.borderTopWidth = 1f;
|
||||
loadBtn.style.borderBottomWidth = 1f;
|
||||
loadBtn.style.borderLeftWidth = 1f;
|
||||
loadBtn.style.borderRightWidth = 1f;
|
||||
loadBtn.style.borderTopLeftRadius = 8f;
|
||||
loadBtn.style.borderTopRightRadius = 8f;
|
||||
loadBtn.style.borderBottomLeftRadius = 8f;
|
||||
loadBtn.style.borderBottomRightRadius = 8f;
|
||||
loadBtn.style.paddingLeft = 10f;
|
||||
loadBtn.style.paddingRight = 10f;
|
||||
loadBtn.style.paddingTop = 10f;
|
||||
loadBtn.style.paddingBottom = 10f;
|
||||
loadBtn.style.fontSize = 30f;
|
||||
loadBtn.style.height = 80f;
|
||||
loadBtn.style.color = new StyleColor(Color.white);
|
||||
|
||||
loadBtn.RegisterCallback<ClickEvent>(_ =>
|
||||
{
|
||||
ApplyConfigToUI(preset.Config);
|
||||
ToggleScreens(false);
|
||||
_root.Remove(_presetConfigContainer);
|
||||
_presetConfigContainer = null;
|
||||
});
|
||||
|
||||
var deleteBtn = new Button { text = "삭제" };
|
||||
deleteBtn.style.width = 120f;
|
||||
deleteBtn.style.marginTop = 0f;
|
||||
deleteBtn.style.marginBottom = 0f;
|
||||
deleteBtn.style.backgroundColor = new StyleColor(new Color(0.25f, 0.12f, 0.12f, 1.0f));
|
||||
deleteBtn.style.borderTopColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
|
||||
deleteBtn.style.borderBottomColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
|
||||
deleteBtn.style.borderLeftColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
|
||||
deleteBtn.style.borderRightColor = new StyleColor(new Color(0.45f, 0.2f, 0.2f, 0.8f));
|
||||
deleteBtn.style.borderTopWidth = 1f;
|
||||
deleteBtn.style.borderBottomWidth = 1f;
|
||||
deleteBtn.style.borderLeftWidth = 1f;
|
||||
deleteBtn.style.borderRightWidth = 1f;
|
||||
deleteBtn.style.borderTopLeftRadius = 8f;
|
||||
deleteBtn.style.borderTopRightRadius = 8f;
|
||||
deleteBtn.style.borderBottomLeftRadius = 8f;
|
||||
deleteBtn.style.borderBottomRightRadius = 8f;
|
||||
deleteBtn.style.paddingLeft = 10f;
|
||||
deleteBtn.style.paddingRight = 10f;
|
||||
deleteBtn.style.paddingTop = 10f;
|
||||
deleteBtn.style.paddingBottom = 10f;
|
||||
deleteBtn.style.fontSize = 30f;
|
||||
deleteBtn.style.height = 80f;
|
||||
deleteBtn.style.color = new StyleColor(Color.white);
|
||||
|
||||
deleteBtn.RegisterCallback<ClickEvent>(_ =>
|
||||
{
|
||||
CommonWarningDialogUI.ShowDialog(
|
||||
"프리셋 삭제",
|
||||
$"'{preset.Name}' 프리셋을 정말 삭제하시겠습니까?",
|
||||
new DialogButton("예", () =>
|
||||
{
|
||||
PresetManager.DeletePreset(preset.Name);
|
||||
refreshPresets();
|
||||
}),
|
||||
new DialogButton("아니오", () => {})
|
||||
);
|
||||
});
|
||||
|
||||
btnContainer.Add(loadBtn);
|
||||
btnContainer.Add(deleteBtn);
|
||||
|
||||
itemRow.Add(nameLabel);
|
||||
itemRow.Add(btnContainer);
|
||||
|
||||
scrollView.Add(itemRow);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
refreshPresets();
|
||||
|
||||
// Save row: Label + TextField + Save Button
|
||||
var saveRow = CreateRow();
|
||||
saveRow.style.marginBottom = 20;
|
||||
|
||||
var nameLabelLabel = CreateFormLabel("새 프리셋 이름");
|
||||
nameLabelLabel.style.marginRight = 10;
|
||||
|
||||
var nameField = new TextField { label = "", name = "NewPresetName" };
|
||||
nameField.style.width = 400;
|
||||
nameField.style.marginRight = 20;
|
||||
|
||||
// 기존 폼 입력 필드 스타일과 맞춤
|
||||
var textInput = nameField.Q("unity-text-input");
|
||||
if (textInput != null)
|
||||
{
|
||||
textInput.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 1.0f));
|
||||
textInput.style.borderTopLeftRadius = 6f;
|
||||
textInput.style.borderTopRightRadius = 6f;
|
||||
textInput.style.borderBottomLeftRadius = 6f;
|
||||
textInput.style.borderBottomRightRadius = 6f;
|
||||
textInput.style.borderTopWidth = 1f;
|
||||
textInput.style.borderBottomWidth = 1f;
|
||||
textInput.style.borderLeftWidth = 1f;
|
||||
textInput.style.borderRightWidth = 1f;
|
||||
textInput.style.borderTopColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
textInput.style.borderBottomColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
textInput.style.borderLeftColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
textInput.style.borderRightColor = new StyleColor(new Color(0.3f, 0.3f, 0.35f, 0.8f));
|
||||
textInput.style.paddingLeft = 8f;
|
||||
textInput.style.color = new StyleColor(Color.white);
|
||||
}
|
||||
else
|
||||
{
|
||||
nameField.style.backgroundColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f, 1.0f));
|
||||
nameField.style.borderTopLeftRadius = 6f;
|
||||
nameField.style.borderTopRightRadius = 6f;
|
||||
nameField.style.borderBottomLeftRadius = 6f;
|
||||
nameField.style.borderBottomRightRadius = 6f;
|
||||
}
|
||||
|
||||
var saveBtn = new Button { text = "저장" };
|
||||
|
||||
saveBtn.RegisterCallback<ClickEvent>(_ =>
|
||||
{
|
||||
string presetName = nameField.value;
|
||||
if (string.IsNullOrWhiteSpace(presetName))
|
||||
{
|
||||
CommonWarningDialogUI.ShowDialog(
|
||||
"저장 실패",
|
||||
"프리셋 이름을 입력해 주세요.",
|
||||
new DialogButton("확인", () => {})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateInputs())
|
||||
{
|
||||
CommonWarningDialogUI.ShowDialog(
|
||||
"저장 실패",
|
||||
"필수 입력 값이 비어 있거나 올바르지 않아 프리셋을 저장할 수 없습니다.",
|
||||
new DialogButton("확인", () => {})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var widthField = _root.Q<IntegerField>("RenderWidth");
|
||||
var heightField = _root.Q<IntegerField>("RenderHeight");
|
||||
var renderFpsField = _root.Q<DoubleField>("RenderFPS");
|
||||
var videoFpsField = _root.Q<DoubleField>("VideoFPS");
|
||||
var delayField = _root.Q<DoubleField>("DelayAfterLevelEnd");
|
||||
var showJudgementText = _root.Q<Toggle>("ShowJudgementText");
|
||||
var processAudio = _root.Q<Toggle>("ProcessAudioToggle");
|
||||
var randomSeedField = _root.Q<IntegerField>("RandomSeed");
|
||||
|
||||
var currentConfig = new RenderConfig
|
||||
{
|
||||
RenderWidth = widthField.value,
|
||||
RenderHeight = heightField.value,
|
||||
RenderFPS = renderFpsField.value,
|
||||
VideoFPS = videoFpsField.value,
|
||||
DelayAfterLevelEnd = delayField.value,
|
||||
ShowJudgementText = showJudgementText.value,
|
||||
ProcessAudio = processAudio.value,
|
||||
Codec = _codecBinder.Value
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(randomSeedField.text))
|
||||
{
|
||||
currentConfig.RandomSeed = randomSeedField.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentConfig.RandomSeed = null;
|
||||
}
|
||||
|
||||
currentConfig.EncodingConfig = RenderManager.Instance.Configuration.EncodingConfig ?? GenerateEncodingConfig();
|
||||
|
||||
PresetManager.SavePreset(presetName, currentConfig);
|
||||
nameField.value = string.Empty;
|
||||
refreshPresets();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
CommonWarningDialogUI.ShowDialog(
|
||||
"저장 실패",
|
||||
$"프리셋 저장 중 오류가 발생했습니다: {ex.Message}",
|
||||
new DialogButton("확인", () => {})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
saveRow.Add(nameLabelLabel);
|
||||
saveRow.Add(nameField);
|
||||
saveRow.Add(saveBtn);
|
||||
_presetConfigContainer.Add(saveRow);
|
||||
|
||||
// Bottom controls: Close Button
|
||||
var closeRow = CreateRow();
|
||||
|
||||
var closeBtn = new Button { text = "닫기" };
|
||||
|
||||
closeBtn.RegisterCallback<ClickEvent>(_ =>
|
||||
{
|
||||
ToggleScreens(false);
|
||||
_root.Remove(_presetConfigContainer);
|
||||
_presetConfigContainer = null;
|
||||
});
|
||||
closeRow.Add(closeBtn);
|
||||
_presetConfigContainer.Add(closeRow);
|
||||
|
||||
_root.Add(_presetConfigContainer);
|
||||
ToggleScreens(true);
|
||||
|
||||
void ToggleScreens(bool showPreset)
|
||||
{
|
||||
if (_root.childCount > 0)
|
||||
{
|
||||
_root[0].style.display = showPreset ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
}
|
||||
_presetConfigContainer.style.display = showPreset ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private VisualElement CreateRow()
|
||||
{
|
||||
var row = new VisualElement
|
||||
{
|
||||
style =
|
||||
{
|
||||
flexDirection = FlexDirection.Row,
|
||||
height = 130,
|
||||
maxHeight = 130,
|
||||
minHeight = 80,
|
||||
justifyContent = Justify.Center,
|
||||
alignItems = Align.Center
|
||||
}
|
||||
};
|
||||
row.AddToClassList("ui-row");
|
||||
return row;
|
||||
}
|
||||
|
||||
private Label CreateFormLabel(string text)
|
||||
{
|
||||
var label = new Label(text)
|
||||
{
|
||||
style =
|
||||
{
|
||||
unityTextAlign = TextAnchor.MiddleRight,
|
||||
color = new StyleColor(Color.white)
|
||||
}
|
||||
};
|
||||
return label;
|
||||
}
|
||||
|
||||
private EncodingConfig GenerateEncodingConfig()
|
||||
{
|
||||
return new EncodingConfig
|
||||
|
||||
+6
-1
@@ -8,7 +8,7 @@
|
||||
<Var name="RenameProperties" value="true" />
|
||||
<Var name="RenameEvents" value="true" />
|
||||
<Var name="RenameFields" value="true" />
|
||||
<Var name="UseUnicodeNames" value="true" />
|
||||
<Var name="UseUnicodeNames" value="false" />
|
||||
<Var name="EmbedManifests" value="true" />
|
||||
|
||||
<Var name="Use=rename" value="true" />
|
||||
@@ -40,5 +40,10 @@
|
||||
|
||||
<!-- Skip UI-associated types (MonoBehaviours loaded from scene/prefabs or bound to UI Toolkit dataSource) -->
|
||||
<SkipType name="ADOFAIRenderer.UI.RenderConfigUIManager" skipFields="true" />
|
||||
|
||||
<!-- Skip Preset & Config types for JSON serialization -->
|
||||
<SkipType name="ADOFAIRenderer.Data.RenderPreset" skipFields="true" skipProperties="true" />
|
||||
<SkipType name="ADOFAIRenderer.Data.RenderConfig" skipFields="true" skipProperties="true" />
|
||||
<SkipType name="ADOFAIRenderer.Data.EncodingConfig" skipFields="true" skipProperties="true" />
|
||||
</Module>
|
||||
</Obfuscator>
|
||||
Reference in new issue
Block a user