Fixes #4183 - BREAKING CHANGE - Finishes Adornments and Rewrites TabView#4829
Fixes #4183 - BREAKING CHANGE - Finishes Adornments and Rewrites TabView#4829tig merged 193 commits intogui-cs:developfrom
Adornments and Rewrites TabView#4829Conversation
Remove all old TabView source files (~3,570 lines across 6 files), old TabView tests, and TabViewExample scenario. Fix compilation errors in scenarios that referenced deleted types by stubbing out TabView- dependent scenarios (Notepad, ConfigurationEditor) and replacing TabView usage with direct views (Editor, Images, AnsiRequests). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add BorderGap record struct and gap lists (TopGaps, BottomGaps, LeftGaps, RightGaps) to Border. Gaps create exclusion regions in the border's LineCanvas, allowing TabView to punch openings where the selected tab connects to the content area. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the new TabView using modern v2 patterns: - Tab: Simple View subclass (Visible=false by default, Dim.Fill) - TabRow: Internal view in Padding that renders tab headers with rounded borders, positioned adjacent with 1-cell overlap - TabView: Orchestrator with Wizard-style show/hide, nullable SelectedTabIndex, keyboard commands (Ctrl+Left/Right/Home/End), IDesignable support, and CWP SelectedTabChanged event Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New scenario for exercising TabView with: - Demo TabView with 3 example tabs (label, text+button, multi-line) - Configuration pane: TabsOnBottom toggle, MaxTabTextWidth, Add/Remove tab buttons, SelectedTabIndex display, Prev/Next nav - AdornmentsEditor targeting the demo TabView - EventLog for monitoring tab change events Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use ValueChangingEventArgs.Handled to clamp MaxTabTextWidth input to 1-100 range instead of non-existent MinValue/MaxValue. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, disposal fixes - Change TabView.Border.Thickness to (1,0,1,1) so tab headers form the top - Add Border.DrawSegmentedVerticalLine for gap-aware vertical line rendering - Use RightGaps instead of Exclude for correct corner glyphs (╮ not ┤) - TabRow draws horizontal continuation line from last tab to right edge - Fix RebuildHeaders to dispose old header views (prevents "Not Disposed" leaks) - Guard OnSubViewRemoved during disposal to prevent orphaned header creation - Clear SelectedTabChanged event before base disposal to prevent ObjectDisposedException - Add disposal tests that fail without fixes (verify Border==null after dispose) - Update all visual rendering tests for new no-top-border layout Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sOnBottom Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactored TabViewExample for more adaptive layout using Dim.Fill and Pos.AnchorEnd, and compacted configuration controls. Updated TabView and TabRow to use base.SuperViewRendersLineCanvas = true. Improved command registration with method group syntax. Enhanced Side enum documentation. Updated tabview-rewrite.md to mark navigation and visual polish as partial, added a detailed TabsOnBottom fix plan, and documented additional known issues. Minor code cleanups and formatting improvements.
- Fix text/continuation line overlap in TabRow.UpdateHeaderAppearance() by adding Padding.Top=1 compensation for selected header when TabsOnBottom - Extract visual rendering tests to TabViewVisualTests.cs (28 tests) - Add 13 new visual regression tests covering tabs-on-bottom, state transitions, padding assertions, and edge cases - Update plan with fix status Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements mnemonic (hotkey) selection for TabView tabs by overriding OnHandlingHotKey in Tab. Ensures TabStop is set for tabs and tab headers. Updates TabViewExample and adds comprehensive unit tests for hotkey selection, event raising, and keyboard navigation. Refactors focus management logic for robustness. Improves TabView disposal and focus behavior. Updates documentation and marks hotkey issue as fixed.
- Replace bool TabsOnBottom with Side TabSide enum (Top/Bottom only, Left/Right ignored) - Add 6 hotkey navigation unit tests proving Tab.OnHandlingHotKey works - Replace CheckBox with OptionSelector<Side> in TabViewExample, disable Left/Right - Update all tests and visual tests for TabSide rename - Add focusable tab headers with hot-normal/hot-focus attribute routing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Make TabRow focusable and add focus event handlers to TabRow/TabView - Enhance mouse and keyboard navigation for tab headers - Refactor Padding subview logic and mouse handling - Set TabView.Padding as a tab stop for accessibility - Add visual tests for tab scrolling and indicator rendering - Clean up code and remove unused usings - Prepares for future tab scrolling support and better accessibility
…iewport scrolling - TabRow.UpdateContentSizeForScrolling() computes total header width and sets ContentSize - TabRow.EnsureHeaderVisible() scrolls viewport to reveal selected tab header - TabView.SelectedTabIndex setter calls EnsureHeaderVisible on tab change - Rewrite scroll tests to verify content size and forward viewport scrolling - Skip scroll-back rendering test pending investigation - Update plan to reflect scrolling progress Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Design plan for implementing tab headers as a first-class Border capability (BorderSettings.Tab) instead of using a separate TabRow with dynamically-created header views. Leverages existing Thickness.Top == 3 rendering and LineCanvas auto-join for the flowing connected tab style. Includes renderings for all four sides (Top, Bottom, Left, Right) and a plan for migrating BorderTests to UnitTestsParallelizable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major rewrite of tabview-border-based-design.md to propose a new, border-based approach for tabbed interfaces in Terminal.Gui v2. Tabs are now rendered as part of the Border adornment, leveraging a shared LineCanvas for auto-joining borders and eliminating the need for TabRow and dynamic header Views. The document details the new API surface (BorderSettings.Tab, TabOffset, TabWidth), explains rendering for all four sides, and provides implementation phases. Scrolling, focus, and mouse handling are addressed, with ASCII diagrams and tables clarifying the design. The new approach aims to be the reference for compound views in v2.
Add support for transparent borders drawn on top Introduce transparent border rendering, similar to transparent margins, by drawing borders with the Transparent flag after all content. Refactor border drawing logic and region exclusion to support this feature. Update demo and editors for improved usability and identifier display. Move ShowViewIdentifier to EditorBase and ensure consistent OnViewToEditChanged behavior. Cache clip regions for correct transparent rendering.
# Conflicts: # Examples/UICatalog/Scenarios/ConfigurationEditor.cs # Examples/UICatalog/Scenarios/Notepad.cs # Examples/UICatalog/Scenarios/TabViewExample.cs # Terminal.Gui/Views/TabView/TabView.cs
- BorderView and TitleView now participate in tab navigation via TabStop. - TitleView can be focused with HotKey; arrow keys are bound for navigation. - Tabs navigation logic updated: arrow keys move focus between tab titles and content, or switch tabs based on tab side. - Added FocusContent method to move focus from tab title to content. - Updated tests to verify new navigation and focus behavior.
Refactor tab header positioning logic and properties (TabSide, TabOffset, TabLength, EffectiveTabLength) from the Border adornment to BorderView, making the view responsible for tab state. Update all usages, tests, and documentation to reference BorderView for tab-related properties. Remove Border.SettingsChanged event in favor of an internal BorderView method. Apply minor code style and formatting improvements for consistency.
Updated Tabs to use Pos.AnchorEnd(TabDepth) for separator positioning when tabs are on the Bottom or Right, improving layout flexibility. Also removed an unnecessary Margin.Thickness assignment in TabsExample.
Adds a TabSpacing property to the Tabs control that controls the gap between adjacent tab headers. Default is -1 (shared border edge, preserving current behavior). 0 = edge-to-edge, positive values insert gaps. Also fixes null-safety in BorderEditor and updates borders.md property table to reflect BorderView ownership of tab properties. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
I love how configurable these are! I also feel that the Spacing parameter is a wonderful option for @BDisp's concerns:
I did notice one unexpected behavior where the Title is not visible and becomes un-clickable when the Side is set to Right or Bottom: |
YourRobotOverlord
left a comment
There was a problem hiding this comment.
@tig I'm sorry, I posted my comment after you'd already closed your pr but github is still asking for a review, so the only thing I could find was the new Tab rendering glitch.
There's so much to like in here!
#4829 (comment)
I can repro this. Happens whenever a subview of the tab exceeds the viewport. Eg. Issue raised here: #4915 |





Overview
This PR completes the v2 Adornments implementation (#2994) by replacing the old
TabView(~1,500 lines across 6 files of manual drawing code) with a newTabsclass built entirely on the v2 infrastructure:BorderSettings.Tab,SuperViewRendersLineCanvas, overlappedViewArrangement, and theIValue<View?>pattern.The Old TabView Problem
The original
TabViewcontained ~800 lines of manualLineCanvasdrawing inTabRow.OnRenderingLineCanvas(), hand-computing line positions, corners, and intersections that the LineCanvas auto-join system was designed to handle automatically. It also had manual viewport calculation, custom mouse/keyboard handling outside the Command system, a separateTabStyleconfiguration class, and aTab.View/Tab.DisplayTextsplit.What Changed
New
Tabsclass — AnyViewcan be a tab. Add views viaView.Add()and the container automatically configures each withBorderSettings.Tab,ViewArrangement.Overlapped, and aBorder.Thicknessderived fromTabDepthandTabSide. The selected tab is the focused one, reported viaIValue<View?>.Value. Zero custom line drawing — the entire visual (tab headers, separator lines, T-junctions, corners) is produced by standardBorderrendering +LineCanvasauto-joins.BorderSettings.Tab— A new border mode that draws a tab-style header on one side of any View's border. Configured viaBorder.TabSide,Border.TabOffset, andBorder.TabLength. This is a general-purpose capability — any View can have a tab border, not just views inside aTabscontainer.BorderViewtab rendering pipeline — New code inBorderViewhandles the tab header drawing: computing header rectangles, drawing separator lines with gaps for the selected tab, rendering the title inside the tab header viaTabTitleView, and managing line canvas intersections. All done through the existingBorder/LineCanvasinfrastructure.Four-side tab support —
TabSidesupportsTop,Bottom,Left, andRight. Tab depth is configurable viaTabDepth.Scroll buttons — When tabs overflow the available space,
ScrollButtonindicators (◄►/▲▼) appear on the separator line.ScrollOffsetprovides programmatic scrolling with clamping.ScrollButtoncontrol — Extracted fromScrollBarinto a standalone 1×1 button view with directional glyphs and mouse-hold repeat. Used by bothScrollBarandTabs.Mouse hold repeat fix —
MouseHoldRepeaterImpl.RaiseMouseIsHeldDownTicknow stops the repeat timer when the view becomes invisible or disabled, preventing runaway timers when scroll buttons are hidden.Drawing Pipeline Changes
View.Drawing.cswas split intoView.Drawing.cs,View.Drawing.Adornments.cs, andView.Drawing.LineCanvas.csfor clarityDoDrawAdornmentsSubViewsnow runs beforeDoRenderLineCanvasso merged lines are present when the LC rendersRegion.Combinenow filters zero-width/zero-height rectangles (Region.Combine: zero-width/zero-height rectangles corrupt Union results #4858) that corrupted Union resultsBreaking Changes
TabView,Tab,TabRow,TabStyle,TabChangedEventArgs,TabMouseEventArgsare all deletedTabs(the container) — tabs are justViewinstances added viaAdd()InsertTab(index, view)for positional insertion;TabCollectionfor logical-order enumerationValueproperty (IValue<View?>) instead ofSelectedTab/SelectedTabIndexScenarios
GraphViewExamplerefactored to useTabs— each graph type is a tab instead of cycling via Ctrl+GTabsExamplescenario replacing the oldTabViewExampleAdornmentsEditor,Notepad,ConfigurationEditor, and other scenarios updatedFixes
BorderSettings.Tabwhich enables any View's border to have top/left/right/bottom "tab" like borders - DONEBorderto use subviews for title and lines, supporting aTabstyle #3407 - DONETabViewto use modern v2 capabilities #4183RemoveTabcall #3365