#nullable enable using System.IO; using ImGuiNET; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using T3.Core.Operator; using T3.Core.Operator.Interfaces; using T3.Core.Resource.Assets; using T3.Core.SystemUi; using T3.Core.Utils; using T3.Editor.Gui.Input; using T3.Editor.Gui.Styling; using T3.Editor.Gui.UiHelpers; using T3.Editor.UiModel.InputsAndTypes; using T3.Editor.UiModel.ProjectHandling; using T3.Serialization; namespace T3.Editor.Gui.InputUi.SimpleInputUis; public sealed class StringInputUi : InputValueUi { private const int MaxStringLength = 32000; public enum UsageType { Default, FilePath, DirectoryPath, Multiline, CustomDropdown, } public UsageType Usage { get; private set; } = UsageType.Default; public string FileFilter { get; set; } = string.Empty; public override IInputUi Clone() { return new StringInputUi { InputDefinition = InputDefinition, Parent = Parent, PosOnCanvas = PosOnCanvas, Relevancy = Relevancy, Size = Size, Usage = Usage, FileFilter = FileFilter, }; } protected override InputEditStateFlags DrawEditControl(string name, Symbol.Child.Input input, ref string? value, bool readOnly) { value ??= string.Empty; var inputEditStateFlags = InputEditStateFlags.Nothing; switch (Usage) { case UsageType.Default: inputEditStateFlags = DrawDefaultTextEdit(ref value) ? InputEditStateFlags.Modified : InputEditStateFlags.Nothing; break; case UsageType.Multiline: inputEditStateFlags = DrawMultilineTextEdit(ref value); break; case UsageType.FilePath: inputEditStateFlags = FilePickingUi.DrawTypeAheadSearch(FileOperations.FilePickerTypes.File, FileFilter, ref value); ImGui.SameLine(); if (ImGui.Button("Edit")) { if (value != null) { AssetRegistry.TryResolveAddress(value, FilePickingUi.SearchResourceConsumer, out var absolutePath, out _); if (!File.Exists(absolutePath)) { Log.Error("Can't open non-existing file " + absolutePath); } else { CoreUi.Instance.OpenWithDefaultApplication(absolutePath); } //OpenFileManager(FileOperations.FilePickerTypes.File, _searchResourceConsumer.AvailableResourcePackages, new string[0], isFolder: false, async: true); } } NormalizePathSeparators(inputEditStateFlags, ref value); break; case UsageType.DirectoryPath: inputEditStateFlags = FilePickingUi.DrawTypeAheadSearch(FileOperations.FilePickerTypes.Folder, FileFilter, ref value); NormalizePathSeparators(inputEditStateFlags, ref value); break; case UsageType.CustomDropdown: inputEditStateFlags = DrawCustomDropdown(input); break; } inputEditStateFlags |= ImGui.IsItemClicked() ? InputEditStateFlags.Started : InputEditStateFlags.Nothing; inputEditStateFlags |= ImGui.IsItemDeactivatedAfterEdit() ? InputEditStateFlags.Finished : InputEditStateFlags.Nothing; return inputEditStateFlags; static void NormalizePathSeparators(InputEditStateFlags inputEditStateFlags, ref string? value) { if (value == null) return; // normalize path separators when modified // use only forward slashes as windows is the only OS that supports backslashes if ((inputEditStateFlags & InputEditStateFlags.Modified) == InputEditStateFlags.Modified || (inputEditStateFlags & InputEditStateFlags.Finished) == InputEditStateFlags.Finished) { value = value.Replace('\\', '/'); // todo: handle trailing slashes //if (value.EndsWith('/')) // value = value[..^1]; } } } private static bool DrawDefaultTextEdit(ref string value) { return ImGui.InputText("##textEdit", ref value, MaxStringLength); } private static InputEditStateFlags DrawMultilineTextEdit(ref string value) { // Wrap to next line ImGui.Dummy(new Vector2(1, 1)); ImGui.PushFont(Fonts.Code); var lineCount = value.LineCount().Clamp(1, 30) + 1; var lineHeight = Fonts.Code.FontSize; ImGui.PushStyleColor(ImGuiCol.FrameBg, UiColors.BackgroundInputField.Rgba); var requestedContentHeight = lineCount * lineHeight; var clampedToWindowHeight = MathF.Min(requestedContentHeight, ImGui.GetWindowSize().Y * 0.5f); var changed = ImGui.InputTextMultiline("##textEdit", ref value, MaxStringLength, new Vector2(-1, clampedToWindowHeight)); ImGui.PopStyleColor(); ImGui.PopFont(); FormInputs.AddVerticalSpace(3); return changed ? InputEditStateFlags.Modified : InputEditStateFlags.Nothing; } private static InputEditStateFlags DrawCustomDropdown(Symbol.Child.Input input) { var instance = ProjectView.Focused?.NodeSelection.GetSelectedInstanceWithoutComposition(); if (instance != null && instance is ICustomDropdownHolder customValueHolder) { var changed = false; var currentValue = customValueHolder.GetValueForInput(input.InputDefinition.Id) ?? string.Empty; if (InputWithTypeAheadSearch.Draw("##customDropdown", customValueHolder.GetOptionsForInput(input.InputDefinition.Id), false, ref currentValue, out var selected, out var isInList)) { ImGui.CloseCurrentPopup(); customValueHolder.HandleResultForInput(input.InputDefinition.Id, selected, isInList); changed = true; } return changed ? InputEditStateFlags.Modified : InputEditStateFlags.Nothing; } else { ImGui.NewLine(); //Log.Warning($"{instance?.Parent?.Symbol?.Name} doesn't support custom inputs"); return InputEditStateFlags.Nothing; } } // private static InputWithTypeAheadSearch.Texts GetTextInfo(string arg) // { // return new InputWithTypeAheadSearch.Texts(arg, arg, null); // } protected override void DrawReadOnlyControl(string name, ref string? value) { if (value != null) { ImGui.InputText(name, ref value, MaxStringLength, ImGuiInputTextFlags.ReadOnly); } else { var nullString = ""; ImGui.InputText(name, ref nullString, MaxStringLength, ImGuiInputTextFlags.ReadOnly); } } public override bool DrawSettings() { var modified= base.DrawSettings(); FormInputs.AddVerticalSpace(); FormInputs.DrawFieldSetHeader("Usage"); { var tmpForRef = Usage; if (FormInputs.AddEnumDropdown(ref tmpForRef, null)) { modified = true; Usage = tmpForRef; } } if (Usage == UsageType.FilePath) { FormInputs.DrawFieldSetHeader("File Filter"); var tmp = FileFilter; /*var warning = !string.IsNullOrEmpty(tmp) && !tmp.Contains('|') ? "Filter must include at least one | symbol.\nPlease read tooltip for examples" : null;*/ if (FormInputs.AddStringInput("##File Filter", ref tmp, null, null, "This will only work for file FilePath-Mode.\nSeparate multiple extensions by a comma.(example: png, jpg, dds)")) { modified = true; FileFilter = tmp; } } return modified; } public override void Write(JsonTextWriter writer) { base.Write(writer); writer.WriteObject(nameof(Usage), Usage.ToString()); if (!string.IsNullOrEmpty(FileFilter)) writer.WriteObject(nameof(FileFilter), FileFilter); } public override void Read(JToken? inputToken) { if (inputToken == null) return; base.Read(inputToken); var usageEnumToken = inputToken[nameof(Usage)]; if (usageEnumToken != null && Enum.TryParse(usageEnumToken.Value(), out var enumValue)) { Usage = enumValue; } FileFilter = inputToken[nameof(FileFilter)]?.Value() ?? string.Empty; } }