mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-03 01:09:29 -04:00
* Add Qt-Advanced-Docking-System 5.0 * Bind new CDockManager, dock areas * Use Valve's CSS & images for ADS as a baseline, load stuff from disk instead of embedded in c++ * dockmanager.css in tool styles, auto loads and hotloads * Rewrite DockManager * delete toolwindowmanager * Delete the 50 different ways to create/add/register a dock, you just add a dock, that's all u need * More dockmanager rejigging * restore all editor dock layouts and asset browser on the new docking system * remove last toolwindowmanager remnants, add ADS to third party legal notice * build classic default layout in code for main editor reset/first-run, add relative area AddDock overload * relativeTo placement API, restore correct default layouts for all tool windows, register [Dock] types closed instead of eagerly docking * styling pass * disable ADS built-in stylesheet load, styling comes from editor stylesheet * lock picker browser tabs against closing/dragging, make ActionGraph reset layout actually reposition docks * add DockManager.OpenDock, dedupe layout code in EditorMainWindow/ActionGraph * CloseDockWidget internal * add "Close Tabs to the Left/Right" to dock tab context menu * expose dock MinimumSizeFromContent * only offer close left/right tab options when open tabs exist on that side * move "Close Tabs to the Left/Right" out of the ADS lib into a components factory * guard dock tab context menu against pinned docks with no dock area * fix crash when pinned tab is destroyed before delayed auto hide timer fires * recreate scene widget swapchains when Qt recreates their native window on dock reparent * wrap all sbox changes to the dock lib in #ifdef SBOX * save a plain window rect as fallback for when Qt rejects saved geometry on mixed-DPI monitors * expose DockWidget Float/IsFloating * always restore window geometry from a plain saved rect instead of Qt's DPI fragile geometry blob * route all window centering through one screen aware helper that handles mixed DPI monitors * restore window geometry onto the screen it was saved on * don't close live docks when a dock type unregisters, hotload substitutes them in place --------- Co-authored-by: Layla <1667289+aylaylay@users.noreply.github.com>
300 lines
7.3 KiB
C#
300 lines
7.3 KiB
C#
using Editor.AssetPickers;
|
|
|
|
namespace Editor;
|
|
|
|
[CustomEditor( typeof( GameObject ) )]
|
|
public class GameObjectControlWidget : ControlWidget
|
|
{
|
|
public override bool SupportsMultiEdit => true;
|
|
bool IsInList => Parent?.Parent is ListControlWidget;
|
|
IconButton PickerButton;
|
|
|
|
public bool ShowFullName { get; set; } = true;
|
|
|
|
public GameObjectControlWidget( SerializedProperty property ) : base( property )
|
|
{
|
|
if ( !property.IsEditable )
|
|
ReadOnly = true;
|
|
|
|
IsDraggable = true;
|
|
|
|
SetSizeMode( SizeMode.Default, SizeMode.Default );
|
|
|
|
Layout = Layout.Column();
|
|
Layout.Spacing = 2;
|
|
|
|
var inner = Layout.Add( Layout.Row() );
|
|
inner.Margin = 2;
|
|
|
|
inner.AddStretchCell( 1 );
|
|
PickerButton = inner.Add( new IconButton( "colorize", () =>
|
|
{
|
|
PropertyStartEdit();
|
|
EyeDropperTool.SetTargetProperty( property );
|
|
EyeDropperTool.OnBackToLastTool = () => PropertyFinishEdit();
|
|
}, this ) );
|
|
PickerButton.FixedWidth = Theme.RowHeight - 4;
|
|
PickerButton.FixedHeight = Theme.RowHeight - 4;
|
|
PickerButton.Visible = false;
|
|
|
|
Cursor = CursorShape.Finger;
|
|
AcceptDrops = true;
|
|
}
|
|
|
|
protected override void OnContextMenu( ContextMenuEvent e )
|
|
{
|
|
var m = new ContextMenu( this );
|
|
|
|
var go = SerializedProperty.GetValue<GameObject>();
|
|
if ( go is PrefabScene prefab )
|
|
{
|
|
var asset = AssetSystem.FindByPath( prefab.Source.ResourcePath );
|
|
|
|
m.AddOption( "Open in Editor", "edit", () => asset?.OpenInEditor() ).Enabled = asset != null && !asset.IsProcedural;
|
|
m.AddOption( "Find in Asset Browser", "search", () => LocalAssetBrowser.OpenTo( asset, true ) ).Enabled = asset is not null;
|
|
m.AddSeparator();
|
|
}
|
|
else if ( go is not null )
|
|
{
|
|
m.AddOption( "Find in Scene", "search", () => EditorUtility.FindInScene( go ) );
|
|
m.AddSeparator();
|
|
}
|
|
|
|
//m.AddOption( "Copy", "file_copy" );
|
|
//m.AddOption( "Paste", "content_paste" );
|
|
//m.AddSeparator();
|
|
|
|
if ( !ReadOnly )
|
|
{
|
|
m.AddOption( "Clear", "backspace", action: Clear );
|
|
}
|
|
|
|
if ( m.OptionCount > 0 )
|
|
{
|
|
m.OpenAtCursor( false );
|
|
}
|
|
|
|
e.Accepted = true;
|
|
}
|
|
|
|
protected override void PaintControl()
|
|
{
|
|
var rect = LocalRect.Shrink( 6, 0 );
|
|
var go = SerializedProperty.GetValue<GameObject>();
|
|
|
|
Paint.SetDefaultFont();
|
|
|
|
if ( SerializedProperty.IsMultipleDifferentValues )
|
|
{
|
|
Paint.SetPen( Theme.MultipleValues );
|
|
Paint.DrawIcon( rect, "panorama_wide_angle_select", 14, TextFlag.LeftCenter );
|
|
rect.Left += 22;
|
|
Paint.DrawText( rect, "Multiple Values", TextFlag.LeftCenter );
|
|
}
|
|
else if ( !go.IsValid() )
|
|
{
|
|
Paint.SetPen( Theme.TextControl.WithAlpha( 0.3f ) );
|
|
Paint.DrawIcon( rect, "radio_button_unchecked", 14, TextFlag.LeftCenter );
|
|
rect.Left += 22;
|
|
Paint.DrawText( rect, "None (GameObject)", TextFlag.LeftCenter );
|
|
}
|
|
else if ( go is PrefabScene )
|
|
{
|
|
Paint.SetPen( Theme.Blue );
|
|
Paint.DrawIcon( rect, "panorama_wide_angle_select", 14, TextFlag.LeftCenter );
|
|
rect.Left += 22;
|
|
Paint.DrawText( rect, go.Name, TextFlag.LeftCenter );
|
|
}
|
|
else
|
|
{
|
|
Paint.SetPen( Theme.Green );
|
|
Paint.DrawIcon( rect, "panorama_wide_angle_select", 14, TextFlag.LeftCenter );
|
|
rect.Left += 22;
|
|
Paint.DrawText( rect, BuildName( go ), TextFlag.LeftCenter );
|
|
}
|
|
|
|
if ( EyeDropperTool.TargetProperty == SerializedProperty )
|
|
{
|
|
Paint.SetPen( ControlHighlightSecondary.WithAlpha( 0.8f ), 2, PenStyle.Dot );
|
|
Paint.SetBrush( ControlHighlightSecondary.WithAlpha( 0.2f ) );
|
|
Paint.DrawRect( LocalRect.Shrink( 2 ), Theme.ControlRadius );
|
|
}
|
|
|
|
PickerButton.Visible = IsControlHovered;
|
|
}
|
|
|
|
private string BuildName( GameObject go )
|
|
{
|
|
if ( !ShowFullName )
|
|
{
|
|
return go.Name;
|
|
}
|
|
|
|
string str = "";
|
|
|
|
if ( go.Parent.IsValid() && go.Parent is not Scene )
|
|
{
|
|
str += $"{BuildName( go.Parent )} > ";
|
|
}
|
|
|
|
str += $"{go.Name}";
|
|
|
|
return str;
|
|
}
|
|
|
|
void UpdateFromAsset( Asset asset )
|
|
{
|
|
if ( asset.TryLoadResource( out PrefabFile prefabFile ) )
|
|
{
|
|
PropertyStartEdit();
|
|
SerializedProperty.SetValue( SceneUtility.GetPrefabScene( prefabFile ) );
|
|
PropertyFinishEdit();
|
|
}
|
|
}
|
|
|
|
void UpdateFromGameObject( GameObject go )
|
|
{
|
|
PropertyStartEdit();
|
|
SerializedProperty.SetValue( go );
|
|
PropertyFinishEdit();
|
|
}
|
|
|
|
void UpdateFromAssets( Asset[] assets )
|
|
{
|
|
if ( assets.Length == 0 ) return;
|
|
|
|
if ( assets[0].TryLoadResource( out PrefabFile prefabFile ) )
|
|
{
|
|
PropertyStartEdit();
|
|
SerializedProperty.SetValue( SceneUtility.GetPrefabScene( prefabFile ) );
|
|
PropertyFinishEdit();
|
|
}
|
|
|
|
if ( IsInList )
|
|
{
|
|
var list = Parent.Parent as ListControlWidget;
|
|
for ( int i = 1; i < assets.Length; i++ )
|
|
{
|
|
if ( assets[i].TryLoadResource( out PrefabFile newPrefabFile ) )
|
|
{
|
|
list.Collection.Add( SceneUtility.GetPrefabScene( newPrefabFile ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnMouseClick( MouseEvent e )
|
|
{
|
|
if ( e.LeftMouseButton )
|
|
{
|
|
e.Accepted = true;
|
|
var go = SerializedProperty.GetValue<GameObject>();
|
|
|
|
if ( ReadOnly ) return;
|
|
|
|
var resource = (go as PrefabScene)?.Source ?? null;
|
|
var asset = resource != null ? AssetSystem.FindByPath( resource.ResourcePath ) : null;
|
|
|
|
// Open Prefab Asset Picker
|
|
var prefabAssetType = AssetType.Find( "prefab", false );
|
|
var picker = AssetPicker.Create( null, prefabAssetType, new AssetPicker.PickerOptions()
|
|
{
|
|
EnableMultiselect = IsInList
|
|
} );
|
|
picker.Window.Title = $"Select GameObject/Prefab";
|
|
picker.OnAssetHighlighted = ( o ) => UpdateFromAsset( o.FirstOrDefault() );
|
|
picker.OnAssetPicked = ( o ) => UpdateFromAssets( o );
|
|
picker.Show();
|
|
|
|
var sceneBrowser = new GameObjectSceneBrowser( null );
|
|
sceneBrowser.OnGameObjectSelect = ( o ) => UpdateFromGameObject( o );
|
|
|
|
if ( picker is GenericPicker genericPicker )
|
|
{
|
|
genericPicker.DockManager.AddDock( "Scene Browser", null, sceneBrowser, DockArea.Center );
|
|
sceneBrowser.ConfirmButton = genericPicker.ConfirmButton;
|
|
sceneBrowser.OnConfirm = () => picker.Close();
|
|
if ( go.IsValid() && go is not PrefabScene )
|
|
{
|
|
sceneBrowser.Focus();
|
|
}
|
|
}
|
|
|
|
picker.SetSelection( asset );
|
|
}
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
PropertyStartEdit();
|
|
|
|
SerializedProperty.SetValue<GameObject>( null );
|
|
SignalValuesChanged();
|
|
|
|
PropertyFinishEdit();
|
|
}
|
|
|
|
protected override void OnDragStart()
|
|
{
|
|
var drag = new Drag( this );
|
|
drag.Data.Object = SerializedProperty.GetValue<GameObject>();
|
|
drag.Execute();
|
|
}
|
|
|
|
public override void OnDragHover( DragEvent ev )
|
|
{
|
|
ev.Action = DropAction.Ignore;
|
|
|
|
if ( ev.Data.OfType<GameObject>().Any() )
|
|
{
|
|
ev.Action = DropAction.Link;
|
|
return;
|
|
}
|
|
|
|
if ( ev.Data.HasFileOrFolder )
|
|
{
|
|
var asset = AssetSystem.FindByPath( ev.Data.Files.First() );
|
|
if ( asset is null ) return;
|
|
if ( asset.AssetType.FileExtension != "prefab" ) return;
|
|
|
|
if ( asset.TryLoadResource( out PrefabFile prefabFile ) )
|
|
{
|
|
ev.Action = DropAction.Link;
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public override void OnDragDrop( DragEvent ev )
|
|
{
|
|
if ( ev.Data.OfType<GameObject>().FirstOrDefault() is { } go )
|
|
{
|
|
PropertyStartEdit();
|
|
|
|
SerializedProperty.SetValue( go );
|
|
|
|
PropertyFinishEdit();
|
|
return;
|
|
}
|
|
|
|
if ( ev.Data.HasFileOrFolder )
|
|
{
|
|
var asset = AssetSystem.FindByPath( ev.Data.Files.First() );
|
|
if ( asset is null ) return;
|
|
if ( asset.AssetType.FileExtension != "prefab" ) return;
|
|
|
|
if ( asset.TryLoadResource( out PrefabFile prefabFile ) )
|
|
{
|
|
PropertyStartEdit();
|
|
|
|
SerializedProperty.SetValue( SceneUtility.GetPrefabScene( prefabFile ) );
|
|
|
|
PropertyFinishEdit();
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|