Files
Shigure/UI/UnitEditorForm.cs
2026-06-14 01:13:58 +08:00

1027 lines
35 KiB
C#

using System.Drawing;
namespace Shigure;
/// <summary>
/// 动态单位 / 数量字段的编辑弹窗: 选择类别(单位/数量)与选择器, 按所选类型动态显隐参数控件。
/// 光环候选来自当前职业/专精的 group 字段。校验名称非空、唯一、非纯数字、不含 '.'/'$'。
/// </summary>
public sealed class UnitEditorForm : Form
{
private const int RowWidth = 800;
private const int LabelWidth = 132;
private const int ControlLeft = LabelWidth + 10;
private static readonly RoleOption[] RoleOptions =
[
new("坦克 (1)", 1),
new("治疗 (2)", 2),
new("输出 (3)", 3)
];
private static readonly DispelTypeOption[] DispelTypeOptions =
[
new("1: 魔法", 1),
new("2: 疾病", 2),
new("3: 诅咒", 3),
new("4: 中毒", 4)
];
private static readonly LowestHealthAuraFilterItem[] LowestHealthAuraFilterOptions =
[
new("不筛选光环", LowestHealthAuraFilterKind.None),
new("带任一光环", LowestHealthAuraFilterKind.WithAnyAura),
new("不带某光环", LowestHealthAuraFilterKind.WithoutAura),
new("带某光环", LowestHealthAuraFilterKind.WithAura),
new("某光环值等于", LowestHealthAuraFilterKind.WithAuraCount)
];
private static readonly SelectorItem[] UnitSelectors =
[
new("生命值最低", UnitSelectorKind.LowestHealth),
new("按职责", UnitSelectorKind.UnitWithRole),
new("按职责且不带某光环", UnitSelectorKind.UnitWithRoleWithoutAura),
new("带某光环(持续最久)", UnitSelectorKind.UnitWithAura),
new("带某驱散类型", UnitSelectorKind.UnitWithDispelType)
];
private static readonly CountItem[] CountSelectors =
[
new("低于阈值的人数", CountKind.UnitsBelowHealth),
new("不带某光环且低血的人数", CountKind.UnitsWithoutAuraBelowHealth),
new("拥有某光环的人数", CountKind.UnitsWithAura)
];
private static readonly ThresholdModeItem[] ThresholdModeOptions =
[
new("固定阈值", false),
new("动态阈值", true)
];
private readonly IReadOnlyList<string> _auraFields;
private readonly IReadOnlyList<string> _thresholdFields;
private readonly HashSet<string> _takenNames;
private readonly Label _healthNameLabel = new();
private readonly TextBox _nameBox = new();
private readonly TextBox _healthNameBox = new();
private readonly ComboBox _categoryBox = new();
private readonly ComboBox _selectorBox = new();
private readonly ComboBox _lowestHealthAuraFilterBox = new();
private readonly FlowLayoutPanel _paramPanel = new();
private readonly NumericUpDown _thresholdBox = new();
private readonly ComboBox _thresholdModeBox = new();
private readonly ComboBox _thresholdFieldBox = new();
private readonly ComboBox _roleBox = new();
private readonly CheckBox _reverseBox = new();
private readonly ComboBox _auraBox = new();
private readonly CheckedListBox _aurasBox = new();
private readonly NumericUpDown _auraCountBox = new();
private readonly ComboBox _dispelTypeBox = new();
private Panel _thresholdModeRow = null!;
private Panel _thresholdRow = null!;
private Panel _thresholdFieldRow = null!;
private Panel _lowestHealthAuraFilterRow = null!;
private Panel _roleRow = null!;
private Panel _reverseRow = null!;
private Panel _auraRow = null!;
private Panel _aurasRow = null!;
private Panel _auraCountRow = null!;
private Panel _dispelRow = null!;
public ModuleUnit? ResultUnit { get; private set; }
public ModuleCountField? ResultCount { get; private set; }
public UnitEditorForm(
IReadOnlyList<string> auraFields,
IReadOnlyList<string> thresholdFields,
IReadOnlyCollection<string> takenNames,
ModuleUnit? existingUnit,
ModuleCountField? existingCount)
{
_auraFields = auraFields;
_thresholdFields = thresholdFields;
_takenNames = new HashSet<string>(takenNames, StringComparer.OrdinalIgnoreCase);
InitializeComponent();
Seed(existingUnit, existingCount);
UpdateParamVisibility();
UpdateHealthNameState();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
UiTheme.ApplyDarkTitleBar(this);
}
private void InitializeComponent()
{
Text = "编辑单位";
Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point);
BackColor = UiTheme.Background;
ForeColor = UiTheme.Text;
ClientSize = new Size(RowWidth + 36, 500);
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;
MinimizeBox = false;
ShowInTaskbar = false;
StartPosition = FormStartPosition.CenterParent;
var root = new TableLayoutPanel
{
Dock = DockStyle.Fill,
BackColor = UiTheme.Background,
Padding = new Padding(14, 12, 14, 12),
ColumnCount = 1,
RowCount = 4
};
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 38));
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 38));
root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 42));
Controls.Add(root);
UiTheme.StyleComboBox(_categoryBox);
_categoryBox.DropDownWidth = 180;
_categoryBox.Items.AddRange(["单位 (可作目标)", "数量 (仅条件)"]);
_categoryBox.SelectedIndex = 0;
_categoryBox.SelectedIndexChanged += (_, _) =>
{
PopulateSelectors();
UpdateParamVisibility();
UpdateHealthNameState();
};
UiTheme.StyleComboBox(_selectorBox);
_selectorBox.DropDownWidth = 360;
_selectorBox.SelectedIndexChanged += (_, _) =>
{
UpdateParamVisibility();
UpdateHealthNameState();
};
root.Controls.Add(BuildSplitRow("类别", _categoryBox, "选择器", _selectorBox), 0, 0);
root.Controls.Add(BuildNameRow(), 0, 1);
_paramPanel.Dock = DockStyle.Fill;
_paramPanel.BackColor = UiTheme.SurfaceRaised;
_paramPanel.FlowDirection = FlowDirection.TopDown;
_paramPanel.WrapContents = false;
_paramPanel.AutoScroll = true;
_paramPanel.Margin = new Padding(0, 8, 0, 6);
_paramPanel.Padding = new Padding(8, 6, 8, 6);
BuildParamRows();
root.Controls.Add(_paramPanel, 0, 2);
root.Controls.Add(BuildActionRow(), 0, 3);
PopulateSelectors();
}
private void BuildParamRows()
{
_thresholdBox.Minimum = 1;
_thresholdBox.Maximum = int.MaxValue;
_thresholdBox.Value = 100;
StyleNumeric(_thresholdBox);
UiTheme.StyleComboBox(_thresholdModeBox);
_thresholdModeBox.DropDownWidth = 160;
_thresholdModeBox.Items.AddRange(ThresholdModeOptions.Cast<object>().ToArray());
_thresholdModeBox.SelectedIndex = 0;
_thresholdModeBox.SelectedIndexChanged += (_, _) => UpdateParamVisibility();
UiTheme.StyleComboBox(_thresholdFieldBox);
_thresholdFieldBox.DropDownWidth = 360;
foreach (var field in _thresholdFields)
{
if (!_thresholdFieldBox.Items.Contains(field))
{
_thresholdFieldBox.Items.Add(field);
}
}
if (_thresholdFieldBox.Items.Count > 0)
{
_thresholdFieldBox.SelectedIndex = 0;
}
UiTheme.StyleComboBox(_lowestHealthAuraFilterBox);
_lowestHealthAuraFilterBox.DropDownWidth = 220;
_lowestHealthAuraFilterBox.Items.AddRange(LowestHealthAuraFilterOptions.Cast<object>().ToArray());
_lowestHealthAuraFilterBox.SelectedIndex = 0;
_lowestHealthAuraFilterBox.SelectedIndexChanged += (_, _) => UpdateParamVisibility();
UiTheme.StyleComboBox(_roleBox);
_roleBox.DropDownWidth = 160;
_roleBox.Items.AddRange(RoleOptions.Cast<object>().ToArray());
_roleBox.SelectedIndex = 0;
_reverseBox.Text = "取逆序最后一个匹配单位";
_reverseBox.ForeColor = UiTheme.Text;
_reverseBox.BackColor = UiTheme.SurfaceRaised;
_reverseBox.AutoSize = false;
_reverseBox.TextAlign = ContentAlignment.MiddleLeft;
UiTheme.StyleComboBox(_auraBox);
_auraBox.DropDownWidth = 360;
foreach (var aura in _auraFields)
{
_auraBox.Items.Add(aura);
}
if (_auraBox.Items.Count > 0)
{
_auraBox.SelectedIndex = 0;
}
UiTheme.StyleCheckedListBox(_aurasBox);
foreach (var aura in _auraFields)
{
_aurasBox.Items.Add(aura);
}
_auraCountBox.Minimum = 0;
_auraCountBox.Maximum = 100;
_auraCountBox.Value = 1;
StyleNumeric(_auraCountBox);
UiTheme.StyleComboBox(_dispelTypeBox);
_dispelTypeBox.DropDownWidth = 180;
_dispelTypeBox.Items.AddRange(DispelTypeOptions.Cast<object>().ToArray());
_dispelTypeBox.SelectedIndex = 0;
_thresholdModeRow = BuildLabeledRow("阈值类型", _thresholdModeBox);
_thresholdRow = BuildLabeledRow("血量阈值 (<)", _thresholdBox);
_thresholdFieldRow = BuildLabeledRow("动态阈值", _thresholdFieldBox);
_lowestHealthAuraFilterRow = BuildLabeledRow("光环筛选", _lowestHealthAuraFilterBox);
_roleRow = BuildLabeledRow("职责", _roleBox);
_reverseRow = BuildLabeledRow(string.Empty, _reverseBox);
_auraRow = BuildLabeledRow("光环", _auraBox);
_aurasRow = BuildLabeledRow("光环 (可多选)", _aurasBox, 116);
_auraCountRow = BuildLabeledRow("光环值", _auraCountBox);
_dispelRow = BuildLabeledRow("驱散类型", _dispelTypeBox);
_paramPanel.Controls.AddRange([_thresholdModeRow, _thresholdRow, _thresholdFieldRow, _lowestHealthAuraFilterRow, _roleRow, _reverseRow, _auraRow, _aurasRow, _auraCountRow, _dispelRow]);
}
private Control BuildActionRow()
{
var row = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.RightToLeft,
WrapContents = false,
BackColor = UiTheme.Background,
Margin = new Padding(0)
};
var okButton = UiTheme.CreateButton("确定", UiTheme.Accent, Color.Black);
okButton.Width = 76;
okButton.Height = 30;
okButton.Margin = new Padding(6, 6, 0, 0);
okButton.Click += (_, _) => OnConfirm();
var cancelButton = UiTheme.CreateButton("取消", UiTheme.Field, UiTheme.Text);
cancelButton.Width = 76;
cancelButton.Height = 30;
cancelButton.Margin = new Padding(6, 6, 0, 0);
cancelButton.Click += (_, _) => DialogResult = DialogResult.Cancel;
row.Controls.Add(okButton);
row.Controls.Add(cancelButton);
CancelButton = cancelButton;
return row;
}
private void PopulateSelectors()
{
_selectorBox.Items.Clear();
if (IsCountCategory)
{
_selectorBox.Items.AddRange(CountSelectors.Cast<object>().ToArray());
}
else
{
_selectorBox.Items.AddRange(UnitSelectors.Cast<object>().ToArray());
}
if (_selectorBox.Items.Count > 0)
{
_selectorBox.SelectedIndex = 0;
}
}
// 值名称只对"生命值最低"单位有意义(把该单位的 生命值 暴露成数值条件字段)。
private void UpdateHealthNameState()
{
var visible = SupportsHealthName();
_healthNameLabel.Visible = visible;
_healthNameBox.Visible = visible;
_healthNameBox.Enabled = visible;
if (!visible)
{
_healthNameBox.Text = string.Empty;
}
}
private void UpdateParamVisibility()
{
bool threshold = false, lowestHealthAuraFilter = false, role = false, reverse = false, auraSingle = false, auraMulti = false, auraCount = false, dispel = false;
if (IsCountCategory)
{
switch ((_selectorBox.SelectedItem as CountItem)?.Kind)
{
case CountKind.UnitsBelowHealth:
threshold = true;
break;
case CountKind.UnitsWithoutAuraBelowHealth:
threshold = auraSingle = true;
break;
case CountKind.UnitsWithAura:
auraSingle = true;
break;
}
}
else
{
switch ((_selectorBox.SelectedItem as SelectorItem)?.Kind)
{
case UnitSelectorKind.LowestHealth:
threshold = lowestHealthAuraFilter = true;
switch (SelectedLowestHealthAuraFilter())
{
case LowestHealthAuraFilterKind.WithAnyAura:
auraMulti = true;
break;
case LowestHealthAuraFilterKind.WithoutAura:
case LowestHealthAuraFilterKind.WithAura:
auraSingle = true;
break;
case LowestHealthAuraFilterKind.WithAuraCount:
auraSingle = auraCount = true;
break;
}
break;
case UnitSelectorKind.LowestHealthWithAnyAura:
threshold = auraMulti = true;
break;
case UnitSelectorKind.LowestHealthWithoutAura:
case UnitSelectorKind.LowestHealthWithAura:
threshold = auraSingle = true;
break;
case UnitSelectorKind.LowestHealthWithAuraCount:
threshold = auraSingle = auraCount = true;
break;
case UnitSelectorKind.UnitWithRole:
role = reverse = true;
break;
case UnitSelectorKind.UnitWithRoleWithoutAura:
role = reverse = auraSingle = true;
break;
case UnitSelectorKind.UnitWithAura:
auraSingle = true;
break;
case UnitSelectorKind.UnitWithDispelType:
dispel = true;
break;
}
}
var dynamicThreshold = IsDynamicThresholdMode();
_thresholdModeRow.Visible = threshold;
_thresholdRow.Visible = threshold && !dynamicThreshold;
_thresholdFieldRow.Visible = threshold && dynamicThreshold;
_lowestHealthAuraFilterRow.Visible = lowestHealthAuraFilter;
_roleRow.Visible = role;
_reverseRow.Visible = reverse;
_auraRow.Visible = auraSingle;
_aurasRow.Visible = auraMulti;
_auraCountRow.Visible = auraCount;
_dispelRow.Visible = dispel;
}
private void Seed(ModuleUnit? unit, ModuleCountField? count)
{
if (count is not null)
{
_nameBox.Text = count.Name;
_categoryBox.SelectedIndex = 1;
PopulateSelectors();
SelectSelector(count.Kind);
if (count.HealthThreshold is { } th)
{
_thresholdBox.Value = Clamp(th, _thresholdBox);
}
SeedThresholdField(count.HealthThresholdField);
SelectAura(_auraBox, count.AuraName);
return;
}
if (unit is not null)
{
_nameBox.Text = unit.Name;
_healthNameBox.Text = unit.HealthName ?? string.Empty;
_categoryBox.SelectedIndex = 0;
PopulateSelectors();
SelectSelector(DisplaySelectorKind(unit.Kind));
SelectLowestHealthAuraFilter(unit.Kind);
if (unit.HealthThreshold is { } th)
{
_thresholdBox.Value = Clamp(th, _thresholdBox);
}
SeedThresholdField(unit.HealthThresholdField);
if (unit.Role is { } r)
{
SelectRole(r);
}
_reverseBox.Checked = unit.Reverse;
SelectAura(_auraBox, unit.AuraNames is { Count: > 0 } ? unit.AuraNames[0] : null);
CheckAuras(unit.AuraNames);
if (unit.AuraCount is { } ac)
{
_auraCountBox.Value = Clamp(ac, _auraCountBox);
}
if (unit.DispelType is { } dt)
{
SelectDispelType(dt);
}
}
}
private void OnConfirm()
{
var name = _nameBox.Text.Trim();
if (!ValidateName(name, out var message))
{
MessageBox.Show(message, "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (IsCountCategory)
{
var kind = (_selectorBox.SelectedItem as CountItem)?.Kind ?? CountKind.UnitsBelowHealth;
var count = new ModuleCountField { Name = name, Kind = kind };
switch (kind)
{
case CountKind.UnitsBelowHealth:
if (!ApplyThreshold(count))
{
return;
}
break;
case CountKind.UnitsWithoutAuraBelowHealth:
if (!ApplyThreshold(count))
{
return;
}
count.AuraName = SelectedAura();
break;
case CountKind.UnitsWithAura:
count.AuraName = SelectedAura();
break;
}
if (RequiresAura(kind) && string.IsNullOrEmpty(count.AuraName))
{
MessageBox.Show("请选择光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
ResultCount = count;
DialogResult = DialogResult.OK;
return;
}
var selectorKind = (_selectorBox.SelectedItem as SelectorItem)?.Kind ?? UnitSelectorKind.LowestHealth;
var moduleUnit = new ModuleUnit { Name = name, Kind = selectorKind };
switch (selectorKind)
{
case UnitSelectorKind.LowestHealth:
if (!ApplyThreshold(moduleUnit))
{
return;
}
switch (SelectedLowestHealthAuraFilter())
{
case LowestHealthAuraFilterKind.WithAnyAura:
moduleUnit.Kind = UnitSelectorKind.LowestHealthWithAnyAura;
moduleUnit.AuraNames = CheckedAuras();
if (moduleUnit.AuraNames.Count == 0)
{
MessageBox.Show("请至少勾选一个光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
break;
case LowestHealthAuraFilterKind.WithoutAura:
moduleUnit.Kind = UnitSelectorKind.LowestHealthWithoutAura;
moduleUnit.AuraNames = SingleAuraList();
break;
case LowestHealthAuraFilterKind.WithAura:
moduleUnit.Kind = UnitSelectorKind.LowestHealthWithAura;
moduleUnit.AuraNames = SingleAuraList();
break;
case LowestHealthAuraFilterKind.WithAuraCount:
moduleUnit.Kind = UnitSelectorKind.LowestHealthWithAuraCount;
moduleUnit.AuraNames = SingleAuraList();
moduleUnit.AuraCount = (int)_auraCountBox.Value;
break;
}
break;
case UnitSelectorKind.LowestHealthWithAnyAura:
if (!ApplyThreshold(moduleUnit))
{
return;
}
moduleUnit.AuraNames = CheckedAuras();
if (moduleUnit.AuraNames.Count == 0)
{
MessageBox.Show("请至少勾选一个光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
break;
case UnitSelectorKind.LowestHealthWithoutAura:
case UnitSelectorKind.LowestHealthWithAura:
if (!ApplyThreshold(moduleUnit))
{
return;
}
moduleUnit.AuraNames = SingleAuraList();
break;
case UnitSelectorKind.LowestHealthWithAuraCount:
if (!ApplyThreshold(moduleUnit))
{
return;
}
moduleUnit.AuraNames = SingleAuraList();
moduleUnit.AuraCount = (int)_auraCountBox.Value;
break;
case UnitSelectorKind.UnitWithRole:
moduleUnit.Role = SelectedRole();
moduleUnit.Reverse = _reverseBox.Checked;
break;
case UnitSelectorKind.UnitWithRoleWithoutAura:
moduleUnit.Role = SelectedRole();
moduleUnit.Reverse = _reverseBox.Checked;
moduleUnit.AuraNames = SingleAuraList();
break;
case UnitSelectorKind.UnitWithAura:
moduleUnit.AuraNames = SingleAuraList();
break;
case UnitSelectorKind.UnitWithDispelType:
moduleUnit.DispelType = SelectedDispelType();
break;
}
if (UnitRequiresAura(moduleUnit.Kind) && (moduleUnit.AuraNames is null || moduleUnit.AuraNames.Count == 0))
{
MessageBox.Show("请选择光环。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
var healthName = SupportsHealthName() ? _healthNameBox.Text.Trim() : string.Empty;
if (healthName.Length > 0)
{
if (string.Equals(healthName, name, StringComparison.OrdinalIgnoreCase))
{
MessageBox.Show("值名称不能与名称相同。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (!ValidateName(healthName, out var healthMessage))
{
MessageBox.Show($"值名称: {healthMessage}", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
}
moduleUnit.HealthName = healthName.Length == 0 ? null : healthName;
ResultUnit = moduleUnit;
DialogResult = DialogResult.OK;
}
private bool ValidateName(string name, out string message)
{
message = string.Empty;
if (name.Length == 0)
{
message = "名称不能为空。";
return false;
}
if (name.Contains('.') || name.Contains('$'))
{
message = "名称不能包含 '.' 或 '$'。";
return false;
}
if (int.TryParse(name, out _))
{
message = "名称不能是纯数字(会与单位编号混淆)。";
return false;
}
if (_takenNames.Contains(name))
{
message = $"名称“{name}”已被其它单位/字段或状态字段占用。";
return false;
}
return true;
}
private bool IsCountCategory => _categoryBox.SelectedIndex == 1;
private bool IsDynamicThresholdMode()
=> (_thresholdModeBox.SelectedItem as ThresholdModeItem)?.UsesDynamicField == true;
private bool ApplyThreshold(ModuleUnit unit)
{
if (TryReadThreshold(out var fixedValue, out var field))
{
unit.HealthThreshold = fixedValue;
unit.HealthThresholdField = field;
return true;
}
return false;
}
private bool ApplyThreshold(ModuleCountField count)
{
if (TryReadThreshold(out var fixedValue, out var field))
{
count.HealthThreshold = fixedValue;
count.HealthThresholdField = field;
return true;
}
return false;
}
private bool TryReadThreshold(out int? fixedValue, out string? field)
{
if (!IsDynamicThresholdMode())
{
fixedValue = (int)_thresholdBox.Value;
field = null;
return true;
}
fixedValue = null;
field = _thresholdFieldBox.SelectedItem?.ToString()?.Trim();
if (!string.IsNullOrWhiteSpace(field))
{
return true;
}
MessageBox.Show("请选择动态阈值。", "Shigure", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
private bool SupportsHealthName()
=> !IsCountCategory
&& (_selectorBox.SelectedItem as SelectorItem)?.Kind == UnitSelectorKind.LowestHealth;
private static bool RequiresAura(CountKind kind)
=> kind is CountKind.UnitsWithoutAuraBelowHealth or CountKind.UnitsWithAura;
private static bool UnitRequiresAura(UnitSelectorKind kind)
=> kind is UnitSelectorKind.LowestHealthWithAnyAura
or UnitSelectorKind.LowestHealthWithoutAura
or UnitSelectorKind.LowestHealthWithAura
or UnitSelectorKind.LowestHealthWithAuraCount
or UnitSelectorKind.UnitWithRoleWithoutAura
or UnitSelectorKind.UnitWithAura;
private string? SelectedAura() => _auraBox.SelectedItem?.ToString();
private LowestHealthAuraFilterKind SelectedLowestHealthAuraFilter()
=> (_lowestHealthAuraFilterBox.SelectedItem as LowestHealthAuraFilterItem)?.Kind
?? LowestHealthAuraFilterKind.None;
private List<string> SingleAuraList()
{
var aura = SelectedAura();
return string.IsNullOrEmpty(aura) ? new List<string>() : new List<string> { aura };
}
private List<string> CheckedAuras()
{
var list = new List<string>();
foreach (var item in _aurasBox.CheckedItems)
{
if (item?.ToString() is { Length: > 0 } name)
{
list.Add(name);
}
}
return list;
}
private int SelectedRole() => (_roleBox.SelectedItem as RoleOption)?.Value ?? 1;
private int SelectedDispelType() => (_dispelTypeBox.SelectedItem as DispelTypeOption)?.Value ?? 1;
private static UnitSelectorKind DisplaySelectorKind(UnitSelectorKind kind)
=> kind is UnitSelectorKind.LowestHealthWithAnyAura
or UnitSelectorKind.LowestHealthWithoutAura
or UnitSelectorKind.LowestHealthWithAura
or UnitSelectorKind.LowestHealthWithAuraCount
? UnitSelectorKind.LowestHealth
: kind;
private void SelectLowestHealthAuraFilter(UnitSelectorKind kind)
{
var filter = kind switch
{
UnitSelectorKind.LowestHealthWithAnyAura => LowestHealthAuraFilterKind.WithAnyAura,
UnitSelectorKind.LowestHealthWithoutAura => LowestHealthAuraFilterKind.WithoutAura,
UnitSelectorKind.LowestHealthWithAura => LowestHealthAuraFilterKind.WithAura,
UnitSelectorKind.LowestHealthWithAuraCount => LowestHealthAuraFilterKind.WithAuraCount,
_ => LowestHealthAuraFilterKind.None
};
for (var i = 0; i < _lowestHealthAuraFilterBox.Items.Count; i++)
{
if (_lowestHealthAuraFilterBox.Items[i] is LowestHealthAuraFilterItem item && item.Kind == filter)
{
_lowestHealthAuraFilterBox.SelectedIndex = i;
return;
}
}
_lowestHealthAuraFilterBox.SelectedIndex = 0;
}
private void SelectSelector(UnitSelectorKind kind)
{
for (var i = 0; i < _selectorBox.Items.Count; i++)
{
if (_selectorBox.Items[i] is SelectorItem item && item.Kind == kind)
{
_selectorBox.SelectedIndex = i;
return;
}
}
}
private void SelectSelector(CountKind kind)
{
for (var i = 0; i < _selectorBox.Items.Count; i++)
{
if (_selectorBox.Items[i] is CountItem item && item.Kind == kind)
{
_selectorBox.SelectedIndex = i;
return;
}
}
}
private void SeedThresholdField(string? field)
{
if (string.IsNullOrWhiteSpace(field))
{
SelectThresholdMode(usesDynamicField: false);
return;
}
SelectThresholdMode(usesDynamicField: true);
SelectThresholdField(field.Trim());
}
private void SelectThresholdMode(bool usesDynamicField)
{
for (var i = 0; i < _thresholdModeBox.Items.Count; i++)
{
if (_thresholdModeBox.Items[i] is ThresholdModeItem item && item.UsesDynamicField == usesDynamicField)
{
_thresholdModeBox.SelectedIndex = i;
return;
}
}
_thresholdModeBox.SelectedIndex = 0;
}
private void SelectThresholdField(string field)
{
var index = _thresholdFieldBox.Items.IndexOf(field);
if (index < 0)
{
_thresholdFieldBox.Items.Add(field);
index = _thresholdFieldBox.Items.Count - 1;
}
_thresholdFieldBox.SelectedIndex = index;
}
private void SelectRole(int role)
{
for (var i = 0; i < _roleBox.Items.Count; i++)
{
if (_roleBox.Items[i] is RoleOption option && option.Value == role)
{
_roleBox.SelectedIndex = i;
return;
}
}
}
private void SelectDispelType(int dispelType)
{
for (var i = 0; i < _dispelTypeBox.Items.Count; i++)
{
if (_dispelTypeBox.Items[i] is DispelTypeOption option && option.Value == dispelType)
{
_dispelTypeBox.SelectedIndex = i;
return;
}
}
_dispelTypeBox.SelectedIndex = 0;
}
private static void SelectAura(ComboBox box, string? aura)
{
if (string.IsNullOrEmpty(aura))
{
return;
}
var index = box.Items.IndexOf(aura);
if (index < 0)
{
box.Items.Add(aura);
index = box.Items.Count - 1;
}
box.SelectedIndex = index;
}
private void CheckAuras(List<string>? auras)
{
if (auras is null)
{
return;
}
foreach (var aura in auras)
{
var index = _aurasBox.Items.IndexOf(aura);
if (index < 0)
{
index = _aurasBox.Items.Add(aura);
}
_aurasBox.SetItemChecked(index, true);
}
}
private static decimal Clamp(int value, NumericUpDown box)
{
return Math.Clamp(value, (int)box.Minimum, (int)box.Maximum);
}
private static Panel BuildLabeledRow(string label, Control control, int height = 32)
{
var panel = new Panel
{
Width = RowWidth,
Height = height,
BackColor = UiTheme.SurfaceRaised,
Margin = new Padding(0, 1, 0, 5)
};
var labelControl = new Label
{
Text = label,
ForeColor = UiTheme.Muted,
TextAlign = ContentAlignment.MiddleLeft,
Bounds = new Rectangle(0, 4, LabelWidth, 24),
AutoEllipsis = true
};
control.Bounds = new Rectangle(ControlLeft, 3, RowWidth - ControlLeft, height - 6);
panel.Controls.Add(control);
panel.Controls.Add(labelControl);
return panel;
}
private Control BuildSplitRow(string labelA, Control controlA, string labelB, Control controlB)
{
var panel = new Panel
{
Dock = DockStyle.Fill,
BackColor = UiTheme.SurfaceRaised,
Margin = new Padding(0)
};
var labelAControl = new Label { Text = labelA, ForeColor = UiTheme.Muted, TextAlign = ContentAlignment.MiddleLeft, Bounds = new Rectangle(0, 5, 72, 28), AutoEllipsis = true };
controlA.Bounds = new Rectangle(80, 5, 230, 28);
var labelBControl = new Label { Text = labelB, ForeColor = UiTheme.Muted, TextAlign = ContentAlignment.MiddleLeft, Bounds = new Rectangle(330, 5, 130, 28), AutoEllipsis = true };
controlB.Bounds = new Rectangle(466, 5, RowWidth - 466, 28);
panel.Controls.Add(controlA);
panel.Controls.Add(labelAControl);
panel.Controls.Add(controlB);
panel.Controls.Add(labelBControl);
return panel;
}
private Control BuildNameRow()
{
var panel = new Panel
{
Dock = DockStyle.Fill,
BackColor = UiTheme.SurfaceRaised,
Margin = new Padding(0)
};
var nameLabel = new Label { Text = "名称", ForeColor = UiTheme.Muted, TextAlign = ContentAlignment.MiddleLeft, Bounds = new Rectangle(0, 5, 72, 28), AutoEllipsis = true };
StyleTextBox(_nameBox);
_nameBox.Bounds = new Rectangle(80, 5, 230, 28);
_healthNameLabel.Text = "值名称";
_healthNameLabel.ForeColor = UiTheme.Muted;
_healthNameLabel.TextAlign = ContentAlignment.MiddleLeft;
_healthNameLabel.Bounds = new Rectangle(330, 5, 130, 28);
_healthNameLabel.AutoEllipsis = true;
StyleTextBox(_healthNameBox);
_healthNameBox.Bounds = new Rectangle(466, 5, RowWidth - 466, 28);
panel.Controls.Add(_nameBox);
panel.Controls.Add(nameLabel);
panel.Controls.Add(_healthNameBox);
panel.Controls.Add(_healthNameLabel);
return panel;
}
private static TextBox StyleTextBox(TextBox box)
{
UiTheme.StyleTextBox(box);
return box;
}
private static void StyleNumeric(NumericUpDown box)
{
UiTheme.StyleNumericUpDown(box);
}
private sealed record SelectorItem(string Text, UnitSelectorKind Kind)
{
public override string ToString() => Text;
}
private sealed record LowestHealthAuraFilterItem(string Text, LowestHealthAuraFilterKind Kind)
{
public override string ToString() => Text;
}
private sealed record CountItem(string Text, CountKind Kind)
{
public override string ToString() => Text;
}
private sealed record ThresholdModeItem(string Text, bool UsesDynamicField)
{
public override string ToString() => Text;
}
private sealed record RoleOption(string Text, int Value)
{
public override string ToString() => Text;
}
private sealed record DispelTypeOption(string Text, int Value)
{
public override string ToString() => Text;
}
private enum LowestHealthAuraFilterKind
{
None,
WithAnyAura,
WithoutAura,
WithAura,
WithAuraCount
}
}