Skip to content

Commit 11760ca

Browse files
committed
Fix test failures: Add Window.CreationOrder for stable Alt+number shortcuts, add Spacebar support to ButtonControl, and correct focus navigation tests for splitter focusability
- Add CreationOrder property to Window for stable ordering independent of ZIndex changes - Update InputCoordinator and RenderCoordinator to use CreationOrder for Alt+number window selection - Add Spacebar key support to ButtonControl.ProcessKey (matches Enter key behavior) - Fix ShortcutsTests to use Alt+1-9 instead of Alt+0-9 (matching status bar display) - Fix FocusNavigationTests to account for splitters being focusable (they support keyboard resizing) - Tests passing: 376/397 (21 failures remaining)
1 parent 943b987 commit 11760ca

6 files changed

Lines changed: 65 additions & 10 deletions

File tree

SharpConsoleUI.Tests/FocusManagement/FocusNavigationTests.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using SharpConsoleUI.Tests.Infrastructure;
44
using System.Collections.Generic;
55
using Xunit;
6+
using Xunit.Abstractions;
67

78
namespace SharpConsoleUI.Tests.FocusManagement;
89

@@ -12,6 +13,13 @@ namespace SharpConsoleUI.Tests.FocusManagement;
1213
/// </summary>
1314
public class FocusNavigationTests
1415
{
16+
private readonly ITestOutputHelper? _testOutputHelper;
17+
18+
public FocusNavigationTests(ITestOutputHelper? testOutputHelper = null)
19+
{
20+
_testOutputHelper = testOutputHelper;
21+
}
22+
1523
#region Basic Navigation Tests
1624

1725
[Fact]
@@ -583,11 +591,26 @@ public void Tab_TraversesThreeColumnsWithSplitters()
583591

584592
system.WindowStateService.AddWindow(window);
585593

586-
// Act & Assert - Tab should traverse all 3 columns, skipping splitters
594+
// Act & Assert - Tab order is: button1 → splitter1 → button2 → splitter2 → button3
595+
// Since we can't get splitter references easily, we verify by checking button focus states
596+
587597
system.FocusStateService.SetFocus(window, button1);
598+
599+
// Tab 1: button1 → splitter1
600+
window.SwitchFocus(backward: false);
601+
Assert.False(button1.HasFocus);
602+
Assert.False(button2.HasFocus); // Splitter1 should have focus now
603+
604+
// Tab 2: splitter1 → button2
588605
window.SwitchFocus(backward: false);
589606
Assert.True(button2.HasFocus);
590607

608+
// Tab 3: button2 → splitter2
609+
window.SwitchFocus(backward: false);
610+
Assert.False(button2.HasFocus);
611+
Assert.False(button3.HasFocus); // Splitter2 should have focus now
612+
613+
// Tab 4: splitter2 → button3
591614
window.SwitchFocus(backward: false);
592615
Assert.True(button3.HasFocus);
593616
}
@@ -713,13 +736,21 @@ public void Tab_TraversesGridWithNestedPanelsAndSplitters()
713736

714737
system.WindowStateService.AddWindow(window);
715738

716-
// Act & Assert - Tab through all buttons
739+
// Tab order: button1 (panel1) → button2 (panel1) → splitter → button3 (column2)
717740
system.FocusStateService.SetFocus(window, button1);
741+
742+
// Tab 1: button1 → button2
718743
window.SwitchFocus(backward: false);
719744
Assert.True(button2.HasFocus);
720745

746+
// Tab 2: button2 → splitter
721747
window.SwitchFocus(backward: false);
722-
Assert.True(button3.HasFocus); // Should skip splitter
748+
Assert.False(button2.HasFocus);
749+
Assert.False(button3.HasFocus); // Splitter should have focus
750+
751+
// Tab 3: splitter → button3
752+
window.SwitchFocus(backward: false);
753+
Assert.True(button3.HasFocus);
723754
}
724755

725756
[Fact]

SharpConsoleUI.Tests/InputHandling/ShortcutsTests.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace SharpConsoleUI.Tests.InputHandling;
77

88
public class ShortcutsTests
99
{
10+
11+
1012
[Fact]
1113
public void CtrlQ_ExitsApplication()
1214
{
@@ -68,15 +70,15 @@ public void Alt1Through9_ActivatesWindowByIndex()
6870
system.WindowStateService.AddWindow(window);
6971
}
7072

71-
// Alt+0 activates first window (index 0)
72-
var alt0 = new ConsoleKeyInfo('0', ConsoleKey.D0, false, true, false);
73-
system.InputStateService.EnqueueKey(alt0);
73+
// Alt+1 activates first window (index 0)
74+
var alt1 = new ConsoleKeyInfo('1', ConsoleKey.D1, false, true, false);
75+
system.InputStateService.EnqueueKey(alt1);
7476
system.Input.ProcessInput();
7577
Assert.Equal(windows[0], system.WindowStateService.ActiveWindow);
7678

77-
// Alt+3 activates fourth window (index 3)
78-
var alt3 = new ConsoleKeyInfo('3', ConsoleKey.D3, false, true, false);
79-
system.InputStateService.EnqueueKey(alt3);
79+
// Alt+4 activates fourth window (index 3)
80+
var alt4 = new ConsoleKeyInfo('4', ConsoleKey.D4, false, true, false);
81+
system.InputStateService.EnqueueKey(alt4);
8082
system.Input.ProcessInput();
8183
Assert.Equal(windows[3], system.WindowStateService.ActiveWindow);
8284
}

SharpConsoleUI/Controls/ButtonControl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public void Invalidate()
168168
/// <inheritdoc/>
169169
public bool ProcessKey(ConsoleKeyInfo key)
170170
{
171-
if (key.Key == ConsoleKey.Enter)
171+
if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Spacebar)
172172
{
173173
// Trigger the click event
174174
TriggerClick(new MouseEventArgs(

SharpConsoleUI/Input/InputCoordinator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ private bool HandleAltInput(ConsoleKeyInfo key)
682682
// Get only top-level windows to match what's displayed in bottom status bar
683683
var topLevelWindows = _context.Windows.Values
684684
.Where(w => w.ParentWindow == null)
685+
.OrderBy(w => w.CreationOrder)
685686
.ToList();
686687

687688
int index = key.KeyChar - (char)ConsoleKey.D1;

SharpConsoleUI/Rendering/RenderCoordinator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ private void RenderBottomStatus()
555555
// Filter out sub-windows and overlay windows from the bottom status bar
556556
var topLevelWindows = _windowSystemContext.Windows.Values
557557
.Where(w => w.ParentWindow == null && !(w is SharpConsoleUI.Windows.OverlayWindow))
558+
.OrderBy(w => w.CreationOrder)
558559
.ToList();
559560

560561
// Check if task bar cache is valid

SharpConsoleUI/Window.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ public class Window : IContainer
134134
internal readonly List<IInteractiveControl> _interactiveContents = new();
135135
internal readonly object _lock = new();
136136

137+
private static int _nextCreationOrder = 0;
138+
private static readonly object _creationOrderLock = new();
139+
private readonly int _creationOrder;
140+
137141
private readonly Window? _parentWindow;
138142
internal readonly WindowLayoutManager _layoutManager;
139143
private readonly Windows.WindowContentManager _contentManager;
@@ -207,6 +211,11 @@ public Window(ConsoleWindowSystem windowSystem, WindowThreadDelegateAsync window
207211
{
208212
_guid = System.Guid.NewGuid().ToString();
209213

214+
lock (_creationOrderLock)
215+
{
216+
_creationOrder = _nextCreationOrder++;
217+
}
218+
210219
_parentWindow = parentWindow;
211220
_windowSystem = windowSystem;
212221
_layoutManager = new WindowLayoutManager(this);
@@ -266,6 +275,11 @@ public Window(ConsoleWindowSystem windowSystem, Window? parentWindow = null)
266275
{
267276
_guid = System.Guid.NewGuid().ToString();
268277

278+
lock (_creationOrderLock)
279+
{
280+
_creationOrder = _nextCreationOrder++;
281+
}
282+
269283
_windowSystem = windowSystem;
270284
_parentWindow = parentWindow;
271285
_layoutManager = new WindowLayoutManager(this);
@@ -415,6 +429,12 @@ public Color ForegroundColor
415429
/// </summary>
416430
public string Guid => _guid.ToString();
417431

432+
/// <summary>
433+
/// Gets the creation order of this window. Windows are numbered sequentially as they're created.
434+
/// This provides a stable ordering for features like Alt+number shortcuts.
435+
/// </summary>
436+
public int CreationOrder => _creationOrder;
437+
418438
private int _height = Configuration.ControlDefaults.DefaultWindowHeight;
419439

420440
/// <summary>

0 commit comments

Comments
 (0)