private static bool ListCreate(CustomSelectListCtrl __instance, CustomSelectListCtrl.OnChangeItemFunc _onChangeItemFunc, List <CustomSelectInfo> ___lstSelectInfo, GameObject ___objContent, GameObject ___objTemp) { __instance.onChangeItemFunc = _onChangeItemFunc; // Remove old list items if recreating the list foreach (Transform c in ___objContent.transform) { Destroy(c.gameObject); } var toggleGroup = ___objContent.GetComponent <ToggleGroup>(); toggleGroup.allowSwitchOff = true; // Figure out how many items fit in the window at most // Need to use this specific RT because it's the most reliable var windowRt = __instance.GetComponent <RectTransform>(); if (ListWidth.Value != 3) { windowRt.sizeDelta += new Vector2((ListWidth.Value - 3) * 120, 0); } var itemsInRow = ((int)windowRt.rect.width - 33) / 120; var itemsInColumn = ((int)windowRt.rect.height - 105) / 120 + 2; var totalVisibleItems = itemsInRow * itemsInColumn; // Create a cache of list items for the virtual list var spawnedItems = new List <CustomSelectInfoComponent>(); var setHandlerMethod = Traverse.Create(__instance).Method("SetToggleHandler", new[] { typeof(GameObject) }); for (int i = 0; i < totalVisibleItems; i++) { var copy = Instantiate(___objTemp, ___objContent.transform, false); var copyInfoComp = copy.GetComponent <CustomSelectInfoComponent>(); copyInfoComp.tgl.group = toggleGroup; copyInfoComp.tgl.isOn = false; setHandlerMethod.GetValue(copy); copyInfoComp.img = copy.GetComponent <Image>(); var newTr = copy.transform.Find("New"); if (newTr) { copyInfoComp.objNew = newTr.gameObject; } spawnedItems.Add(copyInfoComp); copy.SetActive(false); } __instance.imgRaycast = spawnedItems.Select(x => x.img).ToArray(); _listCache[__instance] = new VirtualListData(itemsInRow, spawnedItems, ___objContent.GetComponent <RectTransform>(), ___lstSelectInfo); return(false); }
private static void ChangeItem(CustomSelectListCtrl __instance, CustomSelectInfo itemInfo, VirtualListData listData) { // Calling original whenever possible is probably better for interop since any hooks will run if (itemInfo.sic != null) { __instance.ChangeItem(itemInfo.sic.gameObject); return; } __instance.onChangeItemFunc?.Invoke(itemInfo.index); var tv = new Traverse(__instance); tv.Field <string>("selectDrawName").Value = itemInfo.name; #if KK var tmp = tv.Field <TMPro.TextMeshProUGUI>("textDrawName").Value; if (tmp) { tmp.text = itemInfo.name; } #endif if (VirtualListData.IsItemNew(itemInfo)) { VirtualListData.MarkItemAsNotNew(itemInfo); MarkListDirty(__instance); } listData.SelectedItem = itemInfo; }
private static void ListUpdate(CustomSelectListCtrl __instance) { if (!_listCache.TryGetValue(__instance, out var listData)) { return; } var scrollPosition = listData.ScrollPositionY; // How many items are not visible in current view var visibleItemCount = listData.ItemList.Count(x => !x.disvisible); var offscreenItemCount = Mathf.Max(0, visibleItemCount - listData.ItemCache.Count); // How many items are above current view rect and not visible var rowsAboveViewRect = Mathf.FloorToInt(Mathf.Clamp(scrollPosition / listData.ItemHeight, 0, offscreenItemCount)); var itemsAboveViewRect = rowsAboveViewRect * listData.ItemsInRow; if (listData.LastItemsAbove == itemsAboveViewRect && !listData.IsDirty) { return; } listData.LastItemsAbove = itemsAboveViewRect; listData.IsDirty = false; // Store selected item to preserve selection when moving the list with mouse var selectedItem = listData.ItemList.Find(x => x.sic != null && x.sic.gameObject == EventSystem.current.currentSelectedGameObject); listData.ItemList.ForEach(x => x.sic = null); // Apply visible list items to actual cache items var count = 0; foreach (var item in listData.ItemList.Where(x => !x.disvisible).Skip(itemsAboveViewRect)) { if (count >= listData.ItemCache.Count) { break; } var cachedEntry = listData.ItemCache[count]; count++; cachedEntry.info = item; item.sic = cachedEntry; cachedEntry.Disable(item.disable); cachedEntry.objNew.SetActiveIfDifferent(VirtualListData.IsItemNew(item)); var thumb = listData.GetThumbSprite(item); cachedEntry.img.sprite = thumb; if (ReferenceEquals(selectedItem, item)) { EventSystem.current.SetSelectedGameObject(cachedEntry.gameObject); } cachedEntry.gameObject.SetActiveIfDifferent(true); } // Disable unused cache items if (count < listData.ItemCache.Count) { foreach (var cacheEntry in listData.ItemCache.Skip(count)) { cacheEntry.gameObject.SetActiveIfDifferent(false); } } listData.UpdateSelection(); // Apply top and bottom offsets to create the illusion of having all of the list items var topOffset = Mathf.RoundToInt(rowsAboveViewRect * listData.ItemHeight); listData.LayoutGroup.padding.top = listData.InitialTopPadding + topOffset; var totalHeight = Mathf.CeilToInt((float)visibleItemCount / listData.ItemsInRow) * listData.ItemHeight; var cacheEntriesHeight = Mathf.CeilToInt((float)listData.ItemCache.Count / listData.ItemsInRow) * listData.ItemHeight; var trailingHeight = totalHeight - cacheEntriesHeight - topOffset; listData.LayoutGroup.padding.bottom = Mathf.FloorToInt(Mathf.Max(0, trailingHeight) + listData.InitialBotPadding); // Needed after changing padding since it doesn't make the object dirty LayoutRebuilder.MarkLayoutForRebuild(listData.Content); }