using System.Drawing; namespace Shigure; /// /// 动态单位 / 数量字段的编辑弹窗: 选择类别(单位/数量)与选择器, 按所选类型动态显隐参数控件。 /// 光环候选来自当前职业/专精的 group 字段。校验名称非空、唯一、非纯数字、不含 '.'/'$'。 /// 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.HighestHealingAbsorb), 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.UnitsWithAuraBelowHealth), new("治疗吸收 - 大于阈值", CountKind.UnitsAboveHealingAbsorb), new("治疗吸收 - 大于阈值不带某光环", CountKind.UnitsWithoutAuraAboveHealingAbsorb), new("治疗吸收 - 大于阈值带某光环", CountKind.UnitsWithAuraAboveHealingAbsorb), new("光环 - 带某光环", CountKind.UnitsWithAura) ]; private static readonly ThresholdModeItem[] ThresholdModeOptions = [ new("固定阈值", false), new("动态阈值", true) ]; private readonly IReadOnlyList _auraFields; private readonly IReadOnlyList _thresholdFields; private readonly HashSet _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 Label _previewLabel = new(); private readonly ToolTip _toolTip = 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 Label _thresholdLabel = 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!; private bool _usesHealingAbsorbThreshold; public ModuleUnit? ResultUnit { get; private set; } public ModuleCountField? ResultCount { get; private set; } public UnitEditorForm( IReadOnlyList auraFields, IReadOnlyList thresholdFields, IReadOnlyCollection takenNames, ModuleUnit? existingUnit, ModuleCountField? existingCount) { _auraFields = auraFields; _thresholdFields = thresholdFields; _takenNames = new HashSet(takenNames, StringComparer.OrdinalIgnoreCase); InitializeComponent(); Seed(existingUnit, existingCount); UpdateParamVisibility(); UpdateHealthNameState(); } protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); UiTheme.ApplyDarkTitleBar(this); } protected override void OnShown(EventArgs e) { base.OnShown(e); _nameBox.Focus(); _nameBox.SelectAll(); } 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, 534); 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 = 5 }; 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, 26)); 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); _previewLabel.Dock = DockStyle.Fill; _previewLabel.ForeColor = UiTheme.Muted; _previewLabel.TextAlign = ContentAlignment.MiddleLeft; _previewLabel.AutoEllipsis = true; _previewLabel.Margin = new Padding(2, 2, 2, 0); root.Controls.Add(_previewLabel, 0, 3); root.Controls.Add(BuildActionRow(), 0, 4); PopulateSelectors(); } private void BuildParamRows() { _thresholdBox.Minimum = 1; _thresholdBox.Maximum = int.MaxValue; _thresholdBox.Value = 100; UiTheme.StyleNumericUpDown(_thresholdBox); UiTheme.StyleComboBox(_thresholdModeBox); _thresholdModeBox.DropDownWidth = 160; _thresholdModeBox.Items.AddRange(ThresholdModeOptions.Cast().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().ToArray()); _lowestHealthAuraFilterBox.SelectedIndex = 0; _lowestHealthAuraFilterBox.SelectedIndexChanged += (_, _) => UpdateParamVisibility(); UiTheme.StyleComboBox(_roleBox); _roleBox.DropDownWidth = 160; _roleBox.Items.AddRange(RoleOptions.Cast().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; UiTheme.StyleNumericUpDown(_auraCountBox); UiTheme.StyleComboBox(_dispelTypeBox); _dispelTypeBox.DropDownWidth = 180; _dispelTypeBox.Items.AddRange(DispelTypeOptions.Cast().ToArray()); _dispelTypeBox.SelectedIndex = 0; // 参数值变化刷新底部实时预览(类别/选择器/阈值类型/光环筛选经 UpdateParamVisibility 间接刷新)。 _thresholdBox.ValueChanged += (_, _) => UpdatePreview(); _thresholdFieldBox.SelectedIndexChanged += (_, _) => UpdatePreview(); _roleBox.SelectedIndexChanged += (_, _) => UpdatePreview(); _reverseBox.CheckedChanged += (_, _) => UpdatePreview(); _auraBox.SelectedIndexChanged += (_, _) => UpdatePreview(); _auraCountBox.ValueChanged += (_, _) => UpdatePreview(); _dispelTypeBox.SelectedIndexChanged += (_, _) => UpdatePreview(); // ItemCheck 在勾选状态提交前触发, 延后到提交后再读 CheckedItems。 // Seed 期间 CheckAuras 也会触发本事件, 此时窗口句柄尚未创建, 跳过(构造末尾会统一刷新)。 _aurasBox.ItemCheck += (_, _) => { if (IsHandleCreated) { BeginInvoke(new Action(UpdatePreview)); } }; _thresholdModeRow = BuildLabeledRow("阈值类型", _thresholdModeBox); _thresholdRow = BuildLabeledRow("血量阈值 (<)", _thresholdBox); _thresholdLabel = _thresholdRow.Controls.OfType