Polish modern UI shell, orders toolbar, and Fluent theme infrastructure.

Add status footer with logged-in user, improve FOrders filters layout, migrate dialogs to ModernButton, and extend theming with Fluent Win11 palette and auto button styles.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Magomed 2026-07-14 13:59:58 +03:00
parent d1740d49bf
commit 14c4b08ad6
21 changed files with 639 additions and 234 deletions

26
.github/copilot-instructions.md vendored Normal file
View File

@ -0,0 +1,26 @@
# Copilot instructions — Электронная Фармация
## Stack
- WinForms (.NET Framework 4.7.2), **not** WinUI 3
- UI library: `src/Elfisa.UI` (custom Modern* controls + theming)
- Apply **Fluent Design principles** from `winui-design` via theme tokens, not XAML
## Design rules (from winui-design, adapted)
1. Use `ThemeManager.Colors` / `UiThemeHelper` — never hard-code colors in forms.
2. Typography: Segoe UI 1214; icons: Segoe Fluent Icons via `FluentFonts`.
3. Spacing: 8px grid (8, 16, 24, 32).
4. App shape: developer tool — top nav + document tabs + content cards/grids.
5. Feedback: `ToastNotification` / `UiDialogs`, not blocking `MessageBox` for routine messages.
6. Data grids: styled `ModernDataGridView`; no WPF DataGrid patterns.
## When changing UI
- Prefer editing `Elfisa.UI` theme/palette over per-form colors.
- Test light theme (`FluentWin11`) after visual changes.
- Keep business logic in `ElectronicPharmacy`; keep visuals in `Elfisa.UI`.
## WinUI plugin
See `docs/WINUI-PLUGIN-SETUP.md` for `winui@awesome-copilot` install and CLI usage.

View File

@ -0,0 +1,57 @@
# WinUI agent plugin (Microsoft) — setup for this repo
The [WinUI agent plugin](https://learn.microsoft.com/en-us/windows/apps/develop/ai-assisted/winui-agent-plugin) targets **GitHub Copilot CLI** and **Claude Code**. It does **not** plug into Cursor chat directly.
This WinForms project applies **Fluent Design tokens** from the plugin's `winui-design` skill in `Elfisa.UI` (`FluentWin11` theme).
## Prerequisites
```powershell
winget install Microsoft.winappcli --source winget
winget install GitHub.cli
```
GitHub Copilot subscription required for Copilot CLI.
## Install Copilot CLI + WinUI plugin
```powershell
gh auth login
gh extension install github/gh-copilot
gh copilot plugin install winui@awesome-copilot
gh copilot plugin list
```
## Use with Copilot CLI
```powershell
gh copilot -p "@winui-dev Review the WinForms shell in src/ElectronicPharmacy for Fluent alignment"
```
Interactive session:
```powershell
copilot
# then: @winui-dev Suggest toolbar layout improvements for FOrders
```
## WinForms adaptation (this repo)
| WinUI guidance | WinForms implementation |
|----------------|-------------------------|
| Semantic brushes, no hard-coded `#RRGGBB` in UI code | `ThemeColors` + `FluentWin11Palette` |
| Segoe UI typography | `Segoe UI` 1214 in palette fonts |
| 8px spacing grid | Toolbar layout in `FOrders.ApplyToolbarLayout` |
| Developer-tool silhouette | `ModernAppHeader` + `ModernDocumentTabStrip` |
| WinUI `AccentButton` / `Button` | `ModernButton` + `ModernButtonStyles.ApplyAuto` |
| Focus ring | keyboard focus outline in `ModernButton` |
| Disabled opacity | muted fill/text when `Enabled = false` |
Default theme: `ThemeVariant.FluentWin11` in `Program.cs`.
## Claude Code (optional)
```powershell
claude plugin marketplace add microsoft/win-dev-skills
claude plugin install winui@win-dev-skills
```

View File

@ -30,6 +30,7 @@ namespace Электронная_Фармация.Classes
if (!modernButton.HeaderNavMode && !modernButton.TopNavMode && !modernButton.SidebarMode)
{
modernButton.AllowTextEllipsis = false;
ModernButtonStyles.ApplyAuto(modernButton);
}
}
else if (control is ModernTextBox modernTextBox)
@ -44,10 +45,6 @@ namespace Электронная_Фармация.Classes
{
modernSearch.ApplyTheme(theme);
}
else if (control is ModernComboBox modernCombo)
{
modernCombo.ApplyTheme(theme);
}
else if (control is DataGridView grid)
{
grid.EnableHeadersVisualStyles = false;

View File

@ -8,6 +8,7 @@ using Elfisa.UI.Loading;
using Elfisa.UI.Notifications;
using Elfisa.UI.Theming;
using Электроннаяармация.Classes;
using Электроннаяармация.Properties;
using Электроннаяармация.Forms;
using Электроннаяармация.HelpForms;
using Электроннаяармация.UserControls;
@ -23,11 +24,13 @@ namespace Электронная_Фармация
private ModernAppHeader _appHeader;
private ModernDocumentTabStrip _documentTabStrip;
private Panel _documentHost;
private Panel _statusFooter;
private Label _lblCurrentUser;
private ContextMenuStrip _directoryMenu;
partial void InitializeModernShell()
{
Font = new Font("Segoe UI", 13f);
Font = new Font("Segoe UI", 12f);
AutoScaleMode = AutoScaleMode.Dpi;
KeyPreview = true;
MinimumSize = new Size(900, 620);
@ -54,8 +57,36 @@ namespace Электронная_Фармация
_documentTabStrip = new ModernDocumentTabStrip { Name = "documentTabStrip", Dock = DockStyle.Top };
_documentHost = new Panel { Name = "documentHost", Dock = DockStyle.Fill, Tag = "chrome-child" };
_statusFooter = new Panel
{
Name = "statusFooter",
Dock = DockStyle.Bottom,
Height = 30,
Tag = "status-footer",
Padding = new Padding(12, 0, 16, 0)
};
_statusFooter.Paint += (_, e) =>
{
var border = ThemeManager.Colors.Border;
using (var pen = new Pen(border))
{
e.Graphics.DrawLine(pen, 0, 0, _statusFooter.Width, 0);
}
};
_lblCurrentUser = new Label
{
Name = "lblCurrentUser",
Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleRight,
AutoEllipsis = true,
Tag = "muted"
};
_statusFooter.Controls.Add(_lblCurrentUser);
Controls.Add(_loadingOverlay);
Controls.Add(_documentHost);
Controls.Add(_statusFooter);
Controls.Add(_documentTabStrip);
Controls.Add(_appHeader);
@ -88,6 +119,31 @@ namespace Электронная_Фармация
ThemeManager.CurrentVariant = ThemeVariant.PharmaTrust;
_themeService.Initialize(this);
UiThemeHelper.ApplyToControlTree(this);
RefreshCurrentUserStatus();
}
private void RefreshCurrentUserStatus()
{
if (_lblCurrentUser == null)
{
return;
}
var login = Settings.Default.stringLogin?.Trim();
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
if (!string.IsNullOrWhiteSpace(login) && hasToken)
{
_lblCurrentUser.Text = $"Пользователь: {login}";
}
else if (!string.IsNullOrWhiteSpace(login))
{
_lblCurrentUser.Text = $"Пользователь: {login} (не авторизован)";
}
else
{
_lblCurrentUser.Text = "Пользователь не авторизован";
}
}
private void OnAppHeaderItemSelected(object sender, string key)

View File

@ -573,7 +573,10 @@ namespace Электронная_Фармация
{
var hF_Registration = new HF_Registration();
UiThemeHelper.ApplyToControlTree(hF_Registration);
hF_Registration.ShowDialog();
if (hF_Registration.ShowDialog() == DialogResult.OK)
{
RefreshCurrentUserStatus();
}
}
private async void tsBtnSentData_Click(object sender, EventArgs e)

View File

@ -30,8 +30,8 @@
{
this.dgvConsignees = new Elfisa.UI.Controls.ModernDataGridView();
this.label1 = new System.Windows.Forms.Label();
this.btnConfirmConsignee = new System.Windows.Forms.Button();
this.btnCloseThis = new System.Windows.Forms.Button();
this.btnConfirmConsignee = new Elfisa.UI.Controls.ModernButton();
this.btnCloseThis = new Elfisa.UI.Controls.ModernButton();
((System.ComponentModel.ISupportInitialize)(this.dgvConsignees)).BeginInit();
this.SuspendLayout();
//
@ -113,7 +113,7 @@
private Elfisa.UI.Controls.ModernDataGridView dgvConsignees;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button btnConfirmConsignee;
private System.Windows.Forms.Button btnCloseThis;
private Elfisa.UI.Controls.ModernButton btnConfirmConsignee;
private Elfisa.UI.Controls.ModernButton btnCloseThis;
}
}

View File

@ -59,34 +59,22 @@
//
// btnCancel
//
this.btnCancel.BackColor = System.Drawing.Color.Firebrick;
this.btnCancel.FlatAppearance.BorderSize = 0;
this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnCancel.Font = new System.Drawing.Font("Segoe UI Semibold", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.btnCancel.ForeColor = System.Drawing.Color.White;
this.btnCancel.Location = new System.Drawing.Point(335, 224);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(100, 41);
this.btnCancel.TabIndex = 2;
this.btnCancel.Text = "Отмена";
this.btnCancel.UseVisualStyleBackColor = false;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// btnAddComment
//
this.btnAddComment.BackColor = System.Drawing.SystemColors.HotTrack;
this.btnAddComment.FlatAppearance.BorderSize = 0;
this.btnAddComment.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnAddComment.Font = new System.Drawing.Font("Segoe UI Semibold", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.btnAddComment.ForeColor = System.Drawing.Color.White;
this.btnAddComment.Location = new System.Drawing.Point(472, 224);
this.btnAddComment.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.btnAddComment.Name = "btnAddComment";
this.btnAddComment.Size = new System.Drawing.Size(100, 41);
this.btnAddComment.TabIndex = 3;
this.btnAddComment.Text = "ОК";
this.btnAddComment.UseVisualStyleBackColor = false;
this.btnAddComment.Click += new System.EventHandler(this.btnAddComment_Click);
//
// HF_Comment

View File

@ -83,32 +83,20 @@
//
// btnConfirmRegistration
//
this.btnConfirmRegistration.BackColor = System.Drawing.SystemColors.HotTrack;
this.btnConfirmRegistration.FlatAppearance.BorderSize = 0;
this.btnConfirmRegistration.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnConfirmRegistration.Font = new System.Drawing.Font("Segoe UI Semibold", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.btnConfirmRegistration.ForeColor = System.Drawing.Color.White;
this.btnConfirmRegistration.Location = new System.Drawing.Point(188, 401);
this.btnConfirmRegistration.Name = "btnConfirmRegistration";
this.btnConfirmRegistration.Size = new System.Drawing.Size(215, 43);
this.btnConfirmRegistration.TabIndex = 5;
this.btnConfirmRegistration.Text = "Зарегистрировать";
this.btnConfirmRegistration.UseVisualStyleBackColor = false;
this.btnConfirmRegistration.Click += new System.EventHandler(this.btnConfirmRegistration_Click);
//
// btnCloseThis
//
this.btnCloseThis.BackColor = System.Drawing.Color.Firebrick;
this.btnCloseThis.FlatAppearance.BorderSize = 0;
this.btnCloseThis.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnCloseThis.Font = new System.Drawing.Font("Segoe UI Semibold", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.btnCloseThis.ForeColor = System.Drawing.Color.White;
this.btnCloseThis.Location = new System.Drawing.Point(28, 401);
this.btnCloseThis.Name = "btnCloseThis";
this.btnCloseThis.Size = new System.Drawing.Size(125, 43);
this.btnCloseThis.TabIndex = 6;
this.btnCloseThis.Text = "Отмена";
this.btnCloseThis.UseVisualStyleBackColor = false;
this.btnCloseThis.Click += new System.EventHandler(this.btnCloseThis_Click);
//
// checkTokenGained

View File

@ -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.PharmaTrust;
ThemeManager.CurrentVariant = ThemeVariant.FluentWin11;
Application.Run(new ElectroPharmacy());
}
}

View File

@ -32,9 +32,10 @@
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FOrders));
this.panelToolbar = new System.Windows.Forms.Panel();
this.panelToolbar = new System.Windows.Forms.Panel();
this.txtGoodFromOrders = new Elfisa.UI.Controls.ModernTextBox();
this.btnClearSuppliers = new Elfisa.UI.Controls.ModernButton();
this.comboSuppliers = new System.Windows.Forms.ComboBox();
this.comboSuppliers = new Elfisa.UI.Controls.ModernComboBox();
this.panelOrderItems = new System.Windows.Forms.Panel();
this.dataGridView1 = new Elfisa.UI.Controls.ModernDataGridView();
this.dgvOrderItems = new Elfisa.UI.Controls.ModernDataGridView();
@ -98,12 +99,12 @@
//
// comboSuppliers
//
this.comboSuppliers.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.comboSuppliers.Font = new System.Drawing.Font("Segoe UI", 11.25F);
this.comboSuppliers.FormattingEnabled = true;
this.comboSuppliers.Location = new System.Drawing.Point(874, 31);
this.comboSuppliers.Margin = new System.Windows.Forms.Padding(4);
this.comboSuppliers.Name = "comboSuppliers";
this.comboSuppliers.Size = new System.Drawing.Size(160, 29);
this.comboSuppliers.Size = new System.Drawing.Size(170, 33);
this.comboSuppliers.TabIndex = 21;
this.comboSuppliers.Text = "Поставщик";
this.comboSuppliers.Visible = false;
@ -182,6 +183,7 @@
this.panelOrdersHeader.Location = new System.Drawing.Point(0, 0);
this.panelOrdersHeader.Margin = new System.Windows.Forms.Padding(2);
this.panelOrdersHeader.Name = "panelOrdersHeader";
this.panelOrdersHeader.Padding = new System.Windows.Forms.Padding(8, 14, 8, 6);
this.panelOrdersHeader.Size = new System.Drawing.Size(1178, 200);
this.panelOrdersHeader.TabIndex = 0;
//
@ -260,7 +262,7 @@
// panel1
//
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(0, 96);
this.panel1.Location = new System.Drawing.Point(0, 114);
this.panel1.Margin = new System.Windows.Forms.Padding(2);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(1201, 354);
@ -285,8 +287,8 @@
this.panelToolbar.Dock = System.Windows.Forms.DockStyle.Top;
this.panelToolbar.Location = new System.Drawing.Point(0, 0);
this.panelToolbar.Name = "panelToolbar";
this.panelToolbar.Padding = new System.Windows.Forms.Padding(10, 8, 10, 8);
this.panelToolbar.Size = new System.Drawing.Size(1201, 96);
this.panelToolbar.Padding = new System.Windows.Forms.Padding(12, 10, 12, 14);
this.panelToolbar.Size = new System.Drawing.Size(1201, 114);
this.panelToolbar.TabIndex = 31;
//
// label2
@ -340,33 +342,21 @@
// btnClearGoodSearch
//
this.btnClearGoodSearch.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnClearGoodSearch.BackColor = System.Drawing.Color.Firebrick;
this.btnClearGoodSearch.FlatAppearance.BorderSize = 0;
this.btnClearGoodSearch.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnClearGoodSearch.Font = new System.Drawing.Font("Segoe UI Semibold", 7.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.btnClearGoodSearch.Image = ((System.Drawing.Image)(resources.GetObject("btnClearGoodSearch.Image")));
this.btnClearGoodSearch.Location = new System.Drawing.Point(252, 48);
this.btnClearGoodSearch.Margin = new System.Windows.Forms.Padding(4);
this.btnClearGoodSearch.Name = "btnClearGoodSearch";
this.btnClearGoodSearch.Size = new System.Drawing.Size(36, 32);
this.btnClearGoodSearch.Size = new System.Drawing.Size(34, 32);
this.btnClearGoodSearch.TabIndex = 26;
this.btnClearGoodSearch.UseVisualStyleBackColor = false;
this.btnClearGoodSearch.Click += new System.EventHandler(this.btnClearGoodSearch_Click);
//
// btnClearOrderNumberFilter
//
this.btnClearOrderNumberFilter.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnClearOrderNumberFilter.BackColor = System.Drawing.Color.Firebrick;
this.btnClearOrderNumberFilter.FlatAppearance.BorderSize = 0;
this.btnClearOrderNumberFilter.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnClearOrderNumberFilter.Font = new System.Drawing.Font("Segoe UI Semibold", 7.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.btnClearOrderNumberFilter.Image = ((System.Drawing.Image)(resources.GetObject("btnClearOrderNumberFilter.Image")));
this.btnClearOrderNumberFilter.Location = new System.Drawing.Point(540, 48);
this.btnClearOrderNumberFilter.Margin = new System.Windows.Forms.Padding(4);
this.btnClearOrderNumberFilter.Name = "btnClearOrderNumberFilter";
this.btnClearOrderNumberFilter.Size = new System.Drawing.Size(36, 32);
this.btnClearOrderNumberFilter.Size = new System.Drawing.Size(34, 32);
this.btnClearOrderNumberFilter.TabIndex = 28;
this.btnClearOrderNumberFilter.UseVisualStyleBackColor = false;
this.btnClearOrderNumberFilter.Click += new System.EventHandler(this.btnClearOrderNumberFilter_Click);
//
// txtOrderNumberFilter
@ -384,42 +374,24 @@
//
// btnAddComment
//
this.btnAddComment.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
this.btnAddComment.BackColor = System.Drawing.SystemColors.HotTrack;
this.btnAddComment.FlatAppearance.BorderSize = 0;
this.btnAddComment.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnAddComment.Font = new System.Drawing.Font("Segoe UI Semibold", 10.2F, System.Drawing.FontStyle.Bold);
this.btnAddComment.ForeColor = System.Drawing.Color.White;
this.btnAddComment.Image = ((System.Drawing.Image)(resources.GetObject("btnAddComment.Image")));
this.btnAddComment.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.btnAddComment.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnAddComment.Location = new System.Drawing.Point(584, 46);
this.btnAddComment.Margin = new System.Windows.Forms.Padding(4);
this.btnAddComment.Name = "btnAddComment";
this.btnAddComment.Size = new System.Drawing.Size(240, 36);
this.btnAddComment.Size = new System.Drawing.Size(200, 34);
this.btnAddComment.TabIndex = 29;
this.btnAddComment.Text = "КОММЕНТАРИЙ";
this.btnAddComment.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.btnAddComment.UseVisualStyleBackColor = false;
this.btnAddComment.Click += new System.EventHandler(this.btnAddComment_Click);
//
// btnDeleteOrder
//
this.btnDeleteOrder.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
this.btnDeleteOrder.BackColor = System.Drawing.Color.Firebrick;
this.btnDeleteOrder.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.btnDeleteOrder.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnDeleteOrder.Font = new System.Drawing.Font("Segoe UI Semibold", 10.2F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
this.btnDeleteOrder.ForeColor = System.Drawing.Color.White;
this.btnDeleteOrder.Image = ((System.Drawing.Image)(resources.GetObject("btnDeleteOrder.Image")));
this.btnDeleteOrder.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.btnDeleteOrder.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnDeleteOrder.Location = new System.Drawing.Point(390, 6);
this.btnDeleteOrder.Margin = new System.Windows.Forms.Padding(4);
this.btnDeleteOrder.Name = "btnDeleteOrder";
this.btnDeleteOrder.Size = new System.Drawing.Size(160, 36);
this.btnDeleteOrder.Size = new System.Drawing.Size(130, 34);
this.btnDeleteOrder.TabIndex = 30;
this.btnDeleteOrder.Text = "УДАЛИТЬ";
this.btnDeleteOrder.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.btnDeleteOrder.UseVisualStyleBackColor = false;
this.btnDeleteOrder.Click += new System.EventHandler(this.btnDeleteOrder_Click);
//
// FOrders
@ -454,7 +426,7 @@
#endregion
private Elfisa.UI.Controls.ModernButton btnClearSuppliers;
private System.Windows.Forms.ComboBox comboSuppliers;
private Elfisa.UI.Controls.ModernComboBox comboSuppliers;
private System.Windows.Forms.Panel panelOrderItems;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Panel panelOrdersHeader;

View File

@ -31,6 +31,8 @@ namespace Электронная_Фармация.UserControls
{
UiThemeHelper.ApplyToControlTree(this);
Tag = "chrome-child";
panelToolbar.Tag = "toolbar";
panelOrdersHeader.Padding = new Padding(8, 14, 8, 6);
dtpDateFrom.Value = DateTime.Now.AddDays(-3);
dtpDateTo.Value = DateTime.Now;
@ -45,16 +47,15 @@ namespace Электронная_Фармация.UserControls
panelToolbar.Resize += (_, __) => ApplyToolbarLayout();
}
private ComboBox _comboOrderStatus;
private ComboBox _comboConsignees;
private ModernComboBox _comboOrderStatus;
private ModernComboBox _comboConsignees;
private ModernButton _btnSendOrder;
private void InitOrderToolbar()
{
_comboOrderStatus = new ComboBox
_comboOrderStatus = new ModernComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Font = new Font("Segoe UI", 9.75f),
Font = new Font("Segoe UI", 11.25f),
Name = "comboOrderStatus"
};
_comboOrderStatus.Items.AddRange(new object[] { "Все статусы", "НОВЫЙ", "ОТПРАВЛЕН" });
@ -62,10 +63,9 @@ namespace Электронная_Фармация.UserControls
_comboOrderStatus.SelectionChangeCommitted += (_, __) => ApplyOrderFilters();
panelToolbar.Controls.Add(_comboOrderStatus);
_comboConsignees = new ComboBox
_comboConsignees = new ModernComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Font = new Font("Segoe UI", 9.75f),
Font = new Font("Segoe UI", 11.25f),
Name = "comboConsigneesFilter"
};
_comboConsignees.SelectionChangeCommitted += (_, __) => ApplyOrderFilters();
@ -74,25 +74,28 @@ namespace Электронная_Фармация.UserControls
_btnSendOrder = new ModernButton
{
Text = "ОТПРАВИТЬ",
Font = new Font("Segoe UI Semibold", 10f, FontStyle.Bold),
BackColor = Color.FromArgb(0, 120, 212),
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Name = "btnSendOrder"
};
_btnSendOrder.FlatAppearance.BorderSize = 0;
_btnSendOrder.Click += async (_, __) => await SendSelectedOrderAsync();
panelToolbar.Controls.Add(_btnSendOrder);
UiThemeHelper.ApplyToControlTree(_comboOrderStatus);
UiThemeHelper.ApplyToControlTree(_comboConsignees);
UiThemeHelper.ApplyToControlTree(_btnSendOrder);
}
private void ApplyToolbarLayout()
{
const int pad = 10;
const int pad = 12;
const int gap = 8;
const int row1Y = 8;
const int row2Y = 50;
const int row1Y = 10;
const int controlHeight = 32;
const int filterHeight = 33;
const int buttonHeight = 34;
const int rowGap = 14;
const int row2Y = row1Y + buttonHeight + rowGap;
const int commentWidth = 200;
const int filterY = row1Y + (buttonHeight - filterHeight) / 2;
int x = pad;
@ -112,13 +115,13 @@ namespace Электронная_Фармация.UserControls
_btnSendOrder.SetBounds(right - 150, row1Y, 150, buttonHeight);
right = _btnSendOrder.Left - gap;
_comboConsignees.SetBounds(right - 190, row1Y, 190, controlHeight);
_comboConsignees.SetBounds(right - 190, filterY, 190, filterHeight);
right = _comboConsignees.Left - gap;
comboSuppliers.SetBounds(right - 170, row1Y, 170, controlHeight);
comboSuppliers.SetBounds(right - 170, filterY, 170, filterHeight);
right = comboSuppliers.Left - gap;
_comboOrderStatus.SetBounds(right - 150, row1Y, 150, controlHeight);
_comboOrderStatus.SetBounds(right - 150, filterY, 150, filterHeight);
x = pad;
txtGoodFromOrders.SetBounds(x, row2Y, 220, controlHeight);
@ -130,10 +133,12 @@ namespace Электронная_Фармация.UserControls
btnClearOrderNumberFilter.SetBounds(x, row2Y, 34, controlHeight);
x = btnClearOrderNumberFilter.Right + gap;
int commentWidth = Math.Max(180, panelToolbar.ClientSize.Width - x - pad);
btnAddComment.SetBounds(x, row2Y, commentWidth, buttonHeight);
btnClearSuppliers.Visible = false;
panelToolbar.Height = row2Y + buttonHeight + pad + 8;
panelToolbar.Padding = new Padding(pad, 10, pad, 14);
}
private void ApplyOrderFilters()
@ -166,6 +171,14 @@ namespace Электронная_Фармация.UserControls
parts.Add($"ConsigneeName = '{consignee}'");
}
if (_comboConsignees != null &&
_comboConsignees.SelectedIndex > 0 &&
!string.IsNullOrWhiteSpace(_comboConsignees.Text))
{
var consignee = _comboConsignees.Text.Replace("'", "''");
parts.Add($"ConsigneeName = '{consignee}'");
}
var addon = parts.Count == 0 ? string.Empty : " and " + string.Join(" and ", parts);
LoadOrders(
dtpDateFrom.Value.ToString("dd.MM.yyyy"),
@ -241,6 +254,10 @@ and OrderDate <= '{dateTo}'
{
tableOrders.Columns[9].ColumnName = "Грузополучатель";
}
if (tableOrders.Columns.Count > 9)
{
tableOrders.Columns[9].ColumnName = "Грузополучатель";
}
//DGVOrders.DataSource = tableOrders;

View File

@ -83,38 +83,26 @@
//
// btnEraseOrder
//
this.btnEraseOrder.BackColor = System.Drawing.Color.Firebrick;
this.btnEraseOrder.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnEraseOrder.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, System.Drawing.FontStyle.Bold);
this.btnEraseOrder.ForeColor = System.Drawing.Color.White;
this.btnEraseOrder.Image = ((System.Drawing.Image)(resources.GetObject("btnEraseOrder.Image")));
this.btnEraseOrder.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.btnEraseOrder.Location = new System.Drawing.Point(272, 36);
this.btnEraseOrder.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
this.btnEraseOrder.Name = "btnEraseOrder";
this.btnEraseOrder.Size = new System.Drawing.Size(260, 38);
this.btnEraseOrder.TabIndex = 13;
this.btnEraseOrder.Text = "Очистить корзину";
this.btnEraseOrder.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.btnEraseOrder.UseVisualStyleBackColor = false;
this.btnEraseOrder.Click += new System.EventHandler(this.btnEraseOrder_Click);
//
// btnSendOrder
//
this.btnSendOrder.BackColor = System.Drawing.SystemColors.HotTrack;
this.btnSendOrder.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnSendOrder.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, System.Drawing.FontStyle.Bold);
this.btnSendOrder.ForeColor = System.Drawing.Color.White;
this.btnSendOrder.Image = ((System.Drawing.Image)(resources.GetObject("btnSendOrder.Image")));
this.btnSendOrder.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.btnSendOrder.Location = new System.Drawing.Point(4, 36);
this.btnSendOrder.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
this.btnSendOrder.Name = "btnSendOrder";
this.btnSendOrder.Size = new System.Drawing.Size(260, 38);
this.btnSendOrder.TabIndex = 12;
this.btnSendOrder.Text = "Сохранить заказ";
this.btnSendOrder.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.btnSendOrder.UseVisualStyleBackColor = false;
this.btnSendOrder.Click += new System.EventHandler(this.btnSendOrder_Click);
//
// button1

View File

@ -164,19 +164,13 @@
//
// btnDeleteGood
//
this.btnDeleteGood.BackColor = System.Drawing.Color.Firebrick;
this.btnDeleteGood.FlatAppearance.BorderSize = 0;
this.btnDeleteGood.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnDeleteGood.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, System.Drawing.FontStyle.Bold);
this.btnDeleteGood.ForeColor = System.Drawing.Color.White;
this.btnDeleteGood.Image = ((System.Drawing.Image)(resources.GetObject("btnDeleteGood.Image")));
this.btnDeleteGood.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.btnDeleteGood.Location = new System.Drawing.Point(8, 62);
this.btnDeleteGood.Name = "btnDeleteGood";
this.btnDeleteGood.Size = new System.Drawing.Size(360, 34);
this.btnDeleteGood.TabIndex = 13;
this.btnDeleteGood.Text = "Удалить товар (BACKSPACE)";
this.btnDeleteGood.UseVisualStyleBackColor = false;
this.btnDeleteGood.Click += new System.EventHandler(this.btnDeleteGood_Click);
//
// checkUsePercentageAsDefault
@ -372,16 +366,12 @@
// btnClearSearchInPriceList
//
this.btnClearSearchInPriceList.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnClearSearchInPriceList.BackColor = System.Drawing.Color.Firebrick;
this.btnClearSearchInPriceList.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("btnClearSearchInPriceList.BackgroundImage")));
this.btnClearSearchInPriceList.FlatAppearance.BorderSize = 0;
this.btnClearSearchInPriceList.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnClearSearchInPriceList.Font = new System.Drawing.Font("Segoe UI", 9.75F);
this.btnClearSearchInPriceList.Location = new System.Drawing.Point(393, 7);
this.btnClearSearchInPriceList.Name = "btnClearSearchInPriceList";
this.btnClearSearchInPriceList.Size = new System.Drawing.Size(32, 30);
this.btnClearSearchInPriceList.TabIndex = 9;
this.btnClearSearchInPriceList.UseVisualStyleBackColor = false;
this.btnClearSearchInPriceList.Text = "×";
this.btnClearSearchInPriceList.Click += new System.EventHandler(this.btnClearSearchInPriceList_Click);
//
// btnImport
@ -443,15 +433,11 @@
// btnClearSupplierFilter
//
this.btnClearSupplierFilter.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnClearSupplierFilter.BackColor = System.Drawing.Color.Firebrick;
this.btnClearSupplierFilter.FlatAppearance.BorderSize = 0;
this.btnClearSupplierFilter.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnClearSupplierFilter.Font = new System.Drawing.Font("Segoe UI", 9.75F);
this.btnClearSupplierFilter.Location = new System.Drawing.Point(818, 7);
this.btnClearSupplierFilter.Name = "btnClearSupplierFilter";
this.btnClearSupplierFilter.Size = new System.Drawing.Size(32, 30);
this.btnClearSupplierFilter.TabIndex = 15;
this.btnClearSupplierFilter.UseVisualStyleBackColor = false;
this.btnClearSupplierFilter.Text = "×";
this.btnClearSupplierFilter.Click += new System.EventHandler(this.btnClearSupplierFilter_Click);
//

View File

@ -21,6 +21,7 @@ namespace Elfisa.UI.Controls
private string _navIconGlyph;
private string _iconGlyph;
private bool _compactIconMode;
private bool _focused;
private bool _allowTextEllipsis = true;
private readonly AnimationTimer _animTimer;
@ -30,7 +31,6 @@ namespace Elfisa.UI.Controls
set
{
_compactIconMode = value;
UpdateRoundedRegion();
Invalidate();
}
}
@ -106,15 +106,13 @@ namespace Elfisa.UI.Controls
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw |
ControlStyles.SupportsTransparentBackColor, true);
ControlStyles.ResizeRedraw, true);
FlatStyle = FlatStyle.Flat;
FlatAppearance.BorderSize = 0;
UseVisualStyleBackColor = false;
BackColor = Color.Transparent;
Cursor = Cursors.Hand;
Font = new Font("Segoe UI Semibold", 13f);
Height = 36;
Font = new Font("Segoe UI Semibold", 12f);
Height = ModernButtonStyles.DefaultHeight;
MinimumSize = new Size(64, ModernButtonStyles.DefaultHeight);
Padding = new Padding(14, 6, 14, 6);
TabStop = true;
_animTimer = new AnimationTimer(v => { _hoverAnim = v; Invalidate(); });
@ -125,7 +123,7 @@ namespace Elfisa.UI.Controls
_theme = theme;
if (_headerNavMode)
{
Font = new Font("Segoe UI", 12f, FontStyle.Bold);
Font = new Font("Segoe UI", 11f, FontStyle.Bold);
}
else if (_compactIconMode)
{
@ -135,32 +133,9 @@ namespace Elfisa.UI.Controls
{
Font = theme.FontSemibold14;
}
UpdateRoundedRegion();
Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
UpdateRoundedRegion();
}
private void UpdateRoundedRegion()
{
Region?.Dispose();
Region = null;
if (_headerNavMode || _topNavMode || _sidebarMode || _compactIconMode || Width <= 0 || Height <= 0)
{
return;
}
var theme = _theme ?? ThemeManager.Colors;
var path = GraphicsExtensions.CreateRoundedRectangle(new Rectangle(0, 0, Width, Height), theme.CornerRadiusSmall);
Region = new Region(path);
path.Dispose();
}
protected override void OnMouseEnter(EventArgs e)
{
_hover = true;
@ -201,13 +176,25 @@ namespace Elfisa.UI.Controls
base.OnEnabledChanged(e);
}
protected override void OnGotFocus(EventArgs e)
{
_focused = true;
Invalidate();
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
_focused = false;
Invalidate();
base.OnLostFocus(e);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_animTimer.Dispose();
Region?.Dispose();
Region = null;
}
base.Dispose(disposing);
}
@ -217,10 +204,11 @@ namespace Elfisa.UI.Controls
var theme = _theme ?? ThemeManager.Colors;
var g = pevent.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
var inset = _pressed && !_headerNavMode ? 1 : 0;
var bounds = new Rectangle(inset, inset, Width - (inset * 2), Height - (inset * 2));
var bounds = new Rectangle(0, 0, Width - 1, Height - 1);
var radius = theme.CornerRadiusSmall;
var baseColor = ResolveBaseColor(theme);
var fill = baseColor;
@ -260,9 +248,25 @@ namespace Elfisa.UI.Controls
fill = theme.SidebarItemHover;
}
}
else if (_buttonStyle == ModernButtonStyle.Subtle)
{
fill = Color.Transparent;
if (!Enabled)
{
fill = Color.Transparent;
}
else if (_pressed)
{
fill = GraphicsExtensions.Blend(theme.GridRowHover, theme.BorderSubtle, 0.35f);
}
else if (_hover)
{
fill = GraphicsExtensions.Blend(theme.GridRowHover, Color.Transparent, 1f - (_hoverAnim * 0.65f));
}
}
else if (!Enabled)
{
fill = GraphicsExtensions.Blend(baseColor, theme.BorderSubtle, 0.45f);
fill = ApplyDisabledFill(theme, baseColor);
}
else if (_pressed)
{
@ -278,40 +282,12 @@ namespace Elfisa.UI.Controls
if (!_topNavMode)
{
if (!_sidebarMode && !_headerNavMode && Enabled && IsSolidActionStyle(_buttonStyle) && !_pressed)
if (fill.A > 0)
{
var shadowAlpha = (int)(14 + (_hoverAnim * 10));
var shadowRect = new Rectangle(bounds.X, bounds.Y + 1, bounds.Width, bounds.Height - 1);
using (var shadowBrush = new SolidBrush(Color.FromArgb(shadowAlpha, 0, 0, 0)))
using (var clipPath = GraphicsExtensions.CreateRoundedRectangle(bounds, radius))
{
var state = g.Save();
g.SetClip(clipPath);
GraphicsExtensions.FillRoundedRectangle(g, shadowBrush, shadowRect, radius);
g.Restore(state);
}
}
using (var brush = new SolidBrush(fill))
{
GraphicsExtensions.FillRoundedRectangle(g, brush, bounds, radius);
}
if (!_sidebarMode && !_headerNavMode && Enabled && IsSolidActionStyle(_buttonStyle))
{
var glossRect = new Rectangle(bounds.X + 1, bounds.Y + 1, bounds.Width - 2, Math.Max(4, bounds.Height / 2));
using (var glossBrush = new LinearGradientBrush(
glossRect,
Color.FromArgb(_pressed ? 0 : 42, 255, 255, 255),
Color.FromArgb(0, 255, 255, 255),
LinearGradientMode.Vertical))
using (var clipPath = GraphicsExtensions.CreateRoundedRectangle(bounds, radius))
{
var state = g.Save();
g.SetClip(clipPath);
g.FillRectangle(glossBrush, glossRect);
g.Restore(state);
}
}
}
@ -332,16 +308,41 @@ namespace Elfisa.UI.Controls
}
}
}
else if ((_buttonStyle == ModernButtonStyle.Secondary || _buttonStyle == ModernButtonStyle.DangerSoft) && !_sidebarMode)
else if ((_buttonStyle == ModernButtonStyle.Secondary
|| _buttonStyle == ModernButtonStyle.DangerSoft
|| _buttonStyle == ModernButtonStyle.Subtle) && !_sidebarMode)
{
if (_buttonStyle == ModernButtonStyle.Subtle && !_hover && !_pressed && !_focused)
{
// WinUI SubtleButton — no border at rest.
}
else
{
var borderColor = _buttonStyle == ModernButtonStyle.DangerSoft
? GraphicsExtensions.Blend(theme.Danger, Color.White, 0.65f)
: theme.Border;
? GraphicsExtensions.Blend(theme.Danger, Color.White, 0.55f)
: _buttonStyle == ModernButtonStyle.Subtle
? theme.Border
: theme.InputBorder;
if (_hover && Enabled)
{
borderColor = theme.InputBorderFocus;
}
using (var pen = new Pen(borderColor, 1f))
{
GraphicsExtensions.DrawRoundedRectangle(g, pen, bounds, radius);
}
}
}
if (_focused && Enabled && ShowFocusCues && !_headerNavMode && !_topNavMode && !_sidebarMode)
{
var focusBounds = Rectangle.Inflate(bounds, -2, -2);
using (var pen = new Pen(theme.InputBorderFocus, 2f))
{
GraphicsExtensions.DrawRoundedRectangle(g, pen, focusBounds, Math.Max(2, radius - 1));
}
}
var textColor = _topNavMode
? (_isActiveItem ? theme.AccentPrimary : theme.TextMuted)
@ -349,6 +350,8 @@ namespace Elfisa.UI.Controls
? (_isActiveItem ? theme.SidebarItemActiveText : theme.SidebarItem)
: _buttonStyle == ModernButtonStyle.DangerSoft
? theme.Danger
: _buttonStyle == ModernButtonStyle.Subtle
? theme.TextPrimary
: _buttonStyle == ModernButtonStyle.Secondary
? theme.TextPrimary
: theme.TextOnAccent;
@ -360,7 +363,7 @@ namespace Elfisa.UI.Controls
if (!Enabled)
{
textColor = theme.TextMuted;
textColor = GraphicsExtensions.Blend(textColor, theme.TextMuted, 0.55f);
}
DrawActionButtonContent(g, bounds, textColor);
@ -404,7 +407,7 @@ namespace Elfisa.UI.Controls
? bounds.X + horizontalPad
: bounds.X + horizontalPad + Math.Max(0, (innerWidth - contentWidth) / 2);
using (var iconFont = new Font("Segoe MDL2 Assets", 13f))
using (var iconFont = FluentFonts.CreateIconFont(13f))
{
var iconRect = new Rectangle(startX, bounds.Y, iconWidth, bounds.Height);
TextRenderer.DrawText(
@ -448,7 +451,8 @@ namespace Elfisa.UI.Controls
private void DrawHeaderNavContent(Graphics g, Rectangle bounds, ThemeColors theme)
{
var iconFont = new Font("Segoe MDL2 Assets", 18f);
using (var iconFont = FluentFonts.CreateIconFont(18f))
{
var textColor = Color.White;
var iconRect = new Rectangle(bounds.X, bounds.Y + 8, bounds.Width, 28);
var textRect = new Rectangle(bounds.X, bounds.Y + 34, bounds.Width, bounds.Height - 34);
@ -472,20 +476,24 @@ namespace Elfisa.UI.Controls
textColor,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine);
}
}
private static bool IsSolidActionStyle(ModernButtonStyle style)
private static Color ApplyDisabledFill(ThemeColors theme, Color baseColor)
{
return style == ModernButtonStyle.Primary
|| style == ModernButtonStyle.Success
|| style == ModernButtonStyle.Danger
|| style == ModernButtonStyle.Warning;
if (baseColor == theme.Surface || baseColor == theme.DangerSoft)
{
return GraphicsExtensions.Blend(baseColor, theme.BorderSubtle, 0.35f);
}
return GraphicsExtensions.Blend(baseColor, theme.BorderSubtle, 0.55f);
}
private Color ResolveBaseColor(ThemeColors theme)
{
switch (_buttonStyle)
{
case ModernButtonStyle.Secondary: return theme.AccentSecondary;
case ModernButtonStyle.Secondary: return theme.Surface;
case ModernButtonStyle.Subtle: return Color.Transparent;
case ModernButtonStyle.Success: return theme.Success;
case ModernButtonStyle.Warning: return theme.Warning;
case ModernButtonStyle.Danger: return theme.Danger;
@ -497,6 +505,8 @@ namespace Elfisa.UI.Controls
private static Color ResolveHoverColor(ThemeColors theme, Color baseColor)
{
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.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);

View File

@ -7,6 +7,7 @@ namespace Elfisa.UI.Controls
Success,
Warning,
Danger,
DangerSoft
DangerSoft,
Subtle
}
}

View File

@ -0,0 +1,176 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using Elfisa.UI.Theming;
namespace Elfisa.UI.Controls
{
/// <summary>
/// WinUI 3style presets for <see cref="ModernButton"/> in WinForms.
/// </summary>
public static class ModernButtonStyles
{
public const int DefaultHeight = 32;
public static void ApplyAuto(ModernButton button)
{
if (button == null || button.HeaderNavMode || button.TopNavMode || button.SidebarMode)
{
return;
}
var text = (button.Text ?? string.Empty).Trim();
if (IsClearButton(button, text))
{
ApplyClear(button);
return;
}
if (IsDangerAction(text))
{
ApplyAction(button, ModernButtonStyle.Danger, ResolveIcon(text, "\uE74D"));
return;
}
if (IsPrimaryAction(text))
{
ApplyAction(button, ModernButtonStyle.Primary, ResolveIcon(text, "\uE725"));
return;
}
ApplyAction(button, ModernButtonStyle.Secondary, ResolveIcon(text, null));
}
public static void ApplyAction(ModernButton button, ModernButtonStyle style, string iconGlyph = null)
{
if (button == null)
{
return;
}
button.ButtonStyle = style;
button.CompactIconMode = false;
button.Image = null;
button.BackgroundImage = null;
button.UseVisualStyleBackColor = false;
button.AutoSizeMode = AutoSizeMode.GrowAndShrink;
button.MinimumSize = new Size(64, DefaultHeight);
if (button.Height < DefaultHeight)
{
button.Height = DefaultHeight;
}
button.Padding = new Padding(14, 6, 14, 6);
button.Font = (ThemeManager.Colors?.FontSemibold14) ?? new Font("Segoe UI Semibold", 12f);
button.IconGlyph = iconGlyph;
}
public static void ApplyClear(ModernButton button)
{
if (button == null)
{
return;
}
button.ButtonStyle = ModernButtonStyle.DangerSoft;
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.IconGlyph = null;
}
private static bool IsClearButton(ModernButton button, string text)
{
return text == "×"
|| text.Equals("X", StringComparison.OrdinalIgnoreCase)
|| (button.Width <= 40 && button.Height <= 36 && text.Length <= 2);
}
private static bool IsDangerAction(string text)
{
var lower = text.ToLowerInvariant();
return lower.Contains("удал")
|| lower.Contains("отмен")
|| lower.Contains("очист")
|| lower.Contains("delete")
|| lower.Contains("cancel")
|| lower.Contains("erase");
}
private static bool IsPrimaryAction(string text)
{
var lower = text.ToLowerInvariant();
return lower.Contains("отправ")
|| lower.Contains("сохран")
|| lower.Contains("зарег")
|| lower.Contains("выполн")
|| lower.Contains("коммент")
|| lower.Contains("подтвер")
|| lower.Contains("send")
|| 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;
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Drawing;
namespace Elfisa.UI.Helpers
{
/// <summary>
/// Segoe Fluent Icons with MDL2 fallback (WinUI design guidance).
/// </summary>
public static class FluentFonts
{
private static readonly Lazy<FontFamily> IconFamily = new Lazy<FontFamily>(ResolveIconFamily);
public static Font CreateIconFont(float size, FontStyle style = FontStyle.Regular)
{
return new Font(IconFamily.Value, size, style, GraphicsUnit.Point);
}
private static FontFamily ResolveIconFamily()
{
if (FontFamilyExists("Segoe Fluent Icons"))
{
return new FontFamily("Segoe Fluent Icons");
}
return new FontFamily("Segoe MDL2 Assets");
}
private static bool FontFamilyExists(string familyName)
{
using (var font = new Font(familyName, 10f))
{
return string.Equals(font.FontFamily.Name, familyName, StringComparison.OrdinalIgnoreCase);
}
}
}
}

View File

@ -0,0 +1,99 @@
using System.Drawing;
namespace Elfisa.UI.Theming
{
/// <summary>
/// WinUI 3 / Fluent Design light tokens adapted for WinForms
/// (see microsoft/win-dev-skills winui-design skill).
/// </summary>
public sealed class FluentWin11Palette : IThemePalette
{
public ThemeVariant Variant => ThemeVariant.FluentWin11;
public ThemeColors Colors { get; } = new ThemeColors
{
Name = "Fluent Win11",
Mode = ThemeMode.Light,
// SolidBackgroundFillColorBase / Secondary
BackgroundPrimary = Color.FromArgb(243, 243, 243),
BackgroundSecondary = Color.FromArgb(249, 249, 249),
Surface = Color.White,
SurfaceElevated = Color.White,
// ControlStrokeColorDefault / Divider
Border = Color.FromArgb(229, 229, 229),
BorderSubtle = Color.FromArgb(240, 240, 240),
// TextFillColorPrimary / Secondary / Tertiary
TextPrimary = Color.FromArgb(26, 26, 26),
TextSecondary = Color.FromArgb(97, 97, 97),
TextMuted = Color.FromArgb(138, 138, 138),
TextOnAccent = Color.White,
// AccentFillColorDefault (pharmacy green on Fluent chrome)
AccentPrimary = Color.FromArgb(15, 108, 189),
AccentPrimaryHover = Color.FromArgb(14, 94, 163),
AccentPrimaryPressed = Color.FromArgb(12, 80, 140),
AccentPrimarySoft = Color.FromArgb(235, 243, 252),
AccentPrimaryBorder = Color.FromArgb(199, 224, 244),
AccentSecondary = Color.FromArgb(16, 124, 16),
Success = Color.FromArgb(16, 124, 16),
SuccessSoft = Color.FromArgb(223, 246, 221),
Warning = Color.FromArgb(157, 93, 0),
WarningSoft = Color.FromArgb(255, 244, 206),
Danger = Color.FromArgb(196, 43, 28),
DangerSoft = Color.FromArgb(253, 231, 233),
Info = Color.FromArgb(15, 108, 189),
InfoSoft = Color.FromArgb(235, 243, 252),
// Commanding layer — light Mica-style header (not heavy gradient)
TitleBarTop = Color.FromArgb(249, 249, 249),
TitleBarBottom = Color.FromArgb(237, 237, 237),
NavBackground = Color.FromArgb(249, 249, 249),
CartHeaderBackground = Color.FromArgb(243, 243, 243),
ToolbarBackground = Color.FromArgb(249, 249, 249),
SectionHeaderBackground = Color.FromArgb(243, 243, 243),
SidebarBackground = Color.FromArgb(249, 249, 249),
SidebarItem = Color.FromArgb(97, 97, 97),
SidebarItemHover = Color.FromArgb(243, 243, 243),
SidebarItemActive = Color.FromArgb(235, 243, 252),
SidebarItemActiveText = Color.FromArgb(15, 108, 189),
GridHeaderBackground = Color.FromArgb(243, 243, 243),
GridHeaderForeground = Color.FromArgb(26, 26, 26),
GridRowBackground = Color.White,
GridRowAlternate = Color.FromArgb(249, 249, 249),
GridRowHover = Color.FromArgb(243, 243, 243),
GridRowSelected = Color.FromArgb(15, 108, 189),
GridRowSelectedText = Color.White,
GridBorder = Color.FromArgb(229, 229, 229),
InputBackground = Color.White,
InputBorder = Color.FromArgb(229, 229, 229),
InputBorderFocus = Color.FromArgb(15, 108, 189),
OverlayScrim = Color.FromArgb(120, 0, 0, 0),
Shadow = Color.FromArgb(24, 0, 0, 0),
ScrollbarThumb = Color.FromArgb(200, 200, 200),
ScrollbarTrack = Color.FromArgb(243, 243, 243),
CornerRadiusSmall = 4,
CornerRadiusMedium = 8,
GridRowHeight = 36,
GridHeaderHeight = 40
};
public FluentWin11Palette()
{
Colors.FontRegular12 = new Font("Segoe UI", 12f);
Colors.FontRegular13 = new Font("Segoe UI", 12f);
Colors.FontRegular14 = new Font("Segoe UI", 14f);
Colors.FontSemibold14 = new Font("Segoe UI Semibold", 14f);
Colors.FontSemibold16 = new Font("Segoe UI Semibold", 16f);
Colors.FontBold18 = new Font("Segoe UI Semibold", 18f);
}
}
}

View File

@ -12,10 +12,11 @@ namespace Elfisa.UI.Theming
{ ThemeVariant.ClinicalPro, new ClinicalProPalette() },
{ ThemeVariant.LightOffice, new LightOfficePalette() },
{ ThemeVariant.DarkRider, new DarkRiderPalette() },
{ ThemeVariant.DarkFluent, new DarkFluentPalette() }
{ ThemeVariant.DarkFluent, new DarkFluentPalette() },
{ ThemeVariant.FluentWin11, new FluentWin11Palette() }
};
private static ThemeVariant _currentVariant = ThemeVariant.PharmaTrust;
private static ThemeVariant _currentVariant = ThemeVariant.FluentWin11;
public static event EventHandler ThemeChanged;
@ -39,7 +40,7 @@ namespace Elfisa.UI.Theming
public static ThemeMode CurrentMode => Colors.Mode;
public static IReadOnlyList<ThemeVariant> AvailableVariants { get; } =
new[] { ThemeVariant.PharmaTrust, ThemeVariant.Apotheca, ThemeVariant.ClinicalPro, ThemeVariant.LightOffice, ThemeVariant.DarkRider, ThemeVariant.DarkFluent };
new[] { ThemeVariant.PharmaTrust, ThemeVariant.Apotheca, ThemeVariant.ClinicalPro, ThemeVariant.LightOffice, ThemeVariant.DarkRider, ThemeVariant.DarkFluent, ThemeVariant.FluentWin11 };
public static string GetVariantDisplayName(ThemeVariant variant)
{

View File

@ -1,6 +1,5 @@
using System.Windows.Forms;
using Elfisa.UI.Controls;
using Elfisa.UI.Loading;
namespace Elfisa.UI.Theming
{
@ -26,6 +25,10 @@ namespace Elfisa.UI.Theming
else if (control is ModernButton button)
{
button.ApplyTheme(colors);
if (!button.HeaderNavMode && !button.TopNavMode && !button.SidebarMode)
{
ModernButtonStyles.ApplyAuto(button);
}
}
else if (control is ModernDataGridView grid)
{
@ -87,14 +90,14 @@ namespace Elfisa.UI.Theming
{
uploadPanel.ApplyTheme(colors);
}
else if (control is LoadingOverlay loadingOverlay)
{
loadingOverlay.ApplyTheme(colors);
}
else if (control.Tag as string == "toolbar")
{
control.BackColor = colors.ToolbarBackground;
}
else if (control.Tag as string == "status-footer")
{
control.BackColor = colors.Surface;
}
else if (control is Panel || control is UserControl || control is TableLayoutPanel || control is FlowLayoutPanel)
{
if (control.Tag as string == "chrome-child" || control.Tag as string == "keep-bg")

View File

@ -7,7 +7,8 @@ namespace Elfisa.UI.Theming
ClinicalPro = 2,
LightOffice = 3,
DarkRider = 4,
DarkFluent = 5
DarkFluent = 5,
FluentWin11 = 6
}
public enum ThemeMode