Editor/BoldHierarchyRow.cs
using Editor;
using Sandbox;
using System;
using System.Linq;
using static Editor.BaseItemWidget;
namespace HierarchyChanges;
// Adapted from Facepunch's installed GameObjectNode.OnPaint (2026-09-06).
// Source: sbox-public/game/addons/tools/Code/Scene/SceneTree/GameObjectNode.cs.
// Keep this compatibility copy aligned with upstream hierarchy indicators.
// Only the object name uses weight 700; following status text returns to normal.
internal static class BoldHierarchyRow
{
internal static void PaintBold( VirtualWidget item, GameObject Value, TreeView TreeView )
{
if ( !Value.Scene.IsValid() )
return;
var isEven = item.Row % 2 == 0;
var isHovered = item.Hovered;
var selected = item.Selected || item.Pressed || item.Dragging;
var isBone = Value.Flags.Contains( GameObjectFlags.Bone );
var isProceduralBone = Value.Flags.Contains( GameObjectFlags.ProceduralBone );
var isAttachment = Value.Flags.Contains( GameObjectFlags.Attachment );
var isNetworked = Value.Scene.IsEditor ? Value.NetworkMode == NetworkMode.Object : Value.Network.Active;
var isNetworkRoot = isNetworked && (Value.Scene.IsEditor ? Value.NetworkMode == NetworkMode.Object : Value.IsNetworkRoot);
bool isErrored = Value.Flags.Contains( GameObjectFlags.Error );
bool isLoading = Value.Flags.Contains( GameObjectFlags.Loading );
bool isTemporary = Value.Flags.Contains( GameObjectFlags.NotSaved );
bool isEditorOnly = Value.Flags.Contains( GameObjectFlags.EditorOnly );
var fullSpanRect = item.Rect;
fullSpanRect.Left = 0;
fullSpanRect.Right = TreeView.Width;
float opacity = 0.9f;
if ( !Value.Active ) opacity *= 0.5f;
Color pen = Theme.TextControl;
string icon = "layers";
Color iconColor = Theme.TextControl.WithAlpha( 0.6f );
Color overlayIconColor = iconColor;
string overlayIcon = null;
if ( Value.IsPrefabInstance )
{
pen = Theme.Blue;
overlayIconColor = Theme.Blue;
if ( Value.IsPrefabInstanceRoot )
{
icon = "dataset";
iconColor = Theme.Blue;
if ( EditorUtility.Prefabs.IsInstanceModified( Value ) )
{
Paint.ClearPen();
Paint.SetBrush( Theme.Blue.Darken( 0.25f ) );
var modifiedRect = fullSpanRect;
modifiedRect.Width = 2;
Paint.DrawRect( modifiedRect );
}
}
if ( EditorUtility.Prefabs.IsGameObjectAddedToInstance( Value ) )
{
overlayIcon = "add_circle";
}
}
if ( isBone )
{
icon = "polyline";
iconColor = Theme.Pink.WithAlpha( 0.8f );
if ( isProceduralBone )
{
iconColor = Theme.Blue.WithAlpha( 0.8f );
}
}
if ( isAttachment )
{
icon = "push_pin";
iconColor = Theme.Pink.WithAlpha( 0.8f );
if ( isProceduralBone )
{
iconColor = Theme.Blue.WithAlpha( 0.8f );
}
}
if ( isTemporary )
{
iconColor = Color.White.WithAlpha( 0.5f );
pen = iconColor.Lighten( 0.2f ).WithAlpha( 0.8f );
icon = "no_sim";
}
if ( isNetworked )
{
icon = "rss_feed";
iconColor = Theme.Blue.WithAlpha( 0.8f );
if ( Value.Network.IsOwner )
{
iconColor = Theme.Green.WithAlpha( 0.8f );
}
if ( Value.IsProxy )
{
iconColor = Theme.TextControl.WithAlpha( 0.6f );
}
if ( !isNetworkRoot ) iconColor = iconColor.WithAlphaMultiplied( 0.4f );
}
if ( isErrored )
{
icon = "report";
iconColor = Theme.Red.WithAlpha( 0.8f );
}
if ( isEditorOnly )
{
icon = "highlight_alt";
iconColor = Theme.Yellow.WithAlpha( 0.8f );
pen = Theme.Yellow.WithAlpha( 0.6f );
}
//
// If there's a drag and drop happening, fade out nodes that aren't possible
//
if ( TreeView.IsBeingDroppedOn )
{
if ( TreeView.CurrentItemDragEvent.Data.Object is GameObject[] gos && gos.Any( go => Value.IsAncestor( go ) ) )
{
opacity *= 0.23f;
}
else if ( TreeView.CurrentItemDragEvent.Data.Object is GameObject go && Value.IsAncestor( go ) )
{
opacity *= 0.23f;
}
}
if ( item.Dropping )
{
Paint.ClearPen();
Paint.SetBrush( Theme.Blue );
if ( TreeView.CurrentItemDragEvent.DropEdge.HasFlag( ItemEdge.Top ) )
{
var droprect = item.Rect;
droprect.Top -= 1;
droprect.Height = 2;
Paint.DrawRect( droprect, 2 );
}
else if ( TreeView.CurrentItemDragEvent.DropEdge.HasFlag( ItemEdge.Bottom ) )
{
var droprect = item.Rect;
droprect.Top = droprect.Bottom - 1;
droprect.Height = 2;
Paint.DrawRect( droprect, 2 );
}
else
{
Paint.SetBrushAndPen( Theme.Blue.WithAlpha( 0.2f ), Theme.Blue );
Paint.PenSize = 2;
Paint.DrawRect( item.Rect, 4 );
}
}
if ( selected )
{
//item.PaintBackground( Color.Transparent, 3 );
Paint.ClearPen();
Paint.SetBrush( Theme.SelectedBackground.WithAlpha( opacity ) );
Paint.DrawRect( fullSpanRect );
}
else if ( isHovered )
{
Paint.ClearPen();
Paint.SetBrush( Theme.SelectedBackground.WithAlpha( 0.25f ) );
Paint.DrawRect( fullSpanRect );
}
else if ( isEven )
{
Paint.ClearPen();
Paint.SetBrush( Theme.SurfaceLightBackground.WithAlpha( 0.1f ) );
Paint.DrawRect( fullSpanRect );
}
var name = Value.Name;
if ( string.IsNullOrWhiteSpace( name ) ) name = "Untitled GameObject";
var r = item.Rect;
r.Left += 4;
var iconSize = 16;
Paint.Pen = iconColor.WithAlphaMultiplied( opacity );
Paint.DrawIcon( r, icon, iconSize, TextFlag.LeftCenter );
if ( !string.IsNullOrEmpty( overlayIcon ) )
{
var overlayIconRect = r;
overlayIconRect.Left += 8;
overlayIconRect.Top += 8;
overlayIconRect.Width = 13;
overlayIconRect.Height = 13;
Paint.Pen = Theme.WidgetBackground;
Paint.SetBrush( Theme.WidgetBackground );
Paint.DrawRect( overlayIconRect, 12 );
overlayIconRect.Left += 1;
Paint.Pen = overlayIconColor;
Paint.DrawIcon( overlayIconRect, overlayIcon, 13, TextFlag.Center );
}
r.Left += 22;
Paint.Pen = pen.WithAlphaMultiplied( opacity );
Paint.SetDefaultFont( weight: 700 );
r.Left += Paint.DrawText( r, name, TextFlag.LeftCenter ).Width + 4;
Paint.SetDefaultFont();
if ( isLoading )
{
Paint.Pen = Theme.Blue;
Paint.DrawIcon( r, "access_time_filled", iconSize, TextFlag.LeftCenter );
r.Left += 22;
}
if ( isNetworkRoot && Value.Network.OwnerId != Guid.Empty )
{
var connection = Connection.Find( Value.Network.OwnerId );
if ( connection is null )
{
Paint.Pen = Theme.Blue;
Paint.DrawText( r, $"Unknown Owner - {Value.Network.OwnerId}", TextFlag.LeftCenter );
r.Left += 22;
}
else
{
Paint.Pen = Theme.Blue;
Paint.DrawText( r, $"{connection.DisplayName}", TextFlag.LeftCenter );
r.Left += 22;
}
}
if ( Value.Tags.Has( "hidden" ) )
{
var eyeRect = item.Rect;
eyeRect.Right -= 4;
eyeRect.Left = eyeRect.Right - 18;
Paint.Pen = Theme.TextControl;
Paint.DrawIcon( eyeRect, "visibility_off", 14, TextFlag.Center );
}
}
}