Editor/Widgets/SuiCenterTabsWidget.cs
using System;
using Editor;
using Sandbox;
using SboxUiDesigner.EditorUi.Canvas;
using SboxUiDesigner.Runtime;
namespace SboxUiDesigner.EditorUi.Widgets;
/// <summary>
/// Center area of the editor — 100% custom, no DockManager.
///
/// Layout (Column):
/// 1. <see cref="SuiTabStrip"/> Designer / Preview / Code
/// 2. <see cref="SuiCanvasMiniToolbar"/> Screen / Zoom / Snap (canvas-scoped)
/// 3. Content switcher Canvas | Preview | Code (Visible toggle)
///
/// Bound to a controller for canvas interactions; preview + code refresh on
/// tab activation only.
/// </summary>
public sealed class SuiCenterTabsWidget : Widget
{
private SuiTabStrip _tabs;
private SuiCanvasMiniToolbar _miniToolbar;
private SuiCanvasWidget _canvas;
private SuiPreviewTabWidget _preview;
private SuiCodeTabWidget _code;
private SuiTopBarDropdown _screenDd;
private SuiTopBarDropdown _zoomDd;
private SuiTopBarDropdown _snapDd;
private SuiTopBarButton _rulersBtn;
private SuiTopBarButton _respDebugBtn;
private SuiTopBarButton _anchorsBtn;
private SuiTopBarButton _boundsBtn;
private SuiTopBarButton _fitBtn;
private SuiDocument _document;
public SuiCanvasWidget Canvas => _canvas;
public SuiTopBarDropdown ScreenDropdown => _screenDd;
public SuiTopBarDropdown ZoomDropdown => _zoomDd;
public SuiTopBarDropdown SnapDropdown => _snapDd;
public SuiTopBarButton RulersButton => _rulersBtn;
public SuiTopBarButton ResponsiveDebugButton => _respDebugBtn;
public SuiTopBarButton AnchorsButton => _anchorsBtn;
public SuiTopBarButton BoundsButton => _boundsBtn;
/// <summary>Fired when the Preview tab becomes active. Host uses this to
/// regenerate the preview cache lazily (avoids a hotload on every doc edit).</summary>
public event System.Action PreviewTabActivated;
public SuiCenterTabsWidget( Widget parent = null ) : base( parent )
{
Name = "SuiCenter";
MinimumSize = new Vector2( 400, 300 );
SetStyles( "background-color: rgb(20,20,20);" );
Layout = Layout.Column();
Layout.Margin = 0;
Layout.Spacing = 0;
// 1. Tab strip — small, left-aligned (mockup spec).
// Designer | Preview (embedded SceneRenderingWidget, simulated lifecycle) | Code.
// "Test in Play" on the top toolbar is the separate real-Play workflow.
_tabs = new SuiTabStrip( this );
_tabs.AddTab( "Designer" );
_tabs.AddTab( "Preview" );
_tabs.AddTab( "Code" );
_tabs.FinishLeftAligned();
_tabs.ActiveTabChanged += OnTabChanged;
Layout.Add( _tabs );
// 2. Mini-toolbar — only visible on Designer tab.
_miniToolbar = new SuiCanvasMiniToolbar( this );
Layout.Add( _miniToolbar );
// 3. Content stack — three pages.
_canvas = new SuiCanvasWidget( this );
_preview = new SuiPreviewTabWidget( this );
_code = new SuiCodeTabWidget( this );
Layout.Add( _canvas, 1 );
Layout.Add( _preview, 1 );
Layout.Add( _code, 1 );
ApplyVisibility();
}
public void WireMiniToolbar(
string screenValue, Action<Widget> openScreen,
string zoomValue, Action<Widget> openZoom,
string snapValue, Action<Widget> openSnap,
Action onToggleRulers,
Action onToggleResponsiveDebug,
Action onToggleAnchors,
Action onToggleBounds,
Action<Widget> openAlignMenu,
Action<Widget> openDistributeMenu,
Action onFit )
{
// Group 1: dropdowns (Screen / Zoom / Snap)
_screenDd = _miniToolbar.AddDropdown( "crop_landscape", "Screen", screenValue, openScreen, "Preview screen size" );
_miniToolbar.AddGap( 8 );
_zoomDd = _miniToolbar.AddDropdown( "search", "Zoom", zoomValue, openZoom, "Canvas zoom" );
_miniToolbar.AddGap( 8 );
_snapDd = _miniToolbar.AddDropdown( "add_box", "Snap", snapValue, openSnap, "Snap to grid" );
_miniToolbar.AddSeparator();
// Group 2: view toggles (Rulers / Responsive Debug) — icon-only, label in tooltip.
_rulersBtn = _miniToolbar.AddButton( "", "straighten", onToggleRulers, "Toggle pixel rulers" );
_respDebugBtn = _miniToolbar.AddButton( "", "bug_report", onToggleResponsiveDebug, "Show responsive issues + safe area + bounds" );
_anchorsBtn = _miniToolbar.AddButton( "", "my_location", onToggleAnchors, "Show anchor markers for the selected element" );
_boundsBtn = _miniToolbar.AddButton( "", "select_all", onToggleBounds, "Show layout bounds (dashed outlines around every element)" );
_miniToolbar.AddSeparator();
// Group 3: alignment ops on multi-selection — two dropdowns to keep
// the toolbar compact while exposing all 8 operations.
_miniToolbar.AddMenuButton( "Align", "align_horizontal_center", openAlignMenu, "Align selected elements (≥2 with same parent)" );
_miniToolbar.AddMenuButton( "Distribute", "horizontal_distribute", openDistributeMenu, "Distribute selected elements evenly (≥3 with same parent)" );
_miniToolbar.AddStretch();
// Group 3: fit-to-screen on the right
_fitBtn = _miniToolbar.AddButton( "Fit", "fit_screen", onFit, "Fit canvas to viewport (Ctrl+0)" );
}
public void RefreshToggleStates()
{
if ( _document?.Settings == null ) return;
if ( _rulersBtn != null ) { _rulersBtn.IsActive = _document.Settings.ShowRulers; _rulersBtn.Update(); }
if ( _respDebugBtn != null ) { _respDebugBtn.IsActive = _document.Settings.ResponsiveDebug; _respDebugBtn.Update(); }
if ( _anchorsBtn != null ) { _anchorsBtn.IsActive = _document.Settings.ShowAnchors; _anchorsBtn.Update(); }
if ( _boundsBtn != null ) { _boundsBtn.IsActive = _document.Settings.ShowLayoutBounds; _boundsBtn.Update(); }
}
private void OnTabChanged( int index )
{
ApplyVisibility();
// Preview tab → regenerate cache + remount type. Host uses this for
// lazy regen (avoids a hotload churn on every property edit).
if ( index == 1 )
{
PreviewTabActivated?.Invoke();
_preview?.Reload();
}
// Code tab → regenerate the .razor/.scss text views.
else if ( index == 2 )
{
_code?.Reload();
}
}
private void ApplyVisibility()
{
var idx = _tabs.ActiveIndex;
// Mini-toolbar only appears under Designer.
if ( _miniToolbar.IsValid() ) _miniToolbar.Visible = idx == 0;
if ( _canvas.IsValid() ) _canvas.Visible = idx == 0;
if ( _preview.IsValid() ) _preview.Visible = idx == 1;
if ( _code.IsValid() ) _code.Visible = idx == 2;
}
public void SetController( SuiDesignerController controller )
{
_canvas?.SetController( controller );
}
public void SetDocument( SuiDocument document )
{
_document = document;
_canvas?.SetDocument( document );
_preview?.SetDocument( document );
_code?.SetDocument( document );
}
public SuiCanvasViewport GetViewport() => _canvas?.GetViewport();
}