private static void DrawSearchInput(DictionaryGUIState state, Inspector.Options options)
        {
            if (state.searchInput == null)
            {
                state.searchInput = new SearchInputState();
            }

            if (state.Size() < options.showSearchAtSize)
            {
                ClearSearchInput(state);
                return;
            }

            var now = DateTime.Now;
            SearchInputState input = state.searchInput;

            using (GUITools.Indent())
            {
                using (GUITools.HorizontalScope())
                {
                    var newInput = GUITools.TextField("Search: ", input.text ?? "");
                    if (newInput != input.text)
                    {
                        input.text           = newInput;
                        input.lastTextChange = now;
                        if (input.inputStart.Ticks == 0)
                        {
                            input.inputStart = now;
                        }
                    }
                    else
                    {
                        if (Math.Abs((now - input.lastTextChange).TotalSeconds) > 0.5)
                        {
                            input.inputStart = new DateTime();
                        }
                    }

                    if (input.text != input.filter &&
                        (
                            input.inputStart.Ticks == 0 ||
                            Math.Abs((now - input.lastFilterChange).TotalSeconds) > 1 &&
                            Math.Abs((now - input.inputStart).TotalSeconds) > 1
                        ))
                    {
                        input.changed          = true;
                        input.filter           = input.text;
                        input.lastFilterChange = DateTime.Now;
                    }
                    else
                    {
                        input.changed = false;
                    }
                }
            }
        }
        private static bool Traversal(Inspector inspector, string path, DictionaryGUIState state, int start, int end)
        {
            bool changed    = false;
            var  bucketSize = Math.Max(state.display.bucketSize, 2);
            var  valueType  = state.ValueType();

            if (end - start <= bucketSize)
            {
                for (int index = start; index < end; ++index)
                {
                    changed |= InspectElement(inspector, path, state.display.resultKeys[index], state, valueType);
                }
            }
            else
            {
                // Allow multiple level of bucket, calculate the current step
                int step = bucketSize;
                while (step * bucketSize < end - start)
                {
                    step *= bucketSize;
                }

                for (int inner = start; inner < end; inner += step)
                {
                    int innerEnd = Math.Min(end, inner + step);

                    var innerKeyBegin = state.display.resultKeys[inner];
                    var innerKeyEnd   = state.display.resultKeys[innerEnd - 1];
                    var label         = FormatKeyRange(innerKeyBegin, innerKeyEnd);
                    var foldoutPath   = path + "[" + innerKeyBegin + "~" + innerKeyEnd + "]";

                    inspector.isFoldout[foldoutPath] = GUITools.Foldout(inspector.isFoldout.ContainsKey(foldoutPath) && inspector.isFoldout[foldoutPath], label);
                    if (inspector.isFoldout[foldoutPath])
                    {
                        using (GUITools.Indent())
                        {
                            changed |= Traversal(inspector, path, state, inner, innerEnd);
                        }
                    }
                }
            }

            return(changed);
        }
        public bool InspectInternal(string name, string path, object data,
                                    Type type  = null,
                                    IMark mark = null,
                                    Action <object> OnValueChanged = null)
        {
            if (data != null)
            {
                type = data.GetType();
            }

            GUITools.SetLabelWidth(options.labelWidth);
            VisualizerBase visualizer  = GetVisualizer(type, mark);
            bool           changed     = false;
            object         changedData = data;

            if (visualizer != null)
            {
                string fieldinfo = name;
                var    postfix   = visualizer.GetLabelPostfix(this, data, type);
                if (postfix != null)
                {
                    fieldinfo += postfix;
                }

                if (visualizer.HasChildren())
                {
                    // Note: to avoid infinite expand that may cause by alwaysShowChildren,
                    // If parentAlwaysShowChild, then current node ignores alwaysShowChildren.
                    var  parentAlwaysShowChild = parentIsAlwaysShow.Count > 0 && parentIsAlwaysShow.Peek();
                    bool alwaysShowChildren    = !parentAlwaysShowChild && visualizer.AlwaysShowChildren();
                    if (!alwaysShowChildren)
                    {
                        using (GUITools.HorizontalScope())
                        {
                            var width = options.labelWidth - options.indentOffset * GUITools.GetIndentLevel();
                            using (GUITools.HorizontalScope(width))
                            {
                                isFoldout[path] = GUITools.Foldout(isFoldout.ContainsKey(path) && isFoldout[path], fieldinfo);
                            }
                            changed |= InspectRoot(name, type, ref changedData, visualizer, mark);
                        }
                    }
                    else
                    {
                        changed |= InspectRoot(name, type, ref changedData, visualizer, mark);
                    }

                    if (changedData != null && (alwaysShowChildren || isFoldout[path]))
                    {
                        try
                        {
                            parentIsAlwaysShow.Push(alwaysShowChildren);
                            using (GUITools.Indent())
                                changed |= visualizer.InspectChildren(this, path, ref changedData, type);
                        }
                        finally
                        {
                            parentIsAlwaysShow.Pop();
                        }
                    }
                }
                else
                {
                    changed |= InspectRoot(fieldinfo, type, ref changedData, visualizer, mark);
                }
            }

            if (changed && OnValueChanged != null)
            {
                OnValueChanged(changedData);
            }
            return(changed);
        }