protected override AdvancedDropdownItem BuildRoot()
        {
            var root = new AdvancedDropdownItem("");

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

            var devices = BuildTreeForAbstractDevices();

            if (devices.children.Any())
            {
                root.AddChild(devices);
            }
            var products = BuildTreeForSpecificDevices();

            if (products.children.Any())
            {
                root.AddChild(products);
            }

            return(root);
        }
예제 #2
0
        private void AddDeviceTreeItem(InputControlLayout layout, AdvancedDropdownItem parent)
        {
            // Ignore devices that have no controls. We're looking at fully merged layouts here so
            // we're also taking inherited controls into account.
            if (layout.controls.Count == 0)
            {
                return;
            }

            var deviceItem = new DeviceTreeViewItem(layout);

            AddControlTreeItemsRecursive(layout, deviceItem, "", layout.name, null);

            if (deviceItem.children.Any())
            {
                parent.AddChild(deviceItem);
            }

            foreach (var commonUsage in layout.commonUsages)
            {
                var commonUsageGroup = new DeviceTreeViewItem(layout, commonUsage);
                AddControlTreeItemsRecursive(layout, commonUsageGroup, "", layout.name, commonUsage);
                if (commonUsageGroup.children.Any())
                {
                    parent.AddChild(commonUsageGroup);
                }
            }
        }
        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);
        }
        private void AddControlTreeItemsRecursive(InputControlLayout layout, AdvancedDropdownItem parent, string prefix, string deviceControlId, string commonUsage)
        {
            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 ControlTreeViewItem(control, prefix, deviceControlId, commonUsage);

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

                var childLayout = EditorInputControlLayoutCache.TryGetLayout(control.layout);
                if (childLayout != null)
                {
                    AddControlTreeItemsRecursive(childLayout, parent, child.controlPath, deviceControlId, commonUsage);
                }
            }

            // Add optional layouts for devices
            var optionalLayouts = EditorInputControlLayoutCache.GetOptionalControlsForLayout(layout.name);

            if (optionalLayouts.Any() && layout.isDeviceLayout)
            {
                var optionalGroup = new AdvancedDropdownItem("Optional");
                foreach (var optionalLayout in optionalLayouts)
                {
                    if (LayoutMatchesExpectedControlLayoutFilter(optionalLayout.layout))
                    {
                        optionalGroup.AddChild(new OptionalControlTreeViewItem(optionalLayout, deviceControlId, commonUsage));
                    }
                }
                if (optionalGroup.children.Any())
                {
                    parent.AddChild(optionalGroup);
                }
            }
        }
예제 #5
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);
        }
예제 #6
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);
            }
        }
예제 #7
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.
            AdvancedDropdownItem 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);
            }
        }
예제 #8
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);
                }
            }
        }
예제 #9
0
        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);
        }
예제 #10
0
            protected override AdvancedDropdownItem BuildRoot()
            {
                var root       = new AdvancedDropdownItem("");
                var deviceList = GetDeviceOptions();

                deviceList.Sort();
                foreach (var device in deviceList)
                {
                    root.AddChild(new AddDeviceDropdownItem(device.ToString(), device.id));
                }
                return(root);
            }
        private AdvancedDropdownItem BuildTreeForUsages()
        {
            var usageRoot = new AdvancedDropdownItem("Usages");

            foreach (var usage in EditorInputControlLayoutCache.allUsages)
            {
                var child = new UsageTreeViewItem(usage);
                if (usage.Value.Any(LayoutMatchesExpectedControlLayoutFilter))
                {
                    usageRoot.AddChild(child);
                }
            }
            return(usageRoot);
        }
예제 #12
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);
        }
예제 #13
0
        protected override AdvancedDropdownItem BuildRoot()
        {
            var root = new AdvancedDropdownItem("");

            var usages = BuildTreeForUsages();

            if (usages.children.Any())
            {
                root.AddChild(usages);
            }
            var devices = BuildTreeForAbstractDevices();

            if (devices.children.Any())
            {
                root.AddChild(devices);
            }
            var products = BuildTreeForSpecificDevices();

            if (products.children.Any())
            {
                root.AddChild(products);
            }

            if (m_DeviceFilter != null && m_DeviceFilter.Length > 0)
            {
                var newRoot = new AdvancedDropdownItem("");
                FindDevice(newRoot, root, m_DeviceFilter);
                if (newRoot.children.Count() == 1)
                {
                    return(newRoot.children.ElementAt(0));
                }
                return(newRoot);
            }

            return(root);
        }
예제 #14
0
 void FindDevice(AdvancedDropdownItem newRoot, AdvancedDropdownItem root, string[] deviceFilter)
 {
     foreach (var child in root.children)
     {
         var deviceItem = child as DeviceTreeViewItem;
         if (child is DeviceTreeViewItem)
         {
             if (deviceFilter.Contains(deviceItem.controlPathWithDevice))
             {
                 newRoot.AddChild(deviceItem);
             }
         }
         if (child.children.Any())
         {
             FindDevice(newRoot, child, deviceFilter);
         }
     }
 }
예제 #15
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);
            }
        }
예제 #16
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);
        }
        private AdvancedDropdownItem BuildTreeForSpecificDevices()
        {
            var mainGroup = new AdvancedDropdownItem("Specific Devices");

            foreach (var layout in EditorInputControlLayoutCache.allProductLayouts.OrderBy(a => a.name))
            {
                var rootLayoutName = InputControlLayout.s_Layouts.GetRootLayoutName(layout.name).ToString();
                if (string.IsNullOrEmpty(rootLayoutName))
                {
                    rootLayoutName = "Other";
                }
                else
                {
                    rootLayoutName = rootLayoutName.GetPlural();
                }

                var rootLayoutGroup = mainGroup.children.Any()
                    ? mainGroup.children.FirstOrDefault(x => x.name == rootLayoutName)
                    : null;
                if (rootLayoutGroup == null)
                {
                    rootLayoutGroup = new DeviceTreeViewItem(layout)
                    {
                        name = rootLayoutName,
                        id   = rootLayoutName.GetHashCode(),
                    };
                }

                AddDeviceTreeItem(layout, rootLayoutGroup);

                if (rootLayoutGroup.children.Any() && !mainGroup.children.Contains(rootLayoutGroup))
                {
                    mainGroup.AddChild(rootLayoutGroup);
                }
            }
            return(mainGroup);
        }
예제 #18
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);

            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);
            }
        }
        private void AddDeviceTreeItem(InputControlLayout layout, AdvancedDropdownItem parent)
        {
            // Ignore devices that have no controls. We're looking at fully merged layouts here so
            // we're also taking inherited controls into account.
            // EXCEPTION: We're okay with empty devices if we're picking devices and not controls.
            if (layout.controls.Count == 0 && m_Mode != Mode.PickDevice)
            {
                return;
            }

            var deviceItem = new DeviceTreeViewItem(layout);

            // If we have a device filter, see if we should ignore the device.
            if (m_DeviceFilter != null)
            {
                var matchesAnyInDeviceFilter = false;
                foreach (var entry in m_DeviceFilter)
                {
                    if (entry == layout.name ||
                        InputControlLayout.s_Layouts.IsBasedOn(new InternedString(entry), layout.name))
                    {
                        matchesAnyInDeviceFilter = true;
                    }
                    else
                    {
                        ////FIXME: this also needs to work for full control paths and not just stuff like "<Gamepad>"
                        var expectedLayout = InputControlPath.TryGetDeviceLayout(entry);
                        if (!string.IsNullOrEmpty(expectedLayout) &&
                            (expectedLayout == layout.name ||
                             InputControlLayout.s_Layouts.IsBasedOn(new InternedString(expectedLayout), layout.name)))
                        {
                            matchesAnyInDeviceFilter = true;
                        }
                    }
                }

                if (!matchesAnyInDeviceFilter)
                {
                    return;
                }
            }

            if (m_Mode != Mode.PickDevice)
            {
                AddControlTreeItemsRecursive(layout, deviceItem, "", layout.name, null);
            }

            if (deviceItem.children.Any() || m_Mode == Mode.PickDevice)
            {
                parent.AddChild(deviceItem);
            }

            if (m_Mode != Mode.PickDevice)
            {
                foreach (var commonUsage in layout.commonUsages)
                {
                    var commonUsageGroup = new DeviceTreeViewItem(layout, commonUsage);
                    AddControlTreeItemsRecursive(layout, commonUsageGroup, "", layout.name, commonUsage);
                    if (commonUsageGroup.children.Any())
                    {
                        parent.AddChild(commonUsageGroup);
                    }
                }
            }
        }