diff --git a/src/ElectronicPharmacy/Classes/UiThemeHelper.cs b/src/ElectronicPharmacy/Classes/UiThemeHelper.cs
index 22ba007..e73634a 100644
--- a/src/ElectronicPharmacy/Classes/UiThemeHelper.cs
+++ b/src/ElectronicPharmacy/Classes/UiThemeHelper.cs
@@ -91,6 +91,14 @@ namespace Электронная_Фармация.Classes
label.ForeColor = theme.TextPrimary;
label.Font = theme.FontRegular13;
}
+ else if (control is ModernTabControl modernTabControl)
+ {
+ modernTabControl.ApplyTheme(theme);
+ }
+ else if (control is ModernSplitContainer modernSplitContainer)
+ {
+ modernSplitContainer.ApplyTheme(theme);
+ }
else if (control is TabControl tabControl)
{
tabControl.BackColor = theme.BackgroundSecondary;
@@ -101,7 +109,13 @@ namespace Электронная_Фармация.Classes
toolStrip.BackColor = theme.TitleBarBottom;
toolStrip.ForeColor = Color.White;
}
- else if (control is Panel || control is UserControl || control is TableLayoutPanel || control is SplitContainer)
+ else if (control is SplitContainer splitContainer)
+ {
+ splitContainer.BackColor = theme.Mode == ThemeMode.Dark ? theme.BorderSubtle : theme.Border;
+ splitContainer.Panel1.BackColor = theme.BackgroundPrimary;
+ splitContainer.Panel2.BackColor = theme.BackgroundPrimary;
+ }
+ else if (control is Panel || control is UserControl || control is TableLayoutPanel)
{
if (control.Tag as string == "keep-bg")
{
diff --git a/src/ElectronicPharmacy/UserControls/UCOrderItems.cs b/src/ElectronicPharmacy/UserControls/UCOrderItems.cs
index 0894b74..f59a5f9 100644
--- a/src/ElectronicPharmacy/UserControls/UCOrderItems.cs
+++ b/src/ElectronicPharmacy/UserControls/UCOrderItems.cs
@@ -11,6 +11,7 @@ using System.Data.SQLite;
using Электронная_Фармация.UserControls;
using System.Data.Common;
using Электронная_Фармация.Classes;
+using Elfisa.UI.Theming;
namespace Электронная_Фармация.UserControls
{
@@ -223,6 +224,7 @@ and ConsigneesName = '{consigneeName}'
UiThemeHelper.ApplyToControlTree(this);
_consigneeName = ParentForm?.ConsigneeName ?? string.Empty;
loadDataAboutOrderV2(_supplierName);
+ dgvOrder.ApplyTheme(ThemeManager.Colors);
}
private void btnEraseOrder_Click(object sender, EventArgs e)
diff --git a/src/ElectronicPharmacy/UserControls/UCPriceList.Designer.cs b/src/ElectronicPharmacy/UserControls/UCPriceList.Designer.cs
index 1d696dc..cb0495e 100644
--- a/src/ElectronicPharmacy/UserControls/UCPriceList.Designer.cs
+++ b/src/ElectronicPharmacy/UserControls/UCPriceList.Designer.cs
@@ -32,7 +32,7 @@
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UCPriceList));
this.PContentPriceList = new System.Windows.Forms.Panel();
- this.SplitPriceList = new System.Windows.Forms.SplitContainer();
+ this.SplitPriceList = new Elfisa.UI.Controls.ModernSplitContainer();
this.TLPPriceList = new System.Windows.Forms.TableLayoutPanel();
this.dgvPriceList = new Elfisa.UI.Controls.ModernDataGridView();
this.panel1 = new System.Windows.Forms.Panel();
@@ -45,7 +45,7 @@
this.label3 = new System.Windows.Forms.Label();
this.POrderInfo = new System.Windows.Forms.Panel();
this.dgvOrder = new Elfisa.UI.Controls.ModernDataGridView();
- this.tabOrderItems = new System.Windows.Forms.TabControl();
+ this.tabOrderItems = new Elfisa.UI.Controls.ModernTabControl();
this.lblSumOfOrder = new System.Windows.Forms.Label();
this.btnEraseOrder = new Elfisa.UI.Controls.ModernButton();
this.btnSendOrder = new Elfisa.UI.Controls.ModernButton();
@@ -100,6 +100,7 @@
this.SplitPriceList.Panel2.Controls.Add(this.POrderInfo);
this.SplitPriceList.Size = new System.Drawing.Size(1053, 532);
this.SplitPriceList.SplitterDistance = 290;
+ this.SplitPriceList.SplitterWidth = 2;
this.SplitPriceList.TabIndex = 0;
//
// TLPPriceList
@@ -288,12 +289,13 @@
this.tabOrderItems.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
+ this.tabOrderItems.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabOrderItems.Font = new System.Drawing.Font("Segoe UI", 11.25F);
- this.tabOrderItems.Location = new System.Drawing.Point(2, 24);
+ this.tabOrderItems.Location = new System.Drawing.Point(0, 0);
this.tabOrderItems.Margin = new System.Windows.Forms.Padding(2);
this.tabOrderItems.Name = "tabOrderItems";
this.tabOrderItems.SelectedIndex = 0;
- this.tabOrderItems.Size = new System.Drawing.Size(1048, 215);
+ this.tabOrderItems.Size = new System.Drawing.Size(1048, 238);
this.tabOrderItems.TabIndex = 11;
//
// lblSumOfOrder
@@ -485,7 +487,7 @@
#endregion
private System.Windows.Forms.Panel PContentPriceList;
- private System.Windows.Forms.SplitContainer SplitPriceList;
+ private Elfisa.UI.Controls.ModernSplitContainer SplitPriceList;
private System.Windows.Forms.Panel POrderInfo;
private Elfisa.UI.Controls.ModernTextBox txtSearchGoodNameInPriceList;
private System.Windows.Forms.Label label2;
@@ -499,7 +501,7 @@
private System.Windows.Forms.NumericUpDown numericPercent;
private System.Windows.Forms.TableLayoutPanel TLPPriceList;
private Elfisa.UI.Controls.ModernTextBox txtConsignee;
- private System.Windows.Forms.TabControl tabOrderItems;
+ private Elfisa.UI.Controls.ModernTabControl tabOrderItems;
public Elfisa.UI.Controls.ModernDataGridView dgvPriceList;
private Elfisa.UI.Controls.ModernDataGridView dgvOrder;
private System.Windows.Forms.Label lblSumOfOrder;
diff --git a/src/ElectronicPharmacy/UserControls/UCPriceList.cs b/src/ElectronicPharmacy/UserControls/UCPriceList.cs
index 85d9e86..c6d4092 100644
--- a/src/ElectronicPharmacy/UserControls/UCPriceList.cs
+++ b/src/ElectronicPharmacy/UserControls/UCPriceList.cs
@@ -326,18 +326,18 @@ namespace Электронная_Фармация.UserControls
}
TabPage pageSupplier = new TabPage($"{tabName}");
+ if (SupplierName == string.Empty)
+ {
+ pageSupplier.Tag = "cart";
+ }
+
UCOrderItems UCOrderItemsBySuppliers = new UCOrderItems(SupplierName);
UCOrderItemsBySuppliers.ParentForm = this;
-
- pageSupplier.SuspendLayout();
- pageSupplier.ResumeLayout();
- tabOrderItems.TabPages.Add(pageSupplier);
-
+ UCOrderItemsBySuppliers.Dock = DockStyle.Fill;
pageSupplier.Controls.Add(UCOrderItemsBySuppliers);
- UCOrderItemsBySuppliers.Dock = DockStyle.Fill;
-
+ tabOrderItems.TabPages.Add(pageSupplier);
tabOrderItems.SelectTab(pageSupplier);
}
diff --git a/src/Elfisa.UI/Controls/ModernButton.cs b/src/Elfisa.UI/Controls/ModernButton.cs
index a28415f..520a6fc 100644
--- a/src/Elfisa.UI/Controls/ModernButton.cs
+++ b/src/Elfisa.UI/Controls/ModernButton.cs
@@ -83,6 +83,7 @@ namespace Elfisa.UI.Controls
set
{
_buttonStyle = value;
+ ApplyRoundedRegion();
Invalidate();
}
}
@@ -114,7 +115,56 @@ namespace Elfisa.UI.Controls
return theme.BackgroundSecondary;
}
- return Parent?.BackColor ?? theme.ToolbarBackground;
+ var current = Parent;
+ while (current != null)
+ {
+ if (current.Tag as string == "keep-bg")
+ {
+ current = current.Parent;
+ continue;
+ }
+
+ var backColor = current.BackColor;
+ if (backColor.A > 0 && backColor != Color.Transparent)
+ {
+ return backColor;
+ }
+
+ current = current.Parent;
+ }
+
+ return theme.Surface;
+ }
+
+ private int GetCornerRadius()
+ {
+ if (_headerNavMode || _topNavMode || _sidebarMode)
+ {
+ return 0;
+ }
+
+ if (SkillGradientButtonPaint.UsesGradient(_buttonStyle, _compactIconMode))
+ {
+ return SkillGradientButtonPaint.GetCornerRadius(_buttonStyle, _compactIconMode);
+ }
+
+ return 6;
+ }
+
+ private void ApplyRoundedRegion()
+ {
+ var radius = GetCornerRadius();
+ if (radius <= 0 || Width <= 0 || Height <= 0)
+ {
+ Region = null;
+ return;
+ }
+
+ var bounds = new Rectangle(0, 0, Width, Height);
+ using (var path = GraphicsExtensions.CreateRoundedRectangle(bounds, radius))
+ {
+ Region = new Region(path);
+ }
}
public ModernButton()
@@ -157,6 +207,7 @@ namespace Elfisa.UI.Controls
BackColor = ResolveChromeBackground();
}
+ ApplyRoundedRegion();
Invalidate();
}
@@ -200,6 +251,25 @@ namespace Elfisa.UI.Controls
base.OnEnabledChanged(e);
}
+ protected override void OnHandleCreated(EventArgs e)
+ {
+ base.OnHandleCreated(e);
+ ApplyRoundedRegion();
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ base.OnResize(e);
+ ApplyRoundedRegion();
+ }
+
+ protected override void OnParentChanged(EventArgs e)
+ {
+ base.OnParentChanged(e);
+ ApplyRoundedRegion();
+ Invalidate();
+ }
+
protected override void OnGotFocus(EventArgs e)
{
_focused = true;
@@ -235,6 +305,14 @@ namespace Elfisa.UI.Controls
var bounds = new Rectangle(0, 0, Width, Height);
var usesGradient = SkillGradientButtonPaint.UsesGradient(_buttonStyle, _compactIconMode);
+ if (!_headerNavMode && !_topNavMode && !_sidebarMode)
+ {
+ using (var bgBrush = new SolidBrush(ResolveChromeBackground()))
+ {
+ g.FillRectangle(bgBrush, bounds);
+ }
+ }
+
if (_headerNavMode)
{
var headerBounds = new Rectangle(0, 0, Width - 1, Height - 1);
@@ -262,11 +340,6 @@ namespace Elfisa.UI.Controls
if (usesGradient && !_topNavMode && !_sidebarMode)
{
- using (var bgBrush = new SolidBrush(ResolveChromeBackground()))
- {
- g.FillRectangle(bgBrush, bounds);
- }
-
SkillGradientButtonPaint.Paint(
g,
bounds,
@@ -288,7 +361,7 @@ namespace Elfisa.UI.Controls
}
var flatBounds = new Rectangle(0, 0, Width - 1, Height - 1);
- var radius = _compactIconMode ? 6 : 6;
+ var radius = GetCornerRadius();
var baseColor = ResolveBaseColor(theme);
var fill = baseColor;
diff --git a/src/Elfisa.UI/Controls/ModernDataGridView.cs b/src/Elfisa.UI/Controls/ModernDataGridView.cs
index 2f45d86..b7c4361 100644
--- a/src/Elfisa.UI/Controls/ModernDataGridView.cs
+++ b/src/Elfisa.UI/Controls/ModernDataGridView.cs
@@ -23,17 +23,34 @@ namespace Elfisa.UI.Controls
AllowUserToResizeColumns = true;
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
- ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
+ ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.None;
DoubleBuffered = true;
+ SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
CellFormatting += OnGridCellFormatting;
ApplyTheme(ThemeManager.Colors);
}
+ private void ApplyBorderStyles(ThemeColors theme)
+ {
+ GridColor = theme.GridBorder;
+
+ AdvancedCellBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None;
+ AdvancedCellBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.Single;
+
+ AdvancedColumnHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None;
+ AdvancedColumnHeadersBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.Single;
+
+ if (Rows.Count > 0)
+ {
+ AdvancedRowHeadersBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None;
+ }
+ }
+
public void ApplyTheme(ThemeColors theme)
{
_theme = theme;
BackgroundColor = theme.GridRowBackground;
- GridColor = theme.GridBorder;
+ ApplyBorderStyles(theme);
ColumnHeadersHeight = theme.GridHeaderHeight;
RowTemplate.Height = theme.GridRowHeight;
Font = theme.FontRegular13;
@@ -69,6 +86,24 @@ namespace Elfisa.UI.Controls
Invalidate(true);
}
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ base.OnPaint(e);
+
+ var theme = _theme ?? ThemeManager.Colors;
+ using (var pen = new Pen(theme.GridBorder))
+ {
+ var rect = new Rectangle(0, 0, ClientSize.Width - 1, ClientSize.Height - 1);
+ e.Graphics.DrawRectangle(pen, rect);
+ }
+ }
+
+ protected override void OnRowsAdded(DataGridViewRowsAddedEventArgs e)
+ {
+ base.OnRowsAdded(e);
+ ApplyBorderStyles(_theme ?? ThemeManager.Colors);
+ }
+
private void OnGridCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex < 0 || e.ColumnIndex < 0)
diff --git a/src/Elfisa.UI/Controls/ModernSplitContainer.cs b/src/Elfisa.UI/Controls/ModernSplitContainer.cs
new file mode 100644
index 0000000..a6e3dc2
--- /dev/null
+++ b/src/Elfisa.UI/Controls/ModernSplitContainer.cs
@@ -0,0 +1,24 @@
+using System.Drawing;
+using System.Windows.Forms;
+using Elfisa.UI.Theming;
+
+namespace Elfisa.UI.Controls
+{
+ public class ModernSplitContainer : SplitContainer
+ {
+ public ModernSplitContainer()
+ {
+ SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
+ BorderStyle = BorderStyle.None;
+ SplitterWidth = 2;
+ }
+
+ public void ApplyTheme(ThemeColors theme)
+ {
+ BackColor = theme.Mode == ThemeMode.Dark ? theme.Border : theme.Border;
+ Panel1.BackColor = theme.BackgroundPrimary;
+ Panel2.BackColor = theme.BackgroundPrimary;
+ Invalidate(true);
+ }
+ }
+}
diff --git a/src/Elfisa.UI/Controls/ModernTabControl.cs b/src/Elfisa.UI/Controls/ModernTabControl.cs
new file mode 100644
index 0000000..f02a1e9
--- /dev/null
+++ b/src/Elfisa.UI/Controls/ModernTabControl.cs
@@ -0,0 +1,389 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Windows.Forms;
+using Elfisa.UI.Helpers;
+using Elfisa.UI.Theming;
+
+namespace Elfisa.UI.Controls
+{
+ ///
+ /// Вкладки в стиле документных. Контент показывается в Panel — без системной рамки TabControl.
+ ///
+ public class ModernTabControl : UserControl
+ {
+ private const int StripHeight = 40;
+ private const int TabTop = 6;
+ private const int TabHeight = 28;
+ private const int MinTabWidth = 120;
+ private const int TabGap = 8;
+ private const int ContentOverlap = 2;
+ private const int FramePadding = 1;
+
+ private readonly TabControl _inner;
+ private readonly Panel _contentFrame;
+ private readonly Panel _contentHost;
+ private readonly Panel _strip;
+ private readonly List _tabHitAreas = new List();
+ private ThemeColors _theme;
+ private int _hoverIndex = -1;
+ private TabPage _displayedPage;
+ private bool _isReparenting;
+
+ public TabControl.TabPageCollection TabPages => _inner.TabPages;
+
+ public int SelectedIndex
+ {
+ get => _inner.SelectedIndex;
+ set => _inner.SelectedIndex = value;
+ }
+
+ public int TabCount => _inner.TabCount;
+
+ public ModernTabControl()
+ {
+ SetStyle(ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.OptimizedDoubleBuffer |
+ ControlStyles.ResizeRedraw, true);
+
+ _inner = new TabControl
+ {
+ Visible = false,
+ Size = new Size(0, 0),
+ TabStop = false,
+ Tag = "keep-bg"
+ };
+ _inner.SelectedIndexChanged += (_, __) =>
+ {
+ DisplaySelectedTab();
+ _strip.Invalidate();
+ };
+ _inner.ControlAdded += OnInnerControlAdded;
+ _inner.ControlRemoved += OnInnerControlRemoved;
+
+ _contentHost = new Panel
+ {
+ Dock = DockStyle.Fill,
+ Tag = "keep-bg"
+ };
+
+ _contentFrame = new Panel
+ {
+ Dock = DockStyle.Fill,
+ Padding = new Padding(FramePadding),
+ Tag = "keep-bg"
+ };
+ _contentFrame.Controls.Add(_contentHost);
+
+ _strip = new Panel
+ {
+ Dock = DockStyle.Top,
+ Height = StripHeight,
+ Tag = "keep-bg"
+ };
+ _strip.Paint += OnStripPaint;
+ _strip.MouseMove += OnStripMouseMove;
+ _strip.MouseLeave += OnStripMouseLeave;
+ _strip.MouseClick += OnStripMouseClick;
+
+ Controls.Add(_inner);
+ Controls.Add(_contentFrame);
+ Controls.Add(_strip);
+ }
+
+ public void SelectTab(TabPage page)
+ {
+ var index = _inner.TabPages.IndexOf(page);
+ if (index < 0)
+ {
+ return;
+ }
+
+ if (_inner.SelectedIndex == index)
+ {
+ DisplaySelectedTab();
+ }
+ else
+ {
+ _inner.SelectedIndex = index;
+ }
+ }
+
+ public void ApplyTheme(ThemeColors theme)
+ {
+ _theme = theme;
+ BackColor = theme.BackgroundSecondary;
+ _strip.BackColor = theme.BackgroundSecondary;
+ _contentFrame.BackColor = theme.Border;
+ _contentHost.BackColor = theme.BackgroundPrimary;
+ SyncTabPageTheme();
+ _strip.Invalidate();
+ }
+
+ private void OnInnerControlAdded(object sender, ControlEventArgs e)
+ {
+ SyncTabPageTheme();
+ _strip.Invalidate();
+ }
+
+ private void OnInnerControlRemoved(object sender, ControlEventArgs e)
+ {
+ if (e.Control is TabPage page && page == _displayedPage)
+ {
+ _displayedPage = null;
+ }
+
+ if (_inner.TabCount == 0)
+ {
+ _displayedPage = null;
+ _contentHost.Controls.Clear();
+ }
+
+ _strip.Invalidate();
+ }
+
+ private void DisplaySelectedTab()
+ {
+ if (_isReparenting)
+ {
+ return;
+ }
+
+ _isReparenting = true;
+ try
+ {
+ ReturnControlsToTabPage(_displayedPage);
+
+ if (_inner.SelectedIndex < 0 || _inner.SelectedIndex >= _inner.TabCount)
+ {
+ _displayedPage = null;
+ _contentHost.Controls.Clear();
+ return;
+ }
+
+ _displayedPage = _inner.TabPages[_inner.SelectedIndex];
+ MoveControlsToHost(_displayedPage);
+ }
+ finally
+ {
+ _isReparenting = false;
+ }
+ }
+
+ private void ReturnControlsToTabPage(TabPage page)
+ {
+ if (page == null)
+ {
+ return;
+ }
+
+ var controls = new Control[_contentHost.Controls.Count];
+ _contentHost.Controls.CopyTo(controls, 0);
+ _contentHost.Controls.Clear();
+
+ foreach (var control in controls)
+ {
+ control.Dock = DockStyle.Fill;
+ page.Controls.Add(control);
+ }
+ }
+
+ private void MoveControlsToHost(TabPage page)
+ {
+ _contentHost.Controls.Clear();
+
+ var controls = new Control[page.Controls.Count];
+ page.Controls.CopyTo(controls, 0);
+ page.Controls.Clear();
+
+ foreach (var control in controls)
+ {
+ control.Dock = DockStyle.Fill;
+ _contentHost.Controls.Add(control);
+ }
+ }
+
+ private void OnStripPaint(object sender, PaintEventArgs e)
+ {
+ var theme = _theme ?? ThemeManager.Colors;
+ var g = e.Graphics;
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
+
+ using (var bgBrush = new SolidBrush(theme.BackgroundSecondary))
+ {
+ g.FillRectangle(bgBrush, _strip.ClientRectangle);
+ }
+
+ _tabHitAreas.Clear();
+ if (_inner.TabCount == 0)
+ {
+ DrawStripSeparator(g, theme, null, _strip.Width);
+ return;
+ }
+
+ var x = 10;
+ Rectangle? activeRect = null;
+ using (var textFont = theme.FontSemibold14 ?? new Font("Segoe UI", 10f, FontStyle.Bold))
+ {
+ for (var i = 0; i < _inner.TabCount; i++)
+ {
+ var page = _inner.TabPages[i];
+ var textSize = TextRenderer.MeasureText(page.Text, textFont);
+ var tabWidth = Math.Max(MinTabWidth, textSize.Width + 28);
+ var tabRect = new Rectangle(x, TabTop, tabWidth, TabHeight);
+ _tabHitAreas.Add(tabRect);
+
+ var isActive = _inner.SelectedIndex == i;
+ var isHover = _hoverIndex == i;
+
+ if (isActive)
+ {
+ activeRect = new Rectangle(x, TabTop, tabWidth, StripHeight - TabTop + ContentOverlap);
+ }
+ else
+ {
+ var backColor = theme.Surface;
+ if (isHover)
+ {
+ backColor = GraphicsExtensions.Blend(backColor, theme.AccentPrimarySoft, 0.35f);
+ }
+
+ using (var brush = new SolidBrush(backColor))
+ {
+ GraphicsExtensions.FillRoundedRectangle(g, brush, tabRect, theme.CornerRadiusSmall);
+ }
+
+ using (var pen = new Pen(theme.BorderSubtle))
+ {
+ GraphicsExtensions.DrawRoundedRectangle(g, pen, tabRect, theme.CornerRadiusSmall);
+ }
+
+ TextRenderer.DrawText(
+ g,
+ page.Text,
+ textFont,
+ tabRect,
+ theme.TextSecondary,
+ TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis);
+ }
+
+ x += tabWidth + TabGap;
+ }
+ }
+
+ if (activeRect.HasValue)
+ {
+ var rect = activeRect.Value;
+ using (var brush = new SolidBrush(theme.BackgroundPrimary))
+ {
+ GraphicsExtensions.FillTopRoundedRectangle(g, brush, rect, theme.CornerRadiusSmall);
+ }
+
+ var outlineColor = theme.Mode == ThemeMode.Dark ? theme.BorderSubtle : theme.AccentPrimaryBorder;
+ using (var pen = new Pen(outlineColor, 1f))
+ {
+ GraphicsExtensions.DrawAttachedTabOutline(g, pen, rect, theme.CornerRadiusSmall);
+ }
+
+ using (var seam = new SolidBrush(theme.BackgroundPrimary))
+ {
+ g.FillRectangle(seam, rect.Left, StripHeight - ContentOverlap - 1, rect.Width, ContentOverlap + 2);
+ }
+
+ using (var textFont = theme.FontSemibold14 ?? new Font("Segoe UI", 10f, FontStyle.Bold))
+ {
+ var textRect = new Rectangle(rect.X, TabTop, rect.Width, TabHeight);
+ TextRenderer.DrawText(
+ g,
+ _inner.TabPages[_inner.SelectedIndex].Text,
+ textFont,
+ textRect,
+ theme.TextPrimary,
+ TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine | TextFormatFlags.EndEllipsis);
+ }
+ }
+
+ DrawStripSeparator(g, theme, activeRect, _strip.Width);
+ }
+
+ private static void DrawStripSeparator(Graphics g, ThemeColors theme, Rectangle? activeRect, int width)
+ {
+ var y = StripHeight - 1;
+ using (var separator = new Pen(theme.Border))
+ {
+ if (activeRect.HasValue)
+ {
+ var rect = activeRect.Value;
+ if (rect.Left > 0)
+ {
+ g.DrawLine(separator, 0, y, rect.Left, y);
+ }
+
+ if (rect.Right < width)
+ {
+ g.DrawLine(separator, rect.Right, y, width, y);
+ }
+ }
+ else
+ {
+ g.DrawLine(separator, 0, y, width, y);
+ }
+ }
+ }
+
+ private void OnStripMouseMove(object sender, MouseEventArgs e)
+ {
+ var hoverIndex = HitTestTab(e.Location);
+ if (hoverIndex != _hoverIndex)
+ {
+ _hoverIndex = hoverIndex;
+ _strip.Invalidate();
+ }
+
+ _strip.Cursor = hoverIndex >= 0 ? Cursors.Hand : Cursors.Default;
+ }
+
+ private void OnStripMouseLeave(object sender, EventArgs e)
+ {
+ _hoverIndex = -1;
+ _strip.Cursor = Cursors.Default;
+ _strip.Invalidate();
+ }
+
+ private void OnStripMouseClick(object sender, MouseEventArgs e)
+ {
+ var index = HitTestTab(e.Location);
+ if (index >= 0)
+ {
+ SelectedIndex = index;
+ }
+ }
+
+ private int HitTestTab(Point location)
+ {
+ for (var i = 0; i < _tabHitAreas.Count; i++)
+ {
+ if (_tabHitAreas[i].Contains(location))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ private void SyncTabPageTheme()
+ {
+ var theme = _theme ?? ThemeManager.Colors;
+ foreach (TabPage page in _inner.TabPages)
+ {
+ page.BackColor = theme.BackgroundPrimary;
+ page.ForeColor = theme.TextPrimary;
+ }
+
+ _strip.Invalidate();
+ }
+ }
+}
diff --git a/src/Elfisa.UI/Dialogs/ModernMessageForm.cs b/src/Elfisa.UI/Dialogs/ModernMessageForm.cs
index b72f28e..1f6bb6c 100644
--- a/src/Elfisa.UI/Dialogs/ModernMessageForm.cs
+++ b/src/Elfisa.UI/Dialogs/ModernMessageForm.cs
@@ -19,21 +19,21 @@ namespace Elfisa.UI.Dialogs
ShowInTaskbar = false;
Size = new Size(440, 220);
Padding = new Padding(20);
- BackColor = ThemeManager.Colors.Surface;
var titleLabel = new Label
{
Text = title,
Dock = DockStyle.Top,
Height = 34,
- Font = ThemeManager.Colors.FontSemibold16
+ Font = ThemeManager.Colors.FontSemibold16,
+ BackColor = Color.Transparent
};
var messageLabel = new Label
{
Text = message,
Dock = DockStyle.Fill,
Font = ThemeManager.Colors.FontRegular14,
- Tag = "muted"
+ BackColor = Color.Transparent
};
var buttonPanel = new FlowLayoutPanel
@@ -63,11 +63,41 @@ namespace Elfisa.UI.Dialogs
Controls.Add(messageLabel);
Controls.Add(buttonPanel);
Controls.Add(titleLabel);
+
+ ApplyTheme(ThemeManager.Colors);
+ }
+
+ private void ApplyTheme(ThemeColors theme)
+ {
+ BackColor = theme.Surface;
+ ForeColor = theme.TextPrimary;
+
+ foreach (Control control in Controls)
+ {
+ if (control is Label label)
+ {
+ label.ForeColor = theme.TextPrimary;
+ label.BackColor = Color.Transparent;
+ }
+ else if (control is FlowLayoutPanel panel)
+ {
+ panel.BackColor = theme.Surface;
+ panel.ForeColor = theme.TextPrimary;
+ foreach (Control child in panel.Controls)
+ {
+ if (child is ModernButton button)
+ {
+ button.ApplyTheme(theme);
+ }
+ }
+ }
+ }
}
private void AddButton(FlowLayoutPanel panel, string text, ModernButtonStyle style, DialogResult result)
{
var button = new ModernButton { Text = text, Width = 100, ButtonStyle = style };
+ button.ApplyTheme(ThemeManager.Colors);
button.Click += (_, __) =>
{
DialogResult = result;
diff --git a/src/Elfisa.UI/Helpers/GraphicsExtensions.cs b/src/Elfisa.UI/Helpers/GraphicsExtensions.cs
index a23141a..9bb5573 100644
--- a/src/Elfisa.UI/Helpers/GraphicsExtensions.cs
+++ b/src/Elfisa.UI/Helpers/GraphicsExtensions.cs
@@ -29,6 +29,72 @@ namespace Elfisa.UI.Helpers
return path;
}
+ public static GraphicsPath CreateTopRoundedRectangle(Rectangle bounds, int radius)
+ {
+ var path = new GraphicsPath();
+ if (radius <= 0 || bounds.Width <= 0 || bounds.Height <= 0)
+ {
+ path.AddRectangle(bounds);
+ return path;
+ }
+
+ var diameter = radius * 2;
+ var arc = new Rectangle(bounds.X, bounds.Y, diameter, diameter);
+ path.AddArc(arc, 180, 90);
+ arc.X = bounds.Right - diameter;
+ path.AddArc(arc, 270, 90);
+ path.AddLine(bounds.Right, bounds.Y + radius, bounds.Right, bounds.Bottom);
+ path.AddLine(bounds.Right, bounds.Bottom, bounds.Left, bounds.Bottom);
+ path.AddLine(bounds.Left, bounds.Bottom, bounds.Left, bounds.Y + radius);
+ path.CloseFigure();
+ return path;
+ }
+
+ public static void FillTopRoundedRectangle(Graphics graphics, Brush brush, Rectangle bounds, int radius)
+ {
+ using (var path = CreateTopRoundedRectangle(bounds, radius))
+ {
+ graphics.FillPath(brush, path);
+ }
+ }
+
+ public static void DrawTopRoundedRectangle(Graphics graphics, Pen pen, Rectangle bounds, int radius)
+ {
+ using (var path = CreateTopRoundedRectangle(bounds, radius))
+ {
+ graphics.DrawPath(pen, path);
+ }
+ }
+
+ public static void DrawAttachedTabOutline(Graphics graphics, Pen pen, Rectangle bounds, int radius)
+ {
+ if (bounds.Width <= 0 || bounds.Height <= 0)
+ {
+ return;
+ }
+
+ if (radius <= 0)
+ {
+ graphics.DrawLine(pen, bounds.Left, bounds.Top, bounds.Right, bounds.Top);
+ graphics.DrawLine(pen, bounds.Left, bounds.Top, bounds.Left, bounds.Bottom);
+ graphics.DrawLine(pen, bounds.Right - 1, bounds.Top, bounds.Right - 1, bounds.Bottom);
+ return;
+ }
+
+ using (var path = new GraphicsPath())
+ {
+ var diameter = radius * 2;
+ var arc = new Rectangle(bounds.X, bounds.Y, diameter, diameter);
+ path.AddArc(arc, 180, 90);
+ arc.X = bounds.Right - diameter;
+ path.AddArc(arc, 270, 90);
+ path.AddLine(bounds.Right, bounds.Y + radius, bounds.Right, bounds.Bottom);
+ graphics.DrawPath(pen, path);
+ }
+
+ graphics.DrawLine(pen, bounds.Left, bounds.Y + radius, bounds.Left, bounds.Bottom);
+ }
+
public static void FillRoundedRectangle(Graphics graphics, Brush brush, Rectangle bounds, int radius)
{
using (var path = CreateRoundedRectangle(bounds, radius))
diff --git a/src/Elfisa.UI/Helpers/SkillGradientButtonPaint.cs b/src/Elfisa.UI/Helpers/SkillGradientButtonPaint.cs
index b5ca044..38c9ee6 100644
--- a/src/Elfisa.UI/Helpers/SkillGradientButtonPaint.cs
+++ b/src/Elfisa.UI/Helpers/SkillGradientButtonPaint.cs
@@ -80,41 +80,31 @@ namespace Elfisa.UI.Helpers
clientRect.Width - 1,
clientRect.Height - 1);
- g.SetClip(clientRect);
-
- if (!enabled)
+ using (var clipPath = GraphicsExtensions.CreateRoundedRectangle(buttonRect, radius))
{
- using (var brush = new SolidBrush(Color.FromArgb(220, 230, 235)))
+ g.SetClip(clipPath);
+
+ if (!enabled)
{
- GraphicsExtensions.FillRoundedRectangle(g, brush, buttonRect, radius);
+ using (var brush = new SolidBrush(Color.FromArgb(220, 230, 235)))
+ {
+ GraphicsExtensions.FillRoundedRectangle(g, brush, buttonRect, radius);
+ }
+
+ g.ResetClip();
+ return;
}
- return;
- }
+ GetGradient(style, hover, pressed, out var top, out var bottom);
- // Лёгкая тень — один слой, без чёрной полосы снизу
- var shadowRect = new Rectangle(buttonRect.X, buttonRect.Y + 1, buttonRect.Width, buttonRect.Height);
- using (var shadowPath = GraphicsExtensions.CreateRoundedRectangle(shadowRect, radius))
- using (var shadowBrush = new SolidBrush(Color.FromArgb(28, 0, 0, 0)))
- {
- g.FillPath(shadowBrush, shadowPath);
- }
-
- var faceRect = new Rectangle(buttonRect.X, buttonRect.Y, buttonRect.Width, buttonRect.Height - 1);
- GetGradient(style, hover, pressed, out var top, out var bottom);
-
- using (var path = GraphicsExtensions.CreateRoundedRectangle(faceRect, radius))
- using (var brush = new LinearGradientBrush(faceRect, top, bottom, LinearGradientMode.Vertical))
- {
- g.FillPath(brush, path);
-
- using (var highlightPen = new Pen(Color.FromArgb(36, Color.White), 1f))
+ using (var path = GraphicsExtensions.CreateRoundedRectangle(buttonRect, radius))
+ using (var brush = new LinearGradientBrush(buttonRect, top, bottom, LinearGradientMode.Vertical))
{
- g.DrawArc(highlightPen, faceRect.X, faceRect.Y, radius * 2, radius * 2, 180, 90);
+ g.FillPath(brush, path);
}
- }
- g.ResetClip();
+ g.ResetClip();
+ }
}
public static Rectangle GetContentRect(Rectangle clientRect, ModernButtonStyle style, bool compactIconMode, bool enabled)
diff --git a/src/Elfisa.UI/Helpers/WindowChromeHelper.cs b/src/Elfisa.UI/Helpers/WindowChromeHelper.cs
index 6ca4fb9..c2a9d5f 100644
--- a/src/Elfisa.UI/Helpers/WindowChromeHelper.cs
+++ b/src/Elfisa.UI/Helpers/WindowChromeHelper.cs
@@ -46,8 +46,8 @@ namespace Elfisa.UI.Helpers
TrySetInt(form.Handle, DwmwaUseImmersiveDarkMode, useDarkMode);
TrySetInt(form.Handle, DwmwaUseImmersiveDarkModeLegacy, useDarkMode);
- var caption = isDark ? theme.TitleBarTop : Color.White;
- var text = isDark ? Color.White : Color.Black;
+ var caption = theme.TitleBarTop;
+ var text = IsLightCaption(caption) ? Color.Black : Color.White;
TrySetUInt(form.Handle, DwmwaCaptionColor, ColorToDwm(caption));
TrySetUInt(form.Handle, DwmwaTextColor, ColorToDwm(text));
@@ -62,18 +62,23 @@ namespace Elfisa.UI.Helpers
return;
}
- var isDark = theme.Mode == ThemeMode.Dark;
if (headerPanel != null)
{
- headerPanel.BackColor = isDark ? theme.TitleBarTop : Color.White;
+ headerPanel.BackColor = theme.TitleBarTop;
}
if (headerLabel != null)
{
- headerLabel.ForeColor = isDark ? Color.White : Color.Black;
+ headerLabel.ForeColor = IsLightCaption(theme.TitleBarTop) ? Color.Black : Color.White;
}
}
+ private static bool IsLightCaption(Color color)
+ {
+ var luminance = (0.299 * color.R) + (0.587 * color.G) + (0.114 * color.B);
+ return luminance > 180;
+ }
+
public static void ApplyDialogChrome(Form form, Panel headerPanel = null, Label headerLabel = null)
{
if (form == null)
diff --git a/src/Elfisa.UI/Theming/ThemeRenderer.cs b/src/Elfisa.UI/Theming/ThemeRenderer.cs
index 9d4f107..49e0181 100644
--- a/src/Elfisa.UI/Theming/ThemeRenderer.cs
+++ b/src/Elfisa.UI/Theming/ThemeRenderer.cs
@@ -97,6 +97,14 @@ namespace Elfisa.UI.Theming
{
sectionHeader.ApplyTheme(colors, sectionHeader.Tag as string == "cart");
}
+ else if (control is ModernTabControl modernTabControl)
+ {
+ modernTabControl.ApplyTheme(colors);
+ }
+ else if (control is ModernSplitContainer modernSplitContainer)
+ {
+ modernSplitContainer.ApplyTheme(colors);
+ }
else if (control is ModernUploadZone uploadZone)
{
uploadZone.ApplyTheme(colors);
@@ -140,7 +148,9 @@ namespace Elfisa.UI.Theming
}
else if (control is SplitContainer splitContainer)
{
- splitContainer.BackColor = colors.BackgroundSecondary;
+ splitContainer.BackColor = colors.Mode == ThemeMode.Dark ? colors.BorderSubtle : colors.Border;
+ splitContainer.Panel1.BackColor = colors.BackgroundPrimary;
+ splitContainer.Panel2.BackColor = colors.BackgroundPrimary;
}
else if (control.Tag as string == "toolbar")
{