private void EnterChild(FuzzyOptionNode node) { if (node == null || node.hasChildren == false || node.option is FuzzySeparator) { return; } ExecuteTask(() => { Populate(node, tree.Children(node.option)); }); lastRepaintTime = DateTime.UtcNow; query = string.Empty; delayedQuery = null; GUIUtility.keyboardControl = 0; letQueryClear = true; if (animTarget == 0) { animTarget = 1; animAncestor = null; } else if (anim == 1) { anim = 0; prevAnim = 0; stack.Add(node); } }
private void OnLevelGUI(float anim) { var parent = stack[stack.Count - 1]; anim = Mathf.Floor(anim) + Mathf.SmoothStep(0, 1, Mathf.Repeat(anim, 1)); if (stack.Count > 1 || !string.IsNullOrEmpty(parent.option.headerLabel)) { OnHeaderGUI(); } if (e.type == EventType.Layout || e.type == EventType.Repaint) { minOptionWidth = 0; } OnOptionsGUI(anim, parent); if (anim < 1) { FuzzyOptionNode grandparent = null; if (animTarget == 0 && animAncestor != null) { grandparent = animAncestor; } else if (stack.Count > 1) { grandparent = stack[stack.Count - 2]; } OnOptionsGUI(anim + 1, grandparent); } }
public override void OnFooterGUI(FuzzyOptionNode node, Rect position) { if (editorTarget != value) { editorTarget = value; UEditor.CreateCachedEditor(editorTarget, null, ref editor); } if (editor != null) { editor.DrawPreview(position); } }
private void SelectAncestor(FuzzyOptionNode node) { if (stack.Contains(node)) { animTarget = 0; animAncestor = node; lastRepaintTime = DateTime.UtcNow; query = node.option is SearchOption searchOption ? searchOption.query : string.Empty; delayedQuery = null; GUIUtility.keyboardControl = 0; letQueryClear = true; } }
private void Search() { while (stack.Count > 0 && stack[stack.Count - 1].option is SearchOption) { stack.RemoveAt(stack.Count - 1); } if (hasSearch) { searchCancellationTokenSource?.Cancel(); var query = this.query; var parent = (stack.Count > 1 ? stack[stack.Count - 1] : null); var searchNode = new FuzzyOptionNode(new SearchOption(query)); searchCancellationTokenSource = new CancellationTokenSource(); var searchCancellationToken = searchCancellationTokenSource.Token; ExecuteTask(() => { DisplayProgressBar($"Searching for \"{query}\"...", 0); if (searchCancellationToken.IsCancellationRequested) { return; } lock (guiLock) { searchNode.children.Clear(); searchNode.isPopulated = false; } Populate(searchNode, tree.OrderedSearchResults(query, parent?.option, searchCancellationToken).Take(LudiqCore.Configuration.maxSearchResults)); activeSelectedIndex = activeNodes.Count >= 1 ? 0 : -1; }); stack.Add(searchNode); } else { animTarget = 1; animAncestor = null; lastRepaintTime = DateTime.UtcNow; } }
public override float GetFooterHeight(FuzzyOptionNode node, float width) { if (ZoomIcon(node)) { width -= Styles.zoomSize + Styles.zoomSpacing; } var height = 0f; width -= 2; if (documentation.summary != null) { height += GetSummaryHeight(width); } foreach (var parameter in documentation.parameters) { height += GetParameterHeight(parameter, width); } if (documentation.returns != null) { height += GetReturnsHeight(width); } if (documentation.remarks != null) { height += GetRemarksHeight(width); } if (ZoomIcon(node)) { return(Mathf.Max(Styles.zoomSize + 2 * Styles.zoomSpacing, height)); } return(height); }
private void SelectChild(FuzzyOptionNode node) { if (node == null || node.option is FuzzySeparator) { return; } if (node.option.mode == FuzzyOptionMode.Branch) { if (node.EvaluateHasChildren(tree)) { EnterChild(node); } } else if (node.option.mode == FuzzyOptionMode.Leaf) { if (node == activeNode) { node.option.OnFocusLeave(node); } callback?.Invoke(node.option); } }
private bool ZoomIcon(FuzzyOptionNode node) => zoom && node.icon != null && (!string.IsNullOrWhiteSpace(documentation.summary) || !string.IsNullOrWhiteSpace(documentation.remarks));
private void OnGUI() { if (!isValid) { Close(); return; } try { lock (guiLock) { if (alpha < 1 && e.type == EventType.Repaint) { ReadTransparentBackground(); var innerPosition = new Rect(0, 0, position.width, position.height); GUI.DrawTexture(innerPosition, transparentBackground); GUI.DrawTexture(innerPosition, ColorPalette.unityBackgroundDark.WithAlpha(alpha).GetPixel()); } LudiqGUI.color.BeginOverride(Color.white.WithAlpha(alpha)); GUI.Label(new Rect(0, 0, position.width, position.height), GUIContent.none, Styles.background); HandleKeyboard(); if (tree.searchable) { LudiqGUI.Space(7); if (letQueryClear) { letQueryClear = false; } else { EditorGUI.FocusTextInControl(searchFieldName); } var searchFieldPosition = GUILayoutUtility.GetRect(10, LudiqStyles.searchFieldInnerHeight); searchFieldPosition.x += 8; searchFieldPosition.width -= 16; var newQuery = OnSearchGUI(searchFieldPosition, delayedQuery ?? query); if (newQuery != query || delayedQuery != null) { if (!isAnimating) { query = delayedQuery ?? newQuery; Search(); delayedQuery = null; } else { delayedQuery = newQuery; } } LudiqGUI.Space(5); } OnLevelGUI(anim); prevAnim = anim; if (isAnimating && e.type == EventType.Repaint) { anim = Mathf.MoveTowards(anim, animTarget, repaintDeltaTime * animationSpeed); if (animTarget == 0 && anim == 0 && animAncestor != null) { while (stack.Count > 1 && stack[stack.Count - 1] != animAncestor) { stack.RemoveAt(stack.Count - 1); } anim = 1; prevAnim = 1; animTarget = 1; animAncestor = null; } Repaint(); } if (e.type == EventType.Repaint) { lastRepaintTime = DateTime.UtcNow; } if (!activeParent.isLoading) { if (tree.searchable && hasSearch && activeParent.hasChildren == false) { var searchNotFoundLabel = new GUIContent(string.Format(searchNotFoundFormat, query)); minOptionWidth = Styles.searchNotFound.CalcSize(searchNotFoundLabel).x; EditorGUI.LabelField ( GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)), searchNotFoundLabel, Styles.searchNotFound ); } if (activeNode != null && activeNode.option.hasFooter) { OnFooterGUI(); } } LudiqGUI.color.EndOverride(); if (previousActiveNode != activeNode) { previousActiveNode?.option.OnFocusLeave(previousActiveNode); activeNode?.option?.OnFocusEnter(activeNode); } previousActiveNode = activeNode; OnPositioning(); } } catch (ArgumentException ex) { if (tree.multithreaded && ex.Message.StartsWith("Getting control ")) { // A bunch of happens that might affect the GUI could happen on a // secondary thread, leading to Unity complaining about the amount // of controls changing between the draw call and the layout call. // Because these are hamless and last just one frame, we can safely // ignore them and repaint right away. requireRepaint = true; } else { throw; } } if (requireRepaint) { Repaint(); requireRepaint = false; } }
public virtual void OnFooterGUI(FuzzyOptionNode node, Rect position) { }
private void Initialize(Rect activatorPosition, IFuzzyOptionTree optionTree, Action <IFuzzyOption> callback) { tree = optionTree; // Activator position is assumed to be in screen space, not GUI space // This lets us properly position fuzzy windows from context menus where no GUI context is available this.activatorPosition = activatorPosition; // Create the hierarchy stack = new List <FuzzyOptionNode>(); root = new FuzzyOptionNode(new Root(optionTree.header)); favoritesRoot = new FuzzyOptionNode(new FavoritesRoot()); stack.Add(root); Styles.Initialize(); ExecuteTask(() => { if (!optionTree.prewarmed) { optionTree.Prewarm(); optionTree.prewarmed = true; } else { optionTree.Rewarm(); } Populate(root, optionTree.Root()); UpdateFavorites(); // Fit height to children if there is no depth and no search var hasSubChildren = root.children.Any(option => option.hasChildren != false); if (!optionTree.searchable && !hasSubChildren) { var height = 0f; if (!string.IsNullOrEmpty(root.option.headerLabel)) { height += Styles.headerHeight; } foreach (var child in root.children) { if (child.option is FuzzySeparator) { height += Styles.separatorHeight; } else { height += Styles.optionHeight; } } this.height = height + 1; } }); // Setup the search Search(); // Assign the callback this.callback = callback; // Show and focus the window wantsMouseMove = true; var initialSize = new Vector2(activatorPosition.width, height); this.ShowAsDropDown(activatorPosition, initialSize); Focus(); }
public void Populate(FuzzyOptionNode node, IEnumerable <IFuzzyOption> children, CancellationToken?cancellation = null) { if (node.isPopulated) { return; } if (node.option.mode == FuzzyOptionMode.Branch) { var i = 0; var _children = children.ToArray(); lock (guiLock) { if (node.hasChildren == null) { node.hasChildren = _children.Length > 0; } } var childNodes = new List <FuzzyOptionNode>(); foreach (var child in _children) { if (child == null) { continue; } try { child.OnPopulate(); } catch (Exception ex) { Debug.LogWarning($"Failed to display {child.GetType()}: \n{ex}"); continue; } string label; if (node.option is SearchOption) { label = tree.SearchResultLabel(child, query); } else if (node == favoritesRoot || tree.UseExplicitLabel(node.option, child)) { label = tree.ExplicitLabel(child) ?? child.label; } else { label = child.label; } FuzzyOptionNode childNode; try { childNode = new FuzzyOptionNode(child, label); } catch (Exception ex) { Debug.LogWarning($"Failed to create option node for {child.GetType()} (value = {child.value}): \n{ex}"); continue; } DisplayProgressBar($"{child.label}... ({++i} / {_children.Length})", (float)i / _children.Length); var evaluateBranchesEarly = false; if (child.mode == FuzzyOptionMode.Leaf) { childNode.hasChildren = false; childNodes.Add(childNode); } else if (child.mode == FuzzyOptionMode.Branch) { if (evaluateBranchesEarly) { if (childNode.EvaluateHasChildren(tree)) { childNodes.Add(childNode); } } else { childNodes.Add(childNode); } } cancellation?.ThrowIfCancellationRequested(); } lock (guiLock) { node.children.AddRange(childNodes); } } lock (guiLock) { node.isPopulated = true; } }
private void OnOptionsGUI(float anim, FuzzyOptionNode parent) { var hasHeader = !string.IsNullOrEmpty(parent.option.headerLabel); var headerHeight = hasHeader ? Styles.headerHeight : 0; var searchFieldHeight = tree.searchable ? LudiqStyles.searchFieldOuterHeight : 0; var levelPosition = new Rect ( position.width * (1 - anim) + 1, searchFieldHeight + headerHeight, position.width - 2, height - (searchFieldHeight + 1) - headerHeight ); if (e.type == EventType.MouseDown && e.button == (int)MouseButton.Right && levelPosition.Contains(e.mousePosition)) { SelectParent(); e.Use(); } GUILayout.BeginArea(levelPosition); if (parent.isLoading) { LudiqGUI.BeginVertical(); LudiqGUI.FlexibleSpace(); LudiqGUI.BeginHorizontal(); LudiqGUI.FlexibleSpace(); LudiqGUI.LoaderLayout(); LudiqGUI.FlexibleSpace(); LudiqGUI.EndHorizontal(); LudiqGUI.Space(16); LudiqGUI.BeginHorizontal(); LudiqGUI.Space(10); var progressBarPosition = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.Height(19), GUILayout.ExpandWidth(true)); if (showProgress) { EditorGUI.ProgressBar(progressBarPosition, progress, progressText); } LudiqGUI.Space(10); LudiqGUI.EndHorizontal(); LudiqGUI.Space(LudiqGUI.loaderSize * 1.5f); LudiqGUI.FlexibleSpace(); LudiqGUI.EndVertical(); Repaint(); } else { parent.scroll = GUILayout.BeginScrollView(parent.scroll); EditorGUIUtility.SetIconSize(new Vector2(IconSize.Small, IconSize.Small)); var selectedOptionPosition = default(Rect); foreach (var node in parent.children) { node.EnsurePositionable(); if (e.type == EventType.Layout || e.type == EventType.Repaint) { minOptionWidth = Mathf.Max(minOptionWidth, Mathf.Min(node.width, Styles.maxOptionWidth)); } } for (var i = 0; i < parent.children.Count; i++) { var node = parent.children[i]; var isSeparator = node.option is FuzzySeparator; var optionPosition = GUILayoutUtility.GetRect(IconSize.Small, isSeparator ? Styles.separator.fixedHeight : Styles.optionHeight, GUILayout.ExpandWidth(true)); if (((e.type == EventType.MouseMove && GUIUtility.GUIToScreenPoint(e.mousePosition) != lastMouseMovePosition) || e.type == EventType.MouseDown) && parent.selectedIndex != i && optionPosition.Contains(e.mousePosition)) { parent.selectedIndex = i; Repaint(); lastMouseMovePosition = GUIUtility.GUIToScreenPoint(e.mousePosition); } var optionIsSelected = false; if (i == parent.selectedIndex) { optionIsSelected = true; selectedOptionPosition = optionPosition; } // Clipping if (optionPosition.yMax < parent.scroll.y || optionPosition.yMin > parent.scroll.y + levelPosition.height) { continue; } EditorGUI.BeginDisabledGroup(node.option.mode == FuzzyOptionMode.Branch && node.hasChildren == false); node.EnsureDrawable(); if (e.type == EventType.Repaint) { using (LudiqGUI.color.Override(node.dim ? LudiqGUI.color.value.WithAlphaMultiplied(0.66f) : LudiqGUI.color.value)) { if (node.option is FuzzySeparator) { Styles.separator.Draw(optionPosition, node.label, false, false, false, false); var linePosition = new Rect(optionPosition.x, optionPosition.yMax - 1, optionPosition.width, 1); Styles.separatorLine.Draw(linePosition, GUIContent.none, false, false, false, false); } else { Styles.optionBackground.Draw(optionPosition, GUIContent.none, false, false, optionIsSelected, optionIsSelected); float x = Styles.optionBackground.padding.left; var spaced = false; foreach (var icon in node.icons) { if (icon == null) { continue; } var iconPosition = new Rect ( optionPosition.x + x, optionPosition.y + Styles.optionBackground.padding.top, IconSize.Small, IconSize.Small ); GUI.DrawTexture(iconPosition, icon[IconSize.Small]); x += IconSize.Small; x += Styles.spaceBetweenIcons; spaced = true; } if (spaced) { x -= Styles.spaceBetweenIcons; x += Styles.spaceAfterIcons; } var foregroundPosition = optionPosition; foregroundPosition.x += x; foregroundPosition.width -= x; Styles.optionForeground.Draw(foregroundPosition, node.label, false, false, optionIsSelected, optionIsSelected); } } } var right = optionPosition.xMax; if (node.option.mode == FuzzyOptionMode.Branch) { right -= 13; var rightArrowPosition = new Rect(right, optionPosition.y + 4, 13, 13); if (e.type == EventType.Repaint) { Styles.rightArrow.Draw(rightArrowPosition, false, false, false, false); } } if (node.option.mode == FuzzyOptionMode.Leaf && tree.selected.Contains(node.option.value) && !isSeparator) { right -= 16; var checkPosition = new Rect(right, optionPosition.y + 4, 12, 12); if (e.type == EventType.Repaint) { Styles.check.Draw(checkPosition, false, false, false, false); } } EditorGUI.EndDisabledGroup(); if (tree.favorites != null && tree.CanFavorite(node.option) && (optionIsSelected || tree.favorites.Contains(node.option))) { right -= 19; var starPosition = new Rect(right, optionPosition.y + 2, IconSize.Small, IconSize.Small); EditorGUI.BeginChangeCheck(); var isFavorite = tree.favorites.Contains(node.option); isFavorite = GUI.Toggle(starPosition, isFavorite, GUIContent.none, Styles.star); if (EditorGUI.EndChangeCheck()) { if (isFavorite) { tree.favorites.Add(node.option); } else { tree.favorites.Remove(node.option); } tree.OnFavoritesChange(); ExecuteTask(() => UpdateFavorites()); } } if (e.type == EventType.MouseUp && e.button == (int)MouseButton.Left && optionPosition.Contains(e.mousePosition)) { e.Use(); parent.selectedIndex = i; SelectChild(node); } } EditorGUIUtility.SetIconSize(default(Vector2)); GUILayout.EndScrollView(); if (scrollToSelected && e.type == EventType.Repaint) { scrollToSelected = false; var lastRect = GUILayoutUtility.GetLastRect(); if (selectedOptionPosition.yMax - lastRect.height > parent.scroll.y) { var scroll = parent.scroll; scroll.y = selectedOptionPosition.yMax - lastRect.height; parent.scroll = scroll; Repaint(); } if (selectedOptionPosition.y < parent.scroll.y) { var scroll = parent.scroll; scroll.y = selectedOptionPosition.y; parent.scroll = scroll; Repaint(); } } } GUILayout.EndArea(); }
public override float GetFooterHeight(FuzzyOptionNode node, float width) { return(128); }
public virtual void OnFocusLeave(FuzzyOptionNode node) { }
public virtual void OnFocusEnter(FuzzyOptionNode node) { }
public override void OnFooterGUI(FuzzyOptionNode node, Rect position) { if (ZoomIcon(node)) { var zoomPosition = new Rect ( position.x + Styles.zoomSpacing, position.y + Styles.zoomSpacing, Styles.zoomSize, Styles.zoomSize ); position.x += Styles.zoomSize + Styles.zoomSpacing; position.width -= Styles.zoomSize + Styles.zoomSpacing; GUI.DrawTexture(zoomPosition, Icon()[IconSize.Medium]); } var y = position.y; if (documentation.summary != null) { var summaryPosition = new Rect ( position.x, y, position.width, GetSummaryHeight(position.width) ); OnSummaryGUI(summaryPosition); y = summaryPosition.yMax; } if (documentation.parameters.Any()) { var parameterPosition = new Rect ( position.x, y, position.width, 0 ); foreach (var parameter in documentation.parameters) { parameterPosition.height = GetParameterHeight(parameter, position.width); OnParameterGUI(parameterPosition, parameter); parameterPosition.y += parameterPosition.height; y = parameterPosition.y; } } if (documentation.returns != null) { var returnsPosition = new Rect ( position.x, y, position.width, GetReturnsHeight(position.width) ); OnReturnsGUI(returnsPosition); y = returnsPosition.yMax; } if (documentation.remarks != null) { var remarksPosition = new Rect ( position.x, y, position.width, GetRemarksHeight(position.width) ); OnRemarksGUI(remarksPosition); y = remarksPosition.yMax; } }
public virtual float GetFooterHeight(FuzzyOptionNode node, float width) { return(0); }