#nullable enable using System.Diagnostics; using System.Text; using ImGuiNET; using T3.Core.DataTypes.Vector; using T3.Core.Operator; using T3.Editor.Gui.Input; using T3.Editor.Gui.MagGraph.Interaction; using T3.Editor.Gui.Styling; using T3.Editor.UiModel; using T3.Editor.UiModel.Modification; using T3.Editor.UiModel.ProjectHandling; using T3.Editor.UiModel.Selection; namespace T3.Editor.Gui.Graph.Dialogs; internal static class EditTourPointsPopup { private static bool _isOpen; internal static void ShowNextFrame() { _isOpen = true; } private static string GetSelectionMarkdown() { if (_compositionUi == null) return string.Empty; var sb = new StringBuilder(); var hasCurrentCompositionTour = _compositionUi.TourPoints.Count > 0; if (hasCurrentCompositionTour) { TourDataMarkdownExport.WriteSymbolTourMarkdown(_compositionUi, sb); } else { var orderedChildUis = _selectedChildIds .Select(id => _compositionUi.ChildUis.TryGetValue(id, out var childUi) ? childUi : null) .Where(childUi => childUi != null) .OrderBy(childUi => childUi!.PosOnCanvas.Y); foreach (var childUi in orderedChildUis) { if (childUi == null) continue; TourDataMarkdownExport.WriteSymbolTourMarkdown(childUi.SymbolChild.Symbol.GetSymbolUi(), sb); sb.AppendLine(); } } return sb.ToString(); } internal static ChangeSymbol.SymbolModificationResults Draw(Symbol operatorSymbol, ProjectView projectView) { var result = ChangeSymbol.SymbolModificationResults.Nothing; if (!_isOpen) return ChangeSymbol.SymbolModificationResults.Nothing; ImGui.SetNextWindowSize(new Vector2(500, 500) * T3Ui.UiScaleFactor, ImGuiCond.Once); ImGui.PushStyleVar(ImGuiStyleVar.WindowBorderSize, 1); if (ImGui.Begin("Edit tour points", ref _isOpen)) { ImGui.PushStyleColor(ImGuiCol.ChildBg, UiColors.WindowBackground.Fade(0.8f).Rgba); ImGui.BeginChild("Inner", Vector2.Zero, ImGuiChildFlags.None, ImGuiWindowFlags.NoMove); { _compositionUi = operatorSymbol.GetSymbolUi(); if (CustomComponents.IconButton(Icon.CopyToClipboard, Vector2.Zero)) { ImGui.SetClipboardText(GetSelectionMarkdown()); } if (ImGui.IsItemHovered()) { ImGui.BeginTooltip(); { ImGui.PushFont(Fonts.Code); ImGui.PushTextWrapPos(400); ImGui.TextWrapped(GetSelectionMarkdown()); ImGui.PopTextWrapPos(); ImGui.PopFont(); } ImGui.EndTooltip(); } ImGui.SameLine(); if (CustomComponents.IconButton(Icon.PasteFromClipboard, Vector2.Zero)) { TourDataMarkdownExport.TryPasteTourData(_compositionUi, projectView); } // Handle selection _selectedChildIds.Clear(); _firstSelectedChildId = Guid.Empty; ImGui.Indent(20); FormInputs.AddVerticalSpace(20); foreach (var c in projectView.NodeSelection.GetSelectedChildUis()) { if (_firstSelectedChildId == Guid.Empty) _firstSelectedChildId = c.Id; _selectedChildIds.Add(c.Id); } var modified = false; _completedDragging = false; // List... if (!_isDragging && _listOrderWhileDragging.Count != _compositionUi.TourPoints.Count) { _listOrderWhileDragging.Clear(); for (var index = 0; index < _compositionUi.TourPoints.Count; index++) { _listOrderWhileDragging.Add(index); } } for (var index = 0; index < _compositionUi.TourPoints.Count; index++) { var tourPoint = _compositionUi.TourPoints[index]; ImGui.PushID(tourPoint.Id.GetHashCode()); // Draw floating + button... { var keepCursorPos = ImGui.GetCursorPos(); var h = ImGui.GetFrameHeight(); ImGui.SetCursorPos(keepCursorPos - new Vector2(h, h * 0.5f)); if (CustomComponents.TransparentIconButton(Icon.Plus, Vector2.Zero)) { InsertNewTourPoint(index); } ImGui.SetCursorPos(keepCursorPos); } modified |= DrawItem(tourPoint, index); FormInputs.AddVerticalSpace(2); ImGui.PopID(); } if (_completedDragging) { _isDragging = false; _listOrderWhileDragging.Clear(); modified = true; } if (CustomComponents.DisablableButton("Add Tour Point", CanAdd)) { InsertNewTourPoint(_compositionUi.TourPoints.Count); } if (modified) _compositionUi.FlagAsModified(); } ImGui.EndChild(); ImGui.PopStyleColor(); } ImGui.End(); ImGui.PopStyleVar(); return result; } private static readonly Dictionary _lastItemHeights = new(); private static bool DrawItem(TourPoint tourPoint, int index) { Debug.Assert(_compositionUi != null); var modified = false; var isSelected = _selectedChildIds.Contains(tourPoint.ChildId) && tourPoint.Style == TourPoint.Styles.InfoFor; ImGui.PushStyleColor(ImGuiCol.ChildBg, isSelected ? Color.Mix(UiColors.BackgroundButton, UiColors.BackgroundActive, 0.1f).Rgba : UiColors.BackgroundButton); ImGui.PushStyleVar(ImGuiStyleVar.ChildRounding, 5); ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(5, 5)); // inner spacing if (!_lastItemHeights.TryGetValue(index, out var height)) { height = 0; } ImGui.BeginChild("item", new Vector2(-5, height), ImGuiChildFlags.Borders); { FormInputs.AddVerticalSpace(3); var padding = 10; ImGui.PushFont(Fonts.FontSmall); { ImGui.Indent(6); ImGui.SetNextItemWidth(10); ImGui.AlignTextToFramePadding(); ImGui.PushStyleColor(ImGuiCol.Text, UiColors.TextMuted.Rgba); ImGui.PushStyleColor(ImGuiCol.Button, Color.Transparent.Rgba); var dragIndex = _isDragging ? _listOrderWhileDragging[index] : index; ImGui.Button($"{dragIndex + 1}."); ImGui.PopStyleColor(2); if (ImGui.IsItemActive()) { _isDragging = true; var itemMin = ImGui.GetWindowPos(); var itemMax = ImGui.GetWindowPos() + ImGui.GetWindowSize(); ImGui.GetForegroundDrawList().AddRect(itemMin, itemMax, Color.Gray); var mouseY = ImGui.GetMousePos().Y; var halfHeight = ImGui.GetWindowSize().Y / 2; var indexDelta = 0; if (mouseY < itemMin.Y - halfHeight && index > 0) { indexDelta = -1; } else if (mouseY > itemMax.Y + halfHeight && index < _compositionUi.TourPoints.Count - 1) { indexDelta = 1; } if (indexDelta != 0) { var newIndex = index + indexDelta; if (newIndex >= 0 && index < _compositionUi.TourPoints.Count && newIndex < _compositionUi.TourPoints.Count) { (_compositionUi.TourPoints[newIndex], _compositionUi.TourPoints[index]) = (_compositionUi.TourPoints[index], _compositionUi.TourPoints[newIndex]); (_listOrderWhileDragging[newIndex], _listOrderWhileDragging[index]) = (_listOrderWhileDragging[index], _listOrderWhileDragging[newIndex]); } } } if (ImGui.IsItemDeactivated()) { _completedDragging = true; } // TourPoint type ImGui.SameLine(0, padding); ImGui.SetNextItemWidth(120); modified |= FormInputs.DrawEnumDropdown(ref tourPoint.Style, "style"); // Highlight child if (_compositionUi.ChildUis.TryGetValue(tourPoint.ChildId, out var childUi)) { ImGui.SameLine(0, padding); ImGui.TextUnformatted("on"); ImGui.SameLine(0, padding); if (ImGui.Button(childUi.SymbolChild.ReadableName)) { var comp = ProjectView.Focused!.CompositionInstance; if (comp != null && comp.Children.TryGetChildInstance(tourPoint.ChildId, out var instance)) { ProjectView.Focused.GraphView.OpenAndFocusInstance(instance.InstancePath); } } if (ImGui.IsItemHovered()) { FrameStats.AddHoveredId(tourPoint.ChildId); } } else { CustomComponents.TooltipForLastItem("" + tourPoint.ChildId); } if (!CanAdd && tourPoint.ChildId != _firstSelectedChildId) { ImGui.SameLine(); if (CustomComponents.IconButton(Icon.Close, Vector2.Zero)) { tourPoint.ChildId = Guid.Empty; tourPoint.InputId = Guid.Empty; modified = true; } } else if (CanAdd && tourPoint.ChildId != _firstSelectedChildId) { ImGui.SameLine(); if (CustomComponents.IconButton(Icon.ApplySelection, Vector2.Zero)) { tourPoint.ChildId = _firstSelectedChildId; tourPoint.InputId = Guid.Empty; modified = true; } } if (tourPoint.ChildId != Guid.Empty) { ImGui.SameLine(0, 10); ImGui.SetNextItemWidth(100); ImGui.PushStyleColor(ImGuiCol.FrameBg, UiColors.BackgroundButton.Rgba); ImGui.PushStyleColor(ImGuiCol.FrameBgHovered, UiColors.BackgroundButton.Rgba); ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(4)); if (_compositionUi.ChildUis.TryGetValue(tourPoint.ChildId, out var symbolUi)) { var label = "select input"; if (tourPoint.InputId != Guid.Empty) { var inputDefinition = symbolUi.SymbolChild.Symbol.InputDefinitions.FirstOrDefault(i => i.Id == tourPoint.InputId); if (inputDefinition != null) label = inputDefinition.Name; } if (ImGui.BeginCombo("##test", label)) { if (ImGui.Selectable("--none--", tourPoint.InputId == Guid.Empty)) { tourPoint.InputId = Guid.Empty; modified = true; } foreach (var i in symbolUi.SymbolChild.Symbol.InputDefinitions) { if (ImGui.Selectable(i.Name, i.Id == tourPoint.InputId)) { tourPoint.InputId = i.Id; modified = true; } } ImGui.EndCombo(); } } ImGui.PopStyleColor(2); ImGui.PopStyleVar(); } } ImGui.PopFont(); var x = ImGui.GetContentRegionAvail().X - ImGui.GetFrameHeight(); ImGui.SameLine(x); if (CustomComponents.TransparentIconButton(Icon.Trash, Vector2.Zero, CustomComponents.ButtonStates.Default)) { _compositionUi.TourPoints.Remove(tourPoint); modified = true; } if (_editedTourPointId == tourPoint.Id) { if (_shouldFocusInput) { ImGui.SetKeyboardFocusHere(); _shouldFocusInput = false; } if (ImGui.InputTextMultiline("##Text", ref tourPoint.Description, 16384, new Vector2(-10, 60) * T3Ui.UiScaleFactor)) { modified = true; } if (ImGui.IsItemDeactivated()) { _editedTourPointId = Guid.Empty; } } else { var text = string.IsNullOrEmpty(tourPoint.Description) ? "Add description..." : tourPoint.Description; ImGui.TextWrapped(text); if (ImGui.IsItemClicked()) { _editedTourPointId = tourPoint.Id; _shouldFocusInput = true; } } ImGui.Unindent(10); // Activate tour point if (ImGui.IsWindowHovered() && ImGui.IsMouseClicked(ImGuiMouseButton.Left)) { TourInteraction.SetProgressIndex(_compositionUi.Symbol.Id, index); } FormInputs.AddVerticalSpace(); _lastItemHeights[index] = ImGui.GetCursorPosY(); } ImGui.EndChild(); ImGui.PopStyleVar(2); ImGui.PopStyleColor(); return modified; } private static void InsertNewTourPoint(int index) { _compositionUi?.TourPoints.Insert(index, new TourPoint { Description = string.Empty, Id = Guid.NewGuid(), ChildId = _firstSelectedChildId, Style = TourPoint.Styles.Info }); } private static bool CanAdd => _selectedChildIds.Count == 1; private static bool _shouldFocusInput; private static bool _isDragging; private static readonly List _listOrderWhileDragging = []; private static bool _completedDragging; private static Guid _editedTourPointId; private static SymbolUi? _compositionUi; private static Guid _firstSelectedChildId; private static readonly HashSet _selectedChildIds = []; }