From b00ebb1748e2be30de8a84056c07e81338663e10 Mon Sep 17 00:00:00 2001 From: YourRobotOverlord <47068588+YourRobotOverlord@users.noreply.github.com> Date: Sun, 21 Jun 2026 11:16:42 -0600 Subject: [PATCH 1/5] add color getter delegate to demonstrate issue add color getter delegate to demonstrate issue --- Examples/UICatalog/Scenarios/TreeUseCases.cs | 53 ++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Examples/UICatalog/Scenarios/TreeUseCases.cs b/Examples/UICatalog/Scenarios/TreeUseCases.cs index 31c1ad0f5d..c93679ad2b 100644 --- a/Examples/UICatalog/Scenarios/TreeUseCases.cs +++ b/Examples/UICatalog/Scenarios/TreeUseCases.cs @@ -7,8 +7,10 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("TreeView")] public partial class TreeUseCases : Scenario { + private CheckBox? _customArmyColorsCheckBox; private EventLog? _eventLog; private Runnable? _appWindow; + private TreeView? _armyTree; private TreeViewEditor? _treeViewEditor; private ViewportSettingsEditor? _viewportSettingsEditor; @@ -34,6 +36,15 @@ public override void Main () new MenuItem { Title = "Armies With _Delegate", Action = () => LoadArmies (true) } ])); + _customArmyColorsCheckBox = new CheckBox + { + Title = "_Army Type Colors", + Value = CheckState.Checked + }; + _customArmyColorsCheckBox.ValueChanged += (_, _) => SetArmyTypeColors (); + + menu.Add (new MenuBarItem ("_Style", [new MenuItem { CommandView = _customArmyColorsCheckBox }])); + // EventLog on the right _eventLog = new EventLog { @@ -153,12 +164,16 @@ private void LoadArmies (bool useDelegate) } tree.AddObject (army); + _armyTree = tree; + SetArmyTypeColors (); CurrentTree = tree; } private void LoadRooms () { + _armyTree = null; + House myHouse = new () { Address = "23 Nowhere Street", Rooms = [new Room { Name = "Ballroom" }, new Room { Name = "Bedroom 1" }, new Room { Name = "Bedroom 2" }] @@ -174,6 +189,8 @@ private void LoadRooms () private void LoadEnableForDesign () { + _armyTree = null; + TreeView tree = new (); tree.EnableForDesign (); tree.Title = "_EnableForDesign"; @@ -183,6 +200,42 @@ private void LoadEnableForDesign () private void Quit () => _appWindow?.RequestStop (); + private void SetArmyTypeColors () + { + if (_armyTree is null || _customArmyColorsCheckBox is null) + { + return; + } + + if (_customArmyColorsCheckBox.Value != CheckState.Checked) + { + _armyTree.ColorGetter = null; + _armyTree.SetNeedsDraw (); + + return; + } + + _armyTree.ColorGetter = model => model switch + { + Army => CreateArmyTypeScheme (_armyTree, Color.BrightYellow), + CorpsObject => CreateArmyTypeScheme (_armyTree, Color.BrightCyan), + Division => CreateArmyTypeScheme (_armyTree, Color.BrightGreen), + Brigade => CreateArmyTypeScheme (_armyTree, Color.BrightMagenta), + Unit => CreateArmyTypeScheme (_armyTree, Color.Gray), + _ => null + }; + + _armyTree.SetNeedsDraw (); + } + + private static Scheme CreateArmyTypeScheme (TreeView tree, Color foreground) => + new () + { + Normal = new Attribute (foreground, tree.GetAttributeForRole (VisualRole.Normal).Background), + Focus = new Attribute (foreground, tree.GetAttributeForRole (VisualRole.Focus).Background), + Active = new Attribute (foreground, tree.GetAttributeForRole (VisualRole.Active).Background) + }; + // ── House / Room model (unchanged) ───────────────────────────────────── private class House : TreeNode From d772a40d10dad50df027589e8018d6afd01addff Mon Sep 17 00:00:00 2001 From: YourRobotOverlord <47068588+YourRobotOverlord@users.noreply.github.com> Date: Sun, 21 Jun 2026 11:55:56 -0600 Subject: [PATCH 2/5] apply delegate invoker fix --- Terminal.Gui/Views/TreeView/Branch.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TreeView/Branch.cs b/Terminal.Gui/Views/TreeView/Branch.cs index 9d167e4a16..3501831ad3 100644 --- a/Terminal.Gui/Views/TreeView/Branch.cs +++ b/Terminal.Gui/Views/TreeView/Branch.cs @@ -210,7 +210,9 @@ public virtual void Draw (int y, int availableWidth) if (modelScheme is { }) { // use it - modelColor = isSelected ? modelScheme.Focus : modelScheme.Normal; + modelColor = isSelected + ? _tree.HasFocus ? modelScheme.Focus : modelScheme.Active + : modelScheme.Normal; } else { From 15b036c38f816a1ceaec827b8f773abe730ee09a Mon Sep 17 00:00:00 2001 From: YourRobotOverlord <47068588+YourRobotOverlord@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:19:55 -0600 Subject: [PATCH 3/5] update xml documentation --- Terminal.Gui/Views/TreeView/TreeViewT.cs | 27 ++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TreeView/TreeViewT.cs b/Terminal.Gui/Views/TreeView/TreeViewT.cs index 2a6fafa741..c9ed5fcd48 100644 --- a/Terminal.Gui/Views/TreeView/TreeViewT.cs +++ b/Terminal.Gui/Views/TreeView/TreeViewT.cs @@ -326,9 +326,32 @@ public bool CheckboxMode public AspectGetterDelegate AspectGetter { get; set; } = o => o.ToString () ?? ""; /// - /// Delegate for multi-colored tree views. Return the to use for each passed object or - /// null to use the default. + /// Delegate for basic multi-colored tree view use cases. Return the to use for each passed + /// object, or to use the default tree view scheme. /// + /// + /// + /// TreeView uses only , , and + /// from the returned scheme. + /// + /// + /// Normal + /// Used for unselected branches. + /// + /// + /// Focus + /// Used for selected branches when the tree view has focus. + /// + /// + /// Active + /// Used for selected branches when the tree view does not have focus. + /// + /// + /// + /// + /// For greater control over rendering, handle the event instead. + /// + /// public Func? ColorGetter { get; set; } /// From a2b3f4127598f5fd8c9e342cc268b043c90ca805 Mon Sep 17 00:00:00 2001 From: YourRobotOverlord <47068588+YourRobotOverlord@users.noreply.github.com> Date: Sun, 21 Jun 2026 12:50:40 -0600 Subject: [PATCH 4/5] use role attribute as a base and only override foreground Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Examples/UICatalog/Scenarios/TreeUseCases.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Examples/UICatalog/Scenarios/TreeUseCases.cs b/Examples/UICatalog/Scenarios/TreeUseCases.cs index c93679ad2b..1823a03548 100644 --- a/Examples/UICatalog/Scenarios/TreeUseCases.cs +++ b/Examples/UICatalog/Scenarios/TreeUseCases.cs @@ -231,9 +231,9 @@ private void SetArmyTypeColors () private static Scheme CreateArmyTypeScheme (TreeView tree, Color foreground) => new () { - Normal = new Attribute (foreground, tree.GetAttributeForRole (VisualRole.Normal).Background), - Focus = new Attribute (foreground, tree.GetAttributeForRole (VisualRole.Focus).Background), - Active = new Attribute (foreground, tree.GetAttributeForRole (VisualRole.Active).Background) + Normal = tree.GetAttributeForRole (VisualRole.Normal) with { Foreground = foreground }, + Focus = tree.GetAttributeForRole (VisualRole.Focus) with { Foreground = foreground }, + Active = tree.GetAttributeForRole (VisualRole.Active) with { Foreground = foreground } }; // ── House / Room model (unchanged) ───────────────────────────────────── From 928a33cedc33971f28724aaf11a5643646a2043f Mon Sep 17 00:00:00 2001 From: YourRobotOverlord <47068588+YourRobotOverlord@users.noreply.github.com> Date: Sun, 21 Jun 2026 13:03:05 -0600 Subject: [PATCH 5/5] add regression test --- .../Views/TreeViewTests.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/Tests/UnitTestsParallelizable/Views/TreeViewTests.cs b/Tests/UnitTestsParallelizable/Views/TreeViewTests.cs index 3c5056f0f7..10d948cc35 100644 --- a/Tests/UnitTestsParallelizable/Views/TreeViewTests.cs +++ b/Tests/UnitTestsParallelizable/Views/TreeViewTests.cs @@ -86,6 +86,63 @@ public void Draw_EnableForDesign_Size_Auto_Draws_Correctly () output, driver); } + + // Codex - gpt 5.5 medium + // Regression for issue #5509. + [Fact] + public void Draw_ColorGetter_UsesActiveForSelectedRow_WhenTreeViewDoesNotHaveFocus () + { + using IApplication app = Application.Create (); + app.Init (DriverRegistry.Names.ANSI); + app.Driver!.SetScreenSize (20, 4); + + const string selected = "selected"; + Attribute normal = new (Color.BrightRed, Color.Black); + Attribute focus = new (Color.BrightYellow, Color.Blue); + Attribute active = new (Color.BrightYellow, Color.DarkGray); + + Scheme treeScheme = new () + { + Normal = new Attribute (Color.BrightGreen, Color.Black), + Focus = new Attribute (Color.Black, Color.BrightGreen), + Active = new Attribute (Color.BrightGreen, Color.DarkGray) + }; + + Scheme customScheme = new () + { + Normal = normal, + Focus = focus, + Active = active + }; + + TreeView tree = new (new DelegateTreeBuilder (_ => [], _ => false)) + { + Width = 20, + Height = 2, + ColorGetter = item => item == selected ? customScheme : null + }; + tree.Style.ShowBranchLines = false; + tree.SetScheme (treeScheme); + tree.AddObject (selected); + tree.SelectedObject = selected; + + View otherView = new () { Y = Pos.Bottom (tree), Width = 1, Height = 1, CanFocus = true }; + + using Runnable top = new (); + top.Add (tree, otherView); + + app.Begin (top); + tree.SetFocus (); + app.LayoutAndDraw (); + Assert.Equal (focus, app.Driver.Contents! [0, 1].Attribute); + + otherView.SetFocus (); + app.LayoutAndDraw (); + + Assert.False (tree.HasFocus); + Assert.Equal (active, app.Driver.Contents! [0, 1].Attribute); + } + [Fact] public void CollectionNavigatorMatcher_KeybindingsOverrideNavigator () {