Files
sbox-public/engine/Sandbox.Tools/Qt/QObject.cs
Matt Stevens 18da35110c Qt Advanced Docking System (#4041)
* 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>
2026-07-12 12:41:08 +01:00

194 lines
3.7 KiB
C#

using System;
using System.Runtime.InteropServices;
namespace Editor
{
public class QObject : IValid
{
internal static Dictionary<Native.QObject, QObject> AllObjects = new();
internal Native.QObject _object;
internal Dictionary<GCHandle, CallbackMethod> _handles = new();
bool IsDestroyed;
bool IValid.IsValid => IsValid;
[Hide]
public bool IsValid => _object.IsValid && !IsDestroyed;
internal virtual void NativeInit( IntPtr ptr )
{
if ( ptr == default )
throw new System.Exception( "QObject was null!" );
_object = ptr;
AllObjects[_object] = this;
// Get notified when object is destroyed - so we can invalidate our pointers
WidgetUtil.OnObject_Destroyed( ptr, Callback( NativeShutdown ) );
EditorEvent.Register( this );
}
public virtual void OnDestroyed()
{
}
internal virtual void NativeShutdown()
{
EditorEvent.Unregister( this );
Sandbox.InteropSystem.Free( this );
AllObjects.Remove( _object );
_object = default;
// Can free all the allocated handles now
// because they won't get called
foreach ( var handle in _handles )
{
handle.Key.Free();
}
_handles.Clear();
_handles = null;
OnDestroyed();
}
internal delegate void CallbackMethod( IntPtr qobj );
internal IntPtr Callback( Action cb )
{
CallbackMethod wrapped = ( IntPtr qobj ) =>
{
try
{
cb();
}
catch ( System.Exception ex )
{
try { Log.Error( ex ); }
catch { }
}
};
_handles.Add( GCHandle.Alloc( wrapped ), wrapped );
return Marshal.GetFunctionPointerForDelegate( wrapped );
}
public void Destroy()
{
if ( IsDestroyed )
return;
IsDestroyed = true;
EditorEvent.Unregister( this );
if ( _object.IsValid )
{
OnDestroyingLater();
_object.deleteMuchLater();
}
}
internal virtual void OnDestroyingLater()
{
}
internal unsafe Native.QObject[] GetChildren()
{
if ( !_object.IsValid )
{
return Array.Empty<Native.QObject>();
}
var c = WidgetUtil.GetChildrenCount( _object );
if ( c <= 0 )
return Array.Empty<Native.QObject>();
var list = new Native.QObject[c];
fixed ( Native.QObject* ptr = list )
{
WidgetUtil.GetChildren( _object, ptr, list.Length );
}
return list;
}
static internal QObject FindOrCreate( Native.QObject obj )
{
if ( AllObjects.TryGetValue( obj, out var handle ) )
return handle;
// leafiest first!
{
Native.QPushButton ptr = (Native.QPushButton)obj;
if ( ptr.IsValid ) return new Button( ptr );
}
{
Native.QTabBar ptr = (Native.QTabBar)obj;
if ( ptr.IsValid ) return new TabBar( ptr );
}
{
Native.QMenuBar ptr = (Native.QMenuBar)obj;
if ( ptr.IsValid ) return new MenuBar( ptr );
}
{
Native.CDockWidget ptr = (Native.CDockWidget)obj;
if ( ptr.IsValid ) return new DockWidget( ptr );
}
{
Native.QWidget ptr = (Native.QWidget)obj;
if ( ptr.IsValid ) return new Widget( ptr );
}
{
QMimeData ptr = (QMimeData)obj;
if ( ptr.IsValid ) return new DragData( ptr );
}
return null;
}
public void SetProperty( string name, bool value )
{
_object.setProperty( name, value );
}
public void SetProperty( string name, float value )
{
_object.setProperty( name, value );
}
public void SetProperty( string name, string value )
{
_object.setProperty( name, value );
}
public Sandbox.Bind.Builder Bind( string targetName, Action onChanged = null )
{
var bb = new Sandbox.Bind.Builder
{
system = Sandbox.Internal.GlobalToolsNamespace.BindSystem
};
return bb.Set( this, targetName, onChanged );
}
internal void SetParent( QObject obj )
{
_object.setParent( obj._object );
}
}
}