private AdvancedDropdownItemState GetStateForItem(AdvancedDropdownItem item) { if (m_LastSelectedState != null && m_LastSelectedState.itemId == item.id) { return(m_LastSelectedState); } for (int i = 0; i < states.Length; i++) { if (states[i].itemId == item.id) { m_LastSelectedState = states[i]; return(m_LastSelectedState); } } Array.Resize(ref states, states.Length + 1); states[states.Length - 1] = new AdvancedDropdownItemState(item); m_LastSelectedState = states[states.Length - 1]; return(states[states.Length - 1]); }
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); }
internal void MoveUpSelection(AdvancedDropdownItem item) { var state = GetStateForItem(item); var selectedIndex = state.selectedIndex; do { --selectedIndex; }while (selectedIndex >= 0 && item.children.ElementAt(selectedIndex).IsSeparator()); if (selectedIndex < 0) { selectedIndex = item.children.Count() - 1; } if (selectedIndex >= 0) { SetSelectionOnItem(item, selectedIndex); } }
void SetSelectionFromState() { var selectedIndex = m_State.GetSelectedIndex(m_CurrentlyRenderedTree); while (selectedIndex >= 0) { var child = m_State.GetSelectedChild(m_CurrentlyRenderedTree); if (child == null) { break; } selectedIndex = m_State.GetSelectedIndex(child); if (selectedIndex < 0) { break; } m_ViewsStack.Push(m_CurrentlyRenderedTree); m_CurrentlyRenderedTree = child; } }
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 void DrawDropdown(float anim, AdvancedDropdownItem group) { // Start of animated area (the part that moves left and right) var areaPosition = new Rect(0, 0, position.width, position.height); // Adjust to the frame areaPosition.x += kBorderThickness; areaPosition.y += kBorderThickness; areaPosition.height -= kBorderThickness * 2; areaPosition.width -= kBorderThickness * 2; GUILayout.BeginArea(m_Gui.GetAnimRect(areaPosition, anim)); // Header if (showHeader) { m_Gui.DrawHeader(group, GoToParent, m_ViewsStack.Count > 0); } DrawList(group); GUILayout.EndArea(); }
protected bool AddMatchItem(AdvancedDropdownItem e, string name, string[] searchWords, List <AdvancedDropdownItem> matchesStart, List <AdvancedDropdownItem> matchesWithin) { var didMatchAll = true; var didMatchStart = false; // See if we match ALL the search words. for (var w = 0; w < searchWords.Length; w++) { var search = searchWords[w]; if (name.Contains(search)) { // If the start of the item matches the first search word, make a note of that. if (w == 0 && name.StartsWith(search)) { didMatchStart = true; } } else { // As soon as any word is not matched, we disregard this item. didMatchAll = false; break; } } // We always need to match all search words. // If we ALSO matched the start, this item gets priority. if (didMatchAll) { if (didMatchStart) { matchesStart.Add(e); } else { matchesWithin.Add(e); } } return(didMatchAll); }
internal virtual void DrawItem(AdvancedDropdownItem item, string name, Texture2D icon, bool enabled, bool drawArrow, bool selected, bool hasSearch) { var content = new GUIContent(name, icon); var imgTemp = content.image; //we need to pretend we have an icon to calculate proper width in case if (content.image == null) { content.image = Texture2D.whiteTexture; } var rect = GUILayoutUtility.GetRect(content, lineStyle, GUILayout.ExpandWidth(true)); content.image = imgTemp; if (Event.current.type != EventType.Repaint) { return; } var imageTemp = content.image; if (content.image == null) { lineStyle.Draw(rect, GUIContent.none, false, false, selected, selected); rect.x += iconSize.x + 1; rect.width -= iconSize.x + 1; } EditorGUI.BeginDisabledGroup(!enabled); lineStyle.Draw(rect, content, false, false, selected, selected); content.image = imageTemp; if (drawArrow) { var size = lineStyle.lineHeight; Rect arrowRect = new Rect(rect.x + rect.width - size, rect.y, size, size); lineStyle.Draw(arrowRect, Styles.arrowRightContent, false, false, false, false); } EditorGUI.EndDisabledGroup(); }
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); }
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); }
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); } } } }
internal virtual void DrawFooter(AdvancedDropdownItem selectedItem) { }
protected override void ItemSelected(AdvancedDropdownItem item) { m_PathProperty.stringValue = ((InputControlTreeViewItem)item).controlPathWithDevice; m_OnPickCallback(m_PathProperty); }
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); } }
public void AddChild(AdvancedDropdownItem child) { m_Children.Add(child); }
private void DrawList(AdvancedDropdownItem item) { // Start of scroll view list m_State.SetScrollState(item, GUILayout.BeginScrollView(m_State.GetScrollState(item), GUIStyle.none, GUI.skin.verticalScrollbar)); EditorGUIUtility.SetIconSize(m_Gui.iconSize); Rect selectedRect = new Rect(); for (var i = 0; i < item.children.Count(); i++) { var child = item.children.ElementAt(i); bool selected = m_State.GetSelectedIndex(item) == i; if (child.IsSeparator()) { m_Gui.DrawLineSeparator(); } else { m_Gui.DrawItem(child, child.name, child.icon, child.enabled, child.children.Any(), selected, hasSearch); } var r = GUILayoutUtility.GetLastRect(); if (selected) { selectedRect = r; } // Skip input handling for the tree used for animation if (item != m_CurrentlyRenderedTree) { continue; } // Select the element the mouse cursor is over. // Only do it on mouse move - keyboard controls are allowed to overwrite this until the next time the mouse moves. if (Event.current.type == EventType.MouseMove || Event.current.type == EventType.MouseDrag) { if (!selected && r.Contains(Event.current.mousePosition)) { m_State.SetSelectedIndex(item, i); Event.current.Use(); } } if (Event.current.type == EventType.MouseUp && r.Contains(Event.current.mousePosition)) { m_State.SetSelectedIndex(item, i); var selectedChild = m_State.GetSelectedChild(item); if (selectedChild.children.Any()) { GoToChild(); } else { if (!selectedChild.IsSeparator() && selectionChanged != null) { selectionChanged(selectedChild); } if (closeOnSelection) { CloseWindow(); GUIUtility.ExitGUI(); } } Event.current.Use(); } } EditorGUIUtility.SetIconSize(Vector2.zero); GUILayout.EndScrollView(); // Scroll to selected on windows creation if (m_ScrollToSelected && m_InitialSelectionPosition != 0) { float diffOfPopupAboveTheButton = m_ButtonRectScreenPos.y - position.y; diffOfPopupAboveTheButton -= m_Gui.searchHeight + m_Gui.headerHeight; m_State.SetScrollState(item, new Vector2(0, m_InitialSelectionPosition - diffOfPopupAboveTheButton)); m_ScrollToSelected = false; m_InitialSelectionPosition = 0; } // Scroll to show selected else if (m_ScrollToSelected && Event.current.type == EventType.Repaint) { m_ScrollToSelected = false; Rect scrollRect = GUILayoutUtility.GetLastRect(); if (selectedRect.yMax - scrollRect.height > m_State.GetScrollState(item).y) { m_State.SetScrollState(item, new Vector2(0, selectedRect.yMax - scrollRect.height)); Repaint(); } if (selectedRect.y < m_State.GetScrollState(item).y) { m_State.SetScrollState(item, new Vector2(0, selectedRect.y)); Repaint(); } } }
public void ReloadData() { m_MainTree = FetchData(); }
internal void SetSelectedIndex(AdvancedDropdownItem item, int index) { GetStateForItem(item).selectedIndex = index; }
public AdvancedDropdownItemState(AdvancedDropdownItem item) { itemId = item.id; }
internal Vector2 GetScrollState(AdvancedDropdownItem item) { return(GetStateForItem(item).scroll); }
protected virtual void ItemSelected(AdvancedDropdownItem item) { }
public void RebuildSearch(string search) { m_SearchTree = Search(search); }
protected override void ItemSelected(AdvancedDropdownItem item) { var path = ((InputControlTreeViewItem)item).controlPathWithDevice; m_OnPickCallback(path); }
internal int GetSelectedIndex(AdvancedDropdownItem item) { return(GetStateForItem(item).selectedIndex); }
protected override void ItemSelected(AdvancedDropdownItem item) { m_AddElement((item as AddDeviceDropdownItem).name, (item as AddDeviceDropdownItem).m_DeviceId); }
internal void SetScrollState(AdvancedDropdownItem item, Vector2 scrollState) { GetStateForItem(item).scroll = scrollState; }