256 lines
9.3 KiB
C#
256 lines
9.3 KiB
C#
#nullable enable
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using ImGuiNET;
|
|
using T3.Core.Model;
|
|
using T3.Core.Operator;
|
|
using T3.Core.Operator.Slots;
|
|
using T3.Core.Resource.Assets;
|
|
using T3.Editor.Gui.MagGraph.Model;
|
|
using T3.Editor.Gui.MagGraph.States;
|
|
using T3.Editor.Gui.Styling;
|
|
using T3.Editor.Gui.UiHelpers;
|
|
using T3.Editor.UiModel;
|
|
using T3.Editor.UiModel.Helpers;
|
|
using T3.Editor.UiModel.InputsAndTypes;
|
|
using T3.Editor.UiModel.Modification;
|
|
|
|
namespace T3.Editor.Gui.MagGraph.Ui;
|
|
|
|
/// <summary>
|
|
/// Handles dropping items onto graph.
|
|
/// </summary>
|
|
internal static class DropHandling
|
|
{
|
|
/// <summary>
|
|
/// Called once per frame: draws a faint drop preview under the mouse while a symbol or
|
|
/// asset drag is in flight, so the drag reads before a graph window is hovered. The graph's
|
|
/// drop target draws the full-opacity preview on top when dropping is possible.
|
|
/// </summary>
|
|
internal static void DrawDragIndicator()
|
|
{
|
|
if (DragAndDropHandling.TryGetDragData(DragAndDropHandling.DragTypes.Symbol, out var symbolData))
|
|
{
|
|
if (Guid.TryParse(symbolData, out var symbolId)
|
|
&& SymbolUiRegistry.TryGetSymbolUi(symbolId, out var symbolUi))
|
|
{
|
|
DrawDropPreviewItem(symbolUi.Symbol, fade: 0.2f);
|
|
}
|
|
}
|
|
else if (DragAndDropHandling.TryGetDragData(DragAndDropHandling.DragTypes.FileAsset, out var address)
|
|
&& AssetRegistry.TryGetAsset(address, out var asset))
|
|
{
|
|
DrawDropPreviewItem(asset, fade: 0.2f);
|
|
}
|
|
}
|
|
|
|
internal static void HandleDropOnWindow(GraphUiContext context)
|
|
{
|
|
if (!DragAndDropHandling.IsDragging)
|
|
return;
|
|
|
|
ImGui.SetCursorPos(Vector2.Zero);
|
|
ImGui.InvisibleButton("## drop", ImGui.GetWindowSize());
|
|
|
|
if (HandleDropSymbol(context))
|
|
return;
|
|
|
|
if (HandleDropExternalFile(context))
|
|
return;
|
|
|
|
HandleDropAsset(context);
|
|
}
|
|
|
|
private static void HandleDropAsset(GraphUiContext context)
|
|
{
|
|
var assetResult= DragAndDropHandling.TryHandleDropOnItem(DragAndDropHandling.DragTypes.FileAsset, out var address);
|
|
|
|
if (assetResult != DragAndDropHandling.DragInteractionResult.Hovering
|
|
&& assetResult != DragAndDropHandling.DragInteractionResult.Dropped
|
|
|| address == null) return;
|
|
|
|
if (!AssetRegistry.TryGetAsset(address, out var asset))
|
|
{
|
|
Log.Warning($"Can't get asset for {address}");
|
|
return;
|
|
}
|
|
|
|
if (assetResult == DragAndDropHandling.DragInteractionResult.Hovering)
|
|
{
|
|
DrawDropPreviewItem(asset);
|
|
}
|
|
else
|
|
{
|
|
CreateAssetOperatorOnGraph(context, asset, Vector2.Zero);
|
|
}
|
|
}
|
|
|
|
private static bool HandleDropSymbol(GraphUiContext context)
|
|
{
|
|
var result=DragAndDropHandling.TryHandleDropOnItem(DragAndDropHandling.DragTypes.Symbol, out var data);
|
|
|
|
if (result == DragAndDropHandling.DragInteractionResult.Hovering)
|
|
{
|
|
if (Guid.TryParse(data, out var hoveredSymbolId)
|
|
&& SymbolUiRegistry.TryGetSymbolUi(hoveredSymbolId, out var hoveredSymbolUi))
|
|
{
|
|
DrawDropPreviewItem(hoveredSymbolUi.Symbol);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (result != DragAndDropHandling.DragInteractionResult.Dropped)
|
|
return false;
|
|
|
|
if (!Guid.TryParse(data, out var symbolId))
|
|
{
|
|
Log.Warning("Invalid data format for drop? " + data);
|
|
return true;
|
|
}
|
|
|
|
TryCreateSymbolInstanceOnGraph(context, symbolId, Vector2.Zero, out _);
|
|
return false;
|
|
}
|
|
|
|
private static bool HandleDropExternalFile(GraphUiContext context)
|
|
{
|
|
SymbolPackage? package = context.CompositionInstance.Symbol.SymbolPackage;
|
|
if (package == null)
|
|
return false;
|
|
|
|
var result=DragAndDropHandling.TryHandleDropOnItem(DragAndDropHandling.DragTypes.ExternalFile, out var data);
|
|
|
|
var packageResourcesFolder = package.AssetsFolder;
|
|
|
|
if (result == DragAndDropHandling.DragInteractionResult.Hovering)
|
|
{
|
|
var dl = ImGui.GetForegroundDrawList();
|
|
ReadOnlySpan<char> label = $"""
|
|
Import files to...
|
|
{packageResourcesFolder}
|
|
""";
|
|
var labelSize = ImGui.CalcTextSize(label);
|
|
var mousePos = ImGui.GetMousePos() + new Vector2(-30, -40);
|
|
var area = ImRect.RectWithSize(mousePos, labelSize);
|
|
area.Expand(10);
|
|
dl.AddRectFilled(area.Min, area.Max, UiColors.BackgroundFull.Fade(0.7f), 5);
|
|
|
|
dl.AddText(mousePos, UiColors.ForegroundFull, label);
|
|
return true;
|
|
}
|
|
|
|
if (result != DragAndDropHandling.DragInteractionResult.Dropped
|
|
|| data == null)
|
|
return false;
|
|
|
|
var filePaths = data.Split("|");
|
|
|
|
var dropOffset = Vector2.Zero;
|
|
|
|
foreach (var filepath in filePaths)
|
|
{
|
|
if (!FileImport.TryImportDroppedFile(filepath, package,null, out var asset))
|
|
continue;
|
|
|
|
if (!CreateAssetOperatorOnGraph(context, asset, dropOffset))
|
|
return false;
|
|
|
|
dropOffset += new Vector2(20, 100);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static void DrawDropPreviewItem(Asset asset, float fade = 1)
|
|
{
|
|
if (asset.AssetType.PrimaryOperators.Count == 0)
|
|
return;
|
|
|
|
if (!SymbolUiRegistry.TryGetSymbolUi(asset.AssetType.PrimaryOperators[0], out var mainSymbolUi))
|
|
{
|
|
return;
|
|
}
|
|
|
|
DrawDropPreviewItem(mainSymbolUi.Symbol, fade);
|
|
}
|
|
|
|
private static void DrawDropPreviewItem(Symbol symbol, float fade = 1)
|
|
{
|
|
var color = symbol.OutputDefinitions.Count > 0
|
|
? TypeUiRegistry.GetPropertiesForType(symbol.OutputDefinitions[0]?.ValueType).Color
|
|
: UiColors.Gray;
|
|
|
|
// Foreground list so the preview isn't clipped at the graph window's edges.
|
|
var dl = ImGui.GetForegroundDrawList();
|
|
var pos = ImGui.GetMousePos();
|
|
dl.AddRectFilled(pos, pos + MagGraphItem.GridSize, color.Fade(fade), 4);
|
|
}
|
|
|
|
private static bool CreateAssetOperatorOnGraph(GraphUiContext context,
|
|
Asset asset,
|
|
Vector2 dropOffset)
|
|
{
|
|
if (asset.AssetType.PrimaryOperators.Count == 0)
|
|
{
|
|
Log.Warning($"{asset.Address} of type {asset.AssetType} has no matching operator symbols");
|
|
return false;
|
|
}
|
|
|
|
if (!TryCreateSymbolInstanceOnGraph(context, asset.AssetType.PrimaryOperators[0], dropOffset, out var newInstance))
|
|
{
|
|
Log.Warning("Failed to create operator instance");
|
|
return false;
|
|
}
|
|
|
|
if (!SymbolAnalysis.TryGetFileInputFromInstance(newInstance, out var stringInput, out _))
|
|
{
|
|
Log.Warning("Failed to get file path parameter from op");
|
|
return false;
|
|
}
|
|
|
|
Log.Debug($"Created {newInstance} with {asset.Address}", newInstance);
|
|
|
|
stringInput.TypedInputValue.Assign(new InputValue<string>(asset.Address));
|
|
stringInput.DirtyFlag.ForceInvalidate();
|
|
stringInput.Parent.Parent?.Symbol.InvalidateInputInAllChildInstances(stringInput);
|
|
stringInput.Input.IsDefault = false;
|
|
|
|
var parent = stringInput.Parent.Parent;
|
|
if (parent == null)
|
|
return false;
|
|
|
|
AssetRegistry.AddAssetReference(asset,
|
|
symbolId: parent.Symbol.Id,
|
|
stringInput.Parent.SymbolChildId,
|
|
stringInput.Id);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool TryCreateSymbolInstanceOnGraph(GraphUiContext context,
|
|
Guid guid,
|
|
Vector2 offsetInScreen,
|
|
[NotNullWhen(true)] out Instance? newInstance)
|
|
{
|
|
newInstance = null;
|
|
if (SymbolUiRegistry.TryGetSymbolUi(guid, out var symbolUi))
|
|
{
|
|
var symbol = symbolUi.Symbol;
|
|
var posOnCanvas = context.View.InverseTransformPositionFloat(ImGui.GetMousePos() + offsetInScreen);
|
|
if (!SymbolUiRegistry.TryGetSymbolUi(context.CompositionInstance.Symbol.Id, out var compositionOpSymbolUi))
|
|
{
|
|
Log.Warning("Failed to get symbol id for " + context.CompositionInstance.SymbolChildId);
|
|
return false;
|
|
}
|
|
|
|
var childUi = GraphOperations.AddSymbolChild(symbol, compositionOpSymbolUi, posOnCanvas);
|
|
newInstance = context.CompositionInstance.Children[childUi.Id];
|
|
context.Selector.SetSelection(childUi, newInstance);
|
|
context.Layout.FlagStructureAsChanged();
|
|
return true;
|
|
}
|
|
|
|
Log.Warning($"Symbol {guid} not found in registry");
|
|
return false;
|
|
}
|
|
} |