Polish cart tabs and dialog chrome for light/dark themes.
Add ModernTabControl/SplitContainer for cart UI without system borders, fix button corner artifacts and dialog text contrast, and use TitleBarTop green for auth header in light mode. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
7ed8822bd4
commit
5cc202dea7
@ -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")
|
||||
{
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
24
src/Elfisa.UI/Controls/ModernSplitContainer.cs
Normal file
24
src/Elfisa.UI/Controls/ModernSplitContainer.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
389
src/Elfisa.UI/Controls/ModernTabControl.cs
Normal file
389
src/Elfisa.UI/Controls/ModernTabControl.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Вкладки в стиле документных. Контент показывается в Panel — без системной рамки TabControl.
|
||||
/// </summary>
|
||||
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<Rectangle> _tabHitAreas = new List<Rectangle>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user