diff --git a/Elfisa.sln b/Elfisa.sln
index 0d7cfa2..fc6d9da 100644
--- a/Elfisa.sln
+++ b/Elfisa.sln
@@ -1,23 +1,95 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elfisa.UI", "src\Elfisa.UI\Elfisa.UI.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronicPharmacy", "src\ElectronicPharmacy\ElectronicPharmacy.csproj", "{145D8060-4187-4871-B4FB-8DF795727B66}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elfisa.MaterialDemo", "src\Elfisa.MaterialDemo\Elfisa.MaterialDemo.csproj", "{7974F646-2F23-46E3-A614-A3D0C5D8C844}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elfisa.ButtonSkillDemo", "src\Elfisa.ButtonSkillDemo\Elfisa.ButtonSkillDemo.csproj", "{1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elfisa.GradientButtonDemo", "src\Elfisa.GradientButtonDemo\Elfisa.GradientButtonDemo.csproj", "{19457083-89B8-4C5B-BCA1-BFB66C273502}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
{145D8060-4187-4871-B4FB-8DF795727B66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{145D8060-4187-4871-B4FB-8DF795727B66}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Debug|x64.Build.0 = Debug|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Debug|x86.Build.0 = Debug|Any CPU
{145D8060-4187-4871-B4FB-8DF795727B66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{145D8060-4187-4871-B4FB-8DF795727B66}.Release|Any CPU.Build.0 = Release|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Release|x64.ActiveCfg = Release|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Release|x64.Build.0 = Release|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Release|x86.ActiveCfg = Release|Any CPU
+ {145D8060-4187-4871-B4FB-8DF795727B66}.Release|x86.Build.0 = Release|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Debug|x64.Build.0 = Debug|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Debug|x86.Build.0 = Debug|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Release|x64.ActiveCfg = Release|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Release|x64.Build.0 = Release|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Release|x86.ActiveCfg = Release|Any CPU
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844}.Release|x86.Build.0 = Release|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Debug|x64.Build.0 = Debug|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Debug|x86.Build.0 = Debug|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Release|x64.ActiveCfg = Release|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Release|x64.Build.0 = Release|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Release|x86.ActiveCfg = Release|Any CPU
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85}.Release|x86.Build.0 = Release|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Debug|x64.Build.0 = Debug|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Debug|x86.Build.0 = Debug|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Release|Any CPU.Build.0 = Release|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Release|x64.ActiveCfg = Release|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Release|x64.Build.0 = Release|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Release|x86.ActiveCfg = Release|Any CPU
+ {19457083-89B8-4C5B-BCA1-BFB66C273502}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {7974F646-2F23-46E3-A614-A3D0C5D8C844} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
+ {1A28299F-5E9B-4E2E-AEC1-EB35FB911F85} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
+ {19457083-89B8-4C5B-BCA1-BFB66C273502} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
EndGlobal
diff --git a/src/ElectronicPharmacy/Program.cs b/src/ElectronicPharmacy/Program.cs
index b7d1b4c..a8be127 100644
--- a/src/ElectronicPharmacy/Program.cs
+++ b/src/ElectronicPharmacy/Program.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Windows.Forms;
using Elfisa.UI.Theming;
@@ -11,7 +11,7 @@ namespace Электронная_Фармация
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
- ThemeManager.CurrentVariant = ThemeVariant.FluentWin11;
+ ThemeManager.CurrentVariant = ThemeVariant.PharmaTrust;
Application.Run(new ElectroPharmacy());
}
}
diff --git a/src/ElectronicPharmacy/UserControls/FOrders.cs b/src/ElectronicPharmacy/UserControls/FOrders.cs
index 73cc359..f70554b 100644
--- a/src/ElectronicPharmacy/UserControls/FOrders.cs
+++ b/src/ElectronicPharmacy/UserControls/FOrders.cs
@@ -91,7 +91,7 @@ namespace Электронная_Фармация.UserControls
const int row1Y = 10;
const int controlHeight = 32;
const int filterHeight = 33;
- const int buttonHeight = 34;
+ const int buttonHeight = 37;
const int rowGap = 14;
const int row2Y = row1Y + buttonHeight + rowGap;
const int commentWidth = 200;
@@ -109,10 +109,10 @@ namespace Электронная_Фармация.UserControls
int right = panelToolbar.ClientSize.Width - pad;
- btnDeleteOrder.SetBounds(right - 130, row1Y, 130, buttonHeight);
+ btnDeleteOrder.SetBounds(right - 148, row1Y, 148, buttonHeight);
right = btnDeleteOrder.Left - gap;
- _btnSendOrder.SetBounds(right - 150, row1Y, 150, buttonHeight);
+ _btnSendOrder.SetBounds(right - 168, row1Y, 168, buttonHeight);
right = _btnSendOrder.Left - gap;
_comboConsignees.SetBounds(right - 190, filterY, 190, filterHeight);
@@ -126,14 +126,15 @@ namespace Электронная_Фармация.UserControls
x = pad;
txtGoodFromOrders.SetBounds(x, row2Y, 220, controlHeight);
x = txtGoodFromOrders.Right + gap;
- btnClearGoodSearch.SetBounds(x, row2Y, 34, controlHeight);
+ btnClearGoodSearch.SetBounds(x, row2Y, 32, controlHeight + 3);
x = btnClearGoodSearch.Right + gap;
txtOrderNumberFilter.SetBounds(x, row2Y, 220, controlHeight);
x = txtOrderNumberFilter.Right + gap;
- btnClearOrderNumberFilter.SetBounds(x, row2Y, 34, controlHeight);
+ btnClearOrderNumberFilter.SetBounds(x, row2Y, 32, controlHeight + 3);
x = btnClearOrderNumberFilter.Right + gap;
btnAddComment.SetBounds(x, row2Y, commentWidth, buttonHeight);
+ ModernButtonStyles.FitToText(btnAddComment, buttonHeight);
btnClearSuppliers.Visible = false;
diff --git a/src/ElectronicPharmacy/UserControls/UCPriceList.cs b/src/ElectronicPharmacy/UserControls/UCPriceList.cs
index fe2f19b..c667c75 100644
--- a/src/ElectronicPharmacy/UserControls/UCPriceList.cs
+++ b/src/ElectronicPharmacy/UserControls/UCPriceList.cs
@@ -52,9 +52,9 @@ namespace Электронная_Фармация.UserControls
Tag = "chrome-child";
btnDeleteGood.Height = 34;
- btnClearSearchInPriceList.Height = 32;
+ btnClearSearchInPriceList.Height = 35;
txtSearchGoodNameInPriceList.Height = 32;
- btnClearSupplierFilter.Height = 32;
+ btnClearSupplierFilter.Height = 35;
panel1.SizeChanged += (_, __) => LayoutMarkupRow();
LayoutMarkupRow();
diff --git a/src/Elfisa.ButtonSkillDemo/ButtonSkillDemoForm.cs b/src/Elfisa.ButtonSkillDemo/ButtonSkillDemoForm.cs
new file mode 100644
index 0000000..86c5ca2
--- /dev/null
+++ b/src/Elfisa.ButtonSkillDemo/ButtonSkillDemoForm.cs
@@ -0,0 +1,223 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Elfisa.ButtonSkillDemo;
+
+internal sealed class ButtonSkillDemoForm : Form
+{
+ public ButtonSkillDemoForm()
+ {
+ Text = "Skill ModernButton — демо стилей";
+ Size = new Size(920, 620);
+ MinimumSize = new Size(820, 520);
+ StartPosition = FormStartPosition.CenterScreen;
+ BackColor = Color.FromArgb(18, 18, 22);
+ Font = new Font("Segoe UI", 10f);
+
+ var header = new Label
+ {
+ Text = "Custom Modern Button (skill) — варианты стилей",
+ ForeColor = Color.White,
+ AutoSize = false,
+ Height = 48,
+ Dock = DockStyle.Top,
+ Padding = new Padding(24, 14, 24, 0),
+ Font = new Font("Segoe UI Semibold", 14f, FontStyle.Bold)
+ };
+
+ var scroll = new Panel
+ {
+ Dock = DockStyle.Fill,
+ AutoScroll = true,
+ Padding = new Padding(24)
+ };
+
+ var layout = new TableLayoutPanel
+ {
+ Dock = DockStyle.Top,
+ AutoSize = true,
+ ColumnCount = 2,
+ Padding = new Padding(0, 8, 0, 24)
+ };
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 220));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
+
+ AddSection(layout, "Default (тёмная)", CreateDefaultButton());
+ AddSection(layout, "Светлая тема", CreateLightButton());
+ AddSection(layout, "Неоновый стиль", CreateNeonButton());
+ AddSection(layout, "Glass (полупрозрачный)", CreateGlassButton());
+ AddSection(layout, "Pharma Trust — синяя", CreatePharmaPrimaryButton());
+ AddSection(layout, "Pharma Trust — красный ×", CreatePharmaClearButton());
+ AddSection(layout, "Большая CTA", CreateLargeCtaButton());
+
+ var note = new Label
+ {
+ AutoSize = true,
+ MaximumSize = new Size(820, 0),
+ ForeColor = Color.FromArgb(180, 180, 190),
+ Text =
+ "Это отдельный класс SkillModernButton для демо. Основное приложение использует Elfisa.UI.Controls.ModernButton + ThemeManager.\n" +
+ "Наведите курсор и нажмите кнопки — видны hover и pressed.",
+ Margin = new Padding(0, 8, 0, 0)
+ };
+
+ layout.Controls.Add(note, 0, layout.RowCount);
+ layout.SetColumnSpan(note, 2);
+
+ scroll.Controls.Add(layout);
+ Controls.Add(scroll);
+ Controls.Add(header);
+ }
+
+ private static void AddSection(TableLayoutPanel layout, string title, Control content)
+ {
+ var row = layout.RowCount;
+ layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
+
+ var label = new Label
+ {
+ Text = title,
+ ForeColor = Color.FromArgb(200, 200, 210),
+ AutoSize = true,
+ Margin = new Padding(0, 16, 12, 8),
+ Font = new Font("Segoe UI Semibold", 10.5f, FontStyle.Bold)
+ };
+
+ content.Margin = new Padding(0, 12, 0, 8);
+ layout.Controls.Add(label, 0, row);
+ layout.Controls.Add(content, 1, row);
+ layout.RowCount++;
+ }
+
+ private static FlowLayoutPanel Row(params Control[] buttons)
+ {
+ var panel = new FlowLayoutPanel
+ {
+ AutoSize = true,
+ WrapContents = true,
+ FlowDirection = FlowDirection.LeftToRight
+ };
+ foreach (var btn in buttons)
+ {
+ panel.Controls.Add(btn);
+ }
+ return panel;
+ }
+
+ private static SkillModernButton CreateDefaultButton()
+ {
+ return new SkillModernButton
+ {
+ Text = "Красивая кнопка",
+ Size = new Size(200, 48),
+ Margin = new Padding(0, 0, 12, 0)
+ };
+ }
+
+ private static SkillModernButton CreateLightButton()
+ {
+ return new SkillModernButton
+ {
+ Text = "Светлая кнопка",
+ Size = new Size(200, 48),
+ BackColor = Color.White,
+ ForeColor = Color.FromArgb(30, 30, 30),
+ BorderColor = Color.FromArgb(0, 122, 204),
+ HoverColor = Color.FromArgb(245, 249, 255),
+ PressedColor = Color.FromArgb(230, 240, 250),
+ BorderRadius = 12
+ };
+ }
+
+ private static SkillModernButton CreateNeonButton()
+ {
+ return new SkillModernButton
+ {
+ Text = "Неон",
+ Size = new Size(180, 48),
+ BackColor = Color.FromArgb(20, 24, 28),
+ ForeColor = Color.FromArgb(220, 255, 250),
+ BorderRadius = 8,
+ BorderColor = Color.FromArgb(0, 255, 200),
+ HoverColor = Color.FromArgb(50, 255, 220),
+ PressedColor = Color.FromArgb(0, 180, 140),
+ BorderSize = 2
+ };
+ }
+
+ private static SkillModernButton CreateGlassButton()
+ {
+ return new SkillModernButton
+ {
+ Text = "Glass",
+ Size = new Size(180, 48),
+ BackColor = Color.FromArgb(80, 80, 80),
+ ForeColor = Color.White,
+ BorderColor = Color.FromArgb(160, 160, 170),
+ HoverColor = Color.FromArgb(110, 110, 120),
+ PressedColor = Color.FromArgb(60, 60, 68),
+ BorderRadius = 16,
+ BorderSize = 1
+ };
+ }
+
+ private static SkillModernButton CreatePharmaPrimaryButton()
+ {
+ return new SkillModernButton
+ {
+ Text = "Сохранить заказ",
+ Size = new Size(200, 40),
+ BackColor = Color.FromArgb(3, 105, 161),
+ ForeColor = Color.White,
+ BorderColor = Color.FromArgb(3, 105, 161),
+ HoverColor = Color.FromArgb(7, 89, 133),
+ PressedColor = Color.FromArgb(12, 74, 110),
+ BorderRadius = 6,
+ BorderSize = 0,
+ Font = new Font("Segoe UI Semibold", 10f, FontStyle.Bold)
+ };
+ }
+
+ private static SkillModernButton CreatePharmaClearButton()
+ {
+ return new SkillModernButton
+ {
+ Text = "×",
+ Size = new Size(32, 32),
+ BackColor = Color.FromArgb(220, 38, 38),
+ ForeColor = Color.White,
+ BorderColor = Color.FromArgb(220, 38, 38),
+ HoverColor = Color.FromArgb(239, 68, 68),
+ PressedColor = Color.FromArgb(185, 28, 28),
+ BorderRadius = 6,
+ BorderSize = 0,
+ Font = new Font("Segoe UI Semibold", 13f, FontStyle.Bold)
+ };
+ }
+
+ private static Control CreateLargeCtaButton()
+ {
+ return Row(
+ new SkillModernButton
+ {
+ Text = "Загрузить на сервер",
+ Size = new Size(220, 52),
+ BorderRadius = 20,
+ BorderColor = Color.FromArgb(64, 158, 255),
+ BackColor = Color.FromArgb(30, 30, 30),
+ HoverColor = Color.FromArgb(0, 140, 230),
+ PressedColor = Color.FromArgb(0, 100, 180)
+ },
+ new SkillModernButton
+ {
+ Text = "Отмена",
+ Size = new Size(140, 52),
+ BackColor = Color.White,
+ ForeColor = Color.FromArgb(30, 30, 30),
+ BorderColor = Color.FromArgb(200, 200, 210),
+ HoverColor = Color.FromArgb(245, 245, 248),
+ PressedColor = Color.FromArgb(230, 230, 235),
+ BorderRadius = 20
+ });
+ }
+}
diff --git a/src/Elfisa.ButtonSkillDemo/Elfisa.ButtonSkillDemo.csproj b/src/Elfisa.ButtonSkillDemo/Elfisa.ButtonSkillDemo.csproj
new file mode 100644
index 0000000..663fdb8
--- /dev/null
+++ b/src/Elfisa.ButtonSkillDemo/Elfisa.ButtonSkillDemo.csproj
@@ -0,0 +1,11 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ true
+ enable
+
+
+
\ No newline at end of file
diff --git a/src/Elfisa.ButtonSkillDemo/Program.cs b/src/Elfisa.ButtonSkillDemo/Program.cs
new file mode 100644
index 0000000..9580233
--- /dev/null
+++ b/src/Elfisa.ButtonSkillDemo/Program.cs
@@ -0,0 +1,11 @@
+namespace Elfisa.ButtonSkillDemo;
+
+internal static class Program
+{
+ [STAThread]
+ private static void Main()
+ {
+ ApplicationConfiguration.Initialize();
+ Application.Run(new ButtonSkillDemoForm());
+ }
+}
diff --git a/src/Elfisa.ButtonSkillDemo/README.md b/src/Elfisa.ButtonSkillDemo/README.md
new file mode 100644
index 0000000..9ed95d2
--- /dev/null
+++ b/src/Elfisa.ButtonSkillDemo/README.md
@@ -0,0 +1,24 @@
+# Button Skill Demo
+
+Демо кнопок из skill **Custom Modern Button** (`SkillModernButton`).
+
+Не путать с `Elfisa.UI.Controls.ModernButton` — это другой класс для основного приложения.
+
+## Запуск
+
+```powershell
+dotnet run --project src/Elfisa.ButtonSkillDemo/Elfisa.ButtonSkillDemo.csproj -c Release
+```
+
+Exe: `src/Elfisa.ButtonSkillDemo/bin/Release/net8.0-windows/Elfisa.ButtonSkillDemo.exe`
+
+## Стили в демо
+
+| Секция | Настройки |
+|--------|-----------|
+| Default | тёмный фон, синяя рамка, radius 12 |
+| Светлая | белый фон, тёмный текст |
+| Неон | cyan border + hover |
+| Glass | серый полупрозрачный вид |
+| Pharma Trust | синяя / красная × как в ЭльФиСА |
+| CTA | большие кнопки radius 20 |
diff --git a/src/Elfisa.ButtonSkillDemo/SkillModernButton.cs b/src/Elfisa.ButtonSkillDemo/SkillModernButton.cs
new file mode 100644
index 0000000..c6c79b7
--- /dev/null
+++ b/src/Elfisa.ButtonSkillDemo/SkillModernButton.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace Elfisa.ButtonSkillDemo;
+
+///
+/// Кнопка из skill «Custom Modern Button» (отдельно от Elfisa.UI.ModernButton).
+///
+public sealed class SkillModernButton : Button
+{
+ public Color BorderColor { get; set; } = Color.FromArgb(0, 122, 204);
+ public Color HoverColor { get; set; } = Color.FromArgb(0, 140, 230);
+ public Color PressedColor { get; set; } = Color.FromArgb(0, 100, 180);
+ public int BorderRadius { get; set; } = 12;
+ public int BorderSize { get; set; } = 2;
+
+ private bool _isHover;
+ private bool _isPressed;
+
+ public SkillModernButton()
+ {
+ FlatStyle = FlatStyle.Flat;
+ FlatAppearance.BorderSize = 0;
+ BackColor = Color.FromArgb(30, 30, 30);
+ ForeColor = Color.White;
+ Font = new Font("Segoe UI", 10.5f, FontStyle.Bold);
+ Cursor = Cursors.Hand;
+ Size = new Size(160, 45);
+
+ SetStyle(ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.UserPaint |
+ ControlStyles.OptimizedDoubleBuffer |
+ ControlStyles.ResizeRedraw, true);
+ }
+
+ protected override void OnPaintBackground(PaintEventArgs pevent)
+ {
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
+
+ var rect = new Rectangle(0, 0, Width - 1, Height - 1);
+
+ var currentColor = BackColor;
+ if (_isPressed)
+ {
+ currentColor = PressedColor;
+ }
+ else if (_isHover)
+ {
+ currentColor = HoverColor;
+ }
+
+ using (var path = GetRoundedPath(rect, BorderRadius))
+ {
+ using (var brush = new SolidBrush(currentColor))
+ {
+ g.FillPath(brush, path);
+ }
+
+ if (BorderSize > 0)
+ {
+ using (var pen = new Pen(BorderColor, BorderSize))
+ {
+ g.DrawPath(pen, path);
+ }
+ }
+ }
+
+ var flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine;
+ TextRenderer.DrawText(g, Text, Font, rect, ForeColor, flags);
+ }
+
+ private static GraphicsPath GetRoundedPath(Rectangle rect, int radius)
+ {
+ var path = new GraphicsPath();
+ var r = Math.Max(0, Math.Min(radius, Math.Min(rect.Width, rect.Height) / 2));
+ if (r == 0)
+ {
+ path.AddRectangle(rect);
+ return path;
+ }
+
+ var diameter = r * 2;
+ path.StartFigure();
+ path.AddArc(rect.X, rect.Y, diameter, diameter, 180, 90);
+ path.AddArc(rect.Right - diameter, rect.Y, diameter, diameter, 270, 90);
+ path.AddArc(rect.Right - diameter, rect.Bottom - diameter, diameter, diameter, 0, 90);
+ path.AddArc(rect.X, rect.Bottom - diameter, diameter, diameter, 90, 90);
+ path.CloseFigure();
+ return path;
+ }
+
+ protected override void OnMouseEnter(EventArgs e)
+ {
+ _isHover = true;
+ Invalidate();
+ base.OnMouseEnter(e);
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ _isHover = false;
+ _isPressed = false;
+ Invalidate();
+ base.OnMouseLeave(e);
+ }
+
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Left)
+ {
+ _isPressed = true;
+ Invalidate();
+ }
+ base.OnMouseDown(e);
+ }
+
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ _isPressed = false;
+ Invalidate();
+ base.OnMouseUp(e);
+ }
+}
diff --git a/src/Elfisa.GradientButtonDemo/Elfisa.GradientButtonDemo.csproj b/src/Elfisa.GradientButtonDemo/Elfisa.GradientButtonDemo.csproj
new file mode 100644
index 0000000..508f715
--- /dev/null
+++ b/src/Elfisa.GradientButtonDemo/Elfisa.GradientButtonDemo.csproj
@@ -0,0 +1,10 @@
+
+
+ WinExe
+ net472
+ true
+ latest
+ Elfisa.GradientButtonDemo
+ Elfisa.GradientButtonDemo
+
+
diff --git a/src/Elfisa.GradientButtonDemo/GradientButtonDemoForm.cs b/src/Elfisa.GradientButtonDemo/GradientButtonDemoForm.cs
new file mode 100644
index 0000000..8c701eb
--- /dev/null
+++ b/src/Elfisa.GradientButtonDemo/GradientButtonDemoForm.cs
@@ -0,0 +1,205 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using ModernControls;
+
+namespace Elfisa.GradientButtonDemo
+{
+ internal sealed class GradientButtonDemoForm : Form
+ {
+ public GradientButtonDemoForm()
+ {
+ Text = "winforms-modern-buttons skill — демо";
+ Size = new Size(980, 700);
+ MinimumSize = new Size(860, 600);
+ StartPosition = FormStartPosition.CenterScreen;
+ BackColor = Color.FromArgb(245, 247, 250);
+ Font = new Font("Segoe UI", 10f);
+
+ var header = new Label
+ {
+ Text = "ModernGradientButton — градиент + тень + скругление (.NET Framework 4.7.2)",
+ Dock = DockStyle.Top,
+ Height = 52,
+ Padding = new Padding(24, 16, 24, 0),
+ Font = new Font("Segoe UI Semibold", 13f, FontStyle.Bold),
+ ForeColor = Color.FromArgb(30, 41, 59)
+ };
+
+ var scroll = new Panel
+ {
+ Dock = DockStyle.Fill,
+ AutoScroll = true,
+ Padding = new Padding(24, 8, 24, 24)
+ };
+
+ var layout = new TableLayoutPanel
+ {
+ Dock = DockStyle.Top,
+ AutoSize = true,
+ ColumnCount = 2,
+ Width = 900
+ };
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 240));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
+
+ AddRow(layout, "Primary (skill)", PresetButton(ModernGradientButton.Preset.Primary, "Сохранить"));
+ AddRow(layout, "Success", PresetButton(ModernGradientButton.Preset.Success, "Подтвердить"));
+ AddRow(layout, "Danger", PresetButton(ModernGradientButton.Preset.Danger, "Удалить"));
+ AddRow(layout, "Neutral", PresetButton(ModernGradientButton.Preset.Neutral, "Отмена"));
+
+ AddRow(layout, "Кастом (оранжевый)", CustomOrangeButton());
+ AddRow(layout, "Pharma Trust — синяя", PharmaPrimaryButton());
+ AddRow(layout, "Крестик × у поиска", ClearSearchButton());
+ AddRow(layout, "Toolbar (мелкая)", ToolbarRow());
+
+ var note = new Label
+ {
+ AutoSize = true,
+ MaximumSize = new Size(860, 0),
+ Margin = new Padding(0, 20, 0, 0),
+ ForeColor = Color.FromArgb(100, 116, 139),
+ Text =
+ "Skill: winforms-modern-buttons · Класс ModernControls.ModernGradientButton\n" +
+ "Наведите и нажмите — видны hover/pressed. Тень: ShadowDepth, градиент: GradientTop/Bottom.\n" +
+ "Основное приложение ЭльФиСА пока на Elfisa.UI.ModernButton — это отдельное демо для сравнения."
+ };
+ layout.Controls.Add(note, 0, layout.RowCount);
+ layout.SetColumnSpan(note, 2);
+
+ scroll.Controls.Add(layout);
+ Controls.Add(scroll);
+ Controls.Add(header);
+ }
+
+ private static void AddRow(TableLayoutPanel layout, string title, Control content)
+ {
+ var row = layout.RowCount;
+ layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
+
+ layout.Controls.Add(new Label
+ {
+ Text = title,
+ AutoSize = true,
+ Margin = new Padding(0, 18, 12, 8),
+ Font = new Font("Segoe UI Semibold", 10f, FontStyle.Bold),
+ ForeColor = Color.FromArgb(51, 65, 85)
+ }, 0, row);
+
+ content.Margin = new Padding(0, 14, 0, 4);
+ layout.Controls.Add(content, 1, row);
+ layout.RowCount++;
+ }
+
+ private static ModernGradientButton PresetButton(ModernGradientButton.Preset preset, string text)
+ {
+ var btn = new ModernGradientButton
+ {
+ Text = text,
+ Size = new Size(160, 48),
+ Margin = new Padding(0, 0, 12, 8)
+ };
+ btn.ApplyPreset(preset);
+ btn.Click += (_, __) => MessageBox.Show(text + " — клик!", "Демо", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ return btn;
+ }
+
+ private static ModernGradientButton CustomOrangeButton()
+ {
+ return new ModernGradientButton
+ {
+ Text = "Свой стиль",
+ Size = new Size(180, 50),
+ CornerRadius = 20,
+ ShadowDepth = 6,
+ GradientTop = Color.FromArgb(255, 179, 71),
+ GradientBottom = Color.FromArgb(230, 126, 34),
+ HoverGradientTop = Color.FromArgb(255, 195, 110),
+ HoverGradientBottom = Color.FromArgb(245, 145, 50),
+ PressedGradientTop = Color.FromArgb(220, 120, 30),
+ PressedGradientBottom = Color.FromArgb(190, 100, 20)
+ };
+ }
+
+ private static ModernGradientButton PharmaPrimaryButton()
+ {
+ var btn = new ModernGradientButton
+ {
+ Text = "Сохранить заказ",
+ Size = new Size(190, 44),
+ CornerRadius = 6,
+ ShadowDepth = 4,
+ GradientTop = Color.FromArgb(14, 116, 181),
+ GradientBottom = Color.FromArgb(3, 105, 161),
+ HoverGradientTop = Color.FromArgb(37, 130, 195),
+ HoverGradientBottom = Color.FromArgb(7, 89, 133),
+ PressedGradientTop = Color.FromArgb(12, 74, 110),
+ PressedGradientBottom = Color.FromArgb(8, 60, 90)
+ };
+ return btn;
+ }
+
+ private static Control ClearSearchRow()
+ {
+ var panel = new FlowLayoutPanel { AutoSize = true, WrapContents = false };
+
+ var search = new TextBox
+ {
+ Text = "прокладки",
+ Width = 280,
+ Height = 32,
+ Font = new Font("Segoe UI", 10f),
+ Margin = new Padding(0, 8, 8, 0)
+ };
+
+ var clear = new ModernGradientButton
+ {
+ Text = "×",
+ Size = new Size(36, 36),
+ CornerRadius = 6,
+ ShadowDepth = 3,
+ Font = new Font("Segoe UI Semibold", 13f, FontStyle.Bold),
+ Margin = new Padding(0, 6, 0, 0)
+ };
+ clear.ApplyPreset(ModernGradientButton.Preset.Danger);
+ clear.Click += (_, __) => search.Clear();
+
+ panel.Controls.Add(search);
+ panel.Controls.Add(clear);
+ return panel;
+ }
+
+ private static Control ClearSearchButton()
+ {
+ return ClearSearchRow();
+ }
+
+ private static Control ToolbarRow()
+ {
+ var panel = new FlowLayoutPanel { AutoSize = true };
+
+ var small = new ModernGradientButton
+ {
+ Text = "×",
+ Size = new Size(32, 32),
+ CornerRadius = 5,
+ ShadowDepth = 2,
+ Font = new Font("Segoe UI", 11f, FontStyle.Bold)
+ };
+ small.ApplyPreset(ModernGradientButton.Preset.Danger);
+
+ var neutral = new ModernGradientButton
+ {
+ Text = "Фильтр",
+ Size = new Size(100, 34),
+ CornerRadius = 8,
+ ShadowDepth = 3
+ };
+ neutral.ApplyPreset(ModernGradientButton.Preset.Neutral);
+
+ panel.Controls.Add(small);
+ panel.Controls.Add(neutral);
+ return panel;
+ }
+ }
+}
diff --git a/src/Elfisa.GradientButtonDemo/ModernGradientButton.cs b/src/Elfisa.GradientButtonDemo/ModernGradientButton.cs
new file mode 100644
index 0000000..94424a9
--- /dev/null
+++ b/src/Elfisa.GradientButtonDemo/ModernGradientButton.cs
@@ -0,0 +1,220 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+
+namespace ModernControls
+{
+ ///
+ /// Из skill winforms-modern-buttons: градиент, тень, скругление, hover/pressed.
+ ///
+ public class ModernGradientButton : Button
+ {
+ public enum ButtonState { Normal, Hover, Pressed }
+
+ private ButtonState _state = ButtonState.Normal;
+
+ [Category("Appearance")]
+ public Color GradientTop { get; set; } = Color.FromArgb(66, 133, 244);
+
+ [Category("Appearance")]
+ public Color GradientBottom { get; set; } = Color.FromArgb(30, 87, 194);
+
+ [Category("Appearance")]
+ public Color HoverGradientTop { get; set; } = Color.FromArgb(90, 150, 255);
+
+ [Category("Appearance")]
+ public Color HoverGradientBottom { get; set; } = Color.FromArgb(45, 105, 220);
+
+ [Category("Appearance")]
+ public Color PressedGradientTop { get; set; } = Color.FromArgb(25, 70, 160);
+
+ [Category("Appearance")]
+ public Color PressedGradientBottom { get; set; } = Color.FromArgb(15, 55, 130);
+
+ [Category("Appearance")]
+ public int CornerRadius { get; set; } = 12;
+
+ [Category("Appearance")]
+ public int ShadowDepth { get; set; } = 5;
+
+ [Category("Appearance")]
+ public Color ShadowColor { get; set; } = Color.FromArgb(60, 0, 0, 0);
+
+ [Category("Appearance")]
+ public bool AnimateHover { get; set; } = true;
+
+ public ModernGradientButton()
+ {
+ SetStyle(ControlStyles.AllPaintingInWmPaint
+ | ControlStyles.UserPaint
+ | ControlStyles.ResizeRedraw
+ | ControlStyles.SupportsTransparentBackColor
+ | ControlStyles.OptimizedDoubleBuffer, true);
+
+ FlatStyle = FlatStyle.Flat;
+ FlatAppearance.BorderSize = 0;
+ BackColor = Color.Transparent;
+ ForeColor = Color.White;
+ Font = new Font("Segoe UI", 9.5f, FontStyle.Regular);
+ Cursor = Cursors.Hand;
+ Size = new Size(140, 42);
+ Padding = new Padding(0, 0, 0, ShadowDepth);
+ }
+
+ protected override void OnPaintBackground(PaintEventArgs pevent)
+ {
+ }
+
+ protected override void OnMouseEnter(EventArgs e)
+ {
+ base.OnMouseEnter(e);
+ _state = ButtonState.Hover;
+ Invalidate();
+ }
+
+ protected override void OnMouseLeave(EventArgs e)
+ {
+ base.OnMouseLeave(e);
+ _state = ButtonState.Normal;
+ Invalidate();
+ }
+
+ protected override void OnMouseDown(MouseEventArgs mevent)
+ {
+ base.OnMouseDown(mevent);
+ _state = ButtonState.Pressed;
+ Invalidate();
+ }
+
+ protected override void OnMouseUp(MouseEventArgs mevent)
+ {
+ base.OnMouseUp(mevent);
+ _state = ClientRectangle.Contains(mevent.Location) ? ButtonState.Hover : ButtonState.Normal;
+ Invalidate();
+ }
+
+ private static GraphicsPath RoundedRect(Rectangle bounds, int radius)
+ {
+ var d = radius * 2;
+ var path = new GraphicsPath();
+
+ if (radius <= 0)
+ {
+ path.AddRectangle(bounds);
+ return path;
+ }
+
+ path.StartFigure();
+ path.AddArc(bounds.X, bounds.Y, d, d, 180, 90);
+ path.AddArc(bounds.Right - d, bounds.Y, d, d, 270, 90);
+ path.AddArc(bounds.Right - d, bounds.Bottom - d, d, d, 0, 90);
+ path.AddArc(bounds.X, bounds.Bottom - d, d, d, 90, 90);
+ path.CloseFigure();
+ return path;
+ }
+
+ protected override void OnPaint(PaintEventArgs pevent)
+ {
+ var g = pevent.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
+
+ var buttonRect = new Rectangle(0, 0, Width - 1, Height - 1 - ShadowDepth);
+
+ for (var i = ShadowDepth; i > 0; i--)
+ {
+ var alpha = ShadowColor.A * i / (ShadowDepth * 3);
+ using (var shadowBrush = new SolidBrush(Color.FromArgb(alpha, ShadowColor)))
+ using (var shadowPath = RoundedRect(
+ new Rectangle(i, buttonRect.Y + i + 1, buttonRect.Width - i, buttonRect.Height - 1),
+ CornerRadius))
+ {
+ g.FillPath(shadowBrush, shadowPath);
+ }
+ }
+
+ Color top;
+ Color bottom;
+ switch (_state)
+ {
+ case ButtonState.Hover:
+ top = HoverGradientTop;
+ bottom = HoverGradientBottom;
+ break;
+ case ButtonState.Pressed:
+ top = PressedGradientTop;
+ bottom = PressedGradientBottom;
+ break;
+ default:
+ top = GradientTop;
+ bottom = GradientBottom;
+ break;
+ }
+
+ using (var path = RoundedRect(buttonRect, CornerRadius))
+ using (var brush = new LinearGradientBrush(buttonRect, top, bottom, LinearGradientMode.Vertical))
+ {
+ g.FillPath(brush, path);
+
+ using (var highlightPen = new Pen(Color.FromArgb(40, Color.White), 1))
+ {
+ g.DrawArc(highlightPen, buttonRect.X, buttonRect.Y, CornerRadius * 2, CornerRadius * 2, 180, 90);
+ }
+ }
+
+ TextRenderer.DrawText(
+ g,
+ Text,
+ Font,
+ buttonRect,
+ ForeColor,
+ TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis);
+ }
+
+ public void ApplyPreset(Preset preset)
+ {
+ switch (preset)
+ {
+ case Preset.Primary:
+ GradientTop = Color.FromArgb(66, 133, 244);
+ GradientBottom = Color.FromArgb(30, 87, 194);
+ HoverGradientTop = Color.FromArgb(90, 150, 255);
+ HoverGradientBottom = Color.FromArgb(45, 105, 220);
+ PressedGradientTop = Color.FromArgb(25, 70, 160);
+ PressedGradientBottom = Color.FromArgb(15, 55, 130);
+ break;
+ case Preset.Success:
+ GradientTop = Color.FromArgb(67, 189, 118);
+ GradientBottom = Color.FromArgb(30, 140, 80);
+ HoverGradientTop = Color.FromArgb(90, 205, 135);
+ HoverGradientBottom = Color.FromArgb(40, 160, 95);
+ PressedGradientTop = Color.FromArgb(20, 120, 65);
+ PressedGradientBottom = Color.FromArgb(10, 95, 50);
+ break;
+ case Preset.Danger:
+ GradientTop = Color.FromArgb(235, 87, 87);
+ GradientBottom = Color.FromArgb(200, 40, 40);
+ HoverGradientTop = Color.FromArgb(245, 110, 110);
+ HoverGradientBottom = Color.FromArgb(215, 55, 55);
+ PressedGradientTop = Color.FromArgb(175, 30, 30);
+ PressedGradientBottom = Color.FromArgb(140, 20, 20);
+ break;
+ case Preset.Neutral:
+ GradientTop = Color.FromArgb(120, 130, 140);
+ GradientBottom = Color.FromArgb(80, 90, 100);
+ HoverGradientTop = Color.FromArgb(140, 150, 160);
+ HoverGradientBottom = Color.FromArgb(95, 105, 115);
+ PressedGradientTop = Color.FromArgb(65, 75, 85);
+ PressedGradientBottom = Color.FromArgb(50, 60, 70);
+ break;
+ }
+
+ Invalidate();
+ }
+
+ public enum Preset { Primary, Success, Danger, Neutral }
+ }
+}
diff --git a/src/Elfisa.GradientButtonDemo/Program.cs b/src/Elfisa.GradientButtonDemo/Program.cs
new file mode 100644
index 0000000..5186a2d
--- /dev/null
+++ b/src/Elfisa.GradientButtonDemo/Program.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Windows.Forms;
+
+namespace Elfisa.GradientButtonDemo
+{
+ internal static class Program
+ {
+ [STAThread]
+ private static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new GradientButtonDemoForm());
+ }
+ }
+}
diff --git a/src/Elfisa.GradientButtonDemo/README.md b/src/Elfisa.GradientButtonDemo/README.md
new file mode 100644
index 0000000..82f1013
--- /dev/null
+++ b/src/Elfisa.GradientButtonDemo/README.md
@@ -0,0 +1,20 @@
+# Gradient Button Demo (skill)
+
+Демо по skill **winforms-modern-buttons** — класс `ModernControls.ModernGradientButton`.
+
+- .NET Framework **4.7.2** (как в skill и в ЭльФиСА)
+- Градиент, тень, скругление, 4 пресета
+
+## Запуск
+
+```powershell
+dotnet run --project src/Elfisa.GradientButtonDemo/Elfisa.GradientButtonDemo.csproj -c Release
+```
+
+Exe: `src/Elfisa.GradientButtonDemo/bin/Release/Elfisa.GradientButtonDemo.exe`
+
+## Файлы skill
+
+Исходник skill распакован из `winforms-modern-buttons.skill`:
+- `ModernGradientButton.cs` — копия из `assets/`
+- Skill также можно установить в `~/.cursor/skills/winforms-modern-buttons/`
diff --git a/src/Elfisa.MaterialDemo/Elfisa.MaterialDemo.csproj b/src/Elfisa.MaterialDemo/Elfisa.MaterialDemo.csproj
new file mode 100644
index 0000000..91887ed
--- /dev/null
+++ b/src/Elfisa.MaterialDemo/Elfisa.MaterialDemo.csproj
@@ -0,0 +1,15 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ true
+ enable
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Elfisa.MaterialDemo/PharmacyMaterialForm.cs b/src/Elfisa.MaterialDemo/PharmacyMaterialForm.cs
new file mode 100644
index 0000000..eaa0b01
--- /dev/null
+++ b/src/Elfisa.MaterialDemo/PharmacyMaterialForm.cs
@@ -0,0 +1,286 @@
+using MaterialSkin;
+using MaterialSkin.Controls;
+
+namespace Elfisa.MaterialDemo;
+
+///
+/// Demo: как может выглядеть «Электронная Фармация» на MaterialSkin.2 (отдельно от Elfisa.UI).
+///
+internal sealed class PharmacyMaterialForm : MaterialForm
+{
+ private readonly MaterialTabControl _tabs;
+ private readonly Panel _contentHost;
+ private readonly MaterialLabel _statusLabel;
+
+ public PharmacyMaterialForm()
+ {
+ Text = "ЭльФиСА — MaterialSkin demo";
+ MinimumSize = new Size(960, 640);
+ Size = new Size(1100, 720);
+ StartPosition = FormStartPosition.CenterScreen;
+
+ MaterialSkinManager.Instance.AddFormToManage(this);
+
+ var header = new MaterialLabel
+ {
+ Text = "MaterialSkin.2 — демо для сравнения с Elfisa.UI",
+ FontType = MaterialSkinManager.fontType.H6,
+ Dock = DockStyle.Top,
+ Height = 48,
+ Padding = new Padding(24, 12, 24, 0)
+ };
+
+ _tabs = new MaterialTabControl
+ {
+ Dock = DockStyle.Top,
+ Height = 48,
+ Depth = 0
+ };
+ _tabs.TabPages.Add(new TabPage("Прайс-лист") { Tag = "price" });
+ _tabs.TabPages.Add(new TabPage("Заказы") { Tag = "orders" });
+ _tabs.TabPages.Add(new TabPage("Загрузить") { Tag = "upload" });
+ _tabs.SelectedIndexChanged += (_, __) => ShowTab(_tabs.SelectedTab?.Tag as string ?? "price");
+
+ _contentHost = new Panel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(24)
+ };
+
+ _statusLabel = new MaterialLabel
+ {
+ Dock = DockStyle.Bottom,
+ Height = 36,
+ Text = "Пользователь: demo@apteka (MaterialSkin dark / teal)",
+ FontType = MaterialSkinManager.fontType.Caption,
+ Padding = new Padding(24, 8, 24, 8)
+ };
+
+ Controls.Add(_contentHost);
+ Controls.Add(_statusLabel);
+ Controls.Add(_tabs);
+ Controls.Add(header);
+
+ ShowTab("price");
+ }
+
+ private void ShowTab(string key)
+ {
+ _contentHost.Controls.Clear();
+
+ switch (key)
+ {
+ case "orders":
+ _contentHost.Controls.Add(BuildOrdersView());
+ break;
+ case "upload":
+ _contentHost.Controls.Add(BuildUploadView());
+ break;
+ default:
+ _contentHost.Controls.Add(BuildPriceView());
+ break;
+ }
+ }
+
+ private Control BuildPriceView()
+ {
+ var root = new TableLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ ColumnCount = 1,
+ RowCount = 3
+ };
+ root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
+ root.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+ root.RowStyles.Add(new RowStyle(SizeType.Absolute, 120f));
+
+ var toolbar = new FlowLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ AutoSize = true,
+ WrapContents = false,
+ Padding = new Padding(0, 0, 0, 16)
+ };
+
+ var search = new MaterialTextBox
+ {
+ Hint = "Поиск по наименованию…",
+ Width = 320,
+ Margin = new Padding(0, 8, 12, 0)
+ };
+ var clearBtn = new MaterialButton
+ {
+ Text = "×",
+ Type = MaterialButton.MaterialButtonType.Outlined,
+ Size = new Size(48, 36),
+ Margin = new Padding(0, 8, 12, 0)
+ };
+ var supplier = new MaterialComboBox
+ {
+ Hint = "Поставщик",
+ Width = 200,
+ Margin = new Padding(0, 8, 0, 0)
+ };
+ supplier.Items.AddRange(new object[] { "Катрен", "Протек", "Все поставщики" });
+ supplier.SelectedIndex = 0;
+
+ toolbar.Controls.Add(search);
+ toolbar.Controls.Add(clearBtn);
+ toolbar.Controls.Add(supplier);
+
+ var grid = new MaterialListView
+ {
+ Dock = DockStyle.Fill,
+ FullRowSelect = true,
+ View = View.Details,
+ BorderStyle = BorderStyle.None
+ };
+ grid.Columns.Add("Наименование", 420);
+ grid.Columns.Add("Производитель", 220);
+ grid.Columns.Add("Цена", 90);
+ grid.Columns.Add("Остаток", 90);
+ grid.Items.Add(new ListViewItem(new[] { "Нурофен 200 мг №10", "Reckitt", "189,00", "42" }));
+ grid.Items.Add(new ListViewItem(new[] { "Амоксициллин 500 мг №20", "Sandoz", "124,50", "18" }));
+ grid.Items.Add(new ListViewItem(new[] { "Омез 20 мг №30", "Dr. Reddy's", "312,00", "7" }));
+
+ var cartCard = new MaterialCard
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(16)
+ };
+ var cartLayout = new TableLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ ColumnCount = 4
+ };
+ cartLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40f));
+ cartLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20f));
+ cartLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20f));
+ cartLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20f));
+
+ cartLayout.Controls.Add(new MaterialLabel
+ {
+ Text = "Корзина: 2 позиции · 313,50 ₽",
+ FontType = MaterialSkinManager.fontType.Subtitle1,
+ Dock = DockStyle.Fill
+ }, 0, 0);
+
+ cartLayout.Controls.Add(new MaterialButton
+ {
+ Text = "Сохранить заказ",
+ Type = MaterialButton.MaterialButtonType.Contained,
+ Dock = DockStyle.Fill,
+ HighEmphasis = true
+ }, 1, 0);
+
+ cartLayout.Controls.Add(new MaterialButton
+ {
+ Text = "Очистить",
+ Type = MaterialButton.MaterialButtonType.Outlined,
+ Dock = DockStyle.Fill
+ }, 2, 0);
+
+ cartLayout.Controls.Add(new MaterialButton
+ {
+ Text = "Отправить",
+ Type = MaterialButton.MaterialButtonType.Contained,
+ Dock = DockStyle.Fill
+ }, 3, 0);
+
+ cartCard.Controls.Add(cartLayout);
+
+ root.Controls.Add(toolbar, 0, 0);
+ root.Controls.Add(grid, 0, 1);
+ root.Controls.Add(cartCard, 0, 2);
+ return root;
+ }
+
+ private Control BuildOrdersView()
+ {
+ var card = new MaterialCard
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(24)
+ };
+
+ var title = new MaterialLabel
+ {
+ Text = "Журнал заказов",
+ FontType = MaterialSkinManager.fontType.H5,
+ Dock = DockStyle.Top,
+ Height = 40
+ };
+
+ var grid = new MaterialListView
+ {
+ Dock = DockStyle.Fill,
+ View = View.Details,
+ FullRowSelect = true
+ };
+ grid.Columns.Add("№", 80);
+ grid.Columns.Add("Дата", 120);
+ grid.Columns.Add("Поставщик", 180);
+ grid.Columns.Add("Статус", 140);
+ grid.Columns.Add("Сумма", 100);
+ grid.Items.Add(new ListViewItem(new[] { "1042", "14.07.2026", "Катрен", "Отправлен", "4 820,00" }));
+ grid.Items.Add(new ListViewItem(new[] { "1041", "13.07.2026", "Протек", "Черновик", "1 240,00" }));
+
+ card.Controls.Add(grid);
+ card.Controls.Add(title);
+ return card;
+ }
+
+ private Control BuildUploadView()
+ {
+ var card = new MaterialCard
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(32)
+ };
+
+ var stack = new TableLayoutPanel
+ {
+ Dock = DockStyle.Fill,
+ ColumnCount = 1,
+ RowCount = 4
+ };
+ stack.RowStyles.Add(new RowStyle(SizeType.AutoSize));
+ stack.RowStyles.Add(new RowStyle(SizeType.AutoSize));
+ stack.RowStyles.Add(new RowStyle(SizeType.AutoSize));
+ stack.RowStyles.Add(new RowStyle(SizeType.Percent, 100f));
+
+ stack.Controls.Add(new MaterialLabel
+ {
+ Text = "Загрузка данных с сервера",
+ FontType = MaterialSkinManager.fontType.H5,
+ AutoSize = true,
+ Margin = new Padding(0, 0, 0, 8)
+ }, 0, 0);
+
+ stack.Controls.Add(new MaterialLabel
+ {
+ Text = "MaterialSkin даёт готовые карточки, ripple-кнопки и тёмную тему из коробки.",
+ FontType = MaterialSkinManager.fontType.Body1,
+ AutoSize = true,
+ Margin = new Padding(0, 0, 0, 24)
+ }, 0, 1);
+
+ var buttons = new FlowLayoutPanel { AutoSize = true, WrapContents = false };
+ buttons.Controls.Add(new MaterialButton
+ {
+ Text = "Загрузить прайс",
+ Type = MaterialButton.MaterialButtonType.Contained,
+ HighEmphasis = true,
+ Margin = new Padding(0, 0, 12, 0)
+ });
+ buttons.Controls.Add(new MaterialButton
+ {
+ Text = "Отправить заказы",
+ Type = MaterialButton.MaterialButtonType.Outlined
+ });
+ stack.Controls.Add(buttons, 0, 2);
+
+ card.Controls.Add(stack);
+ return card;
+ }
+}
diff --git a/src/Elfisa.MaterialDemo/Program.cs b/src/Elfisa.MaterialDemo/Program.cs
new file mode 100644
index 0000000..d5623db
--- /dev/null
+++ b/src/Elfisa.MaterialDemo/Program.cs
@@ -0,0 +1,24 @@
+using MaterialSkin;
+using MaterialSkin.Controls;
+
+namespace Elfisa.MaterialDemo;
+
+internal static class Program
+{
+ [STAThread]
+ private static void Main()
+ {
+ ApplicationConfiguration.Initialize();
+
+ var skin = MaterialSkinManager.Instance;
+ skin.Theme = MaterialSkinManager.Themes.DARK;
+ skin.ColorScheme = new ColorScheme(
+ Primary.Teal800,
+ Primary.Teal900,
+ Primary.Teal200,
+ Accent.LightBlue200,
+ TextShade.WHITE);
+
+ Application.Run(new PharmacyMaterialForm());
+ }
+}
diff --git a/src/Elfisa.MaterialDemo/README.md b/src/Elfisa.MaterialDemo/README.md
new file mode 100644
index 0000000..cbd9ab0
--- /dev/null
+++ b/src/Elfisa.MaterialDemo/README.md
@@ -0,0 +1,19 @@
+# MaterialSkin demo
+
+Отдельное приложение для сравнения **MaterialSkin.2** с текущей библиотекой **Elfisa.UI**.
+
+Запуск:
+
+```powershell
+dotnet run --project src/Elfisa.MaterialDemo/Elfisa.MaterialDemo.csproj -c Release
+```
+
+Или exe:
+
+`src/Elfisa.MaterialDemo/bin/Release/net8.0-windows/Elfisa.MaterialDemo.exe`
+
+## Зачем отдельно
+
+Основной клиент (`ElectronicPharmacy`) уже построен на `Elfisa.UI` (owner-draw, PharmaTrust, кастомные контролы). Подмешивать MaterialSkin в ту же форму — конфликт двух систем тем и двойная перерисовка.
+
+Это демо показывает, как выглядел бы тот же сценарий (прайс / заказы / загрузка) на готовой библиотеке Google Material.
diff --git a/src/Elfisa.UI/Controls/ModernButton.cs b/src/Elfisa.UI/Controls/ModernButton.cs
index 2e0213b..ce7cb79 100644
--- a/src/Elfisa.UI/Controls/ModernButton.cs
+++ b/src/Elfisa.UI/Controls/ModernButton.cs
@@ -110,10 +110,10 @@ namespace Elfisa.UI.Controls
FlatStyle = FlatStyle.Flat;
FlatAppearance.BorderSize = 0;
Cursor = Cursors.Hand;
- Font = new Font("Segoe UI Semibold", 12f);
+ Font = new Font("Segoe UI Semibold", 10f, FontStyle.Bold);
Height = ModernButtonStyles.DefaultHeight;
- MinimumSize = new Size(64, ModernButtonStyles.DefaultHeight);
- Padding = new Padding(14, 6, 14, 6);
+ MinimumSize = new Size(72, ModernButtonStyles.DefaultHeight);
+ Padding = new Padding(16, 6, 16, 6);
TabStop = true;
_animTimer = new AnimationTimer(v => { _hoverAnim = v; Invalidate(); });
}
@@ -127,11 +127,13 @@ namespace Elfisa.UI.Controls
}
else if (_compactIconMode)
{
- Font = new Font("Segoe UI", 16f, FontStyle.Bold);
+ Font = new Font("Segoe UI Semibold", 13f, FontStyle.Bold);
}
else
{
- Font = theme.FontSemibold14;
+ Font = theme.FontSemibold14 != null
+ ? new Font(theme.FontSemibold14.FontFamily, 10f, FontStyle.Bold)
+ : new Font("Segoe UI Semibold", 10f, FontStyle.Bold);
}
Invalidate();
}
@@ -208,34 +210,61 @@ namespace Elfisa.UI.Controls
g.CompositingQuality = CompositingQuality.HighQuality;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
- var bounds = new Rectangle(0, 0, Width - 1, Height - 1);
- var radius = theme.CornerRadiusSmall;
- var baseColor = ResolveBaseColor(theme);
- var fill = baseColor;
+ var bounds = new Rectangle(0, 0, Width, Height);
+ var usesGradient = SkillGradientButtonPaint.UsesGradient(_buttonStyle, _compactIconMode);
if (_headerNavMode)
{
+ var headerBounds = new Rectangle(0, 0, Width - 1, Height - 1);
+ var headerRadius = theme.CornerRadiusSmall;
var activeBoost = _isActiveItem ? 0.22f : 0.06f;
var hoverBoost = _hoverAnim * 0.14f;
var alpha = (int)(255 * (activeBoost + hoverBoost));
- fill = Color.FromArgb(alpha, 255, 255, 255);
- using (var brush = new SolidBrush(fill))
+ var headerFill = Color.FromArgb(alpha, 255, 255, 255);
+ using (var brush = new SolidBrush(headerFill))
{
- GraphicsExtensions.FillRoundedRectangle(g, brush, bounds, radius);
+ GraphicsExtensions.FillRoundedRectangle(g, brush, headerBounds, headerRadius);
}
if (_isActiveItem)
{
using (var pen = new Pen(Color.FromArgb(180, 255, 255, 255), 1f))
{
- GraphicsExtensions.DrawRoundedRectangle(g, pen, bounds, radius);
+ GraphicsExtensions.DrawRoundedRectangle(g, pen, headerBounds, headerRadius);
}
}
- DrawHeaderNavContent(g, bounds, theme);
+ DrawHeaderNavContent(g, headerBounds, theme);
return;
}
+ if (usesGradient && !_topNavMode && !_sidebarMode)
+ {
+ SkillGradientButtonPaint.Paint(
+ g,
+ bounds,
+ _buttonStyle,
+ _compactIconMode,
+ _hover && Enabled,
+ _pressed && Enabled,
+ Enabled);
+
+ var contentRect = SkillGradientButtonPaint.GetContentRect(
+ bounds,
+ _buttonStyle,
+ _compactIconMode,
+ Enabled);
+
+ var gradientTextColor = Enabled ? theme.TextOnAccent : theme.TextMuted;
+ DrawActionButtonContent(g, contentRect, gradientTextColor);
+ return;
+ }
+
+ var flatBounds = new Rectangle(0, 0, Width - 1, Height - 1);
+ var radius = _compactIconMode ? 6 : 6;
+ var baseColor = ResolveBaseColor(theme);
+ var fill = baseColor;
+
if (_topNavMode)
{
fill = Color.Transparent;
@@ -277,17 +306,14 @@ namespace Elfisa.UI.Controls
fill = GraphicsExtensions.Blend(
ResolveHoverColor(theme, baseColor),
baseColor,
- 1f - (_hoverAnim * 0.35f));
+ 1f - (_hoverAnim * 0.45f));
}
- if (!_topNavMode)
+ if (!_topNavMode && fill.A > 0)
{
- if (fill.A > 0)
+ using (var brush = new SolidBrush(fill))
{
- using (var brush = new SolidBrush(fill))
- {
- GraphicsExtensions.FillRoundedRectangle(g, brush, bounds, radius);
- }
+ GraphicsExtensions.FillRoundedRectangle(g, brush, flatBounds, radius);
}
}
@@ -310,7 +336,7 @@ namespace Elfisa.UI.Controls
}
else if ((_buttonStyle == ModernButtonStyle.Secondary
|| _buttonStyle == ModernButtonStyle.DangerSoft
- || _buttonStyle == ModernButtonStyle.Subtle) && !_sidebarMode)
+ || _buttonStyle == ModernButtonStyle.Subtle) && !_sidebarMode && !_compactIconMode)
{
if (_buttonStyle == ModernButtonStyle.Subtle && !_hover && !_pressed && !_focused)
{
@@ -330,14 +356,14 @@ namespace Elfisa.UI.Controls
using (var pen = new Pen(borderColor, 1f))
{
- GraphicsExtensions.DrawRoundedRectangle(g, pen, bounds, radius);
+ GraphicsExtensions.DrawRoundedRectangle(g, pen, flatBounds, radius);
}
}
}
if (_focused && Enabled && ShowFocusCues && !_headerNavMode && !_topNavMode && !_sidebarMode)
{
- var focusBounds = Rectangle.Inflate(bounds, -2, -2);
+ var focusBounds = Rectangle.Inflate(flatBounds, -2, -2);
using (var pen = new Pen(theme.InputBorderFocus, 2f))
{
GraphicsExtensions.DrawRoundedRectangle(g, pen, focusBounds, Math.Max(2, radius - 1));
@@ -366,7 +392,7 @@ namespace Elfisa.UI.Controls
textColor = GraphicsExtensions.Blend(textColor, theme.TextMuted, 0.55f);
}
- DrawActionButtonContent(g, bounds, textColor);
+ DrawActionButtonContent(g, flatBounds, textColor);
}
private void DrawActionButtonContent(Graphics g, Rectangle bounds, Color textColor)
@@ -507,9 +533,9 @@ namespace Elfisa.UI.Controls
if (baseColor == theme.AccentPrimary) return theme.AccentPrimaryHover;
if (baseColor == theme.Surface) return theme.GridRowHover;
if (baseColor == theme.DangerSoft) return GraphicsExtensions.Blend(theme.DangerSoft, theme.Danger, 0.08f);
+ if (baseColor == theme.Danger) return GraphicsExtensions.Blend(theme.Danger, Color.White, 0.08f);
if (baseColor == theme.Success) return GraphicsExtensions.Blend(theme.Success, Color.White, 0.12f);
if (baseColor == theme.Warning) return GraphicsExtensions.Blend(theme.Warning, Color.White, 0.12f);
- if (baseColor == theme.Danger) return GraphicsExtensions.Blend(theme.Danger, Color.White, 0.12f);
return GraphicsExtensions.Blend(baseColor, Color.White, 0.08f);
}
diff --git a/src/Elfisa.UI/Controls/ModernButtonStyles.cs b/src/Elfisa.UI/Controls/ModernButtonStyles.cs
index df7cf1b..8105e1b 100644
--- a/src/Elfisa.UI/Controls/ModernButtonStyles.cs
+++ b/src/Elfisa.UI/Controls/ModernButtonStyles.cs
@@ -6,11 +6,13 @@ using Elfisa.UI.Theming;
namespace Elfisa.UI.Controls
{
///
- /// WinUI 3–style presets for in WinForms.
+ /// Стили кнопок по макету Pharma Trust (variant-e-pharma-trust.html).
///
public static class ModernButtonStyles
{
- public const int DefaultHeight = 32;
+ public const int DefaultHeight = 34;
+ public const int ClearSize = 32;
+ private static readonly Font ActionFont = new Font("Segoe UI Semibold", 10f, FontStyle.Bold);
public static void ApplyAuto(ModernButton button)
{
@@ -28,20 +30,20 @@ namespace Elfisa.UI.Controls
if (IsDangerAction(text))
{
- ApplyAction(button, ModernButtonStyle.Danger, ResolveIcon(text, "\uE74D"));
+ ApplyAction(button, ModernButtonStyle.Danger);
return;
}
if (IsPrimaryAction(text))
{
- ApplyAction(button, ModernButtonStyle.Primary, ResolveIcon(text, "\uE725"));
+ ApplyAction(button, ModernButtonStyle.Primary);
return;
}
- ApplyAction(button, ModernButtonStyle.Secondary, ResolveIcon(text, null));
+ ApplyAction(button, ModernButtonStyle.Secondary);
}
- public static void ApplyAction(ModernButton button, ModernButtonStyle style, string iconGlyph = null)
+ public static void ApplyAction(ModernButton button, ModernButtonStyle style)
{
if (button == null)
{
@@ -54,15 +56,16 @@ namespace Elfisa.UI.Controls
button.BackgroundImage = null;
button.UseVisualStyleBackColor = false;
button.AutoSizeMode = AutoSizeMode.GrowAndShrink;
- button.MinimumSize = new Size(64, DefaultHeight);
- if (button.Height < DefaultHeight)
- {
- button.Height = DefaultHeight;
- }
+ button.IconGlyph = null;
+ button.AllowTextEllipsis = false;
+ button.Font = ActionFont;
+ button.Padding = new Padding(16, 6, 16, 6);
+ var height = style == ModernButtonStyle.Primary || style == ModernButtonStyle.Danger
+ ? DefaultHeight + 3
+ : DefaultHeight;
+ button.Height = Math.Max(button.Height, height);
- button.Padding = new Padding(14, 6, 14, 6);
- button.Font = (ThemeManager.Colors?.FontSemibold14) ?? new Font("Segoe UI Semibold", 12f);
- button.IconGlyph = iconGlyph;
+ FitToText(button, height);
}
public static void ApplyClear(ModernButton button)
@@ -72,24 +75,58 @@ namespace Elfisa.UI.Controls
return;
}
- button.ButtonStyle = ModernButtonStyle.DangerSoft;
+ button.ButtonStyle = ModernButtonStyle.Danger;
button.CompactIconMode = true;
button.Image = null;
button.BackgroundImage = null;
button.UseVisualStyleBackColor = false;
button.AutoSizeMode = AutoSizeMode.GrowAndShrink;
button.Text = "×";
- button.Font = new Font("Segoe UI", 13f, FontStyle.Bold);
- button.MinimumSize = new Size(32, DefaultHeight);
- button.Size = new Size(Math.Max(button.Width, 32), DefaultHeight);
+ button.Font = new Font("Segoe UI Semibold", 13f, FontStyle.Bold);
button.IconGlyph = null;
+ button.AllowTextEllipsis = false;
+ button.MinimumSize = new Size(ClearSize, ClearSize + 3);
+ button.Size = new Size(ClearSize, ClearSize + 3);
+ }
+
+ public static void FitToText(ModernButton button, int height = DefaultHeight)
+ {
+ if (button == null || button.CompactIconMode)
+ {
+ return;
+ }
+
+ var text = button.Text ?? string.Empty;
+ if (text.Length == 0)
+ {
+ return;
+ }
+
+ var textWidth = TextRenderer.MeasureText(
+ text,
+ button.Font ?? ActionFont,
+ Size.Empty,
+ TextFormatFlags.SingleLine).Width;
+
+ var width = textWidth + button.Padding.Horizontal + 8;
+ width = Math.Max(width, 72);
+ button.MinimumSize = new Size(width, height);
+ if (button.Width < width)
+ {
+ button.Width = width;
+ }
+
+ if (button.Height < height)
+ {
+ button.Height = height;
+ }
}
private static bool IsClearButton(ModernButton button, string text)
{
return text == "×"
|| text.Equals("X", StringComparison.OrdinalIgnoreCase)
- || (button.Width <= 40 && button.Height <= 36 && text.Length <= 2);
+ || (button.Width <= 40 && button.Height <= 40 && text.Length <= 2);
}
private static bool IsDangerAction(string text)
@@ -116,61 +153,5 @@ namespace Elfisa.UI.Controls
|| lower.Contains("save")
|| lower == "ок";
}
-
- private static string ResolveIcon(string text, string fallback)
- {
- var lower = text.ToLowerInvariant();
- if (lower.Contains("отправ") || lower.Contains("send"))
- {
- return "\uE725";
- }
-
- if (lower.Contains("удал") || lower.Contains("delete"))
- {
- return "\uE74D";
- }
-
- if (lower.Contains("коммент"))
- {
- return "\uE8BD";
- }
-
- if (lower.Contains("сохран") || lower.Contains("save"))
- {
- return "\uE74E";
- }
-
- if (lower.Contains("отмен") || lower.Contains("cancel") || lower.Contains("закры"))
- {
- return "\uE711";
- }
-
- if (lower.Contains("очист") || lower.Contains("clear"))
- {
- return "\uE894";
- }
-
- if (lower.Contains("печат") || lower.Contains("print"))
- {
- return "\uE749";
- }
-
- if (lower.Contains("импорт") || lower.Contains("import"))
- {
- return "\uE896";
- }
-
- if (lower.Contains("подроб") || lower.Contains("info"))
- {
- return "\uE946";
- }
-
- if (lower.Contains("зарег") || lower.Contains("подтвер") || lower == "ок")
- {
- return "\uE73E";
- }
-
- return fallback;
- }
}
}
diff --git a/src/Elfisa.UI/Helpers/SkillGradientButtonPaint.cs b/src/Elfisa.UI/Helpers/SkillGradientButtonPaint.cs
new file mode 100644
index 0000000..7565792
--- /dev/null
+++ b/src/Elfisa.UI/Helpers/SkillGradientButtonPaint.cs
@@ -0,0 +1,159 @@
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using Elfisa.UI.Controls;
+
+namespace Elfisa.UI.Helpers
+{
+ ///
+ /// Градиент и тень из skill winforms-modern-buttons для ModernButton.
+ ///
+ internal static class SkillGradientButtonPaint
+ {
+ private static readonly Color ShadowBase = Color.FromArgb(60, 0, 0, 0);
+
+ public static bool UsesGradient(ModernButtonStyle style, bool compactIconMode)
+ {
+ return style == ModernButtonStyle.Primary
+ || style == ModernButtonStyle.Danger
+ || (compactIconMode && style == ModernButtonStyle.Danger);
+ }
+
+ public static int GetCornerRadius(ModernButtonStyle style, bool compactIconMode)
+ {
+ if (compactIconMode)
+ {
+ return 6;
+ }
+
+ return style == ModernButtonStyle.Primary ? 12 : 8;
+ }
+
+ public static int GetShadowDepth(ModernButtonStyle style, bool compactIconMode, bool enabled)
+ {
+ if (!enabled)
+ {
+ return 0;
+ }
+
+ if (compactIconMode)
+ {
+ return 3;
+ }
+
+ return style == ModernButtonStyle.Primary ? 5 : 4;
+ }
+
+ public static void GetGradient(
+ ModernButtonStyle style,
+ bool hover,
+ bool pressed,
+ out Color top,
+ out Color bottom)
+ {
+ if (style == ModernButtonStyle.Danger)
+ {
+ if (pressed)
+ {
+ top = Color.FromArgb(175, 30, 30);
+ bottom = Color.FromArgb(140, 20, 20);
+ }
+ else if (hover)
+ {
+ top = Color.FromArgb(245, 110, 110);
+ bottom = Color.FromArgb(215, 55, 55);
+ }
+ else
+ {
+ top = Color.FromArgb(235, 87, 87);
+ bottom = Color.FromArgb(200, 40, 40);
+ }
+
+ return;
+ }
+
+ // Primary preset from skill
+ if (pressed)
+ {
+ top = Color.FromArgb(25, 70, 160);
+ bottom = Color.FromArgb(15, 55, 130);
+ }
+ else if (hover)
+ {
+ top = Color.FromArgb(90, 150, 255);
+ bottom = Color.FromArgb(45, 105, 220);
+ }
+ else
+ {
+ top = Color.FromArgb(66, 133, 244);
+ bottom = Color.FromArgb(30, 87, 194);
+ }
+ }
+
+ public static void Paint(
+ Graphics g,
+ Rectangle clientRect,
+ ModernButtonStyle style,
+ bool compactIconMode,
+ bool hover,
+ bool pressed,
+ bool enabled)
+ {
+ var shadowDepth = GetShadowDepth(style, compactIconMode, enabled);
+ var radius = GetCornerRadius(style, compactIconMode);
+ var buttonRect = new Rectangle(
+ clientRect.X,
+ clientRect.Y,
+ clientRect.Width - 1,
+ clientRect.Height - 1 - shadowDepth);
+
+ if (!enabled)
+ {
+ using (var brush = new SolidBrush(Color.FromArgb(220, 230, 235)))
+ {
+ GraphicsExtensions.FillRoundedRectangle(g, brush, buttonRect, radius);
+ }
+
+ return;
+ }
+
+ for (var i = shadowDepth; i > 0; i--)
+ {
+ var alpha = ShadowBase.A * i / (shadowDepth * 3);
+ using (var shadowBrush = new SolidBrush(Color.FromArgb(alpha, ShadowBase)))
+ using (var shadowPath = GraphicsExtensions.CreateRoundedRectangle(
+ new Rectangle(
+ clientRect.X + i,
+ buttonRect.Y + i + 1,
+ buttonRect.Width - i,
+ buttonRect.Height - 1),
+ radius))
+ {
+ g.FillPath(shadowBrush, shadowPath);
+ }
+ }
+
+ GetGradient(style, hover, pressed, out var top, out var bottom);
+
+ using (var path = GraphicsExtensions.CreateRoundedRectangle(buttonRect, radius))
+ using (var brush = new LinearGradientBrush(buttonRect, top, bottom, LinearGradientMode.Vertical))
+ {
+ g.FillPath(brush, path);
+
+ using (var highlightPen = new Pen(Color.FromArgb(40, Color.White), 1f))
+ {
+ g.DrawArc(highlightPen, buttonRect.X, buttonRect.Y, radius * 2, radius * 2, 180, 90);
+ }
+ }
+ }
+
+ public static Rectangle GetContentRect(Rectangle clientRect, ModernButtonStyle style, bool compactIconMode, bool enabled)
+ {
+ var shadowDepth = GetShadowDepth(style, compactIconMode, enabled);
+ return new Rectangle(
+ clientRect.X,
+ clientRect.Y,
+ clientRect.Width - 1,
+ clientRect.Height - 1 - shadowDepth);
+ }
+ }
+}