protected override AdvancedDropdownItem FetchData()
        {
            var rootGroup = new AdvancedDropdownItem(m_Label);

            m_SearchableElements = new List <AdvancedDropdownItem>();

            for (int i = 0; i < m_DisplayedOptions.Length; i++)
            {
                var menuPath = m_DisplayedOptions[i];
                var paths    = menuPath.Split('/');

                AdvancedDropdownItem parent = rootGroup;
                for (var j = 0; j < paths.Length; j++)
                {
                    var path = paths[j];
                    if (j == paths.Length - 1)
                    {
                        var element = new MultiLevelItem(path, menuPath);
                        element.elementIndex = i;
                        parent.AddChild(element);
                        m_SearchableElements.Add(element);

                        if (i == m_SelectedIndex)
                        {
                            selectedIDs.Add(element.id);
//                            var tempParent = parent;
//                            AdvancedDropdownItem searchedItem = element;
                            //TODO fix selecting
//                            while (tempParent != null)
//                            {
//                                state.SetSelectedIndex(tempParent, tempParent.children.IndexOf(searchedItem));
//                                searchedItem = tempParent;
//                                tempParent = tempParent.parent;
//                            }
                        }
                        continue;
                    }

                    var groupPathId = paths[0];
                    for (int k = 1; k <= j; k++)
                    {
                        groupPathId += "/" + paths[k];
                    }

                    var group = parent.children.SingleOrDefault(c => ((MultiLevelItem)c).stringId == groupPathId);
                    if (group == null)
                    {
                        group = new MultiLevelItem(path, groupPathId);
                        parent.AddChild(group);
                    }
                    parent = group;
                }
            }
            return(rootGroup);
        }
Example #2
0
        protected override AdvancedDropdownItem BuildCustomSearch(string searchString,
                                                                  IEnumerable <AdvancedDropdownItem> elements)
        {
            if (!isListening)
            {
                return(null);
            }

            var root = new AdvancedDropdownItem(!string.IsNullOrEmpty(m_ExpectedControlLayout)
                ? $"Listening for {m_ExpectedControlLayout}..."
                : "Listening for input...");

            if (searchString == "\u0017")
            {
                return(root);
            }

            var paths = searchString.Substring(1).Split('\u0017');

            foreach (var element in elements)
            {
                if (element is ControlDropdownItem controlItem && paths.Any(x => controlItem.controlPathWithDevice == x))
                {
                    root.AddChild(element);
                }
            }

            return(root);
        }
Example #3
0
        private static void AddCharacterKeyBindingsTo(AdvancedDropdownItem parent, Keyboard keyboard)
        {
            foreach (var key in keyboard.children.OfType <KeyControl>())
            {
                if (!key.keyCode.IsTextInputKey())
                {
                    continue;
                }

                // We can only bind to characters that can be printed.
                var displayName = key.displayName;
                if (!displayName.All(x => x.IsPrintable()))
                {
                    continue;
                }

                if (displayName.Contains(')'))
                {
                    displayName = string.Join("", displayName.Select(x => "\\" + x));
                }

                ////TODO: should be searchable; when searching, needs different display name
                var item = new ControlDropdownItem(null, $"#({displayName})", "", keyboard.layout, "", false);
                item.name = key.displayName;

                parent.AddChild(item);
            }
        }
Example #4
0
        private void AddItemsForDevices(AdvancedDropdownItem parent)
        {
            // Add devices that are marked as generic types of devices directly to the parent.
            // E.g. adds "Gamepad" and then underneath all the more specific types of gamepads.
            foreach (var deviceLayout in EditorInputControlLayoutCache.allLayouts
                     .Where(x => x.isDeviceLayout && x.isGenericTypeOfDevice && !x.hideInUI).OrderBy(a => a.displayName))
            {
                AddDeviceTreeItemRecursive(deviceLayout, parent);
            }

            // We have devices that are based directly on InputDevice but are not marked as generic types
            // of devices (e.g. Vive Lighthouses). We do not want them to clutter the list at the root so we
            // all of them in a group called "Other" at the end of the list.
            var otherGroup = new AdvancedDropdownItem("Other");

            foreach (var deviceLayout in EditorInputControlLayoutCache.allLayouts
                     .Where(x => x.isDeviceLayout && !x.isGenericTypeOfDevice && x.type.BaseType == typeof(InputDevice) &&
                            !x.hideInUI && !x.baseLayouts.Any()).OrderBy(a => a.displayName))
            {
                AddDeviceTreeItemRecursive(deviceLayout, otherGroup);
            }

            if (otherGroup.children.Any())
            {
                parent.AddChild(otherGroup);
            }
        }
Example #5
0
        private void AddControlTreeItemsRecursive(InputControlLayout layout, DeviceDropdownItem parent,
                                                  string device, string usage, bool searchable, ControlDropdownItem parentControl = null)
        {
            foreach (var control in layout.controls.OrderBy(a => a.name))
            {
                if (control.isModifyingChildControlByPath)
                {
                    continue;
                }

                // Skip variants except the default variant and variants dictated by the layout itself.
                if (!control.variants.IsEmpty() && control.variants != InputControlLayout.DefaultVariant &&
                    (layout.variants.IsEmpty() || !InputControlLayout.VariantsMatch(layout.variants, control.variants)))
                {
                    continue;
                }

                var child = new ControlDropdownItem(parentControl, control.name, control.displayName,
                                                    device, usage, searchable);
                child.icon = EditorInputControlLayoutCache.GetIconForLayout(control.layout);

                if (LayoutMatchesExpectedControlLayoutFilter(control.layout))
                {
                    parent.AddChild(child);
                }

                var controlLayout = EditorInputControlLayoutCache.TryGetLayout(control.layout);
                if (controlLayout != null)
                {
                    AddControlTreeItemsRecursive(controlLayout, parent, device, usage,
                                                 searchable, child);
                }
            }

            // Add optional controls for devices.
            var optionalControls = EditorInputControlLayoutCache.GetOptionalControlsForLayout(layout.name);

            if (optionalControls.Any() && layout.isDeviceLayout)
            {
                var optionalGroup = new AdvancedDropdownItem("Optional Controls");
                foreach (var optionalControl in optionalControls)
                {
                    if (LayoutMatchesExpectedControlLayoutFilter(optionalControl.layout))
                    {
                        var child = new OptionalControlDropdownItem(optionalControl, device, usage);
                        child.icon = EditorInputControlLayoutCache.GetIconForLayout(optionalControl.layout);
                        optionalGroup.AddChild(child);
                    }
                }

                if (optionalGroup.children.Any())
                {
                    var deviceName = EditorInputControlLayoutCache.TryGetLayout(device).m_DisplayName ??
                                     ObjectNames.NicifyVariableName(device);
                    parent.AddSeparator("Controls Present on More Specific " + deviceName.GetPlural());
                    parent.AddChild(optionalGroup);
                }
            }
        }
        protected virtual AdvancedDropdownItem Search(string searchString)
        {
            if (m_SearchableElements == null)
            {
                BuildSearchableElements();
            }
            if (string.IsNullOrEmpty(searchString))
            {
                return(null);
            }

            var searchTree = PerformCustomSearch(searchString);

            if (searchTree == null)
            {
                // Support multiple search words separated by spaces.
                var searchWords = searchString.ToLower().Split(' ');

                // We keep two lists. Matches that matches the start of an item always get first priority.
                var matchesStart  = new List <AdvancedDropdownItem>();
                var matchesWithin = new List <AdvancedDropdownItem>();

                foreach (var e in m_SearchableElements)
                {
                    var name = e.searchableName.ToLower().Replace(" ", "");
                    AddMatchItem(e, name, searchWords, matchesStart, matchesWithin);
                }

                searchTree = new AdvancedDropdownItem(kSearchHeader);
                matchesStart.Sort();
                foreach (var element in matchesStart)
                {
                    searchTree.AddChild(element);
                }
                matchesWithin.Sort();
                foreach (var element in matchesWithin)
                {
                    searchTree.AddChild(element);
                }
            }

            return(searchTree);
        }
        protected override AdvancedDropdownItem FetchData()
        {
            var rootGroup = new AdvancedDropdownItem(m_Label);

            m_SearchableElements = new List <AdvancedDropdownItem>();

            for (int i = 0; i < m_DisplayedOptions.Length; i++)
            {
                var menuPath = m_DisplayedOptions[i];
                var paths    = menuPath.Split('/');

                AdvancedDropdownItem parent = rootGroup;
                for (var j = 0; j < paths.Length; j++)
                {
                    var path = paths[j];
                    if (j == paths.Length - 1)
                    {
                        var element = new MultiLevelItem(path, menuPath);
                        element.elementIndex = i;
                        parent.AddChild(element);
                        m_SearchableElements.Add(element);
                        continue;
                    }

                    var groupPathId = paths[0];
                    for (int k = 1; k <= j; k++)
                    {
                        groupPathId += "/" + paths[k];
                    }

                    var group = parent.children.SingleOrDefault(c => ((MultiLevelItem)c).stringId == groupPathId);
                    if (group == null)
                    {
                        group = new MultiLevelItem(path, groupPathId);
                        parent.AddChild(group);
                    }
                    parent = group;
                }
            }
            return(rootGroup);
        }
Example #8
0
        private static void AddPhysicalKeyBindingsTo(AdvancedDropdownItem parent, Keyboard keyboard, bool searchable)
        {
            foreach (var key in keyboard.children.OfType <KeyControl>())
            {
                // If the key has a display name that differs from the key name, show it in the UI.
                var displayName    = key.m_DisplayNameFromLayout;
                var keyDisplayName = key.displayName;
                if (keyDisplayName.All(x => x.IsPrintable()) && string.Compare(keyDisplayName, displayName,
                                                                               StringComparison.InvariantCultureIgnoreCase) != 0)
                {
                    displayName = $"{displayName} (Current Layout: {key.displayName})";
                }

                // For left/right modifier keys, prepend artificial combined version.
                ButtonControl combinedVersion = null;
                if (key == keyboard.leftShiftKey)
                {
                    combinedVersion = keyboard.shiftKey;
                }
                else if (key == keyboard.leftAltKey)
                {
                    combinedVersion = keyboard.altKey;
                }
                else if (key == keyboard.leftCtrlKey)
                {
                    combinedVersion = keyboard.ctrlKey;
                }
                if (combinedVersion != null)
                {
                    parent.AddChild(new ControlDropdownItem(null, combinedVersion.name, combinedVersion.displayName, keyboard.layout,
                                                            "", searchable));
                }

                var item = new ControlDropdownItem(null, key.name, displayName,
                                                   keyboard.layout, "", searchable);

                parent.AddChild(item);
            }
        }
Example #9
0
        private AdvancedDropdownItem BuildTreeForUsages()
        {
            var usageRoot = new AdvancedDropdownItem("Usages");

            foreach (var usageAndLayouts in EditorInputControlLayoutCache.allUsages)
            {
                if (usageAndLayouts.Item2.Any(LayoutMatchesExpectedControlLayoutFilter))
                {
                    var child = new UsageDropdownItem(usageAndLayouts.Item1);
                    usageRoot.AddChild(child);
                }
            }
            return(usageRoot);
        }
Example #10
0
        private static void AddPhysicalKeyBindingsTo(AdvancedDropdownItem parent, Keyboard keyboard, bool searchable)
        {
            foreach (var key in keyboard.children.OfType <KeyControl>())
            {
                // If the key has a display name that differs from the key name, show it in the UI.
                var displayName    = key.name;
                var keyDisplayName = key.displayName;
                if (keyDisplayName.All(x => x.IsPrintable()) && keyDisplayName != key.name)
                {
                    displayName = $"{key.name} (Current Layout: {key.displayName})";
                }

                var item = new ControlDropdownItem(null, key.name, displayName,
                                                   keyboard.layout, "", searchable);

                parent.AddChild(item);
            }
        }
Example #11
0
        protected override AdvancedDropdownItem BuildRoot()
        {
            var root = new AdvancedDropdownItem(string.Empty);

            // Usages.
            if (m_Mode != InputControlPicker.Mode.PickDevice)
            {
                var usages = BuildTreeForUsages();
                if (usages.children.Any())
                {
                    root.AddChild(usages);
                    root.AddSeparator();
                }
            }

            // Devices.
            AddItemsForDevices(root);

            return(root);
        }
Example #12
0
        private void AddDeviceTreeItemRecursive(InputControlLayout layout, AdvancedDropdownItem parent, bool searchable = true)
        {
            // Find all layouts directly based on this one.
            var childLayouts = EditorInputControlLayoutCache.allLayouts
                               .Where(x => x.isDeviceLayout && !x.hideInUI && x.baseLayouts.Contains(layout.name)).OrderBy(x => x.displayName);

            // See if the entire tree should be excluded.
            var shouldIncludeDeviceLayout          = ShouldIncludeDeviceLayout(layout);
            var shouldIncludeAtLeastOneChildLayout = childLayouts.Any(ShouldIncludeDeviceLayout);

            if (!shouldIncludeDeviceLayout && !shouldIncludeAtLeastOneChildLayout)
            {
                return;
            }

            // Add toplevel item for device.
            var deviceItem = new DeviceDropdownItem(layout, searchable: searchable);

            parent.AddChild(deviceItem);

            // Add common usage variants.
            if (m_Mode != InputControlPicker.Mode.PickDevice && layout.commonUsages.Count > 0)
            {
                foreach (var usage in layout.commonUsages)
                {
                    var usageItem = new DeviceDropdownItem(layout, usage);
                    AddControlTreeItemsRecursive(layout, usageItem, layout.name, usage, searchable);
                    deviceItem.AddChild(usageItem);
                }
                deviceItem.AddSeparator();
            }

            // Add controls.
            var haveControls = false;

            if (shouldIncludeDeviceLayout && m_Mode != InputControlPicker.Mode.PickDevice)
            {
                var childCountBefore = deviceItem.children.Count();

                // The keyboard is special in that we want to allow binding by display name (i.e. character
                // generated by a key) instead of only by physical key location. Also, we want to give an indication
                // of which specific key an entry refers to by taking the current keyboard layout into account.
                //
                // So what we do is add an extra level to the keyboard where key's can be bound by character
                // according to the current layout. And in the top level of the keyboard we display keys with
                // both physical and logical names.
                if (layout.type == typeof(Keyboard) && Keyboard.current != null)
                {
                    var byLocationGroup  = new AdvancedDropdownItem("By Location of Key (Using US Layout)");
                    var byCharacterGroup = new AdvancedDropdownItem("By Character Mapped to Key");

                    deviceItem.AddChild(byLocationGroup);
                    deviceItem.AddChild(byCharacterGroup);

                    var keyboard = Keyboard.current;

                    AddCharacterKeyBindingsTo(byCharacterGroup, keyboard);
                    AddPhysicalKeyBindingsTo(byLocationGroup, keyboard, searchable);
                }
                else
                {
                    AddControlTreeItemsRecursive(layout, deviceItem, layout.name, null, searchable);
                }

                haveControls = deviceItem.children.Count() != childCountBefore;
            }

            // Add child items.
            var isFirstChild = true;

            foreach (var childLayout in childLayouts)
            {
                if (!ShouldIncludeDeviceLayout(childLayout))
                {
                    continue;
                }

                if (isFirstChild && haveControls)
                {
                    deviceItem.AddSeparator("More Specific " + deviceItem.name.GetPlural());
                }
                isFirstChild = false;

                AddDeviceTreeItemRecursive(childLayout, deviceItem, searchable && !childLayout.isGenericTypeOfDevice);
            }

            // When picking devices, it must be possible to select a device that itself has more specific types
            // of devices underneath it. However in the dropdown, such a device will be a foldout and not itself
            // be selectable. We solve this problem by adding an entry for the device underneath the device
            // itself (e.g. "Gamepad >> Gamepad").
            if (m_Mode == InputControlPicker.Mode.PickDevice && deviceItem.m_Children.Count > 0)
            {
                var item = new DeviceDropdownItem(layout);
                deviceItem.m_Children.Insert(0, item);
            }
        }