diff --git a/UI/MainForm.Mode.cs b/UI/MainForm.Mode.cs index 702af2b..2a69e09 100644 --- a/UI/MainForm.Mode.cs +++ b/UI/MainForm.Mode.cs @@ -79,10 +79,43 @@ private void InitializeModeChrome() } }; + _modeLanguageSelector = new ComboBox + { + DropDownStyle = ComboBoxStyle.DropDownList, + Width = ScaleX(110), + Margin = new Padding(0, 4, 8, 4), + AccessibleName = "Quick language selector" + }; + _modeLanguageSelector.SelectedIndexChanged += (s, e) => + { + if (_syncingModeLanguageSelector || _modeLanguageSelector.SelectedItem is not LangItem selected) return; + + for (int i = 0; i < _languageSelector.Items.Count; i++) + { + if (_languageSelector.Items[i] is LangItem item && + item.Code.Equals(selected.Code, StringComparison.OrdinalIgnoreCase)) + { + _languageSelector.SelectedIndex = i; + break; + } + } + }; + + var rightActions = new FlowLayoutPanel + { + AutoSize = true, + WrapContents = false, + FlowDirection = FlowDirection.LeftToRight, + Margin = new Padding(0), + Padding = new Padding(0) + }; + rightActions.Controls.Add(_modeLanguageSelector); + rightActions.Controls.Add(_modeSwitchButton); + leftStack.Controls.Add(_modeStatusLabel, 0, 0); leftStack.Controls.Add(_modeHintLabel, 0, 1); layout.Controls.Add(leftStack, 0, 0); - layout.Controls.Add(_modeSwitchButton, 1, 0); + layout.Controls.Add(rightActions, 1, 0); _modeChrome.Controls.Add(layout); Controls.Add(_modeChrome); @@ -94,10 +127,30 @@ private void EnsureModeChromeDockOrder() { if (_modeChrome == null) return; - _modeChrome.SendToBack(); + // Keep the mode chrome above the main content and reserve layout space. + _modeChrome.BringToFront(); + Controls.SetChildIndex(_modeChrome, 0); + ApplyModeChromeLayoutCompensation(); PerformLayout(); } + private void ApplyModeChromeLayoutCompensation() + { + if (_mainVerticalSplitHost == null) return; + + int topOffset = (_modeChrome != null && _modeChrome.Visible) + ? _modeChrome.Bottom + : 0; + + _mainVerticalSplitHost.Dock = DockStyle.None; + _mainVerticalSplitHost.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right; + _mainVerticalSplitHost.Location = new Point(0, topOffset); + _mainVerticalSplitHost.Size = new Size( + Math.Max(200, ClientSize.Width), + Math.Max(160, ClientSize.Height - topOffset)); + _mainVerticalSplitHost.PerformLayout(); + } + private void EnsureModeSelectorOverlay() { if (_modeSelectorOverlay != null) return; @@ -140,6 +193,7 @@ private void ShowModeSelector() _modeSelectorOverlay.Visible = true; _modeSelectorOverlay.BringToFront(); if (_modeChrome != null) _modeChrome.Visible = false; + ApplyModeChromeLayoutCompensation(); _modeSelectorOverlay.Focus(); } @@ -154,6 +208,7 @@ private void HideModeSelector() _modeChrome.Visible = true; EnsureModeChromeDockOrder(); } + ApplyModeChromeLayoutCompensation(); } private void ToggleUiMode() @@ -270,6 +325,7 @@ private void ApplyUiMode(UiMode mode, bool persist) } HideModeSelector(); + ApplyModeChromeLayoutCompensation(); UpdateModePresentation(); BeginInvoke(new Action(() => @@ -326,6 +382,8 @@ private void UpdateModePresentation() _modeSwitchButton.Text = TMode("Mode_Switch_Button", "Switch mode"); } + SyncModeLanguageSelector(); + if (_modeChrome != null) { bool dark = _darkModeToggle.Checked; @@ -343,6 +401,11 @@ private void UpdateModePresentation() _modeSwitchButton.BackColor = dark ? Color.FromArgb(54, 62, 72) : Color.White; _modeSwitchButton.ForeColor = dark ? Color.WhiteSmoke : Color.FromArgb(28, 28, 28); } + if (_modeLanguageSelector != null) + { + _modeLanguageSelector.BackColor = dark ? Color.FromArgb(54, 62, 72) : Color.White; + _modeLanguageSelector.ForeColor = dark ? Color.WhiteSmoke : Color.FromArgb(28, 28, 28); + } } _modeSelectorOverlay?.UpdatePresentation(_translationService, _darkModeToggle.Checked); @@ -358,6 +421,47 @@ private void SetVisible(bool visible, params Control[] controls) } } + private void SyncModeLanguageSelector() + { + if (_modeLanguageSelector == null) return; + + _syncingModeLanguageSelector = true; + try + { + _modeLanguageSelector.Items.Clear(); + foreach (var item in _languageSelector.Items) + { + if (item is LangItem lang) + { + _modeLanguageSelector.Items.Add(lang); + } + } + + if (_languageSelector.SelectedItem is LangItem selected) + { + for (int i = 0; i < _modeLanguageSelector.Items.Count; i++) + { + if (_modeLanguageSelector.Items[i] is LangItem lang && + lang.Code.Equals(selected.Code, StringComparison.OrdinalIgnoreCase)) + { + _modeLanguageSelector.SelectedIndex = i; + break; + } + } + } + else if (_modeLanguageSelector.Items.Count > 0) + { + _modeLanguageSelector.SelectedIndex = 0; + } + + _modeLanguageSelector.Enabled = _modeLanguageSelector.Items.Count > 0; + } + finally + { + _syncingModeLanguageSelector = false; + } + } + private void EnsureProfessionalCoreControlsVisible() { EnsureControlSectionExpanded("_topControls"); @@ -371,6 +475,18 @@ private void EnsureControlSectionExpanded(string controlName) if (control == null) return; control.Visible = true; + if (control is FlowLayoutPanel flow) + { + flow.AutoSize = true; + flow.AutoSizeMode = AutoSizeMode.GrowAndShrink; + flow.WrapContents = true; + flow.AutoScroll = true; + foreach (Control child in flow.Controls) + { + child.Visible = true; + } + flow.PerformLayout(); + } var current = control.Parent; while (current != null) diff --git a/UI/MainForm.Patient.cs b/UI/MainForm.Patient.cs index 990030c..a43dc2f 100644 --- a/UI/MainForm.Patient.cs +++ b/UI/MainForm.Patient.cs @@ -29,6 +29,7 @@ private void EnsurePatientShell() _patientShell.RunCheckRequested += () => CheckButton_Click(this, EventArgs.Empty); _patientShell.StepChanged += OnPatientStepChanged; _patientShell.RestartRequested += RestartPatientWizard; + _patientShell.ModeSwitchRequested += OnPatientModeSwitchRequested; _mainVerticalSplitHost.Panel1.Controls.Add(_patientShell); _patientShell.BringToFront(); @@ -331,5 +332,16 @@ private void RestartPatientWizard() _settingsService.Save(); } } + + private void OnPatientModeSwitchRequested() + { + if (_settingsService?.Settings.UiMode == null) + { + ShowModeSelector(); + return; + } + + ToggleUiMode(); + } } } \ No newline at end of file diff --git a/UI/MainForm.cs b/UI/MainForm.cs index f413d61..0fc3a32 100644 --- a/UI/MainForm.cs +++ b/UI/MainForm.cs @@ -103,7 +103,9 @@ public GroupHeader(string canonicalCategory, string display) private Panel? _modeChrome; private Label? _modeStatusLabel; private Label? _modeHintLabel; + private ComboBox? _modeLanguageSelector; private Button? _modeSwitchButton; + private bool _syncingModeLanguageSelector; private UiMode? _activeUiMode; // UI: collapse toggle private Button? _collapseBtn; @@ -227,6 +229,7 @@ public MainForm() vs.SplitterDistance = targetV; } ApplyProfessionalShellLayout(); + ApplyModeChromeLayoutCompensation(); } catch { } _filterBox.Focus(); @@ -251,6 +254,7 @@ public MainForm() vs.SplitterDistance = target; } ApplyProfessionalShellLayout(); + ApplyModeChromeLayoutCompensation(); } catch { } }; @@ -507,10 +511,12 @@ private void InitializeLayout() { Dock = DockStyle.Top, AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, WrapContents = true, AutoScroll = true, FlowDirection = FlowDirection.LeftToRight, Padding = new Padding(3), + Margin = new Padding(0), Name = "_topControls" }; var vitalsRow = new FlowLayoutPanel { Dock = DockStyle.Fill, AutoSize = true, WrapContents = true, Name = "_vitalsRow" }; @@ -826,6 +832,12 @@ private void InitializeLayout() _lblPerf.AutoSize = true; _lblPerf.Padding = new Padding(10,6,0,0); _lblPerf.Text = ""; _lblPerf.AccessibleName = "Performance timing"; topControls.Controls.Add(_lblPerf); // Top bar (models etc.) rightPanel.Controls.Add(topControls, 0, 0); + topControls.Resize += (s, e) => + { + // Keep wrapping stable across DPI/scaling changes. + int maxWidth = Math.Max(240, rightPanel.ClientSize.Width - SystemInformation.VerticalScrollBarWidth - 6); + topControls.MaximumSize = new Size(maxWidth, 0); + }; // Vitals row vitalsRow.Controls.Add(_lblVitals); vitalsRow.Controls.Add(_lblTemp); diff --git a/UI/PatientShell.cs b/UI/PatientShell.cs index 9698695..ab3c1fd 100644 --- a/UI/PatientShell.cs +++ b/UI/PatientShell.cs @@ -79,6 +79,7 @@ public SymptomItem(string canonical, string display) private readonly Button _backButton = new(); private readonly Button _nextButton = new(); private readonly Button _restartButton = new(); + private readonly Button _switchModeButton = new(); private TranslationService? _translationService; private bool _darkMode; @@ -95,6 +96,7 @@ public SymptomItem(string canonical, string display) public event Action? VitalsChanged; public event Action? RunCheckRequested; public event Action? RestartRequested; + public event Action? ModeSwitchRequested; public int CurrentStep => _currentStep; public FlowLayoutPanel ResultCardsHost => _resultsCards; @@ -134,6 +136,7 @@ public void UpdatePresentation(TranslationService? translationService, bool dark _backButton.Text = T("Patient_Back", "Back"); _restartButton.Text = T("Patient_Restart", "Start over"); + _switchModeButton.Text = T("Mode_Switch_Button", "Switch mode"); _nextButton.Text = _currentStep == 3 ? T("Patient_RunCheck", "Show results") : T("Patient_Next", "Next"); @@ -330,9 +333,16 @@ private void BuildLayout() _restartButton.Padding = new Padding(12, 7, 12, 7); _restartButton.Click += (s, e) => RestartRequested?.Invoke(); + _switchModeButton.AutoSize = true; + _switchModeButton.FlatStyle = FlatStyle.Flat; + _switchModeButton.Padding = new Padding(12, 7, 12, 7); + _switchModeButton.AccessibleName = "Switch mode"; + _switchModeButton.Click += (s, e) => ModeSwitchRequested?.Invoke(); + _navigationBar.Controls.Add(_nextButton); _navigationBar.Controls.Add(_backButton); _navigationBar.Controls.Add(_restartButton); + _navigationBar.Controls.Add(_switchModeButton); _root.Controls.Add(_header, 0, 0); _root.Controls.Add(_contentHost, 0, 1); @@ -718,6 +728,7 @@ private void ApplyTheme() StyleNavigationButton(_backButton, secondary: true); StyleNavigationButton(_restartButton, secondary: true); + StyleNavigationButton(_switchModeButton, secondary: true); StyleNavigationButton(_nextButton, secondary: false); }