Code/Runtime/SuiPreviewMount.cs
using Sandbox;
namespace SboxUiDesigner.Runtime;
/// <summary>
/// Lives on a GameObject inside the Test-in-Play stage scene
/// (preview_stage.scene). On OnAwake it reads <see cref="SuiPreviewState.PendingTypeFullName"/>,
/// looks up the generated PanelComponent type via TypeLibrary, and mounts it
/// on a child <see cref="ScreenPanel"/> so the user's UI shows up as a real
/// HUD overlay in Play mode.
///
/// Unlike <c>SuiPreviewHost</c> (editor-owned scene + reflection workarounds),
/// this component runs under real Play mode lifecycle — no reflection needed.
/// </summary>
public sealed class SuiPreviewMount : Component
{
[Property, Title( "Mounted Type FQN (debug)" ), ReadOnly]
public string MountedFqn { get; private set; } = "";
private GameObject _panelHost;
protected override void OnAwake()
{
var fqn = SuiPreviewState.PendingTypeFullName;
if ( string.IsNullOrEmpty( fqn ) )
{
Log.Info( "[SuiPreviewMount] No PendingTypeFullName set — running stage without UI." );
return;
}
var typeDesc = TypeLibrary.GetType( fqn );
if ( typeDesc == null )
{
Log.Warning( $"[SuiPreviewMount] TypeLibrary.GetType('{fqn}') returned null. The generated PanelComponent isn't loaded — was the preview cache compiled before EditorScene.Play?" );
return;
}
// Build the host hierarchy: this GO -> ScreenPanelHost -> ScreenPanel + user's PanelComponent.
_panelHost = new GameObject( true, "ScreenPanelHost" );
_panelHost.SetParent( GameObject );
// ScreenPanel is the root for any PanelComponent stack — required so the
// user's UI renders to the screen overlay.
_panelHost.GetOrAddComponent<ScreenPanel>();
var created = _panelHost.Components.Create( typeDesc );
if ( created is not Component panel )
{
Log.Warning( $"[SuiPreviewMount] Components.Create('{fqn}') returned non-Component (got {created?.GetType().FullName ?? "null"})." );
return;
}
MountedFqn = fqn;
Log.Info( $"[SuiPreviewMount] Mounted '{fqn}' on ScreenPanel." );
// Clear so a future Play that wasn't initiated by the launcher doesn't
// silently reuse a stale FQN.
SuiPreviewState.Clear();
}
}