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); }
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); } } }
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); }
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); } }
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); } }
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 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); }
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); }
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); }
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); } } }
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); } }
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); }
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); } } } }