diff --git a/src/ElectronicPharmacy/Classes/UiThemeHelper.cs b/src/ElectronicPharmacy/Classes/UiThemeHelper.cs
index 82b6b4b..22ba007 100644
--- a/src/ElectronicPharmacy/Classes/UiThemeHelper.cs
+++ b/src/ElectronicPharmacy/Classes/UiThemeHelper.cs
@@ -103,9 +103,19 @@ namespace Электронная_Фармация.Classes
}
else if (control is Panel || control is UserControl || control is TableLayoutPanel || control is SplitContainer)
{
- if (control.Tag as string != "keep-bg")
+ if (control.Tag as string == "keep-bg")
+ {
+ // preserve custom background
+ }
+ else if (control.Tag as string == "chrome-child")
+ {
+ control.BackColor = theme.BackgroundPrimary;
+ control.ForeColor = theme.TextPrimary;
+ }
+ else
{
control.BackColor = theme.BackgroundSecondary;
+ control.ForeColor = theme.TextPrimary;
}
}
diff --git a/src/ElectronicPharmacy/Form1.UiShell.cs b/src/ElectronicPharmacy/Form1.UiShell.cs
index 7048abb..da3ddd5 100644
--- a/src/ElectronicPharmacy/Form1.UiShell.cs
+++ b/src/ElectronicPharmacy/Form1.UiShell.cs
@@ -4,6 +4,7 @@ using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Elfisa.UI.Controls;
+using Elfisa.UI.Helpers;
using Elfisa.UI.Loading;
using Elfisa.UI.Notifications;
using Elfisa.UI.Theming;
@@ -112,6 +113,7 @@ namespace Электронная_Фармация
});
_appHeader.ItemSelected += OnAppHeaderItemSelected;
+ _documentTabStrip.ThemeToggleClicked += OnThemeToggleClicked;
_documentTabStrip.TabSelected += OnDocumentTabSelected;
_documentTabStrip.TabClosed += OnDocumentTabClosed;
_documentTabStrip.TabCloseAllRequested += OnDocumentTabCloseAll;
@@ -133,14 +135,41 @@ namespace Электронная_Фармация
_documentTabMenu.Items.Add(_documentCloseTabMenuItem);
_documentTabMenu.Items.Add(_documentCloseAllTabsMenuItem);
- ThemeManager.ThemeChanged += (_, __) => _themeService.Apply(this);
+ ThemeManager.ThemeChanged += (_, __) =>
+ {
+ UiThemeHelper.ApplyToControlTree(this);
+ UpdateThemeToggleState();
+ WindowChromeHelper.ApplyTitleBarTheme(this, ThemeManager.Colors);
+ };
+
+ HandleCreated += (_, __) => WindowChromeHelper.ApplyTitleBarTheme(this, ThemeManager.Colors);
+ }
+
+ private void UpdateThemeToggleState()
+ {
+ if (_documentTabStrip != null)
+ {
+ _documentTabStrip.IsDarkTheme = ThemeManager.IsDarkProductivityActive;
+ }
+ }
+
+ private void OnThemeToggleClicked(object sender, EventArgs e)
+ {
+ ThemeManager.ToggleProductivityTheme();
+ Settings.Default.UseDarkTheme = ThemeManager.IsDarkProductivityActive;
+ Settings.Default.Save();
+ UpdateThemeToggleState();
}
partial void FinalizeModernShell()
{
- ThemeManager.CurrentVariant = ThemeVariant.PharmaTrust;
+ ThemeManager.CurrentVariant = Settings.Default.UseDarkTheme
+ ? ThemeVariant.DarkProductivity
+ : ThemeVariant.PharmaTrust;
_themeService.Initialize(this);
+ UpdateThemeToggleState();
UiThemeHelper.ApplyToControlTree(this);
+ WindowChromeHelper.ApplyTitleBarTheme(this, ThemeManager.Colors);
RefreshCurrentUserStatus();
}
diff --git a/src/ElectronicPharmacy/HelpForms/HF_Registration.cs b/src/ElectronicPharmacy/HelpForms/HF_Registration.cs
index 27784e0..15eb8c7 100644
--- a/src/ElectronicPharmacy/HelpForms/HF_Registration.cs
+++ b/src/ElectronicPharmacy/HelpForms/HF_Registration.cs
@@ -12,6 +12,8 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Электронная_Фармация.Classes;
using System.Net.Http;
+using Elfisa.UI.Helpers;
+using Elfisa.UI.Theming;
namespace Электронная_Фармация.HelpForms
{
@@ -20,6 +22,7 @@ namespace Электронная_Фармация.HelpForms
public HF_Registration()
{
InitializeComponent();
+ Shown += (_, __) => WindowChromeHelper.ApplyTitleBarTheme(this, ThemeManager.Colors);
}
string login= Settings.Default.stringLogin.ToString();
@@ -91,7 +94,7 @@ namespace Электронная_Фармация.HelpForms
private void HF_Registration_Load(object sender, EventArgs e)
{
UiThemeHelper.ApplyToControlTree(this);
- panel1.BackColor = Elfisa.UI.Theming.ThemeManager.Colors.TitleBarBottom;
+ WindowChromeHelper.ApplyDialogChrome(this, panel1, label3);
txtLogin.Text = login;
txtPassword.Text = password;
txtLocationId.Text = Settings.Default.LocationId ?? string.Empty;
diff --git a/src/ElectronicPharmacy/Program.cs b/src/ElectronicPharmacy/Program.cs
index a8be127..4dcc9e3 100644
--- a/src/ElectronicPharmacy/Program.cs
+++ b/src/ElectronicPharmacy/Program.cs
@@ -1,6 +1,5 @@
using System;
using System.Windows.Forms;
-using Elfisa.UI.Theming;
namespace Электронная_Фармация
{
@@ -11,7 +10,6 @@ namespace Электронная_Фармация
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
- ThemeManager.CurrentVariant = ThemeVariant.PharmaTrust;
Application.Run(new ElectroPharmacy());
}
}
diff --git a/src/ElectronicPharmacy/Properties/Settings.Designer.cs b/src/ElectronicPharmacy/Properties/Settings.Designer.cs
index c9b1cff..e0ea4f7 100644
--- a/src/ElectronicPharmacy/Properties/Settings.Designer.cs
+++ b/src/ElectronicPharmacy/Properties/Settings.Designer.cs
@@ -107,5 +107,17 @@ namespace Электронная_Фармация.Properties {
}
}
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool UseDarkTheme {
+ get {
+ return ((bool)(this["UseDarkTheme"]));
+ }
+ set {
+ this["UseDarkTheme"] = value;
+ }
+ }
+
}
}
diff --git a/src/ElectronicPharmacy/Properties/Settings.settings b/src/ElectronicPharmacy/Properties/Settings.settings
index 17ec4d6..d747674 100644
--- a/src/ElectronicPharmacy/Properties/Settings.settings
+++ b/src/ElectronicPharmacy/Properties/Settings.settings
@@ -23,5 +23,8 @@
+
+ False
+
\ No newline at end of file
diff --git a/src/Elfisa.UI/Controls/ModernDataGridView.cs b/src/Elfisa.UI/Controls/ModernDataGridView.cs
index 0cbfc82..2f45d86 100644
--- a/src/Elfisa.UI/Controls/ModernDataGridView.cs
+++ b/src/Elfisa.UI/Controls/ModernDataGridView.cs
@@ -8,6 +8,7 @@ namespace Elfisa.UI.Controls
public class ModernDataGridView : DataGridView
{
private ThemeColors _theme;
+ private int? _hoverRowIndex;
public ModernDataGridView()
{
@@ -24,6 +25,7 @@ namespace Elfisa.UI.Controls
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single;
DoubleBuffered = true;
+ CellFormatting += OnGridCellFormatting;
ApplyTheme(ThemeManager.Colors);
}
@@ -64,30 +66,73 @@ namespace Elfisa.UI.Controls
Font = theme.FontRegular13,
Padding = new Padding(12, 0, 12, 0)
};
- Invalidate();
+ Invalidate(true);
+ }
+
+ private void OnGridCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
+ {
+ if (e.RowIndex < 0 || e.ColumnIndex < 0)
+ {
+ return;
+ }
+
+ var theme = _theme ?? ThemeManager.Colors;
+ var row = Rows[e.RowIndex];
+
+ if (row.Selected)
+ {
+ e.CellStyle.BackColor = theme.GridRowSelected;
+ e.CellStyle.ForeColor = theme.GridRowSelectedText;
+ }
+ else if (_hoverRowIndex == e.RowIndex)
+ {
+ e.CellStyle.BackColor = theme.GridRowHover;
+ e.CellStyle.ForeColor = theme.TextPrimary;
+ }
+ else if (e.RowIndex % 2 == 1)
+ {
+ e.CellStyle.BackColor = theme.GridRowAlternate;
+ e.CellStyle.ForeColor = theme.TextPrimary;
+ }
+ else
+ {
+ e.CellStyle.BackColor = theme.GridRowBackground;
+ e.CellStyle.ForeColor = theme.TextPrimary;
+ }
+
+ e.CellStyle.SelectionBackColor = theme.GridRowSelected;
+ e.CellStyle.SelectionForeColor = theme.GridRowSelectedText;
+ e.CellStyle.Font = theme.FontRegular13;
}
protected override void OnCellMouseEnter(DataGridViewCellEventArgs e)
{
base.OnCellMouseEnter(e);
- if (e.RowIndex >= 0 && e.RowIndex != CurrentCell?.RowIndex)
+ if (e.RowIndex < 0 || e.RowIndex == _hoverRowIndex)
{
- var theme = _theme ?? ThemeManager.Colors;
- Rows[e.RowIndex].DefaultCellStyle.BackColor = theme.GridRowHover;
+ return;
}
+
+ var previous = _hoverRowIndex;
+ _hoverRowIndex = e.RowIndex;
+ if (previous.HasValue)
+ {
+ InvalidateRow(previous.Value);
+ }
+
+ InvalidateRow(e.RowIndex);
}
protected override void OnCellMouseLeave(DataGridViewCellEventArgs e)
{
base.OnCellMouseLeave(e);
- if (e.RowIndex >= 0)
+ if (e.RowIndex < 0 || _hoverRowIndex != e.RowIndex)
{
- var theme = _theme ?? ThemeManager.Colors;
- var isAlt = e.RowIndex % 2 == 1;
- Rows[e.RowIndex].DefaultCellStyle.BackColor = isAlt
- ? theme.GridRowAlternate
- : theme.GridRowBackground;
+ return;
}
+
+ _hoverRowIndex = null;
+ InvalidateRow(e.RowIndex);
}
}
}
diff --git a/src/Elfisa.UI/Controls/ModernDocumentTabStrip.cs b/src/Elfisa.UI/Controls/ModernDocumentTabStrip.cs
index 132565d..20ed68a 100644
--- a/src/Elfisa.UI/Controls/ModernDocumentTabStrip.cs
+++ b/src/Elfisa.UI/Controls/ModernDocumentTabStrip.cs
@@ -27,10 +27,29 @@ namespace Elfisa.UI.Controls
private ThemeColors _theme;
private string _activeTabId;
private string _hoverTabId;
+ private bool _themeToggleHover;
+ private bool _isDarkTheme;
+ private Rectangle _themeToggleRect = Rectangle.Empty;
public event EventHandler TabSelected;
public event EventHandler TabClosed;
public event EventHandler TabCloseAllRequested;
+ public event EventHandler ThemeToggleClicked;
+
+ public bool IsDarkTheme
+ {
+ get => _isDarkTheme;
+ set
+ {
+ if (_isDarkTheme == value)
+ {
+ return;
+ }
+
+ _isDarkTheme = value;
+ Invalidate(_themeToggleRect);
+ }
+ }
private readonly ContextMenuStrip _contextMenu;
private readonly ToolStripMenuItem _closeTabMenuItem;
@@ -62,6 +81,13 @@ namespace Elfisa.UI.Controls
_contextMenu = new ContextMenuStrip();
_contextMenu.Items.Add(_closeTabMenuItem);
_contextMenu.Items.Add(_closeAllTabsMenuItem);
+ Resize += (_, __) => UpdateThemeToggleRect();
+ }
+
+ private void UpdateThemeToggleRect()
+ {
+ const int size = 26;
+ _themeToggleRect = new Rectangle(Width - size - 10, (Height - size) / 2, size, size);
}
public IReadOnlyList Tabs => _tabs;
@@ -140,6 +166,7 @@ namespace Elfisa.UI.Controls
public void ApplyTheme(ThemeColors theme)
{
_theme = theme;
+ UpdateThemeToggleRect();
Invalidate();
}
@@ -168,6 +195,8 @@ namespace Elfisa.UI.Controls
const int minTabWidth = 140;
const int gap = 8;
const int closeSize = 18;
+ const int toggleReserve = 44;
+ var maxTabRight = Math.Max(10, Width - toggleReserve);
using (var textFont = theme.FontSemibold14 ?? new Font("Segoe UI", 10f, FontStyle.Bold))
using (var closeFont = new Font("Segoe MDL2 Assets", 10f, FontStyle.Regular))
@@ -176,6 +205,11 @@ namespace Elfisa.UI.Controls
{
var textSize = TextRenderer.MeasureText(tab.Title, textFont);
var tabWidth = Math.Max(minTabWidth, textSize.Width + 48);
+ if (x + tabWidth > maxTabRight)
+ {
+ break;
+ }
+
var tabRect = new Rectangle(x, tabTop, tabWidth, tabHeight);
_hitAreas[tab.Id] = tabRect;
@@ -232,6 +266,41 @@ namespace Elfisa.UI.Controls
x += tabWidth + gap;
}
}
+
+ DrawThemeToggle(g, theme);
+ }
+
+ private void DrawThemeToggle(Graphics g, ThemeColors theme)
+ {
+ if (_themeToggleRect.IsEmpty)
+ {
+ UpdateThemeToggleRect();
+ }
+
+ var fill = _themeToggleHover
+ ? GraphicsExtensions.Blend(theme.SurfaceElevated, theme.AccentPrimarySoft, 0.45f)
+ : theme.Surface;
+ using (var fillBrush = new SolidBrush(fill))
+ {
+ GraphicsExtensions.FillRoundedRectangle(g, fillBrush, _themeToggleRect, theme.CornerRadiusSmall);
+ }
+
+ using (var pen = new Pen(_themeToggleHover ? theme.AccentPrimaryBorder : theme.Border, 1f))
+ {
+ GraphicsExtensions.DrawRoundedRectangle(g, pen, _themeToggleRect, theme.CornerRadiusSmall);
+ }
+
+ var glyph = _isDarkTheme ? "\uE706" : "\uE708";
+ using (var iconFont = new Font("Segoe MDL2 Assets", 11f))
+ {
+ TextRenderer.DrawText(
+ g,
+ glyph,
+ iconFont,
+ _themeToggleRect,
+ theme.TextSecondary,
+ TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.SingleLine);
+ }
}
protected override void OnPaintBackground(PaintEventArgs e)
@@ -245,6 +314,12 @@ namespace Elfisa.UI.Controls
return;
}
+ if (_themeToggleRect.Contains(e.Location))
+ {
+ ThemeToggleClicked?.Invoke(this, EventArgs.Empty);
+ return;
+ }
+
var tabId = HitTestClose(e.Location);
if (!string.IsNullOrEmpty(tabId))
{
@@ -270,13 +345,23 @@ namespace Elfisa.UI.Controls
Invalidate();
}
+ var toggleHover = _themeToggleRect.Contains(e.Location);
+ if (toggleHover != _themeToggleHover)
+ {
+ _themeToggleHover = toggleHover;
+ Invalidate(_themeToggleRect);
+ }
+
+ Cursor = toggleHover || !string.IsNullOrEmpty(hovered) ? Cursors.Hand : Cursors.Default;
base.OnMouseMove(e);
}
protected override void OnMouseLeave(EventArgs e)
{
_hoverTabId = null;
- Invalidate();
+ _themeToggleHover = false;
+ Invalidate(_themeToggleRect);
+ Cursor = Cursors.Default;
base.OnMouseLeave(e);
}
diff --git a/src/Elfisa.UI/Helpers/WindowChromeHelper.cs b/src/Elfisa.UI/Helpers/WindowChromeHelper.cs
new file mode 100644
index 0000000..6ca4fb9
--- /dev/null
+++ b/src/Elfisa.UI/Helpers/WindowChromeHelper.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using Elfisa.UI.Theming;
+
+namespace Elfisa.UI.Helpers
+{
+ public static class WindowChromeHelper
+ {
+ private const int DwmwaUseImmersiveDarkMode = 20;
+ private const int DwmwaUseImmersiveDarkModeLegacy = 19;
+ private const int DwmwaCaptionColor = 35;
+ private const int DwmwaTextColor = 36;
+
+ private const uint SwpNomove = 0x0002;
+ private const uint SwpNosize = 0x0001;
+ private const uint SwpNozorder = 0x0004;
+ private const uint SwpFramechanged = 0x0020;
+
+ [DllImport("dwmapi.dll", PreserveSig = true)]
+ private static extern int DwmSetWindowAttribute(IntPtr hwnd, int dwAttribute, ref int pvAttribute, int cbAttribute);
+
+ [DllImport("dwmapi.dll", PreserveSig = true)]
+ private static extern int DwmSetWindowAttribute(IntPtr hwnd, int dwAttribute, ref uint pvAttribute, int cbAttribute);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern bool SetWindowPos(
+ IntPtr hWnd,
+ IntPtr hWndInsertAfter,
+ int x,
+ int y,
+ int cx,
+ int cy,
+ uint uFlags);
+
+ public static void ApplyTitleBarTheme(Form form, ThemeColors theme)
+ {
+ if (form == null || theme == null || !form.IsHandleCreated)
+ {
+ return;
+ }
+
+ var isDark = theme.Mode == ThemeMode.Dark;
+ var useDarkMode = isDark ? 1 : 0;
+ TrySetInt(form.Handle, DwmwaUseImmersiveDarkMode, useDarkMode);
+ TrySetInt(form.Handle, DwmwaUseImmersiveDarkModeLegacy, useDarkMode);
+
+ var caption = isDark ? theme.TitleBarTop : Color.White;
+ var text = isDark ? Color.White : Color.Black;
+
+ TrySetUInt(form.Handle, DwmwaCaptionColor, ColorToDwm(caption));
+ TrySetUInt(form.Handle, DwmwaTextColor, ColorToDwm(text));
+
+ RefreshNonClientArea(form);
+ }
+
+ public static void ApplyThemedDialogHeader(Panel headerPanel, Label headerLabel, ThemeColors theme)
+ {
+ if (theme == null)
+ {
+ return;
+ }
+
+ var isDark = theme.Mode == ThemeMode.Dark;
+ if (headerPanel != null)
+ {
+ headerPanel.BackColor = isDark ? theme.TitleBarTop : Color.White;
+ }
+
+ if (headerLabel != null)
+ {
+ headerLabel.ForeColor = isDark ? Color.White : Color.Black;
+ }
+ }
+
+ public static void ApplyDialogChrome(Form form, Panel headerPanel = null, Label headerLabel = null)
+ {
+ if (form == null)
+ {
+ return;
+ }
+
+ var theme = ThemeManager.Colors;
+ ApplyThemedDialogHeader(headerPanel, headerLabel, theme);
+
+ if (form.IsHandleCreated)
+ {
+ ApplyTitleBarTheme(form, theme);
+ }
+ else
+ {
+ form.HandleCreated += (_, __) => ApplyTitleBarTheme(form, ThemeManager.Colors);
+ }
+ }
+
+ private static uint ColorToDwm(Color color)
+ {
+ return (uint)(color.R | (color.G << 8) | (color.B << 16));
+ }
+
+ private static void RefreshNonClientArea(Form form)
+ {
+ SetWindowPos(form.Handle, IntPtr.Zero, 0, 0, 0, 0, SwpNomove | SwpNosize | SwpNozorder | SwpFramechanged);
+ }
+
+ private static void TrySetInt(IntPtr handle, int attribute, int value)
+ {
+ DwmSetWindowAttribute(handle, attribute, ref value, sizeof(int));
+ }
+
+ private static void TrySetUInt(IntPtr handle, int attribute, uint value)
+ {
+ DwmSetWindowAttribute(handle, attribute, ref value, sizeof(uint));
+ }
+ }
+}
diff --git a/src/Elfisa.UI/Theming/DarkProductivityPalette.cs b/src/Elfisa.UI/Theming/DarkProductivityPalette.cs
new file mode 100644
index 0000000..915b18d
--- /dev/null
+++ b/src/Elfisa.UI/Theming/DarkProductivityPalette.cs
@@ -0,0 +1,83 @@
+using System.Drawing;
+
+namespace Elfisa.UI.Theming
+{
+ ///
+ /// Вариант H — Dark Productivity (ui-ux-pro-max: indigo accents, dark surfaces).
+ /// Макет: elfisa-design/desktop/variant-h-dark-productivity.html
+ ///
+ public sealed class DarkProductivityPalette : IThemePalette
+ {
+ public ThemeVariant Variant => ThemeVariant.DarkProductivity;
+
+ public ThemeColors Colors { get; } = new ThemeColors
+ {
+ Name = "Dark Productivity",
+ Mode = ThemeMode.Dark,
+ BackgroundPrimary = Color.FromArgb(17, 24, 39),
+ BackgroundSecondary = Color.FromArgb(15, 23, 42),
+ Surface = Color.FromArgb(31, 41, 55),
+ SurfaceElevated = Color.FromArgb(37, 47, 63),
+ Border = Color.FromArgb(55, 65, 81),
+ BorderSubtle = Color.FromArgb(45, 55, 72),
+ TextPrimary = Color.FromArgb(243, 244, 246),
+ TextSecondary = Color.FromArgb(209, 213, 219),
+ TextMuted = Color.FromArgb(156, 163, 175),
+ TextOnAccent = Color.White,
+ AccentPrimary = Color.FromArgb(79, 70, 229),
+ AccentPrimaryHover = Color.FromArgb(67, 56, 202),
+ AccentPrimaryPressed = Color.FromArgb(55, 48, 163),
+ AccentPrimarySoft = Color.FromArgb(30, 27, 75),
+ AccentPrimaryBorder = Color.FromArgb(129, 140, 248),
+ AccentSecondary = Color.FromArgb(55, 65, 81),
+ Success = Color.FromArgb(52, 211, 153),
+ SuccessSoft = Color.FromArgb(6, 78, 59),
+ Warning = Color.FromArgb(251, 191, 36),
+ WarningSoft = Color.FromArgb(66, 32, 6),
+ Danger = Color.FromArgb(239, 68, 68),
+ DangerSoft = Color.FromArgb(69, 26, 26),
+ Info = Color.FromArgb(129, 140, 248),
+ InfoSoft = Color.FromArgb(30, 27, 75),
+ TitleBarTop = Color.FromArgb(15, 23, 42),
+ TitleBarBottom = Color.FromArgb(30, 27, 75),
+ NavBackground = Color.FromArgb(26, 35, 50),
+ CartHeaderBackground = Color.FromArgb(37, 47, 63),
+ ToolbarBackground = Color.FromArgb(26, 35, 50),
+ SectionHeaderBackground = Color.FromArgb(55, 65, 81),
+ SidebarBackground = Color.FromArgb(17, 24, 39),
+ SidebarItem = Color.FromArgb(156, 163, 175),
+ SidebarItemHover = Color.FromArgb(37, 48, 68),
+ SidebarItemActive = Color.FromArgb(79, 70, 229),
+ SidebarItemActiveText = Color.White,
+ GridHeaderBackground = Color.FromArgb(55, 65, 81),
+ GridHeaderForeground = Color.FromArgb(243, 244, 246),
+ GridRowBackground = Color.FromArgb(17, 24, 39),
+ GridRowAlternate = Color.FromArgb(31, 41, 55),
+ GridRowHover = Color.FromArgb(37, 48, 68),
+ GridRowSelected = Color.FromArgb(79, 70, 229),
+ GridRowSelectedText = Color.White,
+ GridBorder = Color.FromArgb(45, 55, 72),
+ InputBackground = Color.FromArgb(17, 24, 39),
+ InputBorder = Color.FromArgb(55, 65, 81),
+ InputBorderFocus = Color.FromArgb(129, 140, 248),
+ OverlayScrim = Color.FromArgb(180, 0, 0, 0),
+ Shadow = Color.FromArgb(115, 0, 0, 0),
+ ScrollbarThumb = Color.FromArgb(75, 85, 99),
+ ScrollbarTrack = Color.FromArgb(26, 35, 50),
+ CornerRadiusSmall = 6,
+ CornerRadiusMedium = 8,
+ GridRowHeight = 36,
+ GridHeaderHeight = 34
+ };
+
+ public DarkProductivityPalette()
+ {
+ Colors.FontRegular12 = new Font("Segoe UI", 12f);
+ Colors.FontRegular13 = new Font("Segoe UI", 13f);
+ 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", 18f, FontStyle.Bold);
+ }
+ }
+}
diff --git a/src/Elfisa.UI/Theming/ThemeManager.cs b/src/Elfisa.UI/Theming/ThemeManager.cs
index 5d6e702..d9edcfb 100644
--- a/src/Elfisa.UI/Theming/ThemeManager.cs
+++ b/src/Elfisa.UI/Theming/ThemeManager.cs
@@ -13,7 +13,8 @@ namespace Elfisa.UI.Theming
{ ThemeVariant.LightOffice, new LightOfficePalette() },
{ ThemeVariant.DarkRider, new DarkRiderPalette() },
{ ThemeVariant.DarkFluent, new DarkFluentPalette() },
- { ThemeVariant.FluentWin11, new FluentWin11Palette() }
+ { ThemeVariant.FluentWin11, new FluentWin11Palette() },
+ { ThemeVariant.DarkProductivity, new DarkProductivityPalette() }
};
private static ThemeVariant _currentVariant = ThemeVariant.FluentWin11;
@@ -40,7 +41,17 @@ namespace Elfisa.UI.Theming
public static ThemeMode CurrentMode => Colors.Mode;
public static IReadOnlyList AvailableVariants { get; } =
- new[] { ThemeVariant.PharmaTrust, ThemeVariant.Apotheca, ThemeVariant.ClinicalPro, ThemeVariant.LightOffice, ThemeVariant.DarkRider, ThemeVariant.DarkFluent, ThemeVariant.FluentWin11 };
+ new[]
+ {
+ ThemeVariant.PharmaTrust,
+ ThemeVariant.Apotheca,
+ ThemeVariant.ClinicalPro,
+ ThemeVariant.LightOffice,
+ ThemeVariant.DarkRider,
+ ThemeVariant.DarkFluent,
+ ThemeVariant.FluentWin11,
+ ThemeVariant.DarkProductivity
+ };
public static string GetVariantDisplayName(ThemeVariant variant)
{
@@ -52,5 +63,14 @@ namespace Elfisa.UI.Theming
var next = ((int)CurrentVariant + 1) % AvailableVariants.Count;
CurrentVariant = AvailableVariants[next];
}
+
+ public static bool IsDarkProductivityActive => CurrentVariant == ThemeVariant.DarkProductivity;
+
+ public static void ToggleProductivityTheme()
+ {
+ CurrentVariant = IsDarkProductivityActive
+ ? ThemeVariant.PharmaTrust
+ : ThemeVariant.DarkProductivity;
+ }
}
}
diff --git a/src/Elfisa.UI/Theming/ThemeRenderer.cs b/src/Elfisa.UI/Theming/ThemeRenderer.cs
index ff02593..9d4f107 100644
--- a/src/Elfisa.UI/Theming/ThemeRenderer.cs
+++ b/src/Elfisa.UI/Theming/ThemeRenderer.cs
@@ -1,3 +1,4 @@
+using System.Drawing;
using System.Windows.Forms;
using Elfisa.UI.Controls;
@@ -13,6 +14,20 @@ namespace Elfisa.UI.Theming
private static void ApplyRecursive(Control control, ThemeColors colors)
{
+ if (control.Tag as string == "keep-bg")
+ {
+ ApplyChildren(control, colors);
+ return;
+ }
+
+ if (control.Tag as string == "chrome-child")
+ {
+ control.BackColor = colors.BackgroundPrimary;
+ control.ForeColor = colors.TextPrimary;
+ ApplyChildren(control, colors);
+ return;
+ }
+
if (control is Form form)
{
form.BackColor = colors.BackgroundPrimary;
@@ -90,6 +105,43 @@ namespace Elfisa.UI.Theming
{
uploadPanel.ApplyTheme(colors);
}
+ else if (control is DataGridView gridView)
+ {
+ gridView.EnableHeadersVisualStyles = false;
+ gridView.BackgroundColor = colors.GridRowBackground;
+ gridView.GridColor = colors.GridBorder;
+ gridView.ColumnHeadersDefaultCellStyle.BackColor = colors.GridHeaderBackground;
+ gridView.ColumnHeadersDefaultCellStyle.ForeColor = colors.GridHeaderForeground;
+ gridView.DefaultCellStyle.BackColor = colors.GridRowBackground;
+ gridView.DefaultCellStyle.ForeColor = colors.TextPrimary;
+ gridView.DefaultCellStyle.SelectionBackColor = colors.GridRowSelected;
+ gridView.DefaultCellStyle.SelectionForeColor = colors.GridRowSelectedText;
+ gridView.AlternatingRowsDefaultCellStyle.BackColor = colors.GridRowAlternate;
+ }
+ else if (control is CheckBox checkBox)
+ {
+ checkBox.ForeColor = colors.TextPrimary;
+ checkBox.BackColor = Color.Transparent;
+ }
+ else if (control is NumericUpDown numericUpDown)
+ {
+ numericUpDown.BackColor = colors.InputBackground;
+ numericUpDown.ForeColor = colors.TextPrimary;
+ }
+ else if (control is TextBox legacyTextBox)
+ {
+ legacyTextBox.BackColor = colors.InputBackground;
+ legacyTextBox.ForeColor = colors.TextPrimary;
+ }
+ else if (control is TabControl tabControl)
+ {
+ tabControl.BackColor = colors.BackgroundSecondary;
+ tabControl.ForeColor = colors.TextPrimary;
+ }
+ else if (control is SplitContainer splitContainer)
+ {
+ splitContainer.BackColor = colors.BackgroundSecondary;
+ }
else if (control.Tag as string == "toolbar")
{
control.BackColor = colors.ToolbarBackground;
@@ -100,11 +152,6 @@ namespace Elfisa.UI.Theming
}
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")
- {
- return;
- }
-
control.BackColor = colors.BackgroundSecondary;
control.ForeColor = colors.TextPrimary;
}
@@ -117,6 +164,11 @@ namespace Elfisa.UI.Theming
}
}
+ ApplyChildren(control, colors);
+ }
+
+ private static void ApplyChildren(Control control, ThemeColors colors)
+ {
foreach (Control child in control.Controls)
{
ApplyRecursive(child, colors);
diff --git a/src/Elfisa.UI/Theming/ThemeVariant.cs b/src/Elfisa.UI/Theming/ThemeVariant.cs
index 01ce683..c171870 100644
--- a/src/Elfisa.UI/Theming/ThemeVariant.cs
+++ b/src/Elfisa.UI/Theming/ThemeVariant.cs
@@ -8,7 +8,8 @@ namespace Elfisa.UI.Theming
LightOffice = 3,
DarkRider = 4,
DarkFluent = 5,
- FluentWin11 = 6
+ FluentWin11 = 6,
+ DarkProductivity = 7
}
public enum ThemeMode