353 lines
13 KiB
C#
353 lines
13 KiB
C#
#nullable enable
|
|
|
|
using ImGuiNET;
|
|
using SharpDX.Direct3D11;
|
|
using T3.Core.DataTypes.Vector;
|
|
using T3.Core.Operator;
|
|
using T3.Core.Utils;
|
|
using T3.Editor.Gui.Interaction.Variations;
|
|
using T3.Editor.Gui.Interaction.Variations.Model;
|
|
using T3.Editor.Gui.Styling;
|
|
using T3.Editor.Gui.UiHelpers;
|
|
using T3.Editor.Gui.UiHelpers.Thumbnails;
|
|
using T3.Editor.UiModel.Commands;
|
|
using T3.Editor.UiModel.Commands.Graph;
|
|
using T3.Editor.UiModel.ProjectHandling;
|
|
using T3.Editor.UiModel.Selection;
|
|
|
|
namespace T3.Editor.Gui.Windows.Variations;
|
|
|
|
internal static class VariationThumbnail
|
|
{
|
|
public static bool Draw(VariationBaseCanvas canvas, Variation variation, Instance instanceForBlending, ImDrawListPtr drawList, ShaderResourceView? thumbnailAtlasSrv, ThumbnailManager.ThumbnailRect thumbnail)
|
|
{
|
|
var components = ProjectView.Focused;
|
|
if (components == null)
|
|
return false;
|
|
|
|
if (VariationForRenaming == variation)
|
|
{
|
|
ImGui.PushID(variation.ActivationIndex);
|
|
ImGui.SetCursorScreenPos(new Vector2(30, ImGui.GetFrameHeight()) + ImGui.GetWindowPos());
|
|
ImGui.SetKeyboardFocusHere();
|
|
ImGui.InputText("##label", ref variation.Title, 256);
|
|
|
|
if (ImGui.IsItemDeactivatedAfterEdit() || ImGui.IsItemDeactivated())
|
|
{
|
|
VariationForRenaming = null;
|
|
if (VariationHandling.ActivePoolForPresets != null)
|
|
{
|
|
VariationHandling.ActivePoolForPresets.SaveVariationsToFile();
|
|
}
|
|
else if (VariationHandling.ActiveInstanceForSnapshots != null)
|
|
{
|
|
VariationHandling.ActivePoolForSnapshots?.SaveVariationsToFile();
|
|
}
|
|
}
|
|
|
|
ImGui.PopID();
|
|
}
|
|
|
|
var focusOpacity = 1f;
|
|
|
|
_canvas = canvas;
|
|
var pMin = canvas.TransformPosition(variation.PosOnCanvas);
|
|
var sizeOnScreen = canvas.TransformDirectionFloored(ThumbnailSize);
|
|
var pMax = pMin + sizeOnScreen;
|
|
|
|
var areaOnScreen = new ImRect(pMin, pMax);
|
|
drawList.AddRectFilled(pMin, pMax, UiColors.Gray.Fade(0.1f * focusOpacity));
|
|
CustomComponents.FillWithStripes(drawList, areaOnScreen, canvas.Scale.X);
|
|
|
|
if (thumbnailAtlasSrv != null && thumbnail.IsReady)
|
|
{
|
|
drawList.AddImage(thumbnailAtlasSrv.NativePointer,
|
|
pMin,
|
|
pMax,
|
|
thumbnail.UvMin,
|
|
thumbnail.UvMax, Color.White.Fade(focusOpacity)
|
|
);
|
|
}
|
|
|
|
drawList.AddRect(pMin, pMax, UiColors.Gray.Fade(0.2f * focusOpacity));
|
|
|
|
variation.IsSelected = CanvasElementSelection.IsNodeSelected(variation);
|
|
if (variation.IsSelected)
|
|
{
|
|
drawList.AddRect(pMin - Vector2.One, pMax + Vector2.One, UiColors.Selection);
|
|
}
|
|
|
|
const int bottomPadding = 15;
|
|
drawList.AddRectFilledMultiColor(pMin + new Vector2(0, sizeOnScreen.Y - bottomPadding - 20),
|
|
pMax ,
|
|
UiColors.BackgroundFull.Fade(0),
|
|
UiColors.BackgroundFull.Fade(0),
|
|
UiColors.BackgroundFull.Fade(0.6f),
|
|
UiColors.BackgroundFull.Fade(0.6f)
|
|
);
|
|
ImGui.PushClipRect(pMin, pMax, true);
|
|
ImGui.PushFont(Fonts.FontSmall);
|
|
|
|
var fade = canvas.Scale.X.RemapAndClamp(0.3f, 0.6f, 0, 1) * focusOpacity;
|
|
drawList.AddText(pMin + new Vector2(4, sizeOnScreen.Y - bottomPadding),
|
|
UiColors.Text.Fade(1f * fade),
|
|
string.IsNullOrEmpty(variation.Title) ? "Untitled" : variation.Title);
|
|
|
|
drawList.AddText(pMin + new Vector2(sizeOnScreen.X - bottomPadding, sizeOnScreen.Y - bottomPadding),
|
|
UiColors.Text.Fade(0.3f * fade),
|
|
$"{variation.ActivationIndex:00}");
|
|
|
|
ImGui.PopFont();
|
|
|
|
if (variation.State == Variation.States.Active)
|
|
{
|
|
drawList.AddCircleFilled(pMax - Vector2.One * 4, 2, UiColors.WidgetActiveLine);
|
|
}
|
|
|
|
ImGui.SetCursorScreenPos(pMin);
|
|
ImGui.PushID(variation.Id.GetHashCode());
|
|
|
|
ImGui.InvisibleButton("##thumbnail", pMax-pMin);
|
|
var isHovered = ImGui.IsItemVisible() && ImGui.IsItemHovered() && !ImGui.IsAnyItemActive();
|
|
if (isHovered)
|
|
{
|
|
//Log.Debug("here");
|
|
drawList.AddRect(pMin,
|
|
pMax,
|
|
UiColors.ForegroundFull.Fade(0.3f),
|
|
0,
|
|
ImDrawFlags.None,
|
|
2
|
|
);
|
|
}
|
|
|
|
if (_canvas.IsBlendingActive)
|
|
{
|
|
if (_canvas.TryGetBlendWeight(variation, out var weight))
|
|
{
|
|
DrawBlendIndicator(drawList, areaOnScreen, weight);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Handle hover
|
|
if (isHovered)
|
|
{
|
|
|
|
if (variation.IsSnapshot)
|
|
{
|
|
//var nodeSelection = components.NodeSelection;
|
|
foreach (var childId in variation.ParameterSetsForChildIds.Keys)
|
|
{
|
|
FrameStats.AddHoveredId(childId);
|
|
}
|
|
}
|
|
|
|
if (UserSettings.Config.VariationHoverPreview)
|
|
{
|
|
if (_hoveredVariation == null)
|
|
{
|
|
_hoveredVariation = variation;
|
|
_canvas.StartHover(variation, instanceForBlending);
|
|
}
|
|
|
|
|
|
if (ImGui.GetIO().KeyAlt)
|
|
{
|
|
var mouseX = ImGui.GetMousePos().X;
|
|
var blend = (mouseX - pMin.X) / sizeOnScreen.X;
|
|
_canvas.StartBlendTo(variation, blend, instanceForBlending);
|
|
DrawBlendIndicator(drawList, areaOnScreen, blend);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_hoveredVariation == variation)
|
|
{
|
|
_canvas.StopHover();
|
|
_hoveredVariation = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Annotate the live cross-fade weight from the pool's persistent mix (picker faders, MIDI,
|
|
// arrows). Yields to the canvas's own blend gestures, which draw their own indicator.
|
|
var altPreviewing = isHovered && ImGui.GetIO().KeyAlt && UserSettings.Config.VariationHoverPreview;
|
|
if (!_canvas.IsBlendingActive && !altPreviewing && _canvas.TryGetLiveBlendWeight(variation, out var liveWeight))
|
|
{
|
|
DrawBlendIndicator(drawList, areaOnScreen, liveWeight);
|
|
}
|
|
|
|
var modified = false;
|
|
if (!_canvas.IsBlendingActive)
|
|
{
|
|
modified |= HandleMovement(variation, instanceForBlending);
|
|
}
|
|
|
|
ImGui.PopID();
|
|
ImGui.PopClipRect();
|
|
return modified;
|
|
}
|
|
|
|
private static void DrawBlendIndicator(ImDrawListPtr drawList, ImRect areaOnScreen, float blend)
|
|
{
|
|
var x = areaOnScreen.Min.X + areaOnScreen.GetWidth() * blend;
|
|
drawList.AddRectFilled(new Vector2(x, areaOnScreen.Min.Y),
|
|
areaOnScreen.Max,
|
|
new Color(0.1f, 0.1f, 0.1f, 0.7f));
|
|
drawList.AddRectFilled(new Vector2(x, areaOnScreen.Min.Y),
|
|
new Vector2(x, areaOnScreen.Max.Y),
|
|
new Color(0.9f, 0.9f, 0.9f, 0.5f));
|
|
|
|
ImGui.PushFont(Fonts.FontLarge);
|
|
var label = $"{blend * 100:0}%";
|
|
var labelSize = ImGui.CalcTextSize(label);
|
|
drawList.AddText(areaOnScreen.GetCenter() - labelSize / 2, UiColors.ForegroundFull, label);
|
|
ImGui.PopFont();
|
|
}
|
|
|
|
private static bool HandleMovement(Variation variation, Instance instanceForBlending)
|
|
{
|
|
if (ImGui.IsItemActive())
|
|
{
|
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
|
{
|
|
_draggedNodeId = variation.Id;
|
|
if (variation.IsSelected)
|
|
{
|
|
_draggedNodes = CanvasElementSelection.GetSelectedNodes<ISelectableCanvasObject>().ToList();
|
|
}
|
|
else
|
|
{
|
|
_draggedNodes.Add(variation);
|
|
}
|
|
|
|
_moveCommand = new ModifyCanvasElementsCommand(_canvas, _draggedNodes, CanvasElementSelection);
|
|
}
|
|
|
|
HandleNodeDragging(variation);
|
|
}
|
|
else if (ImGui.IsMouseReleased(0) && _moveCommand != null)
|
|
{
|
|
if (_draggedNodeId != variation.Id)
|
|
return false;
|
|
|
|
_draggedNodeId = Guid.Empty;
|
|
_draggedNodes.Clear();
|
|
|
|
var wasDragging = ImGui.GetMouseDragDelta(ImGuiMouseButton.Left).LengthSquared() > UserSettings.Config.ClickThreshold;
|
|
if (wasDragging)
|
|
{
|
|
_moveCommand.StoreCurrentValues();
|
|
UndoRedoStack.Add(_moveCommand);
|
|
return true;
|
|
}
|
|
|
|
if (!CanvasElementSelection.IsNodeSelected(variation))
|
|
{
|
|
if (!ImGui.GetIO().KeyShift)
|
|
{
|
|
CanvasElementSelection.Clear();
|
|
_hoveredVariation = null;
|
|
_canvas.Apply(variation, instanceForBlending);
|
|
}
|
|
|
|
CanvasElementSelection.AddSelection(variation);
|
|
}
|
|
else
|
|
{
|
|
if (ImGui.GetIO().KeyShift)
|
|
{
|
|
CanvasElementSelection.DeselectNode(variation);
|
|
}
|
|
else
|
|
{
|
|
_hoveredVariation = null;
|
|
_canvas.Apply(variation, instanceForBlending);
|
|
}
|
|
}
|
|
|
|
_moveCommand = null;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static void HandleNodeDragging(ISelectableCanvasObject draggedNode)
|
|
{
|
|
if (!ImGui.IsMouseDragging(ImGuiMouseButton.Left))
|
|
{
|
|
_isDragging = false;
|
|
return;
|
|
}
|
|
|
|
if (!_isDragging)
|
|
{
|
|
_dragStartDelta = ImGui.GetMousePos() - _canvas.TransformPosition(draggedNode.PosOnCanvas);
|
|
_isDragging = true;
|
|
}
|
|
|
|
var newDragPos = ImGui.GetMousePos() - _dragStartDelta;
|
|
var newDragPosInCanvas = _canvas.InverseTransformPositionFloat(newDragPos);
|
|
|
|
// Implement snapping to others
|
|
var bestDistanceInCanvas = float.PositiveInfinity;
|
|
var targetSnapPositionInCanvas = Vector2.Zero;
|
|
|
|
foreach (var offset in _snapOffsetsInCanvas)
|
|
{
|
|
foreach (var neighbor in _canvas.GetSelectables())
|
|
{
|
|
if (neighbor == draggedNode || _draggedNodes.Contains(neighbor))
|
|
continue;
|
|
|
|
var snapToNeighborPos = neighbor.PosOnCanvas + offset;
|
|
|
|
var d = Vector2.Distance(snapToNeighborPos, newDragPosInCanvas);
|
|
if (!(d < bestDistanceInCanvas))
|
|
continue;
|
|
|
|
targetSnapPositionInCanvas = snapToNeighborPos;
|
|
bestDistanceInCanvas = d;
|
|
}
|
|
}
|
|
|
|
var snapDistanceInCanvas = _canvas.InverseTransformDirection(new Vector2(6, 6)).Length();
|
|
var isSnapping = bestDistanceInCanvas < snapDistanceInCanvas;
|
|
|
|
var moveDeltaOnCanvas = isSnapping
|
|
? targetSnapPositionInCanvas - draggedNode.PosOnCanvas
|
|
: newDragPosInCanvas - draggedNode.PosOnCanvas;
|
|
|
|
// Drag selection
|
|
foreach (var e in _draggedNodes)
|
|
{
|
|
e.PosOnCanvas += moveDeltaOnCanvas;
|
|
}
|
|
}
|
|
|
|
private static Variation? _hoveredVariation;
|
|
private static bool _isDragging;
|
|
|
|
private static VariationBaseCanvas _canvas = null!;
|
|
|
|
private static CanvasElementSelection CanvasElementSelection => _canvas.CanvasElementSelection;
|
|
private static Guid _draggedNodeId;
|
|
private static List<ISelectableCanvasObject> _draggedNodes = new();
|
|
public static readonly Vector2 ThumbnailSize = new(160, 160 / ThumbnailManager.AspectRatio);
|
|
|
|
public static readonly Vector2 SnapPadding = new(3, 3);
|
|
|
|
private static readonly Vector2[] _snapOffsetsInCanvas =
|
|
{
|
|
new(ThumbnailSize.X + SnapPadding.X, 0),
|
|
new(-ThumbnailSize.X - SnapPadding.X, 0),
|
|
new(0, ThumbnailSize.Y + SnapPadding.Y),
|
|
new(0, -ThumbnailSize.Y - SnapPadding.Y)
|
|
};
|
|
|
|
private static ModifyCanvasElementsCommand? _moveCommand;
|
|
private static Vector2 _dragStartDelta;
|
|
public static Variation? VariationForRenaming;
|
|
} |