using System; using System.Collections.Generic; 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; using Электронная_Фармация.Classes; using Электронная_Фармация.Properties; using Электронная_Фармация.Forms; using Электронная_Фармация.HelpForms; using Электронная_Фармация.UserControls; namespace Электронная_Фармация { public partial class ElectroPharmacy { private readonly Dictionary _documentContents = new Dictionary(); private readonly ThemeService _themeService = new ThemeService(); private ToastService _toastService; private LoadingOverlay _loadingOverlay; private ModernAppHeader _appHeader; private ModernDocumentTabStrip _documentTabStrip; private Panel _documentHost; private Panel _statusFooter; private Label _lblCurrentUser; private ModernButton _btnSettings; private ContextMenuStrip _directoryMenu; private ContextMenuStrip _documentTabMenu; private ToolStripMenuItem _documentCloseTabMenuItem; private ToolStripMenuItem _documentCloseAllTabsMenuItem; partial void InitializeModernShell() { Font = new Font("Segoe UI", 12f); AutoScaleMode = AutoScaleMode.Dpi; KeyPreview = true; MinimumSize = new Size(900, 620); bindControl.Visible = false; tabContent.Visible = false; _toastService = new ToastService(this); AppServices.Initialize(_toastService); _loadingOverlay = new LoadingOverlay { Name = "loadingOverlay", Dock = DockStyle.Fill }; _appHeader = new ModernAppHeader { Name = "appHeader", Dock = DockStyle.Top }; _appHeader.SetItems(new[] { new SidebarItem { Key = "price", Text = "ПРАЙС-ЛИСТ", IconGlyph = "\uE8FD" }, new SidebarItem { Key = "orders", Text = "ЗАКАЗЫ", IconGlyph = "\uE719" }, new SidebarItem { Key = "invoices", Text = "НАКЛАДНЫЕ", IconGlyph = "\uE8A5" }, new SidebarItem { Key = "upload", Text = "ЗАГРУЗИТЬ", IconGlyph = "\uE896" }, new SidebarItem { Key = "send", Text = "ОТПРАВИТЬ", IconGlyph = "\uE725" }, new SidebarItem { Key = "refusals", Text = "ОТКАЗЫ", IconGlyph = "\uE711" }, new SidebarItem { Key = "directory", Text = "СПРАВОЧНИК", IconGlyph = "\uE8FD" }, new SidebarItem { Key = "auth", Text = "АВТОРИЗАЦИЯ", IconGlyph = "\uE192" } }); _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 = 28, Tag = "status-footer", Padding = new Padding(10, 0, 4, 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.MiddleLeft, AutoEllipsis = true, Tag = "muted", Padding = new Padding(0, 0, 4, 0) }; _btnSettings = new ModernButton { Name = "btnSettings", Dock = DockStyle.Right, Margin = new Padding(0, 2, 2, 2), Text = "Настройки", CompactIconMode = false, AllowTextEllipsis = false, MinimumSize = new Size(88, 22), Size = new Size(88, 22), Padding = new Padding(6, 0, 6, 0), Font = new Font("Segoe UI Semibold", 8.5f, FontStyle.Bold), TabIndex = 0 }; _btnSettings.Click += (_, __) => OpenSettingsDialog(); // Сначала Fill (пользователь), потом Right (кнопка) — кнопка справа, текст не перекрывается. _statusFooter.Controls.Add(_lblCurrentUser); _statusFooter.Controls.Add(_btnSettings); // Клик по строке "аптека" открывает выбор грузополучателя без перезапуска приложения. // Т.к. метка у нас одна (Dock=Fill), обработчик навешиваем на весь Label. _lblCurrentUser.Cursor = Cursors.Hand; _lblCurrentUser.Click += (_, __) => SwitchConsignee(); Controls.Add(_loadingOverlay); Controls.Add(_documentHost); Controls.Add(_statusFooter); Controls.Add(_documentTabStrip); Controls.Add(_appHeader); _directoryMenu = new ContextMenuStrip(); var contractorsItem = new ToolStripMenuItem("Поставщики"); contractorsItem.Click += (_, __) => tsItemContractors_Click(_, __); var consigneesItem = new ToolStripMenuItem("Грузополучатели"); consigneesItem.Click += (_, __) => tsConsignee_Click(_, __); var switchConsigneeItem = new ToolStripMenuItem("Сменить грузополучателя"); switchConsigneeItem.Click += (_, __) => SwitchConsignee(); var invoicesItem = new ToolStripMenuItem("Накладные"); invoicesItem.Click += (_, __) => tsItemInvoices_Click(_, __); var debugItem = new ToolStripMenuItem("Отладка"); debugItem.Click += (_, __) => tsDebug_Click(_, __); _directoryMenu.Items.AddRange(new ToolStripItem[] { contractorsItem, consigneesItem, switchConsigneeItem, invoicesItem, new ToolStripSeparator(), debugItem }); _appHeader.ItemSelected += OnAppHeaderItemSelected; _documentTabStrip.ThemeToggleClicked += OnThemeToggleClicked; _documentTabStrip.TabSelected += OnDocumentTabSelected; _documentTabStrip.TabClosed += OnDocumentTabClosed; _documentTabStrip.TabCloseAllRequested += OnDocumentTabCloseAll; _documentCloseTabMenuItem = new ToolStripMenuItem("Закрыть вкладку"); _documentCloseTabMenuItem.Click += (_, __) => { var tabId = _documentTabStrip.ActiveTabId; if (!string.IsNullOrEmpty(tabId)) { CloseDocumentTab(tabId); } }; _documentCloseAllTabsMenuItem = new ToolStripMenuItem("Закрыть все вкладки"); _documentCloseAllTabsMenuItem.Click += (_, __) => ClearDocumentTabs(); _documentTabMenu = new ContextMenuStrip(); _documentTabMenu.Items.Add(_documentCloseTabMenuItem); _documentTabMenu.Items.Add(_documentCloseAllTabsMenuItem); 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 = Settings.Default.UseDarkTheme ? ThemeVariant.DarkProductivity : ThemeVariant.PharmaTrust; _themeService.Initialize(this); UpdateThemeToggleState(); UiThemeHelper.ApplyToControlTree(this); WindowChromeHelper.ApplyTitleBarTheme(this, ThemeManager.Colors); RefreshCurrentUserStatus(); } private void RefreshCurrentUserStatus() { if (_lblCurrentUser == null) { return; } var login = Settings.Default.stringLogin?.Trim(); var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken); var pharmacy = ConsigneeName?.Trim(); if (hasToken && (string.IsNullOrWhiteSpace(pharmacy) || pharmacy == "Прайс-Лист")) { pharmacy = ConsigneeHelper.GetDefaultConsigneeName(); if (!string.IsNullOrWhiteSpace(pharmacy) && (string.IsNullOrWhiteSpace(ConsigneeName) || ConsigneeName == "Прайс-Лист")) { ConsigneeName = pharmacy; } } string userPart; if (!string.IsNullOrWhiteSpace(login) && hasToken) { userPart = $"Пользователь: {login}"; } else if (!string.IsNullOrWhiteSpace(login)) { userPart = $"Пользователь: {login} (не авторизован)"; } else { userPart = "Пользователь не авторизован"; } if (hasToken && !string.IsNullOrWhiteSpace(pharmacy) && pharmacy != "Прайс-Лист") { _lblCurrentUser.Text = $"{userPart} | Аптека: {pharmacy}"; } else { _lblCurrentUser.Text = $"{userPart} | Аптека: не выбрана"; } } private void OnAppHeaderItemSelected(object sender, string key) { switch (key) { case "price": loadDefaultPriceList(); break; case "orders": OpenOrdersTab(); break; case "invoices": OpenInvoicesTab(); break; case "upload": tsBtnDownloadData_Click(this, EventArgs.Empty); _appHeader.SetActive("upload"); break; case "send": tsBtnSentData_Click(this, EventArgs.Empty); _appHeader.SetActive("send"); break; case "refusals": OpenRefusalsTab(); break; case "directory": _directoryMenu.Show(_appHeader, new Point(_appHeader.Width - 220, _appHeader.Height - 4)); break; case "auth": tsBtnAuth_Click(this, EventArgs.Empty); break; } } private void OnDocumentTabSelected(object sender, string tabId) { ShowDocumentTab(tabId); var tab = _documentTabStrip.Tabs.FirstOrDefault(item => item.Id == tabId); if (tab != null) { _appHeader.SetActive(tab.NavKey); } } private string OpenDocumentTab(string title, string navKey, Control content, bool allowDuplicate) { if (!allowDuplicate) { var existing = _documentTabStrip.FindByNavKey(navKey); if (existing != null) { _documentTabStrip.SelectTab(existing.Id); ShowDocumentTab(existing.Id); _appHeader.SetActive(navKey); return existing.Id; } } var tabId = _documentTabStrip.AddTab(title, navKey); content.Dock = DockStyle.Fill; content.Tag = "chrome-child"; content.Visible = false; _documentContents[tabId] = content; _documentHost.Controls.Add(content); UiThemeHelper.ApplyToControlTree(content); WireDocumentContextMenu(content); _documentTabStrip.SelectTab(tabId); ShowDocumentTab(tabId); _appHeader.SetActive(navKey); return tabId; } private void ShowDocumentTab(string tabId) { foreach (var pair in _documentContents) { pair.Value.Visible = pair.Key == tabId; } } private void OnDocumentTabClosed(object sender, string tabId) { CloseDocumentTab(tabId); } private void OnDocumentTabCloseAll(object sender, EventArgs e) { ClearDocumentTabs(); } private void WireDocumentContextMenu(Control control) { control.MouseUp -= OnDocumentContentMouseUp; control.MouseUp += OnDocumentContentMouseUp; foreach (Control child in control.Controls) { WireDocumentContextMenu(child); } } private void OnDocumentContentMouseUp(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Right || _documentTabStrip.Tabs.Count == 0) { return; } if (sender is Control clicked && HasDedicatedContextMenu(clicked)) { return; } _documentCloseTabMenuItem.Enabled = !string.IsNullOrEmpty(_documentTabStrip.ActiveTabId); _documentCloseAllTabsMenuItem.Enabled = _documentTabStrip.Tabs.Count > 0; var screenPoint = (sender as Control)?.PointToScreen(e.Location) ?? Cursor.Position; _documentTabMenu.Show(screenPoint); } private static bool HasDedicatedContextMenu(Control control) { for (var current = control; current != null; current = current.Parent) { if (current.ContextMenuStrip != null) { return true; } } return false; } private void CloseDocumentTab(string tabId) { if (_documentContents.TryGetValue(tabId, out var control)) { _documentHost.Controls.Remove(control); control.Dispose(); _documentContents.Remove(tabId); } _documentTabStrip.RemoveTab(tabId); } private void ClearDocumentTabs() { foreach (var control in _documentContents.Values) { _documentHost.Controls.Remove(control); control.Dispose(); } _documentContents.Clear(); _documentTabStrip.ClearTabs(); } private void OpenOrdersTab() { var orders = new FOrders { Dock = DockStyle.Fill, TopLevel = false, TopMost = true }; OpenDocumentTab("Заказы", "orders", orders, allowDuplicate: true); orders.Show(); } public void OpenInvoicesTab(string sourceOrderId = null) { var existing = _documentTabStrip.FindByNavKey("invoices"); if (existing != null && _documentContents.TryGetValue(existing.Id, out var existingControl) && existingControl is UCInvoices existingInvoices) { existingInvoices.SetSourceOrderFilter(sourceOrderId); _documentTabStrip.SelectTab(existing.Id); ShowDocumentTab(existing.Id); _appHeader.SetActive("invoices"); return; } var invoices = new UCInvoices(sourceOrderId); OpenDocumentTab("Накладные", "invoices", invoices, allowDuplicate: false); } private void OpenRefusalsTab() { var refusals = new UCRefusals(); OpenDocumentTab("Отказы", "refusals", refusals, allowDuplicate: false); } partial void ShowShellLoading(string message) { _loadingOverlay?.Show(message); _loadingOverlay?.BringToFront(); } partial void HideShellLoading() { _loadingOverlay?.HideOverlay(); } private bool HasDocumentTabs() { return _documentContents.Count > 0; } partial void RefreshOpenPriceLists() { foreach (var control in _documentContents.Values.OfType()) { control.RefreshFromDatabase(); } if (!_documentContents.Values.OfType().Any()) { loadDefaultPriceList(); } } } }