diff --git a/Core/Resource/CollectionUtils.cs b/Core/Resource/CollectionUtils.cs new file mode 100644 index 0000000000..2be941a2da --- /dev/null +++ b/Core/Resource/CollectionUtils.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Resource +{ + public static class CollectionUtils + { + /// + /// A simple method to get a new index by "jumping" from a startIndex + /// When the jump would make the index exceed the valid range of the collection, + /// it will return an index that "wraps" around the other side + /// + /// Index to jump from + /// How far to jump (usually 1 or -1 for forward and backward respectively) + /// The collection whose Count (Length for arrays) will be referenced + /// If false, this will default to making every wrap result in the first or last index of the collection. + /// If true, it will be a "real" wrap, where if you jump in excess of X, you will wrap to the index X distance from the other side. + /// + public static int WrapIndex(int startIndex, int jumpAmount, System.Collections.IList collection, bool realWrap = false) + { + int index = startIndex + jumpAmount; + index %= collection.Count; + + bool newIndexIsNegative = index < 0; + int negativeWrapIndex = realWrap ? collection.Count + index : collection.Count - 1; + + return newIndexIsNegative ? negativeWrapIndex : index; + } + } +} diff --git a/Core/Resource/MathUtils.cs b/Core/Resource/MathUtils.cs index 1cdcdbde11..b9507f1be3 100644 --- a/Core/Resource/MathUtils.cs +++ b/Core/Resource/MathUtils.cs @@ -1,7 +1,6 @@ using System; using System.Numerics; using T3.Core.Animation; -using T3.Core.Operator; namespace T3.Core { diff --git a/T3/Gui/Graph/Interaction/SymbolBrowser.cs b/T3/Gui/Graph/Interaction/SymbolBrowser.cs index e5add61fab..abf37cfb0b 100644 --- a/T3/Gui/Graph/Interaction/SymbolBrowser.cs +++ b/T3/Gui/Graph/Interaction/SymbolBrowser.cs @@ -3,6 +3,8 @@ using System.Diagnostics; using System.Linq; using System.Numerics; +using System.Threading.Tasks; +using Core.Resource; using ImGuiNET; using T3.Core; using T3.Core.IO; @@ -51,20 +53,19 @@ public void Draw() { if (!IsOpen) { - if (!ImGui.IsWindowFocused() || !ImGui.IsKeyReleased((ImGuiKey)Key.Tab)) + if (!ImGui.IsWindowFocused() || !ImGui.IsKeyReleased((ImGuiKey)Key.Tab) || !ImGui.IsWindowHovered()) return; - if (NodeSelection.GetSelectedChildUis().Count() == 1) + if (NodeSelection.GetSelectedChildUis().Count() != 1) { - var childUi = NodeSelection.GetSelectedChildUis().ToList()[0]; - { - var instance = NodeSelection.GetInstanceForSymbolChildUi(childUi); - ConnectionMaker.OpenBrowserWithSingleSelection(this, childUi, instance); - } + OpenAt(GraphCanvas.Current.InverseTransformPositionFloat(ImGui.GetIO().MousePos + new Vector2(-4, -20)), null, null, false, null); + return; } - else + + var childUi = NodeSelection.GetSelectedChildUis().ToList()[0]; { - OpenAt(GraphCanvas.Current.InverseTransformPositionFloat(ImGui.GetIO().MousePos + new Vector2(-4, -20)), null, null, false, null); + var instance = NodeSelection.GetInstanceForSymbolChildUi(childUi); + ConnectionMaker.OpenBrowserWithSingleSelection(this, childUi, instance); } return; @@ -76,62 +77,60 @@ public void Draw() ImGui.PushID(UiId); { - _posInWindow = GraphCanvas.Current.ChildPosFromCanvas(PosOnCanvas); + var posInWindow = GraphCanvas.Current.ChildPosFromCanvas(PosOnCanvas); _posInScreen = GraphCanvas.Current.TransformPosition(PosOnCanvas); _drawList = ImGui.GetWindowDrawList(); ImGui.SetNextWindowFocus(); - DrawMatchesList(); - DrawSearchInput(); + + Vector2 browserPositionInWindow = posInWindow + _browserPositionOffset; + Vector2 browserSize = _resultListSize; + ShiftPositionToFitOnCanvas(ref browserPositionInWindow, ref browserSize); + + ImGui.SetCursorPos(browserPositionInWindow); + + DrawResultsList(browserSize); + + if (_symbolUiForDescription != null) + { + DrawDescriptionPanel(browserPositionInWindow, browserSize, _symbolUiForDescription); + } + + DrawSearchInput(posInWindow, _posInScreen, _size); } + ImGui.PopID(); } #endregion - private bool _selectedItemWasChanged; - #region internal implementation ----------------------------------------------------------- - private void DrawSearchInput() - { - ImGui.SetCursorPos(_posInWindow); - ImGui.SetNextItemWidth(90); + private void DrawSearchInput(Vector2 posInWindow, Vector2 posInScreen, Vector2 size) + { if (_focusInputNextTime) { ImGui.SetKeyboardFocusHere(); _focusInputNextTime = false; } - ImGui.SetCursorPos(_posInWindow + new Vector2(1, 1)); - ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(7, 6)); - ImGui.SetNextItemWidth(_size.X); - ImGui.InputText("##filter", ref _filter.SearchString, 10); - _drawList.AddRect(_posInScreen, _posInScreen + _size, Color.Gray); + ImGui.SetCursorPos(posInWindow); + + var padding = new Vector2(7, 6); + ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, padding); + ImGui.SetNextItemWidth(size.X); + + ImGui.InputText("##symbolbrowserfilter", ref _filter.SearchString, 10); + + // Search input outline + _drawList.AddRect(posInScreen, posInScreen + size, Color.Gray); if (ImGui.IsKeyReleased((ImGuiKey)Key.CursorDown)) { - if (_filter.MatchingSymbolUis.Count > 0) - { - var index = _filter.MatchingSymbolUis.IndexOf(_selectedSymbolUi); - index++; - index %= _filter.MatchingSymbolUis.Count; - _selectedSymbolUi = _filter.MatchingSymbolUis[index]; - - _selectedItemWasChanged = true; - } + SelectNextSymbolUi(_selectedSymbolUi); } else if (ImGui.IsKeyReleased((ImGuiKey)Key.CursorUp)) { - if (_filter.MatchingSymbolUis.Count > 0) - { - var index = _filter.MatchingSymbolUis.IndexOf(_selectedSymbolUi); - index--; - if (index < 0) - index = _filter.MatchingSymbolUis.Count - 1; - - _selectedSymbolUi = _filter.MatchingSymbolUis[index]; - _selectedItemWasChanged = true; - } + SelectPreviousSymbol(_selectedSymbolUi); } else if (ImGui.IsKeyPressed((ImGuiKey)Key.Return)) { @@ -150,26 +149,43 @@ private void DrawSearchInput() } var clickedOutside = ImGui.IsMouseClicked(ImGuiMouseButton.Left) && ImGui.IsWindowHovered(); - if (clickedOutside + var shouldCancelConnectionMaker = clickedOutside || ImGui.IsMouseClicked(ImGuiMouseButton.Right) - || ImGui.IsKeyDown((ImGuiKey)Key.Esc)) + || ImGui.IsKeyDown((ImGuiKey)Key.Esc); + + if (shouldCancelConnectionMaker) { ConnectionMaker.Cancel(); - //Log.Debug("Closing..."); Cancel(); } ImGui.PopStyleVar(); - if (ImGui.IsItemActive()) + if (!ImGui.IsItemActive()) + return; + + if (ImGui.IsMouseDragging(ImGuiMouseButton.Left)) { - if (ImGui.IsMouseDragging(ImGuiMouseButton.Left)) - { - PosOnCanvas += GraphCanvas.Current.InverseTransformDirection(ImGui.GetIO().MouseDelta); - } + PosOnCanvas += GraphCanvas.Current.InverseTransformDirection(ImGui.GetIO().MouseDelta); } } + private void SelectNextSymbolUi(SymbolUi selectedSymbolUi) => JumpThroughMatchingSymbolList(selectedSymbolUi, 1); + private void SelectPreviousSymbol(SymbolUi selectedSymbolUi) => JumpThroughMatchingSymbolList(selectedSymbolUi, -1); + + private void JumpThroughMatchingSymbolList(SymbolUi currentSelectedSymbolUi, int jump) + { + if (_filter.MatchingSymbolUis.Count == 0) + { + return; + } + + var index = _filter.MatchingSymbolUis.IndexOf(currentSelectedSymbolUi); + index = CollectionUtils.WrapIndex(index, jump, _filter.MatchingSymbolUis); + _selectedSymbolUi = _filter.MatchingSymbolUis[index]; + _selectedItemWasChanged = true; + } + private void Cancel() { if (_prepareCommand != null) @@ -184,50 +200,32 @@ private void Cancel() private void Close() { THelpers.RestoreImGuiKeyboardNavigation(); - //T3Ui.OpenedPopUpName = null; IsOpen = false; var win = GraphWindow.GetPrimaryGraphWindow(); win?.Focus(); } - private void DrawMatchesList() + private void DrawResultsList(Vector2 size) { - ImGui.SetCursorPos(_posInWindow + new Vector2(1, _size.Y + 1)); ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, new Vector2(5, 5)); ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(10, 10)); - SymbolUi itemForHelp = null; + var itemForHelpIsHovered = false; - if (ImGui.BeginChildFrame(999, _resultListSize)) + if (ImGui.BeginChildFrame(999, size)) { - if (_filter.MatchingSymbolUis.Count > 0 && !_filter.MatchingSymbolUis.Contains(_selectedSymbolUi)) + var gotAMatch = _filter.MatchingSymbolUis.Count > 0 && !_filter.MatchingSymbolUis.Contains(_selectedSymbolUi); + if (gotAMatch) _selectedSymbolUi = _filter.MatchingSymbolUis[0]; if ((_selectedSymbolUi == null && SymbolUiRegistry.Entries.Values.Any())) _selectedSymbolUi = SymbolUiRegistry.Entries.Values.FirstOrDefault(); - // Print type filter - if (_filter.FilterInputType != null || _filter.FilterOutputType != null) - { - ImGui.PushFont(Fonts.FontSmall); - - var inputTypeName = _filter.FilterInputType != null - ? TypeNameRegistry.Entries[_filter.FilterInputType] - : string.Empty; - - var outputTypeName = _filter.FilterOutputType != null - ? TypeNameRegistry.Entries[_filter.FilterOutputType] - : string.Empty; - - var isMultiInput = _filter.OnlyMultiInputs ? "[..]" : ""; - - var headerLabel = $"{inputTypeName}{isMultiInput} -> {outputTypeName}"; - ImGui.TextDisabled(headerLabel); - ImGui.PopFont(); - } + PrintTypeFilter(); foreach (var symbolUi in _filter.MatchingSymbolUis) { - ImGui.PushID(symbolUi.Symbol.Id.GetHashCode()); + var symbolHash = symbolUi.Symbol.Id.GetHashCode(); + ImGui.PushID(symbolHash); { var color = symbolUi.Symbol.OutputDefinitions.Count > 0 ? TypeUiRegistry.GetPropertiesForType(symbolUi.Symbol.OutputDefinitions[0]?.ValueType).Color @@ -241,24 +239,22 @@ private void DrawMatchesList() ImGui.PushStyleColor(ImGuiCol.Text, ColorVariations.OperatorLabel.Apply(color).Rgba); var isSelected = symbolUi == _selectedSymbolUi; - if (isSelected && _selectedItemWasChanged) - { - ScrollToMakeItemVisible(); - _selectedItemWasChanged = false; - } - if (itemForHelp == null && isSelected) + ImGui.Selectable($"##Selectable{symbolHash.ToString()}", isSelected); + bool selectionChangedToThis = isSelected && _selectedItemWasChanged; + + if (!itemForHelpIsHovered) { - itemForHelp = symbolUi; + var isHovered = ImGui.IsItemHovered(); + itemForHelpIsHovered = DetermineItemForHelp(isHovered, symbolUi, isSelected); } - ImGui.Selectable("", isSelected); - - if (ImGui.IsItemHovered()) + if (selectionChangedToThis) { - itemForHelp = symbolUi; + ScrollToMakeItemVisible(); + _selectedItemWasChanged = false; } - + if (ImGui.IsItemActivated()) { CreateInstance(symbolUi.Symbol); @@ -287,34 +283,117 @@ private void DrawMatchesList() ImGui.EndChildFrame(); - DrawDescriptionPanel(itemForHelp); - ImGui.PopStyleVar(2); } - private static void DrawDescriptionPanel(SymbolUi itemForHelp) + private bool DetermineItemForHelp(bool isHovered, SymbolUi symbolUi, bool isSelected) { - if (itemForHelp == null) + if (isHovered) + { + _symbolUiForDescription = symbolUi; + _timeDescriptionSymbolUiLastHovered = DateTime.Now; + return true; + } + + if (isSelected && !_descriptionPanelHovered) + { + if ((DateTime.Now - _timeDescriptionSymbolUiLastHovered).Milliseconds > 50) + { + _symbolUiForDescription = symbolUi; + _timeDescriptionSymbolUiLastHovered = DateTime.Now; + } + } + + return false; + } + + private void PrintTypeFilter() + { + if (_filter.FilterInputType == null && _filter.FilterOutputType == null) return; + ImGui.PushFont(Fonts.FontSmall); + + var inputTypeName = _filter.FilterInputType != null + ? TypeNameRegistry.Entries[_filter.FilterInputType] + : string.Empty; + + var outputTypeName = _filter.FilterOutputType != null + ? TypeNameRegistry.Entries[_filter.FilterOutputType] + : string.Empty; + + var isMultiInput = _filter.OnlyMultiInputs ? "[..]" : ""; + + var headerLabel = $"{inputTypeName}{isMultiInput} -> {outputTypeName}"; + ImGui.TextDisabled(headerLabel); + ImGui.PopFont(); + } + + + private void ShiftPositionToFitOnCanvas(ref Vector2 position, ref Vector2 size) + { + var maxXPos = position.X + size.X; + var maxYPos = position.Y + size.Y; + + var windowSize = GraphCanvas.Current.WindowSize; + + var shouldShiftLeft = maxXPos > windowSize.X; + var xPositionOffset = shouldShiftLeft ? windowSize.X - maxXPos : 0; + + var yOverflow = maxYPos > windowSize.Y; + var yShrinkage = yOverflow ? windowSize.Y - maxYPos : 0; + + position.X += xPositionOffset; + size.Y += yShrinkage; + } + + private void DrawDescriptionPanel(Vector2 position, Vector2 size, SymbolUi itemForHelp) + { + if (itemForHelp == null) + return; + ExampleSymbolLinking.ExampleSymbols.TryGetValue(itemForHelp.Symbol.Id, out var examples2); var hasExamples = examples2 is { Count: > 0 }; var hasDescription = !string.IsNullOrEmpty(itemForHelp.Description); if (!hasExamples && !hasDescription) return; - - ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.One); // Padding between panels - ImGui.SameLine(); - if (ImGui.GetContentRegionAvail().X < 250) + var maxXPos = position.X + size.X * 2; + bool overflow = maxXPos >= GraphCanvas.Current.WindowSize.X; + + if (overflow && !UserSettings.Config.AlwaysShowDescriptionPanel) { - ImGui.PopStyleVar(); return; } - - - if (ImGui.BeginChildFrame(998, new Vector2(250, _resultListSize.Y))) + + if (!overflow) + { + position.X += size.X; + } + else //if it's overflowing but AlwaysShowDescriptionPanel is true + { + position.X -= size.X; + + //if it simply doesn't fit on screen, return + if (position.X <= 0) + return; + } + + ImGui.SetCursorPos(position); + DrawDescriptionPanelImGui(itemForHelp, size, hasExamples); + + + _descriptionPanelHovered = ImGui.IsItemHovered(); + if (_descriptionPanelHovered) + _timeDescriptionSymbolUiLastHovered = DateTime.Now; + } + + private void DrawDescriptionPanelImGui(SymbolUi itemForHelp, Vector2 size, bool hasExamples) + { + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.One); // Padding between panels + + if (ImGui.BeginChildFrame(998, size)) { if (!string.IsNullOrEmpty(itemForHelp.Description)) { @@ -326,7 +405,6 @@ private static void DrawDescriptionPanel(SymbolUi itemForHelp) if (hasExamples) { ImGui.Dummy(new Vector2(10, 10)); - ListExampleOperators(itemForHelp); } @@ -338,18 +416,18 @@ private static void DrawDescriptionPanel(SymbolUi itemForHelp) public static void ListExampleOperators(SymbolUi itemForHelp) { - if (ExampleSymbolLinking.ExampleSymbols.TryGetValue(itemForHelp.Symbol.Id, out var examples)) + if (!ExampleSymbolLinking.ExampleSymbols.TryGetValue(itemForHelp.Symbol.Id, out var examples)) + return; + + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f); + foreach (var guid in examples) { - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f); - for (var index = 0; index < examples.Count; index++) - { - var label = "Example"; - var exampleId = examples[index]; - DrawExampleOperator(exampleId, label); - } - - ImGui.PopStyleVar(); + var label = "Example"; + var exampleId = guid; + DrawExampleOperator(exampleId, label); } + + ImGui.PopStyleVar(); } public static void DrawExampleOperator(Guid exampleId, string label) @@ -384,6 +462,7 @@ private void ScrollToMakeItemVisible() var scrollTarget = ImGui.GetCursorPos(); var windowSize = ImGui.GetWindowSize(); var scrollPos = ImGui.GetScrollY(); + if (scrollTarget.Y < scrollPos) { ImGui.SetScrollY(scrollTarget.Y); @@ -475,17 +554,21 @@ private void CreateInstance(Symbol symbol) public bool IsOpen; private readonly Vector2 _size = SymbolChildUi.DefaultOpSize; + private static Vector2 _browserPositionOffset => new Vector2(0, 40); private bool _focusInputNextTime; private Vector2 _posInScreen; private ImDrawListPtr _drawList; - private Vector2 _posInWindow; + private bool _selectedItemWasChanged; private static readonly Vector2 _resultListSize = new Vector2(250, 300); private readonly Vector4 _namespaceColor = new Color(1, 1, 1, 0.4f); private SymbolUi _selectedSymbolUi; + private SymbolUi _symbolUiForDescription; + private bool _descriptionPanelHovered; + private DateTime _timeDescriptionSymbolUiLastHovered; private static readonly int UiId = "DraftNode".GetHashCode(); } } \ No newline at end of file diff --git a/T3/Gui/SettingsUi.cs b/T3/Gui/SettingsUi.cs new file mode 100644 index 0000000000..003660abad --- /dev/null +++ b/T3/Gui/SettingsUi.cs @@ -0,0 +1,101 @@ +using ImGuiNET; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using T3.Gui.Windows; + +namespace t3.Gui +{ + public static class SettingsUi + { + /// + /// Draws a table of s + /// + /// Unique identifier for your table - will not be displayed + /// Settings to display + /// Returns true if a setting has been modified + public static bool DrawSettingsTable(string tableID, UIControlledSetting[] settings) + { + ImGui.NewLine(); + bool changed = false; + if (ImGui.BeginTable(tableID, 2, ImGuiTableFlags.BordersInnerH | ImGuiTableFlags.SizingFixedSame | ImGuiTableFlags.PadOuterX)) + { + foreach (UIControlledSetting setting in settings) + { + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.Indent(); + + if (setting.DrawOnLeft) + { + var valueChanged = setting.DrawGUIControl(true); + changed |= valueChanged; + + ImGui.SameLine(); + ImGui.Dummy(_leftCheckboxSpacing); + ImGui.SameLine(); + + ImGui.Text(setting.CleanLabel); + DrawTooltip(setting); + + ImGui.Unindent(); + } + else + { + ImGui.Text(setting.CleanLabel); + DrawTooltip(setting); + ImGui.Unindent(); + ImGui.TableNextColumn(); + var valueChanged = setting.DrawGUIControl(true); + changed |= valueChanged; + } + + } + } + + ImGui.EndTable(); + ImGui.NewLine(); + + return changed; + } + + /// + /// Draws a series of settings in order + /// + /// + /// + public static bool DrawSettings(UIControlledSetting[] settings) + { + ImGui.NewLine(); + + bool changed = false; + + foreach (var setting in settings) + { + ImGui.Text(setting.CleanLabel); + DrawTooltip(setting); + ImGui.SameLine(); + changed |= setting.DrawGUIControl(true); + } + + ImGui.NewLine(); + return changed; + } + + private static void DrawTooltip(UIControlledSetting setting) + { + if (!string.IsNullOrEmpty(setting.Tooltip)) + { + if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) + { + ImGui.SetTooltip(setting.Tooltip); + } + } + } + + private static readonly Vector2 _leftCheckboxSpacing = new Vector2(0f, 20f); + } +} diff --git a/T3/Gui/Styling/CustomComponents.cs b/T3/Gui/Styling/CustomComponents.cs index 2a6e53d752..2aaf7f10fe 100644 --- a/T3/Gui/Styling/CustomComponents.cs +++ b/T3/Gui/Styling/CustomComponents.cs @@ -473,23 +473,22 @@ public static bool DrawSegmentedToggle(ref int currentIndex, List option } - public static bool FloatValueEdit(string label, ref float value, float min= float.NegativeInfinity, float max= float.PositiveInfinity, float scale= 0) + public static bool FloatValueEdit(string label, ref float value, float min = float.NegativeInfinity, float max = float.PositiveInfinity, float scale = 0.01f, bool clamp = false) { var labelSize = ImGui.CalcTextSize(label); const float leftPadding = 200; var p = ImGui.GetCursorPos(); - ImGui.SetCursorPosX( MathF.Max(leftPadding - labelSize.X,0)+10); + ImGui.SetCursorPosX(MathF.Max(leftPadding - labelSize.X, 0) + 10); ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted(label); + + string cleanedLabel = label.Split(_idSpecifier)[0]; + ImGui.TextUnformatted(cleanedLabel); ImGui.SetCursorPos(p); - + ImGui.SameLine(); ImGui.SetCursorPosX(leftPadding + 20); - var size = new Vector2(150, ImGui.GetFrameHeight()); - ImGui.PushID(label); - var result = SingleValueEdit.Draw(ref value, size, min, max); - var modified = (result & InputEditStateFlags.Modified) != InputEditStateFlags.Nothing; - ImGui.PopID(); + var modified = DrawSingleValueEdit(cleanedLabel, ref value, min, max, clamp, scale); + return modified; } @@ -500,17 +499,35 @@ public static bool IntValueEdit(string label, ref int value, int min = int.MinVa var p = ImGui.GetCursorPos(); ImGui.SetCursorPosX(MathF.Max(leftPadding - labelSize.X, 0) + 10); ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted(label); + + string cleanedLabel = label.Split(_idSpecifier)[0]; + ImGui.TextUnformatted(cleanedLabel); + ImGui.SetCursorPos(p); ImGui.SameLine(); ImGui.SetCursorPosX(leftPadding + 20); + + var modified = DrawSingleValueEdit(cleanedLabel, ref value, min, max, true, scale); + return modified; + } + + public static bool DrawSingleValueEdit(string label, ref int value, int min = int.MinValue, int max = int.MaxValue, bool clamp = false, float scale = 1f) + { + ImGui.PushID(label); var size = new Vector2(150, ImGui.GetFrameHeight()); + var result = SingleValueEdit.Draw(ref value, size, min, max, clamp, scale); + ImGui.PopID(); + return (result & InputEditStateFlags.Modified) != InputEditStateFlags.Nothing; + } + + public static bool DrawSingleValueEdit(string label, ref float value, float min = float.MinValue, float max = float.MaxValue, bool clamp = false, float scale = 1f) + { ImGui.PushID(label); - var result = SingleValueEdit.Draw(ref value, size, min, max, true, scale); - var modified = (result & InputEditStateFlags.Modified) != InputEditStateFlags.Nothing; + var size = new Vector2(150, ImGui.GetFrameHeight()); + var result = SingleValueEdit.Draw(ref value, size, min, max, clamp, scale); ImGui.PopID(); - return modified; + return (result & InputEditStateFlags.Modified) != InputEditStateFlags.Nothing; } public static bool StringValueEdit(string label, ref string value) @@ -521,7 +538,10 @@ public static bool StringValueEdit(string label, ref string value) var p = ImGui.GetCursorPos(); ImGui.SetCursorPosX( MathF.Max(leftPadding - labelSize.X,0)+10); ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted(label); + + string cleanedLabel = label.Split(_idSpecifier)[0]; + ImGui.TextUnformatted(cleanedLabel); + ImGui.SetCursorPos(p); ImGui.SameLine(); @@ -541,7 +561,10 @@ public static bool DrawEnumSelector(ref int index, string label) var p = ImGui.GetCursorPos(); ImGui.SetCursorPosX(MathF.Max(200 - labelSize.X, 0) + 10); ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted(label); + + string cleanedLabel = label.Split(_idSpecifier)[0]; + ImGui.TextUnformatted(cleanedLabel); + ImGui.SetCursorPos(p); // Dropdown @@ -567,6 +590,8 @@ public static bool DrawEnumSelector(ref int index, string label) var modified = ImGui.Combo($"##dropDown{enumType}{label}", ref index, valueNames, valueNames.Length, valueNames.Length); return modified; } + + private const string _idSpecifier = "##"; } public static class InputWithTypeAheadSearch @@ -665,6 +690,6 @@ public static bool Draw(string id, ref string text, IOrderedEnumerable i private static List _lastResults = new List(); private static int _selectedResultIndex = 0; - private static bool _isSearchResultWindowOpen; + private static bool _isSearchResultWindowOpen; } } \ No newline at end of file diff --git a/T3/Gui/UiHelpers/UIControlledSetting.cs b/T3/Gui/UiHelpers/UIControlledSetting.cs new file mode 100644 index 0000000000..c1e6acdefe --- /dev/null +++ b/T3/Gui/UiHelpers/UIControlledSetting.cs @@ -0,0 +1,84 @@ +using ImGuiNET; +using System; +using T3.Gui.UiHelpers; + +namespace T3.Gui.Windows +{ + public class UIControlledSetting + { + /// + /// The generated unique label + /// + private string _uniqueLabel; + private string _hiddenUniqueLabel; + + /// + /// The label provided in the constructor + /// + public string CleanLabel { get; private set; } + public bool DrawOnLeft { get; private set; } + + public string Tooltip { get; private set; } + private string _additionalNotes; + private Func _guiFunc; + private Action _OnValueChanged; + + // Cheaper than GUIDs + // In case we want to have the same variable be changeable with different UI controls + // or if multiple settings have the same label + static ushort _countForUniqueID = ushort.MaxValue; + + /// + /// For the sake of simple use of the optional parameters and populating/maintaining many settings, the recommended way to call this constructor is: + /// + /// new UIControlledSetting + /// ( + /// label: "My Setting", + /// tooltip: "The global scale of all rendered UI in the application", + /// guiFunc: (string guiLabel) => CustomComponents.FloatValueEdit(guiLabel, ref UserSettings.Config.UiScaleFactor, 0.01f, 0.5f, 3f), + /// OnValueChanged: () => //your action + /// ); + /// + /// + /// The label to display next to the gui control + /// The - based function that draws the setting control and + /// returns true if the control was changed. The input to this function see is a unique ID based on the label provided + /// Tooltip displayed when hovering over the control + /// Additional notes displayed alongside the tooltip [Not currently in use, but will be once T3 tooltips are integrated] + /// An action performed when the value is changed + public UIControlledSetting(string label, Func guiFunc, string tooltip = null, string additionalNotes = null, bool drawOnLeft = false, Action OnValueChanged = null) + { + CleanLabel = label; + _hiddenUniqueLabel = $"##{label}{_countForUniqueID--}"; + _uniqueLabel = $"{label}##{_countForUniqueID--}"; + + _guiFunc = guiFunc; + Tooltip = tooltip; + _additionalNotes = additionalNotes; + _OnValueChanged = OnValueChanged; + DrawOnLeft = drawOnLeft; + } + + /// + /// Draws the GUI for this setting using the Func provided in its constructor + /// + /// True if changed, false if unchanged. + /// If an Action was provided in constructor, it will be executed when value is changed. + public bool DrawGUIControl(bool hideLabel) + { + var changed = DrawCommand(hideLabel); + + if (changed) + { + _OnValueChanged?.Invoke(); + } + + return changed; + } + + bool DrawCommand(bool hideLabel) + { + return _guiFunc.Invoke(hideLabel ? _hiddenUniqueLabel : _uniqueLabel); + } + } +} \ No newline at end of file diff --git a/T3/Gui/UiHelpers/UserSettings.cs b/T3/Gui/UiHelpers/UserSettings.cs index 13bbf413ff..5e2cc9bed0 100644 --- a/T3/Gui/UiHelpers/UserSettings.cs +++ b/T3/Gui/UiHelpers/UserSettings.cs @@ -79,6 +79,9 @@ public class ConfigData public float SpaceMouseMoveSpeedFactor = 1f; public float SpaceMouseDamping = 0.5f; + // Symbol Browser + public bool AlwaysShowDescriptionPanel = false; + [JsonConverter(typeof(StringEnumConverter))] public TimeFormat.TimeDisplayModes TimeDisplayMode = TimeFormat.TimeDisplayModes.Bars; diff --git a/T3/Gui/Windows/SettingsInSettingsWindow.cs b/T3/Gui/Windows/SettingsInSettingsWindow.cs new file mode 100644 index 0000000000..927af25de2 --- /dev/null +++ b/T3/Gui/Windows/SettingsInSettingsWindow.cs @@ -0,0 +1,231 @@ +using ImGuiNET; +using System; +using System.Numerics; +using T3.Gui.Graph; +using T3.Gui.UiHelpers; + +namespace T3.Gui.Windows +{ + public partial class SettingsWindow + { + static readonly UIControlledSetting[] userInterfaceSettings = new UIControlledSetting[] + { + new UIControlledSetting + ( + label: "Warn before Lib modifications", + tooltip: "This warning pops up when you attempt to enter an Operator that ships with the application.\n" + + "If unsure, this is best left checked.", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.WarnBeforeLibEdit), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "Use arc connections", + tooltip: "Affects the shape of the connections between your Operators", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.UseArcConnections), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "Use Jog Dial Control", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.UseJogDialControl), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "Show Graph thumbnails", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.ShowThumbnails), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "Drag snapped nodes", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.SmartGroupDragging), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "Fullscreen Window Swap", + tooltip: "Swap main and second windows when fullscreen", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.SwapMainAnd2ndWindowsWhenFullscreen), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "UI Scale", + tooltip: "The global scale of all rendered UI in the application", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.UiScaleFactor, 0.1f, 5f, true, 0.01f) + ), + + new UIControlledSetting + ( + label: "Scroll smoothing", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.ScrollSmoothing, 0f, 0.2f, true, 0.01f) + ), + + new UIControlledSetting + ( + label: "Snap strength", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.SnapStrength) + ), + + new UIControlledSetting + ( + label: "Click threshold", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.ClickThreshold) + + ), + + new UIControlledSetting + ( + label: "Timeline Raster Density", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.TimeRasterDensity) + ), + }; + + static readonly UIControlledSetting[] spaceMouseSettings = new UIControlledSetting[] + { + new UIControlledSetting + ( + label: "Smoothing", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.SpaceMouseDamping, 0.01f, 1f) + ), + + new UIControlledSetting + ( + label: "Move Speed", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.SpaceMouseMoveSpeedFactor, 0, 10f) + ), + + new UIControlledSetting + ( + label: "Rotation Speed", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.SpaceMouseRotationSpeedFactor, 0, 10f) + ) + }; + + static readonly UIControlledSetting[] additionalSettings = new UIControlledSetting[] + { + new UIControlledSetting + ( + label: "Gizmo size", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.GizmoSize) + ), + + new UIControlledSetting + ( + label: "Tooltip delay", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref UserSettings.Config.TooltipDelay) + ), + + + // These settings were laid out in the old UI, kept here for someone else to ultimately choose to yeet or unyeet them + + //new UIControlledSetting + //( + // label: "Show Title", + // guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.ShowTitleAndDescription) + //), + + //new UIControlledSetting + //( + // label: "Show Timeline", + // guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.ShowTimeline) + //) + }; + + static readonly UIControlledSetting[] debugSettings = new UIControlledSetting[] + { + new UIControlledSetting + ( + label: "VSync", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UseVSync), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "Show Window Regions", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref WindowRegionsVisible), + drawOnLeft: true + ), + + new UIControlledSetting + ( + label: "Show Item Regions", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref ItemRegionsVisible), + drawOnLeft: true + ), + }; + + static readonly UIControlledSetting[] t3UiStyleSettings = new UIControlledSetting[] + { + new UIControlledSetting + ( + label: "Label position", + guiFunc: (string guiLabel) => ImGui.DragFloat2(guiLabel, ref GraphNode.LabelPos) + ), + + new UIControlledSetting + ( + label: "Height Connection Zone", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref GraphNode.UsableSlotThickness) + ), + + new UIControlledSetting + ( + label: "Slot Gaps", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref GraphNode.SlotGaps, 0, 10f) + ), + + new UIControlledSetting + ( + label: "Input Slot Margin Y", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref GraphNode.InputSlotMargin, 0, 10f) + ), + + new UIControlledSetting + ( + label: "Input Slot Thickness", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref GraphNode.InputSlotThickness, 0, 10f) + ), + + new UIControlledSetting + ( + label: "Output Slot Margin", + guiFunc: (string guiLabel) => CustomComponents.DrawSingleValueEdit(guiLabel, ref GraphNode.OutputSlotMargin, 0, 10f) + ), + + new UIControlledSetting + ( + label: "Value Label Color", + guiFunc: (string guiLabel) => ImGui.ColorEdit4(guiLabel, ref T3Style.Colors.ValueLabelColor.Rgba) + ), + + new UIControlledSetting + ( + label: "Value Label Color Hover", + guiFunc: (string guiLabel) => ImGui.ColorEdit4(guiLabel, ref T3Style.Colors.ValueLabelColorHover.Rgba) + ), + }; + + static readonly UIControlledSetting[] symbolBrowserSettings = new UIControlledSetting[] + { + new UIControlledSetting + ( + label: "Always Show Description", + tooltip: "Shifts the Description panel to the left of the Symbol Browser when\n" + + "it is too close to the right edge of the screen to display it.", + guiFunc: (string guiLabel) => ImGui.Checkbox(guiLabel, ref UserSettings.Config.AlwaysShowDescriptionPanel), + drawOnLeft: true + ), + }; + + } +} \ No newline at end of file diff --git a/T3/Gui/Windows/SettingsWindow.cs b/T3/Gui/Windows/SettingsWindow.cs index b61e205435..b080e6d7e9 100644 --- a/T3/Gui/Windows/SettingsWindow.cs +++ b/T3/Gui/Windows/SettingsWindow.cs @@ -1,13 +1,17 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using System.Threading.Channels; +using System.Windows.Forms; using ImGuiNET; using T3.Gui.Commands; using T3.Gui.Graph; using T3.Gui.TypeColors; using T3.Gui.UiHelpers; +using System.Numerics; +using t3.Gui; namespace T3.Gui.Windows { - public class SettingsWindow : Window + public partial class SettingsWindow : Window { public SettingsWindow() { @@ -22,57 +26,38 @@ public SettingsWindow() protected override void DrawContent() { var changed = false; + ImGui.NewLine(); if (ImGui.TreeNode("User Interface")) { - if (ImGui.DragFloat("UI Scale", ref UserSettings.Config.UiScaleFactor, 0.01f, 0.5f, 3f)) - { - changed = true; - } - changed |= ImGui.Checkbox("Warn before Lib modifications", ref UserSettings.Config.WarnBeforeLibEdit); - changed |= ImGui.Checkbox("Use arc connections", ref UserSettings.Config.UseArcConnections); - changed |= ImGui.Checkbox("Use Jog Dial Control", ref UserSettings.Config.UseJogDialControl); - changed |= ImGui.DragFloat("Scroll smoothing", ref UserSettings.Config.ScrollSmoothing); - changed |= ImGui.Checkbox("Show Graph thumbnails", ref UserSettings.Config.ShowThumbnails); - changed |= ImGui.Checkbox("Drag snapped nodes", ref UserSettings.Config.SmartGroupDragging); - ImGui.Separator(); - changed |= ImGui.DragFloat("Snap strength", ref UserSettings.Config.SnapStrength); - changed |= ImGui.DragFloat("Click threshold", ref UserSettings.Config.ClickThreshold); - changed |= ImGui.DragFloat("Keyboard scroll speed", ref UserSettings.Config.KeyboardScrollAcceleration); - - changed |= ImGui.DragFloat("Timeline Raster Density", ref UserSettings.Config.TimeRasterDensity, 0.01f); - - changed |= ImGui.Checkbox("Swap Main & 2nd windows when fullscreen", ref UserSettings.Config.SwapMainAnd2ndWindowsWhenFullscreen); - - + changed |= SettingsUi.DrawSettingsTable("##uisettingstable", userInterfaceSettings); ImGui.TreePop(); } if (ImGui.TreeNode("Space Mouse")) { - changed |= ImGui.DragFloat("Smoothing", ref UserSettings.Config.SpaceMouseDamping, 0.01f, 0.01f, 1f); - changed |= ImGui.DragFloat("Move Speed", ref UserSettings.Config.SpaceMouseMoveSpeedFactor, 0.01f, 0, 10f); - changed |= ImGui.DragFloat("Rotation Speed", ref UserSettings.Config.SpaceMouseRotationSpeedFactor, 0.01f, 0, 10f); + changed |= SettingsUi.DrawSettingsTable("##settingspacemousetable", spaceMouseSettings); ImGui.TreePop(); } - - if (ImGui.TreeNode("Additional settings")) + if (ImGui.TreeNode("Additional Settings")) { - //ImGui.Checkbox("Show Timeline", ref UserSettings.Config.ShowTimeline); - //ImGui.Checkbox("Show Title", ref UserSettings.Config.ShowTitleAndDescription); - changed |= ImGui.DragFloat("Gizmo size", ref UserSettings.Config.GizmoSize); - changed |= ImGui.DragFloat("Tooltip delay", ref UserSettings.Config.TooltipDelay); + changed |= SettingsUi.DrawSettingsTable("##additionalsettingstable", additionalSettings); + + if(ImGui.TreeNode("Symbol Browser Settings")) + { + changed |= SettingsUi.DrawSettingsTable("##symbolbrowsersettingstable", symbolBrowserSettings); + } + ImGui.TreePop(); } +#if DEBUG if (ImGui.TreeNode("Debug Options")) { - ImGui.Checkbox("VSync", ref UseVSync); - ImGui.Checkbox("Show Window Regions", ref WindowRegionsVisible); - ImGui.Checkbox("Show Item Regions", ref ItemRegionsVisible); - + SettingsUi.DrawSettings(debugSettings); if (ImGui.TreeNode("Undo Queue")) { + ImGui.Indent(); ImGui.TextUnformatted("Undo"); ImGui.Indent(); foreach (var c in UndoRedoStack.UndoStack) @@ -89,10 +74,11 @@ protected override void DrawContent() ImGui.Selectable(c.Name); } + ImGui.Unindent(); ImGui.Unindent(); ImGui.TreePop(); } - + if (ImGui.TreeNode("Modified Symbols")) { foreach (var symbolUi in UiModel.GetModifiedSymbolUis()) @@ -108,22 +94,16 @@ protected override void DrawContent() ImGui.TreePop(); } - + +#endif +#if DEBUG if (ImGui.TreeNode("Look (not saved)")) { ColorVariations.DrawSettingsUi(); if (ImGui.TreeNode("T3 Ui Style")) { - ImGui.DragFloat("Height Connection Zone", ref GraphNode.UsableSlotThickness); - ImGui.DragFloat2("Label position", ref GraphNode.LabelPos); - ImGui.DragFloat("Slot Gaps", ref GraphNode.SlotGaps, 0.1f, 0, 10f); - ImGui.DragFloat("Input Slot Margin Y", ref GraphNode.InputSlotMargin, 0.1f, 0, 10f); - ImGui.DragFloat("Input Slot Thickness", ref GraphNode.InputSlotThickness, 0.1f, 0, 10f); - ImGui.DragFloat("Output Slot Margin", ref GraphNode.OutputSlotMargin, 0.1f, 0, 10f); - - ImGui.ColorEdit4("ValueLabelColor", ref T3Style.Colors.ValueLabelColor.Rgba); - ImGui.ColorEdit4("ValueLabelColorHover", ref T3Style.Colors.ValueLabelColorHover.Rgba); + SettingsUi.DrawSettingsTable("##t3uistylesettings", t3UiStyleSettings); } if (ImGui.TreeNode("T3 Graph colors")) @@ -133,12 +113,15 @@ protected override void DrawContent() } ImGui.TreePop(); } - - if(changed) +#endif + + if (changed) UserSettings.Save(); - + +#if DEBUG ImGui.Separator(); T3Metrics.Draw(); +#endif } public override List GetInstances()