diff --git a/UI/ClassConfigEditorControl.cs b/UI/ClassConfigEditorControl.cs index 73c6a36d..2714ccb5 100644 --- a/UI/ClassConfigEditorControl.cs +++ b/UI/ClassConfigEditorControl.cs @@ -98,13 +98,12 @@ public sealed class ClassConfigEditorControl : UserControl private Control BuildSidebar() { - var panel = new TableLayoutPanel + var panel = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, ColumnCount = 1, RowCount = 2, - Padding = new Padding(12), + Padding = new Padding(14), Margin = new Padding(0, 0, 12, 0) }; panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 28)); @@ -117,6 +116,7 @@ public sealed class ClassConfigEditorControl : UserControl ForeColor = UiTheme.Text, Font = new Font(Font.FontFamily, 10F, FontStyle.Bold), TextAlign = ContentAlignment.MiddleLeft, + BackColor = Color.Transparent, Margin = new Padding(0) }, 0, 0); @@ -125,6 +125,7 @@ public sealed class ClassConfigEditorControl : UserControl _classList, item => (item as ClassListItem)?.ClassId, iconSize: 40); + _classList.BackColor = UiTheme.SurfaceRaised; _classList.SelectedIndexChanged += (_, _) => { if (_suppressUi) @@ -140,13 +141,12 @@ public sealed class ClassConfigEditorControl : UserControl private Control BuildSpecSidebar() { - var panel = new TableLayoutPanel + var panel = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, ColumnCount = 1, RowCount = 2, - Padding = new Padding(12), + Padding = new Padding(14), Margin = new Padding(0, 0, 12, 0) }; panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 28)); @@ -159,6 +159,7 @@ public sealed class ClassConfigEditorControl : UserControl ForeColor = UiTheme.Text, Font = new Font(Font.FontFamily, 10F, FontStyle.Bold), TextAlign = ContentAlignment.MiddleLeft, + BackColor = Color.Transparent, Margin = new Padding(0) }, 0, 0); @@ -167,6 +168,7 @@ public sealed class ClassConfigEditorControl : UserControl _specList, item => item is SpecOption spec ? (spec.ClassId, spec.Id) : null, iconSize: 40); + _specList.BackColor = UiTheme.SurfaceRaised; _specList.SelectedIndexChanged += (_, _) => { if (_suppressUi) @@ -202,14 +204,13 @@ public sealed class ClassConfigEditorControl : UserControl root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); root.RowStyles.Add(new RowStyle(SizeType.Absolute, 64)); - var header = new TableLayoutPanel + var header = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, ColumnCount = 2, RowCount = 2, Padding = new Padding(14, 12, 14, 12), - Margin = new Padding(0, 0, 0, 8) + Margin = new Padding(0, 0, 0, 12) }; header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 56)); header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); @@ -233,14 +234,13 @@ public sealed class ClassConfigEditorControl : UserControl root.Controls.Add(BuildSectionTabs(), 0, 1); - var actionRow = new TableLayoutPanel + var actionRow = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.Surface, ColumnCount = 2, RowCount = 1, - Margin = new Padding(0), - Padding = new Padding(0, 8, 12, 12) + Margin = new Padding(0, 12, 0, 0), + Padding = new Padding(14, 10, 14, 10) }; actionRow.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); actionRow.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 228)); @@ -250,7 +250,7 @@ public sealed class ClassConfigEditorControl : UserControl Dock = DockStyle.Fill, FlowDirection = FlowDirection.LeftToRight, WrapContents = false, - BackColor = UiTheme.Surface, + BackColor = Color.Transparent, Margin = new Padding(0) }; StyleActionButton(_reloadButton); @@ -276,16 +276,16 @@ public sealed class ClassConfigEditorControl : UserControl RowCount = 2, Margin = new Padding(0) }; - root.RowStyles.Add(new RowStyle(SizeType.Absolute, 40)); + root.RowStyles.Add(new RowStyle(SizeType.Absolute, 42)); root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); var tabBar = new TableLayoutPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.Border, + BackColor = UiTheme.Surface, ColumnCount = 5, RowCount = 1, - Margin = new Padding(0), + Margin = new Padding(0, 0, 0, 8), Padding = new Padding(0) }; for (var i = 0; i < 5; i++) @@ -293,13 +293,25 @@ public sealed class ClassConfigEditorControl : UserControl tabBar.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20)); } + var contentCard = new UiCardPanel + { + Dock = DockStyle.Fill, + ColumnCount = 1, + RowCount = 1, + Margin = new Padding(0), + Padding = new Padding(14) + }; + contentCard.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + contentCard.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); + var contentHost = new Panel { Dock = DockStyle.Fill, BackColor = UiTheme.SurfaceRaised, Margin = new Padding(0), - Padding = new Padding(12) + Padding = new Padding(0) }; + contentCard.Controls.Add(contentHost, 0, 0); var pages = new Control[] { @@ -313,10 +325,11 @@ public sealed class ClassConfigEditorControl : UserControl { page.Dock = DockStyle.Fill; page.Visible = false; + page.BackColor = UiTheme.SurfaceRaised; contentHost.Controls.Add(page); } - var labels = new Label[5]; + var tabs = new UiPillTab[5]; var selectedIndex = -1; void SelectTab(int index) { @@ -326,12 +339,10 @@ public sealed class ClassConfigEditorControl : UserControl } selectedIndex = index; - for (var i = 0; i < labels.Length; i++) + for (var i = 0; i < tabs.Length; i++) { var selected = i == index; - labels[i].BackColor = selected ? UiTheme.Field : UiTheme.Surface; - labels[i].ForeColor = selected ? UiTheme.Text : UiTheme.Muted; - labels[i].Invalidate(); + tabs[i].Selected = selected; pages[i].Visible = selected; if (selected) { @@ -344,49 +355,14 @@ public sealed class ClassConfigEditorControl : UserControl for (var i = 0; i < titles.Length; i++) { var index = i; - var label = new Label - { - Text = titles[i], - Dock = DockStyle.Fill, - TextAlign = ContentAlignment.MiddleCenter, - AutoSize = false, - BackColor = UiTheme.Surface, - ForeColor = UiTheme.Muted, - Cursor = Cursors.Hand, - Margin = new Padding(i == 0 ? 0 : 1, 0, 0, 0) - }; - label.Click += (_, _) => SelectTab(index); - label.MouseEnter += (_, _) => - { - if (selectedIndex != index) - { - label.BackColor = UiTheme.Hover; - } - }; - label.MouseLeave += (_, _) => - { - if (selectedIndex != index) - { - label.BackColor = UiTheme.Surface; - } - }; - label.Paint += (_, e) => - { - if (selectedIndex != index) - { - return; - } - - using var accent = new SolidBrush(UiTheme.Accent); - e.Graphics.FillRectangle(accent, 8, label.Height - 3, Math.Max(0, label.Width - 16), 2); - }; - label.SizeChanged += (_, _) => label.Invalidate(); - labels[i] = label; - tabBar.Controls.Add(label, i, 0); + var tab = new UiPillTab(titles[i]); + tab.Click += (_, _) => SelectTab(index); + tabs[i] = tab; + tabBar.Controls.Add(tab, i, 0); } root.Controls.Add(tabBar, 0, 0); - root.Controls.Add(contentHost, 0, 1); + root.Controls.Add(contentCard, 0, 1); SelectTab(0); return root; } @@ -432,7 +408,7 @@ public sealed class ClassConfigEditorControl : UserControl var tabBar = new TableLayoutPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.Border, + BackColor = UiTheme.SurfaceRaised, ColumnCount = categories.Length, RowCount = 1, Margin = new Padding(0), @@ -443,15 +419,12 @@ public sealed class ClassConfigEditorControl : UserControl tabBar.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F / categories.Length)); } - var labels = new Label[categories.Length]; + var tabs = new UiPillTab[categories.Length]; void ApplySelection() { - for (var i = 0; i < labels.Length; i++) + for (var i = 0; i < tabs.Length; i++) { - var selected = string.Equals(categories[i], _selectedStateCategory, StringComparison.Ordinal); - labels[i].BackColor = selected ? UiTheme.Field : UiTheme.Surface; - labels[i].ForeColor = selected ? UiTheme.Text : UiTheme.Muted; - labels[i].Invalidate(); + tabs[i].Selected = string.Equals(categories[i], _selectedStateCategory, StringComparison.Ordinal); } } @@ -474,45 +447,10 @@ public sealed class ClassConfigEditorControl : UserControl for (var i = 0; i < categories.Length; i++) { var category = categories[i]; - var label = new Label - { - Text = category, - Dock = DockStyle.Fill, - TextAlign = ContentAlignment.MiddleCenter, - AutoSize = false, - BackColor = UiTheme.Surface, - ForeColor = UiTheme.Muted, - Cursor = Cursors.Hand, - Margin = new Padding(i == 0 ? 0 : 1, 0, 0, 0) - }; - label.Click += (_, _) => SelectCategory(category); - label.MouseEnter += (_, _) => - { - if (!string.Equals(category, _selectedStateCategory, StringComparison.Ordinal)) - { - label.BackColor = UiTheme.Hover; - } - }; - label.MouseLeave += (_, _) => - { - if (!string.Equals(category, _selectedStateCategory, StringComparison.Ordinal)) - { - label.BackColor = UiTheme.Surface; - } - }; - label.Paint += (_, e) => - { - if (!string.Equals(category, _selectedStateCategory, StringComparison.Ordinal)) - { - return; - } - - using var accent = new SolidBrush(UiTheme.Accent); - e.Graphics.FillRectangle(accent, 8, label.Height - 3, Math.Max(0, label.Width - 16), 2); - }; - label.SizeChanged += (_, _) => label.Invalidate(); - labels[i] = label; - tabBar.Controls.Add(label, i, 0); + var tab = new UiPillTab(category); + tab.Click += (_, _) => SelectCategory(category); + tabs[i] = tab; + tabBar.Controls.Add(tab, i, 0); } ApplySelection(); @@ -859,6 +797,7 @@ public sealed class ClassConfigEditorControl : UserControl Dock = DockStyle.Fill, AutoSize = false, ForeColor = UiTheme.Muted, + BackColor = Color.Transparent, TextAlign = ContentAlignment.MiddleLeft, Margin = new Padding(0), AutoEllipsis = true @@ -869,6 +808,7 @@ public sealed class ClassConfigEditorControl : UserControl label.Dock = DockStyle.Fill; label.AutoSize = false; label.ForeColor = foreColor; + label.BackColor = Color.Transparent; label.TextAlign = ContentAlignment.MiddleLeft; label.AutoEllipsis = true; label.Margin = new Padding(0); diff --git a/UI/ClassMacrosEditorControl.cs b/UI/ClassMacrosEditorControl.cs index 6fced80d..5c11e188 100644 --- a/UI/ClassMacrosEditorControl.cs +++ b/UI/ClassMacrosEditorControl.cs @@ -73,13 +73,12 @@ public sealed class ClassMacrosEditorControl : UserControl private Control BuildSidebar() { - var panel = new TableLayoutPanel + var panel = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, ColumnCount = 1, RowCount = 2, - Padding = new Padding(12), + Padding = new Padding(14), Margin = new Padding(0, 0, 12, 0) }; panel.RowStyles.Add(new RowStyle(SizeType.Absolute, 28)); @@ -92,6 +91,7 @@ public sealed class ClassMacrosEditorControl : UserControl ForeColor = UiTheme.Text, Font = new Font(Font.FontFamily, 10F, FontStyle.Bold), TextAlign = ContentAlignment.MiddleLeft, + BackColor = Color.Transparent, Margin = new Padding(0) }, 0, 0); @@ -100,6 +100,7 @@ public sealed class ClassMacrosEditorControl : UserControl _classList, item => (item as ClassListItem)?.ClassId, iconSize: 40); + _classList.BackColor = UiTheme.SurfaceRaised; _classList.SelectedIndexChanged += (_, _) => { if (!_suppressUi) @@ -134,14 +135,13 @@ public sealed class ClassMacrosEditorControl : UserControl root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); root.RowStyles.Add(new RowStyle(SizeType.Absolute, 64)); - var header = new TableLayoutPanel + var header = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, ColumnCount = 2, RowCount = 2, Padding = new Padding(14, 12, 14, 12), - Margin = new Padding(0, 0, 0, 8) + Margin = new Padding(0, 0, 0, 12) }; header.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 56)); header.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); @@ -166,6 +166,7 @@ public sealed class ClassMacrosEditorControl : UserControl _offsetLabel.Dock = DockStyle.Fill; _offsetLabel.AutoSize = false; _offsetLabel.ForeColor = UiTheme.Muted; + _offsetLabel.BackColor = Color.Transparent; _offsetLabel.TextAlign = ContentAlignment.MiddleLeft; _offsetLabel.Padding = new Padding(14, 0, 14, 0); _offsetLabel.Margin = new Padding(0); @@ -175,14 +176,13 @@ public sealed class ClassMacrosEditorControl : UserControl root.Controls.Add(BuildSectionTabs(), 0, 2); - var actionRow = new TableLayoutPanel + var actionRow = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.Surface, ColumnCount = 2, RowCount = 1, - Margin = new Padding(0), - Padding = new Padding(0, 8, 12, 12) + Margin = new Padding(0, 12, 0, 0), + Padding = new Padding(14, 10, 14, 10) }; actionRow.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); actionRow.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 228)); @@ -192,7 +192,7 @@ public sealed class ClassMacrosEditorControl : UserControl Dock = DockStyle.Fill, FlowDirection = FlowDirection.LeftToRight, WrapContents = false, - BackColor = UiTheme.Surface, + BackColor = Color.Transparent, Margin = new Padding(0) }; StyleActionButton(_reloadButton); @@ -218,29 +218,42 @@ public sealed class ClassMacrosEditorControl : UserControl RowCount = 2, Margin = new Padding(0) }; - root.RowStyles.Add(new RowStyle(SizeType.Absolute, 40)); + root.RowStyles.Add(new RowStyle(SizeType.Absolute, 42)); root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); var tabBar = new TableLayoutPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.Border, + BackColor = UiTheme.Surface, ColumnCount = 3, RowCount = 1, - Margin = new Padding(0) + Margin = new Padding(0, 0, 0, 8), + Padding = new Padding(0) }; for (var i = 0; i < 3; i++) { tabBar.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.33f)); } + var contentCard = new UiCardPanel + { + Dock = DockStyle.Fill, + ColumnCount = 1, + RowCount = 1, + Margin = new Padding(0), + Padding = new Padding(14) + }; + contentCard.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + contentCard.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); + var contentHost = new Panel { Dock = DockStyle.Fill, BackColor = UiTheme.SurfaceRaised, Margin = new Padding(0), - Padding = new Padding(12) + Padding = new Padding(0) }; + contentCard.Controls.Add(contentHost, 0, 0); var pages = new Control[] { @@ -252,10 +265,11 @@ public sealed class ClassMacrosEditorControl : UserControl { page.Dock = DockStyle.Fill; page.Visible = false; + page.BackColor = UiTheme.SurfaceRaised; contentHost.Controls.Add(page); } - var labels = new Label[3]; + var tabs = new UiPillTab[3]; var selectedIndex = -1; void SelectTab(int index) { @@ -265,12 +279,10 @@ public sealed class ClassMacrosEditorControl : UserControl } selectedIndex = index; - for (var i = 0; i < labels.Length; i++) + for (var i = 0; i < tabs.Length; i++) { var selected = i == index; - labels[i].BackColor = selected ? UiTheme.Field : UiTheme.Surface; - labels[i].ForeColor = selected ? UiTheme.Text : UiTheme.Muted; - labels[i].Invalidate(); + tabs[i].Selected = selected; pages[i].Visible = selected; if (selected) { @@ -283,49 +295,14 @@ public sealed class ClassMacrosEditorControl : UserControl for (var i = 0; i < titles.Length; i++) { var index = i; - var label = new Label - { - Text = titles[i], - Dock = DockStyle.Fill, - TextAlign = ContentAlignment.MiddleCenter, - AutoSize = false, - BackColor = UiTheme.Surface, - ForeColor = UiTheme.Muted, - Cursor = Cursors.Hand, - Margin = new Padding(i == 0 ? 0 : 1, 0, 0, 0) - }; - label.Click += (_, _) => SelectTab(index); - label.MouseEnter += (_, _) => - { - if (selectedIndex != index) - { - label.BackColor = UiTheme.Hover; - } - }; - label.MouseLeave += (_, _) => - { - if (selectedIndex != index) - { - label.BackColor = UiTheme.Surface; - } - }; - label.Paint += (_, e) => - { - if (selectedIndex != index) - { - return; - } - - using var accent = new SolidBrush(UiTheme.Accent); - e.Graphics.FillRectangle(accent, 8, label.Height - 3, Math.Max(0, label.Width - 16), 2); - }; - label.SizeChanged += (_, _) => label.Invalidate(); - labels[i] = label; - tabBar.Controls.Add(label, i, 0); + var tab = new UiPillTab(titles[i]); + tab.Click += (_, _) => SelectTab(index); + tabs[i] = tab; + tabBar.Controls.Add(tab, i, 0); } root.Controls.Add(tabBar, 0, 0); - root.Controls.Add(contentHost, 0, 1); + root.Controls.Add(contentCard, 0, 1); SelectTab(0); return root; } @@ -614,6 +591,7 @@ public sealed class ClassMacrosEditorControl : UserControl Dock = DockStyle.Fill, AutoSize = false, ForeColor = UiTheme.Muted, + BackColor = Color.Transparent, TextAlign = ContentAlignment.MiddleLeft, Margin = new Padding(0), AutoEllipsis = true @@ -624,6 +602,7 @@ public sealed class ClassMacrosEditorControl : UserControl label.Dock = DockStyle.Fill; label.AutoSize = false; label.ForeColor = foreColor; + label.BackColor = Color.Transparent; label.TextAlign = ContentAlignment.MiddleLeft; label.AutoEllipsis = true; label.Margin = new Padding(0); diff --git a/UI/ModuleEditorControl.cs b/UI/ModuleEditorControl.cs index 4d1f29f3..01408f81 100644 --- a/UI/ModuleEditorControl.cs +++ b/UI/ModuleEditorControl.cs @@ -109,6 +109,9 @@ public sealed class ModuleEditorControl : UserControl _rulesGrid.Invalidate(); } + private const int ModuleFooterBarHeight = 56; + private const int ModuleFooterButtonHeight = 36; + private void InitializeComponent() { Dock = DockStyle.Fill; @@ -121,29 +124,32 @@ public sealed class ModuleEditorControl : UserControl Dock = DockStyle.Fill, BackColor = UiTheme.Surface, ColumnCount = 2, - RowCount = 1, + RowCount = 2, Margin = new Padding(0) }; root.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 300)); root.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); + root.RowStyles.Add(new RowStyle(SizeType.Absolute, ModuleFooterBarHeight)); Controls.Add(root); root.Controls.Add(BuildSidebar(), 0, 0); root.Controls.Add(BuildEditor(), 1, 0); + root.Controls.Add(BuildSidebarFooter(), 0, 1); + root.Controls.Add(BuildActionRow(), 1, 1); } private Control BuildSidebar() { - var sidebar = new TableLayoutPanel + var sidebar = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.Background, - Padding = new Padding(0, 0, 14, 0), + Padding = new Padding(14), + Margin = new Padding(0, 0, 12, 12), ColumnCount = 1, - RowCount = 2 + RowCount = 1 }; sidebar.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); - sidebar.RowStyles.Add(new RowStyle(SizeType.Absolute, 50)); _moduleList.Dock = DockStyle.Fill; UiTheme.StyleListBox( @@ -152,39 +158,39 @@ public sealed class ModuleEditorControl : UserControl index => index >= 0 && index < _modules.Count ? (_modules[index].Match.ClassId, _modules[index].Match.SpecId) : (null, null)); - _moduleList.BackColor = UiTheme.Background; + _moduleList.BackColor = UiTheme.SurfaceRaised; _moduleList.SelectedIndexChanged += (_, _) => SelectModule(_moduleList.SelectedIndex); sidebar.Controls.Add(_moduleList, 0, 0); + return sidebar; + } - var buttons = new TableLayoutPanel + private Control BuildSidebarFooter() + { + var footer = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.Background, - Margin = new Padding(0), - Padding = new Padding(14, 3, 14, 3), + Padding = new Padding(14, 10, 14, 10), + Margin = new Padding(0, 0, 12, 0), ColumnCount = 3, RowCount = 1 }; - buttons.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50)); - buttons.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 6)); - buttons.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50)); - buttons.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); + footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50)); + footer.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 8)); + footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50)); + footer.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); - var reloadButton = UiTheme.CreateButton("刷新", UiTheme.Field, UiTheme.Text); - reloadButton.AutoSize = false; + var reloadButton = UiTheme.CreateButton("刷新", UiTheme.ButtonKind.Secondary); + StyleModuleFooterButton(reloadButton); reloadButton.Dock = DockStyle.Fill; - reloadButton.Margin = new Padding(0); reloadButton.Click += (_, _) => LoadModules(); var getModulesButton = UiTheme.CreateButton( "获取模块", Color.FromArgb(252, 238, 10), Color.Black); - getModulesButton.AutoSize = false; + StyleModuleFooterButton(getModulesButton); getModulesButton.Dock = DockStyle.Fill; - getModulesButton.Margin = new Padding(0); getModulesButton.Padding = new Padding(0, 2, 24, 2); - getModulesButton.TextAlign = ContentAlignment.MiddleCenter; getModulesButton.FlatAppearance.BorderColor = Color.FromArgb(252, 238, 10); getModulesButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(255, 244, 64); getModulesButton.FlatAppearance.MouseDownBackColor = Color.FromArgb(220, 207, 8); @@ -197,11 +203,9 @@ public sealed class ModuleEditorControl : UserControl getModulesButton.DeviceDpi / 96F); getModulesButton.Click += (_, _) => OpenModuleWebsite(); - buttons.Controls.Add(reloadButton, 0, 0); - buttons.Controls.Add(getModulesButton, 2, 0); - sidebar.Controls.Add(buttons, 0, 1); - - return sidebar; + footer.Controls.Add(reloadButton, 0, 0); + footer.Controls.Add(getModulesButton, 2, 0); + return footer; } private static void DrawExternalLinkIcon( @@ -280,20 +284,18 @@ public sealed class ModuleEditorControl : UserControl { Dock = DockStyle.Fill, BackColor = UiTheme.Surface, - Padding = new Padding(10, 0, 0, 6), + Padding = new Padding(0), + Margin = new Padding(0, 0, 0, 12), ColumnCount = 1, - RowCount = 4 + RowCount = 3 }; - editor.RowStyles.Add(new RowStyle(SizeType.Absolute, 82)); - // 匹配卡片略增高,给首行居中和推荐天赋行的上下间距留出空间。 - editor.RowStyles.Add(new RowStyle(SizeType.Absolute, 112)); + editor.RowStyles.Add(new RowStyle(SizeType.Absolute, 86)); + editor.RowStyles.Add(new RowStyle(SizeType.Absolute, 120)); editor.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); - editor.RowStyles.Add(new RowStyle(SizeType.Absolute, 54)); editor.Controls.Add(BuildNameRow(), 0, 0); editor.Controls.Add(BuildMatchRow(), 0, 1); editor.Controls.Add(BuildEditorTabs(), 0, 2); - editor.Controls.Add(BuildActionRow(), 0, 3); return editor; } @@ -302,13 +304,13 @@ public sealed class ModuleEditorControl : UserControl var root = new TableLayoutPanel { Dock = DockStyle.Fill, - Margin = new Padding(0, 12, 0, 8), + Margin = new Padding(0, 12, 0, 0), Padding = new Padding(0), BackColor = UiTheme.Surface, ColumnCount = 1, RowCount = 2 }; - root.RowStyles.Add(new RowStyle(SizeType.Absolute, 38)); + root.RowStyles.Add(new RowStyle(SizeType.Absolute, 42)); root.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); var tabBar = new TableLayoutPanel @@ -317,7 +319,7 @@ public sealed class ModuleEditorControl : UserControl BackColor = UiTheme.Surface, ColumnCount = 3, RowCount = 1, - Margin = new Padding(0), + Margin = new Padding(0, 0, 0, 8), Padding = new Padding(0) }; tabBar.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); @@ -326,12 +328,24 @@ public sealed class ModuleEditorControl : UserControl tabBar.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F / 3F)); } + var contentCard = new UiCardPanel + { + Dock = DockStyle.Fill, + ColumnCount = 1, + RowCount = 1, + Margin = new Padding(0), + Padding = new Padding(0) + }; + contentCard.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + contentCard.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); + var contentHost = new Panel { Dock = DockStyle.Fill, BackColor = UiTheme.SurfaceRaised, Margin = new Padding(0) }; + contentCard.Controls.Add(contentHost, 0, 0); var pages = new[] { @@ -343,6 +357,7 @@ public sealed class ModuleEditorControl : UserControl { page.Dock = DockStyle.Fill; page.Visible = false; + page.BackColor = UiTheme.SurfaceRaised; contentHost.Controls.Add(page); } @@ -355,7 +370,7 @@ public sealed class ModuleEditorControl : UserControl contentHost.Controls.Add(_editorEmptyHint); _editorEmptyHint.BringToFront(); - var labels = new Label[3]; + var tabs = new UiPillTab[3]; var selectedIndex = -1; void SelectTab(int index) @@ -366,12 +381,10 @@ public sealed class ModuleEditorControl : UserControl } selectedIndex = index; - for (var i = 0; i < labels.Length; i++) + for (var i = 0; i < tabs.Length; i++) { var selected = i == index; - labels[i].BackColor = selected ? UiTheme.Field : UiTheme.Surface; - labels[i].ForeColor = selected ? UiTheme.Text : UiTheme.Muted; - labels[i].Invalidate(); + tabs[i].Selected = selected; pages[i].Visible = selected; if (selected) { @@ -384,51 +397,14 @@ public sealed class ModuleEditorControl : UserControl for (var i = 0; i < titles.Length; i++) { var index = i; - var label = new Label - { - Text = titles[i], - Dock = DockStyle.Fill, - TextAlign = ContentAlignment.MiddleCenter, - AutoSize = false, - BackColor = UiTheme.Surface, - ForeColor = UiTheme.Muted, - Cursor = Cursors.Hand, - Margin = new Padding(i == 0 ? 0 : 1, 0, 0, 0) - }; - label.Click += (_, _) => SelectTab(index); - label.MouseEnter += (_, _) => - { - if (selectedIndex != index) - { - label.BackColor = UiTheme.Hover; - } - }; - label.MouseLeave += (_, _) => - { - if (selectedIndex != index) - { - label.BackColor = UiTheme.Surface; - } - }; - label.Paint += (_, e) => - { - if (selectedIndex != index) - { - return; - } - - using var accent = new SolidBrush(UiTheme.Accent); - e.Graphics.FillRectangle(accent, 8, label.Height - 3, Math.Max(0, label.Width - 16), 2); - }; - // 标签随窗口/侧栏宽度变化时重绘, 否则选中下划线会停留在旧宽度。 - label.SizeChanged += (_, _) => label.Invalidate(); - - labels[i] = label; - tabBar.Controls.Add(label, i, 0); + var tab = new UiPillTab(titles[i]); + tab.Click += (_, _) => SelectTab(index); + tabs[i] = tab; + tabBar.Controls.Add(tab, i, 0); } root.Controls.Add(tabBar, 0, 0); - root.Controls.Add(contentHost, 0, 1); + root.Controls.Add(contentCard, 0, 1); SelectTab(0); return root; } @@ -553,14 +529,13 @@ public sealed class ModuleEditorControl : UserControl private Control BuildNameRow() { - var row = new TableLayoutPanel + var row = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, ColumnCount = 4, RowCount = 2, - Padding = new Padding(12, 10, 12, 4), - Margin = new Padding(0, 0, 0, 10) + Padding = new Padding(14, 10, 14, 8), + Margin = new Padding(0, 0, 0, 12) }; // 名称/作者各占剩余宽度的一半, 两个输入框等宽并铺满窗口。 row.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 58)); @@ -584,6 +559,7 @@ public sealed class ModuleEditorControl : UserControl _pathLabel.Dock = DockStyle.Fill; _pathLabel.ForeColor = UiTheme.Muted; + _pathLabel.BackColor = Color.Transparent; _pathLabel.TextAlign = ContentAlignment.MiddleLeft; _pathLabel.AutoEllipsis = true; _pathLabel.TextChanged += (_, _) => _pathToolTip.SetToolTip(_pathLabel, _pathLabel.Text); @@ -593,6 +569,7 @@ public sealed class ModuleEditorControl : UserControl // 版本号紧贴窗口右侧, 右对齐显示在"路径"同一行。 _versionLabel.Dock = DockStyle.Fill; _versionLabel.ForeColor = UiTheme.Muted; + _versionLabel.BackColor = Color.Transparent; _versionLabel.TextAlign = ContentAlignment.MiddleRight; _versionLabel.AutoEllipsis = true; row.Controls.Add(_versionLabel, 3, 1); @@ -604,13 +581,12 @@ public sealed class ModuleEditorControl : UserControl { var matchLabels = new[] { "职业", "专精", "英雄天赋", "队伍类型" }; - var row = new TableLayoutPanel + var row = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, ColumnCount = 12, RowCount = 2, - Padding = new Padding(12), + Padding = new Padding(14), Margin = new Padding(0) }; foreach (var label in matchLabels) @@ -649,7 +625,7 @@ public sealed class ModuleEditorControl : UserControl var recommendedTalentRow = new TableLayoutPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, + BackColor = Color.Transparent, ColumnCount = 2, RowCount = 1, // 推荐天赋整行相对原位置下移 4px,并与上方匹配项保持清晰间距。 @@ -2218,7 +2194,7 @@ public sealed class ModuleEditorControl : UserControl var oldSmoothingMode = e.Graphics.SmoothingMode; e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; - using (var path = CreateRoundedRectanglePath(buttonBounds, 4)) + using (var path = UiTheme.CreateRoundedRectanglePath(buttonBounds, 4)) using (var background = new SolidBrush(selected ? UiTheme.Pressed : UiTheme.Hover)) using (var border = new Pen(selected ? UiTheme.Accent : UiTheme.Border)) { @@ -2243,18 +2219,6 @@ public sealed class ModuleEditorControl : UserControl e.Handled = true; } - private static GraphicsPath CreateRoundedRectanglePath(Rectangle bounds, int radius) - { - var path = new GraphicsPath(); - var diameter = radius * 2; - path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90); - path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90); - path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90); - path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90); - path.CloseFigure(); - return path; - } - // 自绘 2×3 六点抓手, 不依赖字体里是否有 grip 字形; 新行不画。 private void PaintRuleDragHandle(DataGridViewCellPaintingEventArgs e) { @@ -2869,56 +2833,128 @@ public sealed class ModuleEditorControl : UserControl private Control BuildActionRow() { - var row = new Panel + var row = new UiCardPanel { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, + ColumnCount = 3, + RowCount = 1, Margin = new Padding(0), - Padding = new Padding(12, 0, 12, 0) + Padding = new Padding(14, 10, 14, 10) }; + row.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 148)); + row.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + row.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 360)); + row.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); - var hint = new Label + var openFolderButton = UiTheme.CreateButton("打开目录", UiTheme.ButtonKind.Secondary); + StyleModuleFooterButton(openFolderButton); + openFolderButton.Dock = DockStyle.Fill; + openFolderButton.Margin = new Padding(0, 0, 8, 0); + openFolderButton.Click += (_, _) => OpenModuleFolder(); + _pathToolTip.SetToolTip( + openFolderButton, + "在资源管理器中打开模块目录;若已选中已保存模块则定位到对应文件"); + + var spacer = new Panel { - Text = "目标可选技能支持的单位或上方定义的动态单位;宏条件来自对应宏的方括号;点击“条件”列打开可视化编辑器", Dock = DockStyle.Fill, - ForeColor = UiTheme.Muted, - TextAlign = ContentAlignment.MiddleLeft, - AutoEllipsis = true, + BackColor = Color.Transparent, Margin = new Padding(0) }; - var buttons = new FlowLayoutPanel + var buttons = new TableLayoutPanel { - AutoSize = true, - AutoSizeMode = AutoSizeMode.GrowAndShrink, - Dock = DockStyle.Right, - FlowDirection = FlowDirection.RightToLeft, - WrapContents = false, - BackColor = UiTheme.SurfaceRaised, + Dock = DockStyle.Fill, + BackColor = Color.Transparent, + ColumnCount = 3, + RowCount = 1, Margin = new Padding(0), - Padding = new Padding(0, 8, 0, 8) + Padding = new Padding(0) }; - - _saveButton = UiTheme.CreateButton("保存", UiTheme.ButtonKind.Primary); - _saveButton.Margin = new Padding(8, 0, 0, 0); - _saveButton.Click += async (_, _) => await RunModuleCommandAsync(SaveSelectedModuleAsync); - - _deleteButton = UiTheme.CreateButton("删除", UiTheme.ButtonKind.Danger); - _deleteButton.Margin = new Padding(8, 0, 0, 0); - _deleteButton.Click += async (_, _) => await RunModuleCommandAsync(DeleteSelectedModuleAsync); + buttons.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.33f)); + buttons.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.33f)); + buttons.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33.34f)); + buttons.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); _addButton = UiTheme.CreateButton("新建", UiTheme.ButtonKind.Secondary); - _addButton.Margin = new Padding(8, 0, 0, 0); + StyleModuleFooterButton(_addButton); + _addButton.Dock = DockStyle.Fill; + _addButton.Margin = new Padding(0, 0, 8, 0); _addButton.Click += async (_, _) => await RunModuleCommandAsync(AddModuleAsync); - buttons.Controls.Add(_saveButton); - buttons.Controls.Add(_deleteButton); - buttons.Controls.Add(_addButton); - row.Controls.Add(hint); - row.Controls.Add(buttons); + _deleteButton = UiTheme.CreateButton("删除", UiTheme.ButtonKind.Danger); + StyleModuleFooterButton(_deleteButton); + _deleteButton.Dock = DockStyle.Fill; + _deleteButton.Margin = new Padding(0, 0, 8, 0); + _deleteButton.Click += async (_, _) => await RunModuleCommandAsync(DeleteSelectedModuleAsync); + + _saveButton = UiTheme.CreateButton("保存", UiTheme.ButtonKind.Primary); + StyleModuleFooterButton(_saveButton); + _saveButton.Dock = DockStyle.Fill; + _saveButton.Margin = new Padding(0); + _saveButton.Click += async (_, _) => await RunModuleCommandAsync(SaveSelectedModuleAsync); + + buttons.Controls.Add(_addButton, 0, 0); + buttons.Controls.Add(_deleteButton, 1, 0); + buttons.Controls.Add(_saveButton, 2, 0); + + row.Controls.Add(openFolderButton, 0, 0); + row.Controls.Add(spacer, 1, 0); + row.Controls.Add(buttons, 2, 0); return row; } + private void OpenModuleFolder() + { + var moduleDirectory = ModuleStore.ResolveModuleDirectory(_baseDirectory); + var filePath = _selectedModule?.FilePath; + try + { + if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(filePath)) + { + Process.Start(new ProcessStartInfo + { + FileName = "explorer.exe", + Arguments = $"/select,\"{filePath}\"", + UseShellExecute = true + }); + return; + } + + if (!Directory.Exists(moduleDirectory)) + { + Directory.CreateDirectory(moduleDirectory); + } + + Process.Start(new ProcessStartInfo + { + FileName = "explorer.exe", + Arguments = $"\"{moduleDirectory}\"", + UseShellExecute = true + }); + } + catch (Exception ex) + { + MessageBox.Show( + $"无法打开模块文件夹:{ex.Message}", + "Shigure", + MessageBoxButtons.OK, + MessageBoxIcon.Warning); + } + } + + private static void StyleModuleFooterButton(Button button) + { + button.AutoSize = false; + button.Height = ModuleFooterButtonHeight; + button.Margin = new Padding(0); + button.Padding = new Padding(10, 0, 10, 0); + button.TextAlign = ContentAlignment.MiddleCenter; + } + + private static void StyleModuleActionButton(Button button) + => StyleModuleFooterButton(button); + private void LoadModules() { _moduleStore.Reload(); @@ -3358,6 +3394,7 @@ public sealed class ModuleEditorControl : UserControl Text = text, Dock = DockStyle.Fill, ForeColor = UiTheme.Muted, + BackColor = Color.Transparent, TextAlign = ContentAlignment.MiddleLeft, AutoEllipsis = true }; diff --git a/UI/StatusForm.cs b/UI/StatusForm.cs index c7ec7800..51b696d7 100644 --- a/UI/StatusForm.cs +++ b/UI/StatusForm.cs @@ -651,7 +651,7 @@ public sealed class StatusForm : Form AboutWatermarkOpacity) { Dock = DockStyle.Fill, - BackColor = UiTheme.SurfaceRaised, + BackColor = UiTheme.Surface, AutoScroll = true, Margin = new Padding(0) }; @@ -663,13 +663,25 @@ public sealed class StatusForm : Form AutoSizeMode = AutoSizeMode.GrowAndShrink, BackColor = Color.Transparent, ColumnCount = 1, - RowCount = 4, - Padding = new Padding(18) + RowCount = 3, + Padding = new Padding(0), + Margin = new Padding(0) }; panel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); panel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); panel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); - panel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + + var infoCard = new UiCardPanel + { + AutoSize = true, + Dock = DockStyle.Top, + ColumnCount = 1, + RowCount = 2, + Padding = new Padding(14), + Margin = new Padding(0, 0, 0, 12) + }; + infoCard.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + infoCard.RowStyles.Add(new RowStyle(SizeType.AutoSize)); var heading = new TableLayoutPanel { @@ -678,7 +690,7 @@ public sealed class StatusForm : Form BackColor = Color.Transparent, ColumnCount = 1, RowCount = 2, - Margin = new Padding(0, 0, 0, 22) + Margin = new Padding(0, 0, 0, 16) }; heading.RowStyles.Add(new RowStyle(SizeType.AutoSize)); heading.RowStyles.Add(new RowStyle(SizeType.AutoSize)); @@ -687,6 +699,7 @@ public sealed class StatusForm : Form Text = "Shigure", AutoSize = true, ForeColor = UiTheme.Text, + BackColor = Color.Transparent, Font = new Font(Font.FontFamily, 18F, FontStyle.Bold), Margin = new Padding(0, 0, 0, 4) }, 0, 0); @@ -695,10 +708,11 @@ public sealed class StatusForm : Form Text = "应用信息与 ClassBlocks 可用状态字段参考", AutoSize = true, ForeColor = UiTheme.Muted, + BackColor = Color.Transparent, Font = new Font(Font.FontFamily, 9.5F, FontStyle.Regular), Margin = new Padding(0) }, 0, 1); - panel.Controls.Add(heading, 0, 0); + infoCard.Controls.Add(heading, 0, 0); var assembly = Assembly.GetExecutingAssembly(); var version = AppInfo.Version; @@ -709,13 +723,13 @@ public sealed class StatusForm : Form AutoSize = true, Dock = DockStyle.Top, BackColor = Color.Transparent, - ColumnCount = 3, + ColumnCount = 2, RowCount = 0, - Padding = new Padding(0) + Padding = new Padding(0), + Margin = new Padding(0) }; details.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 120)); details.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); - details.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 72)); AddAboutRow(details, "产品", "Shigure"); AddAboutRow(details, "公司", company); @@ -727,17 +741,18 @@ public sealed class StatusForm : Form var configPath = ConfigService.ResolveConfigPath(AppPaths.BaseDirectory); AddAboutRow(details, "模块目录", FormatAboutPath(modulePath), modulePath); AddAboutRow(details, "配置目录", FormatAboutPath(configPath), configPath); - - panel.Controls.Add(details, 0, 1); + infoCard.Controls.Add(details, 0, 1); + panel.Controls.Add(infoCard, 0, 0); panel.Controls.Add(new Label { Text = "可用状态字段", AutoSize = true, ForeColor = UiTheme.Text, + BackColor = Color.Transparent, Font = new Font(Font.FontFamily, 12F, FontStyle.Bold), - Margin = new Padding(0, 18, 0, 12) - }, 0, 2); + Margin = new Padding(2, 6, 0, 12) + }, 0, 1); var fields = new TableLayoutPanel { @@ -789,7 +804,7 @@ public sealed class StatusForm : Form ["类型", "生命值", "距离", "施法", "施法可打断", "引导", "引导可打断"], 104), 1, 2); - panel.Controls.Add(fields, 0, 3); + panel.Controls.Add(fields, 0, 2); scrollHost.Controls.Add(panel); return scrollHost; } @@ -799,12 +814,12 @@ public sealed class StatusForm : Form private Control CreateAboutFieldCard(string title, IReadOnlyList items, int minimumHeight) { - var card = new AboutFieldCardPanel + var card = new UiCardPanel { Dock = DockStyle.Fill, ColumnCount = 1, RowCount = 2, - Padding = new Padding(16, 14, 16, 14), + Padding = new Padding(14), Margin = new Padding(0, 0, 12, 12), MinimumSize = new Size(0, minimumHeight) }; @@ -816,6 +831,7 @@ public sealed class StatusForm : Form Text = title, Dock = DockStyle.Fill, ForeColor = UiTheme.Accent, + BackColor = Color.Transparent, Font = new Font(Font.FontFamily, 10.5F, FontStyle.Bold), TextAlign = ContentAlignment.MiddleLeft, Margin = new Padding(0) @@ -826,6 +842,7 @@ public sealed class StatusForm : Form Dock = DockStyle.Fill, AutoSize = false, ForeColor = UiTheme.Text, + BackColor = Color.Transparent, Font = new Font(Font.FontFamily, 9.5F, FontStyle.Regular), TextAlign = ContentAlignment.TopLeft, Margin = new Padding(0, 6, 0, 0) @@ -853,7 +870,7 @@ public sealed class StatusForm : Form } } - private void AddAboutRow(TableLayoutPanel panel, string name, string value, string? copyValue = null) + private void AddAboutRow(TableLayoutPanel panel, string name, string value, string? tooltip = null) { var row = panel.RowCount++; panel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); @@ -864,6 +881,7 @@ public sealed class StatusForm : Form Width = 104, Height = 26, ForeColor = UiTheme.Muted, + BackColor = Color.Transparent, AutoEllipsis = true, TextAlign = ContentAlignment.MiddleLeft, Margin = new Padding(0, 0, 18, 14) @@ -875,23 +893,12 @@ public sealed class StatusForm : Form AutoSize = false, AutoEllipsis = true, ForeColor = UiTheme.Text, + BackColor = Color.Transparent, TextAlign = ContentAlignment.MiddleLeft, - Margin = new Padding(0, 0, 12, 14) + Margin = new Padding(0, 0, 0, 14) }; - _toolTip.SetToolTip(valueLabel, copyValue ?? value); + _toolTip.SetToolTip(valueLabel, tooltip ?? value); panel.Controls.Add(valueLabel, 1, row); - - if (!string.IsNullOrWhiteSpace(copyValue)) - { - var copyButton = UiTheme.CreateButton("复制", UiTheme.ButtonKind.Secondary); - copyButton.AutoSize = false; - copyButton.Dock = DockStyle.Top; - copyButton.Height = 28; - copyButton.Margin = new Padding(0, 0, 0, 10); - copyButton.Padding = new Padding(4, 0, 4, 0); - copyButton.Click += (_, _) => Clipboard.SetText(copyValue); - panel.Controls.Add(copyButton, 2, row); - } } private sealed class WatermarkPanel : Panel @@ -966,25 +973,6 @@ public sealed class StatusForm : Form } } - private sealed class AboutFieldCardPanel : TableLayoutPanel - { - private const int BackgroundOpacity = 166; - - public AboutFieldCardPanel() - { - SetStyle(ControlStyles.SupportsTransparentBackColor, true); - BackColor = Color.Transparent; - DoubleBuffered = true; - } - - protected override void OnPaintBackground(PaintEventArgs e) - { - base.OnPaintBackground(e); - using var background = new SolidBrush(Color.FromArgb(BackgroundOpacity, UiTheme.Field)); - e.Graphics.FillRectangle(background, e.ClipRectangle); - } - } - public void ShowOrActivate(RenderSnapshot? snapshot) { if (snapshot is not null) @@ -1447,7 +1435,7 @@ public sealed class StatusForm : Form : _hovered ? UiTheme.Hover : UiTheme.Background; - using (var path = CreateRoundedPath(bounds, Math.Max(6, (int)Math.Round(8 * scale)))) + using (var path = UiTheme.CreateRoundedRectanglePath(bounds, Math.Max(6, (int)Math.Round(8 * scale)))) using (var background = new SolidBrush(backgroundColor)) { graphics.FillPath(background, path); @@ -1502,18 +1490,6 @@ public sealed class StatusForm : Form } } - private static GraphicsPath CreateRoundedPath(Rectangle bounds, int radius) - { - var diameter = Math.Min(radius * 2, Math.Min(bounds.Width, bounds.Height)); - var path = new GraphicsPath(); - path.AddArc(bounds.Left, bounds.Top, diameter, diameter, 180, 90); - path.AddArc(bounds.Right - diameter, bounds.Top, diameter, diameter, 270, 90); - path.AddArc(bounds.Right - diameter, bounds.Bottom - diameter, diameter, diameter, 0, 90); - path.AddArc(bounds.Left, bounds.Bottom - diameter, diameter, diameter, 90, 90); - path.CloseFigure(); - return path; - } - private static void DrawIcon(Graphics graphics, SettingsNavIcon icon, Rectangle bounds, Color color, float scale) { var x = bounds.Left; diff --git a/UI/UiCardPanel.cs b/UI/UiCardPanel.cs index 19e9a011..fee6183e 100644 --- a/UI/UiCardPanel.cs +++ b/UI/UiCardPanel.cs @@ -1,10 +1,12 @@ +using System.ComponentModel; using System.Drawing.Drawing2D; namespace Shigure; internal sealed class UiCardPanel : TableLayoutPanel { - private const int CornerRadius = 8; + private Color _fillColor = UiTheme.SurfaceRaised; + private int _cornerRadius = 10; public UiCardPanel() { @@ -17,6 +19,41 @@ internal sealed class UiCardPanel : TableLayoutPanel BackColor = Color.Transparent; } + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Color FillColor + { + get => _fillColor; + set + { + if (_fillColor == value) + { + return; + } + + _fillColor = value; + Invalidate(); + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int CornerRadius + { + get => _cornerRadius; + set + { + var next = Math.Max(0, value); + if (_cornerRadius == next) + { + return; + } + + _cornerRadius = next; + Invalidate(); + } + } + protected override void OnPaintBackground(PaintEventArgs e) { base.OnPaintBackground(e); @@ -25,8 +62,8 @@ internal sealed class UiCardPanel : TableLayoutPanel return; } - using var path = CreateRoundedPath(ClientRectangle, UiTheme.Scale(this, CornerRadius)); - using var fill = new SolidBrush(UiTheme.SurfaceRaised); + using var path = UiTheme.CreateRoundedRectanglePath(ClientRectangle, UiTheme.Scale(this, CornerRadius)); + using var fill = new SolidBrush(FillColor); e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.FillPath(fill, path); } @@ -40,25 +77,9 @@ internal sealed class UiCardPanel : TableLayoutPanel } var bounds = Rectangle.Inflate(ClientRectangle, -1, -1); - using var path = CreateRoundedPath(bounds, UiTheme.Scale(this, CornerRadius)); + using var path = UiTheme.CreateRoundedRectanglePath(bounds, UiTheme.Scale(this, CornerRadius)); using var outline = new Pen(UiTheme.Border); e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.DrawPath(outline, path); } - - private static GraphicsPath CreateRoundedPath(Rectangle bounds, int radius) - { - var diameter = Math.Max(2, Math.Min(radius * 2, Math.Min(bounds.Width, bounds.Height))); - var arc = new Rectangle(bounds.Location, new Size(diameter, diameter)); - var path = new GraphicsPath(); - path.AddArc(arc, 180, 90); - arc.X = bounds.Right - diameter; - path.AddArc(arc, 270, 90); - arc.Y = bounds.Bottom - diameter; - path.AddArc(arc, 0, 90); - arc.X = bounds.Left; - path.AddArc(arc, 90, 90); - path.CloseFigure(); - return path; - } } diff --git a/UI/UiPillTab.cs b/UI/UiPillTab.cs new file mode 100644 index 00000000..1ae8ee25 --- /dev/null +++ b/UI/UiPillTab.cs @@ -0,0 +1,129 @@ +using System.ComponentModel; +using System.Drawing.Drawing2D; + +namespace Shigure; + +/// +/// 编辑器分区标签:圆角 pill,选中为 AccentSoft 填充。 +/// +internal sealed class UiPillTab : Control +{ + private bool _selected; + private bool _hovered; + private bool _pressed; + + public UiPillTab(string text) + { + SetStyle( + ControlStyles.AllPaintingInWmPaint + | ControlStyles.OptimizedDoubleBuffer + | ControlStyles.ResizeRedraw + | ControlStyles.UserPaint + | ControlStyles.SupportsTransparentBackColor, + true); + Text = text; + Dock = DockStyle.Fill; + Cursor = Cursors.Hand; + BackColor = Color.Transparent; + ForeColor = UiTheme.Muted; + Margin = new Padding(3, 2, 3, 2); + TabStop = false; + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool Selected + { + get => _selected; + set + { + if (_selected == value) + { + return; + } + + _selected = value; + Invalidate(); + } + } + + protected override void OnMouseEnter(EventArgs e) + { + base.OnMouseEnter(e); + _hovered = true; + Invalidate(); + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + _hovered = false; + _pressed = false; + Invalidate(); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + if (e.Button == MouseButtons.Left) + { + _pressed = true; + Invalidate(); + } + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + if (_pressed) + { + _pressed = false; + Invalidate(); + } + } + + protected override void OnPaint(PaintEventArgs e) + { + var graphics = e.Graphics; + graphics.SmoothingMode = SmoothingMode.AntiAlias; + + var bounds = new Rectangle(0, 0, Math.Max(1, Width - 1), Math.Max(1, Height - 1)); + var fill = _selected + ? UiTheme.AccentSoft + : _pressed + ? UiTheme.Pressed + : _hovered + ? UiTheme.Hover + : Color.Transparent; + + if (fill.A > 0) + { + using var path = UiTheme.CreateRoundedRectanglePath(bounds, UiTheme.Scale(this, 8)); + using var brush = new SolidBrush(fill); + graphics.FillPath(brush, path); + if (_selected) + { + using var border = new Pen(Color.FromArgb(90, UiTheme.Accent)); + graphics.DrawPath(border, path); + } + } + + var textColor = _selected || _hovered ? UiTheme.Text : UiTheme.Muted; + if (_selected) + { + textColor = UiTheme.Accent; + } + + TextRenderer.DrawText( + graphics, + Text, + Font, + ClientRectangle, + textColor, + TextFormatFlags.HorizontalCenter + | TextFormatFlags.VerticalCenter + | TextFormatFlags.SingleLine + | TextFormatFlags.EndEllipsis + | TextFormatFlags.NoPrefix); + } +} diff --git a/UI/UiTheme.cs b/UI/UiTheme.cs index 3061ed63..75c1a163 100644 --- a/UI/UiTheme.cs +++ b/UI/UiTheme.cs @@ -1,4 +1,5 @@ using System.Drawing; +using System.Drawing.Drawing2D; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -223,6 +224,7 @@ internal static class UiTheme button.FlatAppearance.BorderColor = backColor == Accent ? Accent : Border; button.FlatAppearance.MouseOverBackColor = backColor == Accent ? Color.FromArgb(112, 234, 221) : Hover; button.FlatAppearance.MouseDownBackColor = backColor == Accent ? Color.FromArgb(62, 194, 181) : Pressed; + ApplyControlRoundedRegion(button, 8); return button; } @@ -241,6 +243,71 @@ internal static class UiTheme public static int Scale(Control control, int logicalPixels) => Math.Max(1, (int)Math.Round(logicalPixels * control.DeviceDpi / 96F)); + public static GraphicsPath CreateRoundedRectanglePath(Rectangle bounds, int radius) + { + var path = new GraphicsPath(); + if (bounds.Width <= 0 || bounds.Height <= 0) + { + path.AddRectangle(bounds); + return path; + } + + var diameter = Math.Max(2, Math.Min(radius * 2, Math.Min(bounds.Width, bounds.Height))); + var arc = new Rectangle(bounds.Location, new Size(diameter, diameter)); + path.AddArc(arc, 180, 90); + arc.X = bounds.Right - diameter; + path.AddArc(arc, 270, 90); + arc.Y = bounds.Bottom - diameter; + path.AddArc(arc, 0, 90); + arc.X = bounds.Left; + path.AddArc(arc, 90, 90); + path.CloseFigure(); + return path; + } + + public static void ApplyControlRoundedRegion(Control control, int logicalRadius = 8) + { + void UpdateRegion() + { + if (control.Width <= 0 || control.Height <= 0) + { + return; + } + + var diameter = Math.Min( + Scale(control, logicalRadius) * 2, + Math.Min(control.Width, control.Height)); + var regionHandle = CreateRoundRectRgn(0, 0, control.Width + 1, control.Height + 1, diameter, diameter); + if (regionHandle == 0) + { + return; + } + + try + { + var previous = control.Region; + control.Region = Region.FromHrgn(regionHandle); + previous?.Dispose(); + } + finally + { + _ = DeleteObject(regionHandle); + } + } + + control.Resize -= OnRoundedRegionResize; + control.Resize += OnRoundedRegionResize; + control.HandleCreated -= OnRoundedRegionHandleCreated; + control.HandleCreated += OnRoundedRegionHandleCreated; + if (control.IsHandleCreated) + { + UpdateRegion(); + } + + void OnRoundedRegionResize(object? sender, EventArgs e) => UpdateRegion(); + void OnRoundedRegionHandleCreated(object? sender, EventArgs e) => UpdateRegion(); + } + public static void StyleTextBox(TextBox textBox) { textBox.BackColor = Field;