#nullable enable using ImGuiNET; using T3.Core.DataTypes.Vector; using T3.Core.Operator; using T3.Editor.Gui.Styling; namespace T3.Editor.Gui.Windows.TimeLine; /// /// Bottom-of-timeline detail pane that hosts either the inline curve editor or the /// inline DataClip editor. Mode priority: ClipEditing > CurveEditing > Hidden. /// Owns the shared chrome (resize handle, close button); each inner renderer owns its /// own BeginChild + content and calls from inside. /// [HelpUiID("AnimationArea")] internal sealed class TimelineDetailsArea { public enum DetailMode { Hidden, ClipEditing, CurveEditing, } public TimelineDetailsArea(TimeLineCanvas timeLineCanvas, InlineCurveArea curveArea, InlineDataClipArea clipArea) { _timeLineCanvas = timeLineCanvas; _curveArea = curveArea; _clipArea = clipArea; } public DetailMode ResolveMode(Instance compositionOp) { if (_timeLineCanvas.Mode != TimeLineCanvas.Modes.DopeView) return DetailMode.Hidden; // Clip editing wins when both are eligible — explicit toggle beats implicit. if (_timeLineCanvas.InlineDataClipEditEnabled && InlineDataClipArea.HasSelectedDataClipInstance(_timeLineCanvas, compositionOp)) { return DetailMode.ClipEditing; } if (_timeLineCanvas.CurveEditingParamHashes.Count > 0) return DetailMode.CurveEditing; return DetailMode.Hidden; } public bool IsActive(Instance compositionOp) => ResolveMode(compositionOp) != DetailMode.Hidden; /// Drag-resize handle between the top pane and the detail pane. public void DrawResizeHandle(float reserveHeight) { ImGui.InvisibleButton("##timelineDetailsResize", new Vector2(-1, reserveHeight)); if (ImGui.IsItemHovered() || ImGui.IsItemActive()) ImGui.SetMouseCursor(ImGuiMouseCursor.ResizeNS); if (ImGui.IsItemActivated()) { _heightOnDragStart = _timeLineCanvas.DetailsAreaHeight; _mouseYOnDragStart = ImGui.GetMousePos().Y; } if (ImGui.IsItemActive()) { var dy = ImGui.GetMousePos().Y - _mouseYOnDragStart; _timeLineCanvas.DetailsAreaHeight = _heightOnDragStart - dy; } var rectMin = ImGui.GetItemRectMin(); var rectMax = ImGui.GetItemRectMax(); var color = (ImGui.IsItemHovered() || ImGui.IsItemActive()) ? UiColors.StatusActivated : UiColors.GridLines.Fade(0.6f); var dl = ImGui.GetWindowDrawList(); var centerY = (int)((rectMin.Y + rectMax.Y) * 0.5f); dl.AddRectFilled(new Vector2(rectMin.X, centerY), new Vector2(rectMax.X, centerY + 1), color); } /// Dispatches to the active mode's renderer. Caller sizes the outer child. public void Draw(Instance compositionOp, List animationParameters, float height, float fitReferenceHeight, bool modeChanged) { switch (ResolveMode(compositionOp)) { case DetailMode.ClipEditing: _clipArea.Draw(compositionOp, height); break; case DetailMode.CurveEditing: _curveArea.Draw(compositionOp, animationParameters, height, fitReferenceHeight, modeChanged); break; } } /// /// Shared close button — called by each renderer from inside its own pane. /// Clears both gates so the pane goes away regardless of which mode was active. /// public static void DrawCloseButton(TimeLineCanvas timeLineCanvas) { var scale = T3Ui.UiScaleFactor; var iconSize = 15f * scale; var hitSize = new Vector2(iconSize + 4 * scale, iconSize + 4 * scale); var padding = 4 * scale; var paneMin = ImGui.GetWindowPos(); var paneMax = paneMin + ImGui.GetWindowSize(); // Avoid overlapping the vertical scrollbar — when present it eats the rightmost // ScrollbarSize px and the close button would render under it. var scrollbarOffset = ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ScrollbarSize : 0f; var closePos = new Vector2(paneMax.X - padding - hitSize.X - scrollbarOffset, paneMin.Y + padding); ImGui.SetCursorScreenPos(closePos); if (ImGui.InvisibleButton("##timelineDetailsClose", hitSize)) { timeLineCanvas.InlineDataClipEditEnabled = false; timeLineCanvas.CurveEditingParamHashes.Clear(); timeLineCanvas.VisibleComponentMask.Clear(); } var hovered = ImGui.IsItemHovered(); var dl = ImGui.GetWindowDrawList(); if (hovered) dl.AddRectFilled(closePos, closePos + hitSize, UiColors.BackgroundFull.Fade(0.6f)); var iconColor = UiColors.ForegroundFull.Fade(hovered ? 1f : 0.6f); var iconPos = closePos + (hitSize - new Vector2(iconSize, iconSize)) * 0.5f; Icons.DrawIconAtScreenPosition(Icon.Close, iconPos, dl, iconColor); } private readonly TimeLineCanvas _timeLineCanvas; private readonly InlineCurveArea _curveArea; private readonly InlineDataClipArea _clipArea; private float _heightOnDragStart; private float _mouseYOnDragStart; }