Penumbra/Penumbra/UI/AdvancedWindow/ModEditWindow.Materials.ConstantEditor.cs
2023-09-21 02:15:23 +02:00

247 lines
9.2 KiB
C#

using ImGuiNET;
using OtterGui.Raii;
using OtterGui;
using Penumbra.GameData;
namespace Penumbra.UI.AdvancedWindow;
public partial class ModEditWindow
{
private interface IConstantEditor
{
bool Draw(Span<float> values, bool disabled);
}
private sealed class FloatConstantEditor : IConstantEditor
{
public static readonly FloatConstantEditor Default = new(null, null, 0.1f, 0.0f, 1.0f, 0.0f, 3, string.Empty);
private readonly float? _minimum;
private readonly float? _maximum;
private readonly float _speed;
private readonly float _relativeSpeed;
private readonly float _factor;
private readonly float _bias;
private readonly string _format;
public FloatConstantEditor(float? minimum, float? maximum, float speed, float relativeSpeed, float factor, float bias, byte precision,
string unit)
{
_minimum = minimum;
_maximum = maximum;
_speed = speed;
_relativeSpeed = relativeSpeed;
_factor = factor;
_bias = bias;
_format = $"%.{Math.Min(precision, (byte)9)}f";
if (unit.Length > 0)
_format = $"{_format} {unit.Replace("%", "%%")}";
}
public bool Draw(Span<float> values, bool disabled)
{
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
var fieldWidth = (ImGui.CalcItemWidth() - (values.Length - 1) * spacing) / values.Length;
var ret = false;
// Not using DragScalarN because of _relativeSpeed and other points of lost flexibility.
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
{
if (valueIdx > 0)
ImGui.SameLine(0.0f, spacing);
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
var value = (values[valueIdx] - _bias) / _factor;
if (disabled)
{
ImGui.DragFloat($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), value, value, _format);
}
else
{
if (ImGui.DragFloat($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), _minimum ?? 0.0f,
_maximum ?? 0.0f, _format))
{
values[valueIdx] = Clamp(value) * _factor + _bias;
ret = true;
}
}
}
return ret;
}
private float Clamp(float value)
=> Math.Clamp(value, _minimum ?? float.NegativeInfinity, _maximum ?? float.PositiveInfinity);
}
private sealed class IntConstantEditor : IConstantEditor
{
private readonly int? _minimum;
private readonly int? _maximum;
private readonly float _speed;
private readonly float _relativeSpeed;
private readonly float _factor;
private readonly float _bias;
private readonly string _format;
public IntConstantEditor(int? minimum, int? maximum, float speed, float relativeSpeed, float factor, float bias, string unit)
{
_minimum = minimum;
_maximum = maximum;
_speed = speed;
_relativeSpeed = relativeSpeed;
_factor = factor;
_bias = bias;
_format = "%d";
if (unit.Length > 0)
_format = $"{_format} {unit.Replace("%", "%%")}";
}
public bool Draw(Span<float> values, bool disabled)
{
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
var fieldWidth = (ImGui.CalcItemWidth() - (values.Length - 1) * spacing) / values.Length;
var ret = false;
// Not using DragScalarN because of _relativeSpeed and other points of lost flexibility.
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
{
if (valueIdx > 0)
ImGui.SameLine(0.0f, spacing);
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
var value = (int)Math.Clamp(MathF.Round((values[valueIdx] - _bias) / _factor), int.MinValue, int.MaxValue);
if (disabled)
{
ImGui.DragInt($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), value, value, _format);
}
else
{
if (ImGui.DragInt($"##{valueIdx}", ref value, Math.Max(_speed, value * _relativeSpeed), _minimum ?? 0, _maximum ?? 0,
_format))
{
values[valueIdx] = Clamp(value) * _factor + _bias;
ret = true;
}
}
}
return ret;
}
private int Clamp(int value)
=> Math.Clamp(value, _minimum ?? int.MinValue, _maximum ?? int.MaxValue);
}
private sealed class ColorConstantEditor : IConstantEditor
{
private readonly bool _squaredRgb;
private readonly bool _clamped;
public ColorConstantEditor(bool squaredRgb, bool clamped)
{
_squaredRgb = squaredRgb;
_clamped = clamped;
}
public bool Draw(Span<float> values, bool disabled)
{
switch (values.Length)
{
case 3:
{
var value = new Vector3(values);
if (_squaredRgb)
value = PseudoSqrtRgb(value);
if (!ImGui.ColorEdit3("##0", ref value, ImGuiColorEditFlags.Float | (_clamped ? 0 : ImGuiColorEditFlags.HDR)) || disabled)
return false;
if (_squaredRgb)
value = PseudoSquareRgb(value);
if (_clamped)
value = Vector3.Clamp(value, Vector3.Zero, Vector3.One);
value.CopyTo(values);
return true;
}
case 4:
{
var value = new Vector4(values);
if (_squaredRgb)
value = PseudoSqrtRgb(value);
if (!ImGui.ColorEdit4("##0", ref value,
ImGuiColorEditFlags.Float | ImGuiColorEditFlags.AlphaPreviewHalf | (_clamped ? 0 : ImGuiColorEditFlags.HDR))
|| disabled)
return false;
if (_squaredRgb)
value = PseudoSquareRgb(value);
if (_clamped)
value = Vector4.Clamp(value, Vector4.Zero, Vector4.One);
value.CopyTo(values);
return true;
}
default: return FloatConstantEditor.Default.Draw(values, disabled);
}
}
}
private sealed class EnumConstantEditor : IConstantEditor
{
private readonly IReadOnlyList<(string Label, float Value, string Description)> _values;
public EnumConstantEditor(IReadOnlyList<(string Label, float Value, string Description)> values)
=> _values = values;
public bool Draw(Span<float> values, bool disabled)
{
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
var fieldWidth = (ImGui.CalcItemWidth() - (values.Length - 1) * spacing) / values.Length;
var ret = false;
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
{
using var id = ImRaii.PushId(valueIdx);
if (valueIdx > 0)
ImGui.SameLine(0.0f, spacing);
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
var currentValue = values[valueIdx];
var currentLabel = _values.FirstOrNull(v => v.Value == currentValue)?.Label
?? currentValue.ToString(CultureInfo.CurrentCulture);
ret = disabled
? ImGui.InputText(string.Empty, ref currentLabel, (uint)currentLabel.Length, ImGuiInputTextFlags.ReadOnly)
: DrawCombo(currentLabel, ref values[valueIdx]);
}
return ret;
}
private bool DrawCombo(string label, ref float currentValue)
{
using var c = ImRaii.Combo(string.Empty, label);
if (!c)
return false;
var ret = false;
foreach (var (valueLabel, value, valueDescription) in _values)
{
if (ImGui.Selectable(valueLabel, value == currentValue))
{
currentValue = value;
ret = true;
}
if (valueDescription.Length > 0)
ImGuiUtil.SelectableHelpMarker(valueDescription);
}
return ret;
}
}
}