Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Examples/UICatalog/Scenarios/TreeUseCases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<GameObject>? _armyTree;
private TreeViewEditor? _treeViewEditor;
private ViewportSettingsEditor? _viewportSettingsEditor;

Expand All @@ -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
{
Expand Down Expand Up @@ -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" }]
Expand All @@ -174,6 +189,8 @@ private void LoadRooms ()

private void LoadEnableForDesign ()
{
_armyTree = null;

TreeView tree = new ();
tree.EnableForDesign ();
tree.Title = "_EnableForDesign";
Expand All @@ -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<GameObject> tree, Color foreground) =>
new ()
{
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) ─────────────────────────────────────

private class House : TreeNode
Expand Down
4 changes: 3 additions & 1 deletion Terminal.Gui/Views/TreeView/Branch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment thread
YourRobotOverlord marked this conversation as resolved.
}
else
{
Expand Down
27 changes: 25 additions & 2 deletions Terminal.Gui/Views/TreeView/TreeViewT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,32 @@ public bool CheckboxMode
public AspectGetterDelegate<T> AspectGetter { get; set; } = o => o.ToString () ?? "";

/// <summary>
/// Delegate for multi-colored tree views. Return the <see cref="Scheme"/> to use for each passed object or
/// null to use the default.
/// Delegate for basic multi-colored tree view use cases. Return the <see cref="Scheme"/> to use for each passed
/// object, or <see langword="null"/> to use the default tree view scheme.
/// </summary>
/// <remarks>
/// <para>
/// TreeView uses only <see cref="Scheme.Normal"/>, <see cref="Scheme.Focus"/>, and <see cref="Scheme.Active"/>
/// from the returned scheme.
/// <list type="bullet">
/// <item>
/// <term>Normal</term>
/// <description>Used for unselected branches.</description>
/// </item>
/// <item>
/// <term>Focus</term>
/// <description>Used for selected branches when the tree view has focus.</description>
/// </item>
/// <item>
/// <term>Active</term>
/// <description>Used for selected branches when the tree view does not have focus.</description>
/// </item>
/// </list>
/// </para>
/// <para>
/// For greater control over rendering, handle the <see cref="DrawLine"/> event instead.
/// </para>
/// </remarks>
public Func<T, Scheme?>? ColorGetter { get; set; }

/// <summary>
Expand Down
57 changes: 57 additions & 0 deletions Tests/UnitTestsParallelizable/Views/TreeViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> tree = new (new DelegateTreeBuilder<string> (_ => [], _ => 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 ()
{
Expand Down
Loading