From d1011573454f76a44e6b14dc6c809936bb5a4eb8 Mon Sep 17 00:00:00 2001 From: Tyfon <29051038+tyfon7@users.noreply.github.com> Date: Thu, 23 May 2024 16:19:26 -0700 Subject: [PATCH] Flea market filter back button --- Patches/FixTogglesPatches.cs | 20 +-- Patches/FleaPrevSearchPatches.cs | 242 +++++++++++++++++++++++++++++++ Plugin.cs | 2 + R.cs | 36 ++++- Settings.cs | 16 +- 5 files changed, 295 insertions(+), 21 deletions(-) create mode 100644 Patches/FleaPrevSearchPatches.cs diff --git a/Patches/FixTogglesPatches.cs b/Patches/FixTogglesPatches.cs index c82130b..330c41e 100644 --- a/Patches/FixTogglesPatches.cs +++ b/Patches/FixTogglesPatches.cs @@ -15,7 +15,6 @@ namespace UIFixes { new DoNotToggleOnMouseOverPatch().Enable(); new ToggleOnOpenPatch().Enable(); - new ToggleOnClosePatch().Enable(); } public class DoNotToggleOnMouseOverPatch : ModulePatch @@ -26,7 +25,7 @@ namespace UIFixes } [PatchPostfix] - public static void Postfix(PointerEventData eventData, Image ____toggleImage, Sprite ____closeSprite, bool ___bool_3) + public static void Postfix(Image ____toggleImage, Sprite ____closeSprite, bool ___bool_3) { if (!___bool_3) { @@ -51,22 +50,5 @@ namespace UIFixes } } } - - public class ToggleOnClosePatch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return AccessTools.Method(typeof(CategoryView), nameof(CategoryView.OpenCategory)); - } - - [PatchPostfix] - public static void Postfix(Image ____toggleImage, Sprite ____closeSprite, bool ___bool_3) - { - if (!___bool_3) - { - ____toggleImage.sprite = ____closeSprite; - } - } - } } } diff --git a/Patches/FleaPrevSearchPatches.cs b/Patches/FleaPrevSearchPatches.cs new file mode 100644 index 0000000..90505bf --- /dev/null +++ b/Patches/FleaPrevSearchPatches.cs @@ -0,0 +1,242 @@ +using Aki.Reflection.Patching; +using EFT.UI; +using EFT.UI.Ragfair; +using EFT.UI.Utilities.LightScroller; +using HarmonyLib; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; + +namespace UIFixes +{ + public static class FleaPrevSearchPatches + { + private class HistoryEntry + { + public FilterRule filterRule; + public float scrollPosition = 0f; + } + + private static readonly Stack History = new(); + + private static bool FirstFilter = true; + private static bool GoingBack = false; + + private static DefaultUIButton PreviousButton; + + public static void Enable() + { + new RagfairScreenShowPatch().Enable(); + new OfferViewDoneLoadingPatch().Enable(); + new OfferViewChangedPatch().Enable(); + } + + public class RagfairScreenShowPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(RagfairScreen), nameof(RagfairScreen.Show)); + } + + [PatchPrefix] + public static void Prefix(RagfairScreen __instance, ISession session, DefaultUIButton ____addOfferButton) + { + // Create previous button + if (Settings.EnableFleaHistory.Value && PreviousButton == null) + { + var addOfferLayout = ____addOfferButton.GetComponent(); + PreviousButton = UnityEngine.Object.Instantiate(____addOfferButton, ____addOfferButton.transform.parent, false); + PreviousButton.transform.SetAsFirstSibling(); + PreviousButton.SetRawText("< BACK", 20); + + session.RagFair.OnFilterRuleChanged += (source, clear, updateCategories) => OnFilterRuleChanged(__instance, session); + + PreviousButton.OnClick.AddListener(() => + { + History.Pop(); // remove current + if (History.Count < 2) + { + PreviousButton.Interactable = false; + } + + GoingBack = true; + ApplyFullFilter(session.RagFair, History.Peek().filterRule); + GoingBack = false; + }); + } + } + + [PatchPostfix] + public static void Postfix(RagfairScreen __instance, ISession session, DefaultUIButton ____addOfferButton) + { + // Delete the upper right display options, since they aren't even implemented + var tabs = __instance.GetComponentsInChildren().FirstOrDefault(c => c.name == "Tabs"); + tabs?.gameObject.SetActive(false); + + if (!Settings.EnableFleaHistory.Value) + { + return; + } + + // Resize the Add Offer button to use less extra space + var addOfferLayout = ____addOfferButton.GetComponent(); + addOfferLayout.minWidth = -1; + addOfferLayout.preferredWidth = -1; + + // Recenter the add offer text + var addOfferLabel = ____addOfferButton.GetComponentsInChildren().First(c => c.name == "SizeLabel"); + addOfferLabel.localPosition = new Vector3(0f, 0f, 0f); + + // For some reason the widths revert + var prevButtonLayout = PreviousButton.GetComponent(); + prevButtonLayout.minWidth = -1; + prevButtonLayout.preferredWidth = -1; + + if (History.Count < 2) + { + PreviousButton.Interactable = false; + } + + PreviousButton.gameObject.SetActive(session.RagFair.FilterRule.ViewListType == EViewListType.AllOffers); + } + + private static void OnFilterRuleChanged(RagfairScreen ragScreen, ISession session) + { + if (GoingBack || + FirstFilter || + session.RagFair.FilterRule.ViewListType != EViewListType.AllOffers || + History.Any() && History.Peek().filterRule.Matches(session.RagFair.FilterRule)) + { + FirstFilter = false; + return; + } + + // Save the current scroll position before pushing the new entry + if (History.Any()) + { + LightScroller scroller = ragScreen.R().OfferViewList.R().Scroller; + History.Peek().scrollPosition = scroller.NormalizedScrollPosition; + } + + History.Push(new HistoryEntry() { filterRule = session.RagFair.FilterRule }); + + if (History.Count >= 2) + { + PreviousButton.Interactable = true; + } + + // Basic sanity to keep this from growing out of control + if (History.Count > 50) + { + var tempStack = new Stack(); + for (int i = History.Count / 2; i >= 0; i--) + { + tempStack.Push(History.Pop()); + } + + History.Clear(); + + while (tempStack.Any()) + { + History.Push(tempStack.Pop()); + } + } + } + + + // Using GClass because it's easier + private static void ApplyFullFilter(RagFairClass ragFair, FilterRule filterRule) + { + // copied from RagFairClass.AddSearchesInRule, but actually all of the properties + var searches = new List + { + new(EFilterType.Currency, filterRule.CurrencyType, filterRule.CurrencyType != 0), + new(EFilterType.PriceFrom, filterRule.PriceFrom, filterRule.PriceFrom != 0), + new(EFilterType.PriceTo, filterRule.PriceTo, filterRule.PriceTo != 0), + new(EFilterType.QuantityFrom, filterRule.QuantityFrom, filterRule.QuantityFrom != 0), + new(EFilterType.QuantityTo, filterRule.QuantityTo, filterRule.QuantityTo != 0), + new(EFilterType.ConditionFrom, filterRule.ConditionFrom, filterRule.ConditionFrom != 0), + new(EFilterType.ConditionTo, filterRule.ConditionTo, filterRule.ConditionTo != 100), + new(EFilterType.OneHourExpiration, filterRule.OneHourExpiration ? 1 : 0, filterRule.OneHourExpiration), + new(EFilterType.RemoveBartering, filterRule.RemoveBartering ? 1 : 0, filterRule.RemoveBartering), + new(EFilterType.OfferOwnerType, filterRule.OfferOwnerType, filterRule.OfferOwnerType != 0), + new(EFilterType.OnlyFunctional, filterRule.OnlyFunctional ? 1 : 0, filterRule.OnlyFunctional), + + // The following are all mutually exclusive + new(EFilterType.FilterSearch, filterRule.FilterSearchId, !filterRule.FilterSearchId.IsNullOrEmpty()), + new(EFilterType.NeededSearch, filterRule.NeededSearchId, !filterRule.NeededSearchId.IsNullOrEmpty()), + new(EFilterType.LinkedSearch, filterRule.LinkedSearchId, !filterRule.LinkedSearchId.IsNullOrEmpty()) + }; + + ragFair.method_24(filterRule.ViewListType, [.. searches], false, out FilterRule newRule); + + // Other properties + newRule.Page = filterRule.Page; + newRule.SortType = filterRule.SortType; + newRule.SortDirection = filterRule.SortDirection; + newRule.HandbookId = filterRule.HandbookId; + + ragFair.SetFilterRule(newRule, true, true); + } + } + + public class OfferViewChangedPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(RagfairScreen), nameof(RagfairScreen.method_7)); + } + + [PatchPostfix] + public static void Postfix(EViewListType type) + { + PreviousButton?.gameObject.SetActive(type == EViewListType.AllOffers); + } + } + + public class OfferViewDoneLoadingPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(OfferViewList), nameof(OfferViewList.method_12)); + } + + [PatchPostfix] + public static async void Postfix(Task __result, LightScroller ____scroller) + { + await __result; + + if (History.Any()) + { + ____scroller.SetScrollPosition(History.Peek().scrollPosition); + } + } + } + + public static bool Matches(this FilterRule one, FilterRule two) + { + return one.ViewListType == two.ViewListType && + one.Page == two.Page && + one.CurrencyType == two.CurrencyType && + one.SortType == two.SortType && + one.SortDirection == two.SortDirection && + one.PriceFrom == two.PriceFrom && + one.PriceTo == two.PriceTo && + one.QuantityFrom == two.QuantityFrom && + one.QuantityTo == two.QuantityTo && + one.ConditionFrom == two.ConditionFrom && + one.ConditionTo == two.ConditionTo && + one.OneHourExpiration == two.OneHourExpiration && + one.RemoveBartering == two.RemoveBartering && + one.OfferOwnerType == two.OfferOwnerType && + one.OnlyFunctional == two.OnlyFunctional && + one.HandbookId == two.HandbookId && + one.FilterSearchId == two.FilterSearchId && + one.LinkedSearchId == two.LinkedSearchId && + one.NeededSearchId == two.NeededSearchId; + } + } +} diff --git a/Plugin.cs b/Plugin.cs index dc2de30..6653a64 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -28,6 +28,8 @@ namespace UIFixes WeaponPresetConfirmPatches.Enable(); WeaponZoomPatches.Enable(); new MoveTaskbarPatch().Enable(); + FixTogglesPatches.Enable(); + FleaPrevSearchPatches.Enable(); } } } diff --git a/R.cs b/R.cs index 84b2ffb..0b4adf2 100644 --- a/R.cs +++ b/R.cs @@ -5,6 +5,8 @@ using EFT.InputSystem; using EFT.InventoryLogic; using EFT.UI; using EFT.UI.DragAndDrop; +using EFT.UI.Ragfair; +using EFT.UI.Utilities.LightScroller; using HarmonyLib; using System; using System.Collections.Generic; @@ -36,6 +38,8 @@ namespace UIFixes SwapOperation.InitTypes(); InteractionButtonsContainer.InitTypes(); ContextMenuButton.InitTypes(); + RagfairScreen.InitTypes(); + OfferViewList.InitTypes(); } public abstract class Wrapper(object value) @@ -298,7 +302,7 @@ namespace UIFixes public class ContextMenuButton(object value) : Wrapper(value) { - public static Type Type { get; private set;} + public static Type Type { get; private set; } private static FieldInfo TextField; public static void InitTypes() @@ -309,6 +313,34 @@ namespace UIFixes public TextMeshProUGUI Text { get { return (TextMeshProUGUI)TextField.GetValue(Value); } } } + + public class RagfairScreen(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo OfferViewListField; + + public static void InitTypes() + { + Type = typeof(EFT.UI.Ragfair.RagfairScreen); + OfferViewListField = AccessTools.Field(Type, "offerViewList_0"); + } + + public EFT.UI.Ragfair.OfferViewList OfferViewList { get { return (EFT.UI.Ragfair.OfferViewList)OfferViewListField.GetValue(Value); } } + } + + public class OfferViewList(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo ScrollerField; + + public static void InitTypes() + { + Type = typeof(EFT.UI.Ragfair.OfferViewList); + ScrollerField = AccessTools.Field(Type, "_scroller"); + } + + public LightScroller Scroller { get { return (LightScroller)ScrollerField.GetValue(Value); } } + } } public static class RExtentensions @@ -320,5 +352,7 @@ namespace UIFixes public static R.GridView R(this GridView value) => new(value); public static R.InteractionButtonsContainer R(this InteractionButtonsContainer value) => new(value); public static R.ContextMenuButton R(this ContextMenuButton value) => new(value); + public static R.RagfairScreen R(this RagfairScreen value) => new(value); + public static R.OfferViewList R(this OfferViewList value) => new(value); } } diff --git a/Settings.cs b/Settings.cs index bdf2f4d..bcdb88e 100644 --- a/Settings.cs +++ b/Settings.cs @@ -26,7 +26,8 @@ namespace UIFixes private const string InventorySection = "3. Inventory"; private const string InspectSection = "4. Inspect Windows"; private const string InRaidSection = "5. In Raid"; - private const string AdvancedSection = "6. Advanced"; + private const string FleaMarketSection = "6. Flea Market"; + private const string AdvancedSection = "7. Advanced"; // General public static ConfigEntry ShowPresetConfirmations { get; set; } @@ -54,6 +55,9 @@ namespace UIFixes // In Raid public static ConfigEntry RemoveDisabledActions { get; set; } + // Flea Market + public static ConfigEntry EnableFleaHistory { get; set; } + // Advanced public static ConfigEntry StyleItemPanel { get; set; } @@ -210,6 +214,16 @@ namespace UIFixes null, new ConfigurationManagerAttributes { }))); + // Flea Market + configEntries.Add(EnableFleaHistory = config.Bind( + FleaMarketSection, + "Show Filter Back Button", + true, + new ConfigDescription( + "Keep a history of flea market searches and filters, and show a back button to navigate it", + null, + new ConfigurationManagerAttributes { }))); + // Advanced configEntries.Add(StyleItemPanel = config.Bind( AdvancedSection,