• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orion-ecs / keen-eye / 20871273650

10 Jan 2026 02:23AM UTC coverage: 86.895% (-0.07%) from 86.962%
20871273650

push

github

tyevco
fix(ui): Fix UI widget tests for arrows, TabView visibility, and TextInput

- Use Unicode arrows (▼/▶) instead of ASCII (v/>) for accordion and tree view expand/collapse indicators
- Set visibility, UITabPanel, and UIHiddenTag on both scrollView and scrollContent in CreateTabView
- Fix UITextInput test assertions to expect ShowingPlaceholder=false when no placeholder text is provided

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

9264 of 12568 branches covered (73.71%)

Branch coverage included in aggregate %.

15 of 15 new or added lines in 6 files covered. (100.0%)

321 existing lines in 16 files now uncovered.

159741 of 181925 relevant lines covered (87.81%)

1.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/src/KeenEyes.UI/Systems/UISplitterSystem.cs
1
using KeenEyes.UI.Abstractions;
2

3
namespace KeenEyes.UI;
4

5
/// <summary>
6
/// System that handles splitter drag operations to resize panes.
7
/// </summary>
8
/// <remarks>
9
/// <para>
10
/// This system listens for drag events on splitter handles (<see cref="UISplitterHandle"/>)
11
/// and updates the parent splitter's <see cref="UISplitter.SplitRatio"/> accordingly.
12
/// </para>
13
/// <para>
14
/// The system respects minimum pane sizes defined in the splitter component and
15
/// fires <see cref="UISplitterChangedEvent"/> when the ratio changes.
16
/// </para>
17
/// <para>
18
/// This system should run in <see cref="SystemPhase.EarlyUpdate"/> phase after
19
/// <see cref="UIWindowSystem"/>.
20
/// </para>
21
/// </remarks>
22
public sealed class UISplitterSystem : SystemBase
23
{
24
    private EventSubscription? dragSubscription;
25

26
    /// <inheritdoc />
27
    protected override void OnInitialize()
28
    {
29
        // Subscribe to drag events for splitter handle movement
30
        dragSubscription = World.Subscribe<UIDragEvent>(OnDrag);
1✔
31
    }
1✔
32

33
    /// <inheritdoc />
34
    protected override void Dispose(bool disposing)
35
    {
36
        if (disposing)
1✔
37
        {
38
            dragSubscription?.Dispose();
1✔
39
            dragSubscription = null;
1✔
40
        }
41

42
        base.Dispose(disposing);
1✔
43
    }
1✔
44

45
    /// <inheritdoc />
46
    public override void Update(float deltaTime)
47
    {
48
        // Splitter behavior is event-driven, no per-frame work needed
49
    }
1✔
50

51
    private void OnDrag(UIDragEvent e)
52
    {
53
        // Check if dragging a splitter handle
54
        if (!World.Has<UISplitterHandle>(e.Element))
1✔
55
        {
56
            return;
1✔
57
        }
58

59
        HandleSplitterDrag(e);
1✔
60
    }
1✔
61

62
    private void HandleSplitterDrag(UIDragEvent e)
63
    {
64
        ref readonly var handle = ref World.Get<UISplitterHandle>(e.Element);
1✔
65
        var splitterContainer = handle.SplitterContainer;
1✔
66

67
        if (!World.IsAlive(splitterContainer) || !World.Has<UISplitter>(splitterContainer))
1✔
68
        {
69
            return;
1✔
70
        }
71

72
        ref var splitter = ref World.Get<UISplitter>(splitterContainer);
1✔
73

74
        // Get the splitter's computed bounds to calculate ratio
75
        if (!World.Has<UIRect>(splitterContainer))
1✔
76
        {
77
            return;
1✔
78
        }
79

80
        ref readonly var rect = ref World.Get<UIRect>(splitterContainer);
1✔
81
        var bounds = rect.ComputedBounds;
1✔
82

83
        // Calculate the new ratio based on drag delta
84
        float oldRatio = splitter.SplitRatio;
1✔
85
        float newRatio;
86

87
        if (splitter.Orientation == LayoutDirection.Horizontal)
1✔
88
        {
89
            // Horizontal orientation: panes are side by side, drag changes X
90
            float totalWidth = bounds.Width - splitter.HandleSize;
1✔
91
            if (totalWidth <= 0)
1✔
92
            {
93
                return;
1✔
94
            }
95

96
            float firstPaneWidth = totalWidth * splitter.SplitRatio;
1✔
97
            firstPaneWidth += e.Delta.X;
1✔
98

99
            // Clamp to minimum sizes
100
            firstPaneWidth = Math.Max(firstPaneWidth, splitter.MinFirstPane);
1✔
101
            firstPaneWidth = Math.Min(firstPaneWidth, totalWidth - splitter.MinSecondPane);
1✔
102

103
            newRatio = firstPaneWidth / totalWidth;
1✔
104
        }
105
        else
106
        {
107
            // Vertical orientation: panes are stacked, drag changes Y
108
            float totalHeight = bounds.Height - splitter.HandleSize;
1✔
109
            if (totalHeight <= 0)
1✔
110
            {
111
                return;
1✔
112
            }
113

114
            float firstPaneHeight = totalHeight * splitter.SplitRatio;
1✔
115
            firstPaneHeight += e.Delta.Y;
1✔
116

117
            // Clamp to minimum sizes
118
            firstPaneHeight = Math.Max(firstPaneHeight, splitter.MinFirstPane);
1✔
119
            firstPaneHeight = Math.Min(firstPaneHeight, totalHeight - splitter.MinSecondPane);
1✔
120

121
            newRatio = firstPaneHeight / totalHeight;
1✔
122
        }
123

124
        // Clamp ratio to valid range
125
        newRatio = Math.Clamp(newRatio, 0.0f, 1.0f);
1✔
126

127
        // Only update if changed significantly (larger epsilon reduces jitter)
128
        if (Math.Abs(newRatio - oldRatio) > 0.001f)
1✔
129
        {
130
            splitter.SplitRatio = newRatio;
1✔
131

132
            // Update the first pane's size to reflect the new ratio
133
            UpdatePaneSizes(splitterContainer, splitter);
1✔
134

135
            // Mark layout dirty
136
            if (!World.Has<UILayoutDirtyTag>(splitterContainer))
1✔
137
            {
138
                World.Add(splitterContainer, new UILayoutDirtyTag());
1✔
139
            }
140

141
            // Fire changed event
142
            World.Send(new UISplitterChangedEvent(splitterContainer, oldRatio, newRatio));
1✔
143
        }
144
    }
1✔
145

146
    private void UpdatePaneSizes(Entity splitterContainer, in UISplitter splitter)
147
    {
148
        // Find and update the first pane's percentage size
149
        foreach (var child in World.GetChildren(splitterContainer))
1✔
150
        {
UNCOV
151
            if (World.Has<UISplitterFirstPane>(child) && World.Has<UIRect>(child))
×
152
            {
UNCOV
153
                ref var paneRect = ref World.Get<UIRect>(child);
×
154

155
                // Convert ratio to percentage (0-100)
UNCOV
156
                float percentage = splitter.SplitRatio * 100f;
×
157

UNCOV
158
                if (splitter.Orientation == LayoutDirection.Horizontal)
×
159
                {
UNCOV
160
                    paneRect.Size = new System.Numerics.Vector2(percentage, paneRect.Size.Y);
×
161
                }
162
                else
163
                {
UNCOV
164
                    paneRect.Size = new System.Numerics.Vector2(paneRect.Size.X, percentage);
×
165
                }
166

167
                // Mark this pane dirty too
UNCOV
168
                if (!World.Has<UILayoutDirtyTag>(child))
×
169
                {
UNCOV
170
                    World.Add(child, new UILayoutDirtyTag());
×
171
                }
172

UNCOV
173
                break;
×
174
            }
175
        }
176
    }
1✔
177
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc