From 7b1fb16d611a4349e7ad32b06a936c28fe62b603 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 01:51:27 +0000 Subject: [PATCH] fix(Table): Correct BoxStyle.Heavy body separation layout Implemented strict mapping of Heavy body separator characters (\u2501, \u254B, \u2523, \u252B) within `TableBoxChars`. Refactored `TableSeparator` to properly consume these characters based on its parent's Table BoxStyle rather than forcing unoptimized/default interior rendering. Added an exhaustive, dynamically-resizing coordinate assertion unit test to guarantee strict boundary mapping without layout truncations. Co-authored-by: tedd <493224+tedd@users.noreply.github.com> --- .jules/validator.md | 4 ++ src/Tedd.TUI.Tests/TableCoverageTests.cs | 8 +-- src/Tedd.TUI.Tests/ValidatorTableTests.cs | 64 +++++++++++++++++++++++ src/Tedd.TUI/Table.cs | 15 +++--- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/.jules/validator.md b/.jules/validator.md index d90e334..5b7aef1 100644 --- a/.jules/validator.md +++ b/.jules/validator.md @@ -1,3 +1,7 @@ ## 2024-05-24 - Exhaustive Spatial and Hierarchical Validation of Table Component **Observation:** Auditing the `Table` component revealed requirements for robust testing against extreme constraints and explicit character verifications, especially when `BoxStyle.Heavy` is enabled. Traditional tests do not adequately cover the absolute positional accuracy of characters under dynamic grid-based hierarchical structures, nor do they isolate rendering behaviors when constrained to extremely tight boundaries (e.g., `Size(0,0)`). The control encapsulates complex subcomponents such as `ScrollViewer`, `TableRow`, and internal layout logic. **Strategic Action:** Developed an exhaustive validation suite (`ValidatorTableTests.cs`) under the xUnit framework. The matrix enforces: 1) Hierarchical Composition and Dynamic Mutation validation by embedding the `Table` alongside a `Border` within a proportionally-sized `Grid` and verifying structural integrity upon parent resize. 2) Coordinate-Precise Assertion evaluating the exact mapped placement of Unicode Box Drawing (`Heavy` style) symbols (e.g., `\u250F`, `\u2523`) against a `VirtualBuffer`. 3) Boundary verification enforcing correct behavior at `0x0` and `1x1` dimensions. + +## 2024-05-18 - Exhaustive Spatial Validation for Table Component (BoxStyle.Heavy) +**Observation:** The `Table` component was missing strict body separation validation for `BoxStyle.Heavy`, falling back to default or unoptimized rendering paths for inner cross junctions and horizontal separators. Legacy tests validated single and double styles but left `Heavy` unchecked, potentially allowing rendering regression. +**Strategic Action:** Analyzed `TableBoxChars` to correctly assign heavy drawing characters (`BodySepH: \u2501`, `BodySepTLeft: \u2523`, `BodySepTRight: \u252B`, `BodySepCross: \u254B`). Refactored `TableSeparator` to directly consume these characters from `TableBoxChars` instead of legacy `BoxDrawingChars.GetInterior`. Implemented `Table_CoordinatePreciseCharacterAssertion_HeavyBodySeparation` to mandate exact X/Y coordinate assertion of the characters in the VirtualBuffer, validating hierarchical layout boundary behaviors. diff --git a/src/Tedd.TUI.Tests/TableCoverageTests.cs b/src/Tedd.TUI.Tests/TableCoverageTests.cs index 8e05b9d..83a265e 100644 --- a/src/Tedd.TUI.Tests/TableCoverageTests.cs +++ b/src/Tedd.TUI.Tests/TableCoverageTests.cs @@ -363,18 +363,18 @@ public void Table_Separator_Rendering() // 6: └───────┘ // Row 4 should be separator - // X=0: Left Junction (u2520 ┠) if ShowBorder && ShowHorizontalLines + // X=0: Left Junction (u2523 ┣) if ShowBorder && ShowHorizontalLines // X=1..3: Horz Line (u2501 ━) — matches default BorderStyle.Heavy // X=4: Cross (u254B ╋) if ShowVerticalLines // X=5..7: Horz Line - // X=8: Right Junction (u2528 ┨) + // X=8: Right Junction (u252B ┫) // Verify Row 4 - Assert.Equal('\u2520', buffer.GetPixel(0, 4).Character); + Assert.Equal('\u2523', buffer.GetPixel(0, 4).Character); Assert.Equal('\u2501', buffer.GetPixel(1, 4).Character); Assert.Equal('\u254B', buffer.GetPixel(4, 4).Character); // Cross Assert.Equal('\u2501', buffer.GetPixel(5, 4).Character); - Assert.Equal('\u2528', buffer.GetPixel(8, 4).Character); + Assert.Equal('\u252B', buffer.GetPixel(8, 4).Character); } [Fact] diff --git a/src/Tedd.TUI.Tests/ValidatorTableTests.cs b/src/Tedd.TUI.Tests/ValidatorTableTests.cs index 7bbc077..9ef55b6 100644 --- a/src/Tedd.TUI.Tests/ValidatorTableTests.cs +++ b/src/Tedd.TUI.Tests/ValidatorTableTests.cs @@ -315,6 +315,70 @@ public void Table_CoordinatePreciseCharacterAssertion_SingleStyle() Assert.Equal('\u2534', buffer.GetPixel(4, 7).Character); } + [Fact] + public void Table_CoordinatePreciseCharacterAssertion_HeavyBodySeparation() + { + // Architectural Mandate: Nest in a hierarchy and trigger dynamic resize + var parentGrid = new Grid(); + parentGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); + parentGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + + var table = new Table + { + ShowBorder = true, + ShowHeader = true, + ShowVerticalLines = true, + ShowHorizontalLines = true, + BorderStyle = BoxStyle.Heavy + }; + + var col1 = new TableColumn { Header = "ID", Width = new GridLength(3, GridUnitType.Pixel) }; + var col2 = new TableColumn { Header = "Name", Width = new GridLength(4, GridUnitType.Pixel) }; + table.Columns.Add(col1); + table.Columns.Add(col2); + + table.AddRow("1", "Bob"); + table.AddRow("2", "Alice"); + + parentGrid.Children.Add(table); + + // Initial layout pass + parentGrid.Measure(new Size(12, 8)); + parentGrid.Arrange(new Rect(0, 0, 12, 8)); + + var sv = table.GetVisualChild(0) as ScrollViewer; + if (sv != null) + { + sv.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled; + sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; + } + + var buffer = new VirtualBuffer(12, 8); + parentGrid.Render(buffer, 0, 0); + + // Assert coordinates on initial constraint + Assert.Equal('\u254B', buffer.GetPixel(4, 2).Character); + Assert.Equal('\u2523', buffer.GetPixel(0, 4).Character); + Assert.Equal('\u252B', buffer.GetPixel(11, 4).Character); + Assert.Equal('\u254B', buffer.GetPixel(4, 4).Character); + Assert.Equal('\u2501', buffer.GetPixel(2, 4).Character); + + // Dynamic Resize state mutation + parentGrid.Measure(new Size(20, 10)); + parentGrid.Arrange(new Rect(0, 0, 20, 10)); + + var bufferResize = new VirtualBuffer(20, 10); + parentGrid.Render(bufferResize, 0, 0); + + // Table spans full star width, so width is 20 now. + // Body Sep Right is now at 19. Cross remains at 4 because column widths are fixed pixels. + Assert.Equal('\u254B', bufferResize.GetPixel(4, 2).Character); // Header cross junction + Assert.Equal('\u2523', bufferResize.GetPixel(0, 4).Character); // Left junction + Assert.Equal('\u252B', bufferResize.GetPixel(19, 4).Character); // Right junction moved to 19 + Assert.Equal('\u254B', bufferResize.GetPixel(4, 4).Character); // Cross remains at 4 + Assert.Equal('\u2501', bufferResize.GetPixel(2, 4).Character); // Horizontal remains line + } + [Fact] public void Table_CoordinatePreciseCharacterAssertion_DoubleStyle() { diff --git a/src/Tedd.TUI/Table.cs b/src/Tedd.TUI/Table.cs index 7f6c5c1..ccbab48 100644 --- a/src/Tedd.TUI/Table.cs +++ b/src/Tedd.TUI/Table.cs @@ -409,9 +409,10 @@ public static TableBoxChars Get(BoxStyle style) c.TLeft = '\u2523'; c.TRight = '\u252B'; c.HeaderCross = '\u254B'; - c.BodySepTLeft = '\u2520'; - c.BodySepTRight = '\u2528'; - c.BodySepCross = '\u2542'; // ╂ (Heavy Vert, Light Horz) + c.BodySepH = '\u2501'; + c.BodySepTLeft = '\u2523'; + c.BodySepTRight = '\u252B'; + c.BodySepCross = '\u254B'; break; case BoxStyle.Double: c.TDown = '\u2566'; @@ -419,6 +420,7 @@ public static TableBoxChars Get(BoxStyle style) c.TLeft = '\u2560'; c.TRight = '\u2563'; c.HeaderCross = '\u256C'; + c.BodySepH = '\u2500'; // Light horizontal for Double body sep c.BodySepTLeft = '\u255F'; // ╟ (Double Vert, Single Right) c.BodySepTRight = '\u2562'; // ╢ (Double Vert, Single Left) c.BodySepCross = '\u256B'; // ╫ (Double Vert, Single Horz) @@ -429,6 +431,7 @@ public static TableBoxChars Get(BoxStyle style) c.TLeft = '\u251C'; c.TRight = '\u2524'; c.HeaderCross = '\u253C'; + c.BodySepH = '\u2500'; c.BodySepTLeft = '\u251C'; c.BodySepTRight = '\u2524'; c.BodySepCross = '\u253C'; @@ -1076,9 +1079,9 @@ public override void Render(VirtualBuffer buffer, int offsetX, int offsetY) // rather than the infinite horizontal scroll space given by ScrollViewer. int width = Math.Min(RenderSize.Width, table.RenderSize.Width); - var interior = BoxDrawingChars.GetInterior(table.BorderStyle); - char hChar = interior.Horizontal; - char crossChar = BoxDrawingChars.GetInteriorCross(table.BorderStyle); + var chars = Table.TableBoxChars.Get(table.BorderStyle); + char hChar = chars.BodySepH; + char crossChar = chars.BodySepCross; buffer.DrawHLine(x, y, width, hChar, TuiColor.Gray, TuiColor.Black);