/// <summary>The method invoked when the interface has finished rendering.</summary>
        private void ReceiveHudRendered()
        {
            // render update warning
            if (this.Config.CheckForUpdates && !this.HasSeenUpdateWarning && this.NewRelease != null)
            {
                try
                {
                    this.HasSeenUpdateWarning = true;
                    CommonHelper.ShowInfoMessage($"You can update Chests Anywhere from {this.CurrentVersion} to {this.NewRelease}.");
                }
                catch (Exception ex)
                {
                    this.HandleError(ex, "showing the new version available");
                }
            }

            // show chest label
            if (this.Config.ShowHoverTooltips)
            {
                ManagedChest cursorChest = ChestFactory.GetChestFromTile(Game1.currentCursorTile);
                if (cursorChest != null)
                {
                    Vector2 tooltipPosition = new Vector2(Game1.getMouseX(), Game1.getMouseY()) + new Vector2(Game1.tileSize / 2f);
                    CommonHelper.DrawHoverBox(Game1.spriteBatch, cursorChest.Name, tooltipPosition, Game1.viewport.Width - tooltipPosition.X - Game1.tileSize / 2f);
                }
            }
        }
Ejemplo n.º 2
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="menu">The underlying chest menu.</param>
 /// <param name="chest">The selected chest.</param>
 /// <param name="chests">The available chests.</param>
 /// <param name="config">The mod configuration.</param>
 /// <param name="keys">The configured key bindings.</param>
 /// <param name="events">The SMAPI events available for mods.</param>
 /// <param name="input">An API for checking and changing input state.</param>
 /// <param name="reflection">Simplifies access to private code.</param>
 /// <param name="showAutomateOptions">Whether to show Automate options.</param>
 public ShopMenuOverlay(ShopMenu menu, ManagedChest chest, ManagedChest[] chests, ModConfig config, ModConfigKeys keys, IModEvents events, IInputHelper input, IReflectionHelper reflection, bool showAutomateOptions)
     : base(menu, chest, chests, config, keys, events, input, reflection, showAutomateOptions, keepAlive: () => Game1.activeClickableMenu is ShopMenu, topOffset: Game1.pixelZoom * 6)
 {
     this.Menu = menu;
     this.DefaultPurchaseFilter       = menu.canPurchaseCheck;
     this.DefaultInventoryHighlighter = menu.inventory.highlightMethod;
 }
Ejemplo n.º 3
0
        /// <summary>Open the menu UI.</summary>
        private void OpenMenu()
        {
            if (this.Config.Range == ChestRange.None)
            {
                return;
            }

            // handle disabled location
            if (this.IsDisabledLocation(Game1.currentLocation))
            {
                CommonHelper.ShowInfoMessage("Remote chest access is disabled here. :)", duration: 1000);
                return;
            }


            // get chests
            RangeHandler range = this.GetCurrentRange();

            ManagedChest[] chests        = this.ChestFactory.GetChests(range, excludeHidden: true).ToArray();
            ManagedChest   selectedChest = chests.FirstOrDefault(p => p.Container.IsSameAs(this.SelectedInventory)) ?? chests.FirstOrDefault();

            // render menu
            if (selectedChest != null)
            {
                Game1.activeClickableMenu = selectedChest.OpenMenu();
            }
            else
            {
                CommonHelper.ShowInfoMessage(
                    "You don't have any chests " + (this.Config.Range == ChestRange.Unlimited ? "yet" : "in range") + ". :)",
                    duration: 1000
                    );
            }
        }
Ejemplo n.º 4
0
        /// <summary>Open the menu UI.</summary>
        private void OpenMenu()
        {
            if (this.Config.Range == ChestRange.None)
            {
                return;
            }

            // handle disabled location
            if (this.IsDisabledLocation(Game1.currentLocation))
            {
                CommonHelper.ShowInfoMessage(I18n.Errors_DisabledFromHere(), duration: 1000);
                return;
            }

            // get chests
            RangeHandler range = this.GetCurrentRange();

            ManagedChest[] chests        = this.ChestFactory.GetChests(range, excludeHidden: true).ToArray();
            ManagedChest   selectedChest =
                ChestFactory.GetBestMatch(chests, this.LastChest.Value)
                ?? chests.FirstOrDefault(p => object.ReferenceEquals(p.Location, Game1.currentLocation))
                ?? chests.FirstOrDefault();

            // show error
            if (selectedChest == null)
            {
                CommonHelper.ShowInfoMessage(this.GetNoChestsFoundError(), duration: 1000);
                return;
            }

            // render menu
            Game1.activeClickableMenu = selectedChest.OpenMenu();
        }
        /// <summary>The method invoked when the active menu changes.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void MenuEvents_MenuChanged(object sender, EventArgsClickableMenuChanged e)
        {
            // remove overlay
            if (e.PriorMenu is ItemGrabMenu)
            {
                this.ManageChestOverlay?.Dispose();
                this.ManageChestOverlay = null;
            }

            // add overlay
            if (e.NewMenu is ItemGrabMenu chestMenu)
            {
                // get open chest
                ManagedChest chest = this.ChestFactory.GetChestFromMenu(chestMenu);
                if (chest == null)
                {
                    return;
                }

                // reopen shipping box in standard chest UI
                if (chest.Container is ShippingBinContainer && !chestMenu.showReceivingMenu)
                {
                    Game1.activeClickableMenu = chest.OpenMenu();
                }

                // add overlay
                ManagedChest[] chests = this.ChestFactory.GetChestsForDisplay(selected: chest.Container).ToArray();
                this.ManageChestOverlay = new ManageChestOverlay(chestMenu, chest, chests, this.Config, this.Helper.Translation);
                this.ManageChestOverlay.OnChestSelected += selected =>
                {
                    this.SelectedInventory    = selected.Container.Inventory;
                    Game1.activeClickableMenu = selected.OpenMenu();
                };
            }
        }
Ejemplo n.º 6
0
        /// <summary>Open the menu UI.</summary>
        private void OpenMenu()
        {
            if (this.Config.Range == ChestRange.None)
            {
                return;
            }

            // handle disabled location
            if (this.IsDisabledLocation(Game1.currentLocation))
            {
                CommonHelper.ShowInfoMessage(this.Helper.Translation.Get("errors.disabled-from-here"), duration: 1000);
                return;
            }

            // get chests
            RangeHandler range = this.GetCurrentRange();

            ManagedChest[] chests        = this.ChestFactory.GetChests(range, excludeHidden: true).ToArray();
            ManagedChest   selectedChest = chests.FirstOrDefault(p => p.Container.IsSameAs(this.SelectedInventory)) ?? chests.FirstOrDefault();

            // show error
            if (selectedChest == null)
            {
                string translationKey = this.GetNoChestsFoundErrorKey();
                CommonHelper.ShowInfoMessage(this.Helper.Translation.Get(translationKey), duration: 1000);
                return;
            }

            // render menu
            Game1.activeClickableMenu = selectedChest.OpenMenu();
        }
Ejemplo n.º 7
0
        /// <summary>Change the chest UI overlay if needed to match the current menu.</summary>
        /// <remarks>Since the menu gets reopened whenever the chest inventory changes, this method needs to be called before/after tick to avoid a visible UI flicker.</remarks>
        private void ChangeOverlayIfNeeded()
        {
            IClickableMenu menu = Game1.activeClickableMenu;

            // already matches menu
            if (this.CurrentOverlay?.ForMenuInstance == menu)
            {
                return;
            }

            // remove old overlay
            if (this.CurrentOverlay != null)
            {
                this.CurrentOverlay?.Dispose();
                this.CurrentOverlay = null;
            }

            // get open chest
            ManagedChest chest = this.ChestFactory.GetChestFromMenu(menu);

            if (chest == null)
            {
                return;
            }

            // reopen shipping box in standard chest UI if needed
            // This is called in two cases:
            // - When the player opens the shipping bin directly, it opens the shipping bin view instead of the full chest view.
            // - When the player changes the items in the chest view, it reopens itself but loses the constructor args (e.g. highlight function).
            if (this.Config.EnableShippingBin && chest.Container is ShippingBinContainer)
            {
                if (menu is ItemGrabMenu chestMenu && (!chestMenu.showReceivingMenu || !(chestMenu.inventory.highlightMethod?.Target is ShippingBinContainer)))
                {
                    menu = (ItemGrabMenu)chest.OpenMenu();
                    Game1.activeClickableMenu = menu;
                }
            }

            // add overlay
            RangeHandler range = this.GetCurrentRange();

            ManagedChest[] chests = this.ChestFactory.GetChests(range, excludeHidden: true, alwaysIncludeContainer: chest.Container).ToArray();
            bool           isAutomateInstalled = this.Helper.ModRegistry.IsLoaded("Pathoschild.Automate");

            switch (menu)
            {
            case ItemGrabMenu chestMenu:
                this.CurrentOverlay = new ChestOverlay(chestMenu, chest, chests, this.Config, this.Keys, this.Helper.Events, this.Helper.Input, this.Helper.Translation, showAutomateOptions: isAutomateInstalled && chest.CanConfigureAutomate);
                break;

            case ShopMenu shopMenu:
                this.CurrentOverlay = new ShopMenuOverlay(shopMenu, chest, chests, this.Config, this.Keys, this.Helper.Events, this.Helper.Input, this.Helper.Translation, showAutomateOptions: isAutomateInstalled && chest.CanConfigureAutomate);
                break;
            }
            this.CurrentOverlay.OnChestSelected += selected =>
            {
                this.SelectedInventory    = selected.Container.Inventory;
                Game1.activeClickableMenu = selected.OpenMenu();
            };
        }
Ejemplo n.º 8
0
        /// <summary>The method invoked when the active menu changes.</summary>
        /// <param name="previousMenu">The previous menu (if any)</param>
        /// <param name="newMenu">The new menu (if any).</param>
        private void ReceiveMenuChanged(IClickableMenu previousMenu, IClickableMenu newMenu)
        {
            // remove overlay
            if (previousMenu is ItemGrabMenu)
            {
                this.ManageChestOverlay?.Dispose();
                this.ManageChestOverlay = null;
            }

            // add overlay
            if (newMenu is ItemGrabMenu chestMenu)
            {
                // get open chest
                ManagedChest chest = this.ChestFactory.GetChestFromMenu(chestMenu);
                if (chest == null)
                {
                    return;
                }

                // add overlay
                ManagedChest[] chests = this.ChestFactory.GetChestsForDisplay(selectedChest: chest.Chest).ToArray();
                this.ManageChestOverlay = new ManageChestOverlay(chestMenu, chest, chests, this.Config, this.Helper.Translation);
                this.ManageChestOverlay.OnChestSelected += selected =>
                {
                    this.SelectedChest        = selected.Chest;
                    Game1.activeClickableMenu = selected.OpenMenu();
                };
            }
        }
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="menu">The underlying chest menu.</param>
        /// <param name="chest">The selected chest.</param>
        /// <param name="chests">The available chests.</param>
        /// <param name="config">The mod configuration.</param>
        public ManageChestOverlay(ItemGrabMenu menu, ManagedChest chest, ManagedChest[] chests, ModConfig config)
            : base(keepAlive: () => Game1.activeClickableMenu is ItemGrabMenu)
        {
            // menu
            this.Menu = menu;
#if SDV_1_2
            this.MenuInventoryMenu = ((ItemGrabMenu)Game1.activeClickableMenu).ItemsToGrabMenu;
#else
            this.MenuInventoryMenu = (InventoryMenu)typeof(ItemGrabMenu).GetField("ItemsToGrabMenu", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(menu);
            if (this.MenuInventoryMenu == null)
            {
                throw new InvalidOperationException("The menu doesn't seem to have a player inventory.");
            }
#endif
            this.DefaultChestHighlighter     = menu.inventory.highlightMethod;
            this.DefaultInventoryHighlighter = this.MenuInventoryMenu.highlightMethod;

            // chests & config
            this.Chest  = chest;
            this.Chests = chests;
            this.Groups = chests.Select(p => p.GetGroup()).Distinct().OrderBy(p => p).ToArray();
            this.Config = config;

            // components
            this.ReinitialiseComponents();
        }
Ejemplo n.º 10
0
        /// <summary>Open the menu UI.</summary>
        private void OpenMenu()
        {
            if (this.Config.Range == ChestRange.None)
            {
                return;
            }

            // get chests
            RangeHandler rangeHandler = new RangeHandler(this.Data.WorldAreas, this.Config.Range, Game1.currentLocation);

            ManagedChest[] chests        = this.ChestFactory.GetChests(rangeHandler, excludeHidden: true).ToArray();
            ManagedChest   selectedChest = chests.FirstOrDefault(p => p.Container.Inventory == this.SelectedInventory) ?? chests.FirstOrDefault();

            // render menu
            if (selectedChest != null)
            {
                Game1.activeClickableMenu = selectedChest.OpenMenu();
            }
            else
            {
                CommonHelper.ShowInfoMessage(
                    "You don't have any chests " + (this.Config.Range == ChestRange.Unlimited ? "yet" : "in range") + ". :)",
                    duration: 1000
                    );
            }
        }
Ejemplo n.º 11
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="menu">The underlying chest menu.</param>
 /// <param name="chest">The selected chest.</param>
 /// <param name="chests">The available chests.</param>
 /// <param name="config">The mod configuration.</param>
 /// <param name="keys">The configured key bindings.</param>
 /// <param name="events">The SMAPI events available for mods.</param>
 /// <param name="input">An API for checking and changing input state.</param>
 /// <param name="translations">Provides translations stored in the mod's folder.</param>
 /// <param name="showAutomateOptions">Whether to show Automate options.</param>
 public ChestOverlay(ItemGrabMenu menu, ManagedChest chest, ManagedChest[] chests, ModConfig config, ModConfigKeys keys, IModEvents events, IInputHelper input, ITranslationHelper translations, bool showAutomateOptions)
     : base(menu, chest, chests, config, keys, events, input, translations, showAutomateOptions, keepAlive: () => Game1.activeClickableMenu is ItemGrabMenu, topOffset: -Game1.pixelZoom * 9)
 {
     this.Menu = menu;
     this.MenuInventoryMenu           = menu.ItemsToGrabMenu;
     this.DefaultChestHighlighter     = menu.inventory.highlightMethod;
     this.DefaultInventoryHighlighter = this.MenuInventoryMenu.highlightMethod;
 }
Ejemplo n.º 12
0
        /// <summary>Notify Automate that a chest's automation options updated.</summary>
        /// <param name="chest">The chest that was updated.</param>
        private void NotifyAutomateOfChestUpdate(ManagedChest chest)
        {
            long hostId  = Game1.MasterPlayer.UniqueMultiplayerID;
            var  message = new AutomateUpdateChestMessage {
                LocationName = chest.Location.Name, Tile = chest.Tile
            };

            this.Helper.Multiplayer.SendMessage(message, nameof(AutomateUpdateChestMessage), modIDs: new[] { "Pathoschild.Automate" }, playerIDs: new[] { hostId });
        }
Ejemplo n.º 13
0
 /// <summary>The method invoked when the interface has finished rendering.</summary>
 private void ReceiveHudRendered()
 {
     // show chest label
     if (this.Config.ShowHoverTooltips)
     {
         ManagedChest cursorChest = this.ChestFactory.GetChestFromTile(Game1.currentCursorTile);
         if (cursorChest != null)
         {
             Vector2 tooltipPosition = new Vector2(Game1.getMouseX(), Game1.getMouseY()) + new Vector2(Game1.tileSize / 2f);
             CommonHelper.DrawHoverBox(Game1.spriteBatch, cursorChest.Name, tooltipPosition, Game1.viewport.Width - tooltipPosition.X - Game1.tileSize / 2f);
         }
     }
 }
Ejemplo n.º 14
0
 /// <summary>The method invoked when the interface has finished rendering.</summary>
 /// <param name="sender">The event sender.</param>
 /// <param name="e">The event arguments.</param>
 private void GraphicsEvents_OnPostRenderHudEvent(object sender, EventArgs e)
 {
     // show chest label
     if (this.Config.ShowHoverTooltips)
     {
         ManagedChest cursorChest = this.ChestFactory.GetChestFromTile(Game1.currentCursorTile);
         if (cursorChest != null && !cursorChest.HasDefaultName())
         {
             Vector2 tooltipPosition = new Vector2(Game1.getMouseX(), Game1.getMouseY()) + new Vector2(Game1.tileSize / 2f);
             CommonHelper.DrawHoverBox(Game1.spriteBatch, cursorChest.DisplayName, tooltipPosition, Game1.viewport.Width - tooltipPosition.X - Game1.tileSize / 2f);
         }
     }
 }
Ejemplo n.º 15
0
        /// <summary>Get the player chest from the specified menu (if any).</summary>
        /// <param name="menu">The menu to check.</param>
        public static ManagedChest GetChestFromMenu(ItemGrabMenu menu)
        {
            // from menu target
            Chest        target = menu.behaviorOnItemGrab?.Target as Chest;
            ManagedChest chest  = target != null
                ? ChestFactory.GetChests().FirstOrDefault(p => p.Chest == target)
                : null;

            if (chest != null)
            {
                return(chest);
            }

            // fallback to open chest
            return(ChestFactory.GetChests().FirstOrDefault(p => p.Chest.currentLidFrame == 135));
        }
        /// <summary>Open the menu UI.</summary>
        private void OpenMenu()
        {
            // get chests
            ManagedChest[] chests        = this.ChestFactory.GetChestsForDisplay().ToArray();
            ManagedChest   selectedChest = chests.FirstOrDefault(p => p.Container.Inventory == this.SelectedInventory) ?? chests.FirstOrDefault();

            // render menu
            if (selectedChest != null)
            {
                Game1.activeClickableMenu = selectedChest.OpenMenu();
            }
            else
            {
                CommonHelper.ShowInfoMessage("You don't have any chests yet. :)", duration: 1000);
            }
        }
Ejemplo n.º 17
0
        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="menu">The underlying chest menu.</param>
        /// <param name="chest">The selected chest.</param>
        /// <param name="chests">The available chests.</param>
        /// <param name="config">The mod configuration.</param>
        public ManageChestOverlay(ItemGrabMenu menu, ManagedChest chest, ManagedChest[] chests, ModConfig config)
            : base(keepAlive: () => Game1.activeClickableMenu is ItemGrabMenu)
        {
            // menu
            this.Menu = menu;
            this.MenuInventoryMenu           = ((ItemGrabMenu)Game1.activeClickableMenu).ItemsToGrabMenu;
            this.DefaultChestHighlighter     = menu.inventory.highlightMethod;
            this.DefaultInventoryHighlighter = this.MenuInventoryMenu.highlightMethod;

            // chests & config
            this.Chest  = chest;
            this.Chests = chests;
            this.Groups = chests.Select(p => p.GetGroup()).Distinct().OrderBy(p => p).ToArray();
            this.Config = config;

            // components
            this.ReinitialiseComponents();
        }
Ejemplo n.º 18
0
        /// <summary>Get the player chest from the specified menu (if any).</summary>
        /// <param name="menu">The menu to check.</param>
        public ManagedChest GetChestFromMenu(ItemGrabMenu menu)
        {
            // get from opened inventory
            {
                object      target    = menu.behaviorOnItemGrab?.Target;
                List <Item> inventory = (target as Chest)?.items ?? (target as IContainer)?.Inventory;
                if (inventory != null)
                {
                    ManagedChest chest = this.GetChests().FirstOrDefault(p => p.Container.Inventory == inventory);
                    if (chest != null)
                    {
                        return(chest);
                    }
                }
            }

            // fallback to open chest
            return(this.GetChests().FirstOrDefault(p => p.Container.IsOpen()));
        }
Ejemplo n.º 19
0
        /// <summary>The method invoked when the active menu changes.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void MenuEvents_MenuChanged(object sender, EventArgsClickableMenuChanged e)
        {
            // remove overlay
            if (e.PriorMenu is ItemGrabMenu)
            {
                this.ManageChestOverlay?.Dispose();
                this.ManageChestOverlay = null;
            }

            // add overlay
            if (e.NewMenu is ItemGrabMenu chestMenu)
            {
                // get open chest
                ManagedChest chest = this.ChestFactory.GetChestFromMenu(chestMenu);
                if (chest == null)
                {
                    return;
                }

                // reopen shipping box in standard chest UI if needed
                // This is called in two cases:
                // - When the player opens the shipping bin directly, it opens the shipping bin view instead of the full chest view.
                // - When the player changes the items in the chest view, it reopens itself but loses the constructor args (e.g. highlight function).
                if (this.Config.EnableShippingBin && chest.Container is ShippingBinContainer && (!chestMenu.showReceivingMenu || !(chestMenu.inventory.highlightMethod?.Target is ShippingBinContainer)))
                {
                    chestMenu = chest.OpenMenu();
                    Game1.activeClickableMenu = chestMenu;
                }

                // add overlay
                RangeHandler   range  = this.GetCurrentRange();
                ManagedChest[] chests = this.ChestFactory.GetChests(range, excludeHidden: true, alwaysIncludeContainer: chest.Container).ToArray();
                bool           isAutomateInstalled = this.Helper.ModRegistry.IsLoaded("Pathoschild.Automate");
                this.ManageChestOverlay = new ManageChestOverlay(chestMenu, chest, chests, this.Config, this.Helper.Translation, showAutomateOptions: isAutomateInstalled);
                this.ManageChestOverlay.OnChestSelected += selected =>
                {
                    this.SelectedInventory    = selected.Container.Inventory;
                    Game1.activeClickableMenu = selected.OpenMenu();
                };
            }
        }
Ejemplo n.º 20
0
        /// <summary>Get all player chests.</summary>
        /// <param name="range">Determines whether given locations are in range of the player for remote chest access.</param>
        /// <param name="excludeHidden">Whether to exclude chests marked as hidden.</param>
        /// <param name="alwaysInclude">A chest to include even if it would normally be hidden.</param>
        public IEnumerable <ManagedChest> GetChests(RangeHandler range, bool excludeHidden = false, ManagedChest alwaysInclude = null)
        {
            IEnumerable <ManagedChest> Search()
            {
                // get location info
                var locations =
                    (
                        from GameLocation location in this.GetAccessibleLocations()
                        select new
                {
                    Location = location,
                    Category = this.GetCategory(location)
                }
                    )
                    .ToArray();
                IDictionary <string, int> defaultCategories = locations
                                                              .GroupBy(p => p.Category)
                                                              .Where(p => p.Count() > 1)
                                                              .ToDictionary(p => p.Key, p => 0);

                // find chests
                foreach (var entry in locations)
                {
                    IDictionary <string, int> nameCounts = new Dictionary <string, int>();

                    // get info
                    GameLocation location = entry.Location;
                    string       category = defaultCategories.ContainsKey(entry.Category)
                        ? I18n.DefaultCategory_Duplicate(locationName: entry.Category, number: ++defaultCategories[entry.Category])
                        : entry.Category;

                    // chests in location
                    foreach (KeyValuePair <Vector2, SObject> pair in location.Objects.Pairs)
                    {
                        Vector2 tile = pair.Key;
                        SObject obj  = pair.Value;

                        // chests
                        if (obj is Chest chest && chest.playerChest.Value)
                        {
                            yield return(new ManagedChest(
                                             container: new ChestContainer(chest, context: chest, showColorPicker: this.CanShowColorPicker(chest, location), this.Reflection),
                                             location: location,
                                             tile: tile,
                                             mapEntity: chest,
                                             defaultDisplayName: this.GetDisambiguatedDefaultName(chest.DisplayName, nameCounts),
                                             defaultCategory: category
                                             ));
                        }
        /// <summary>The method invoked when the player left-clicks.</summary>
        /// <param name="x">The X-position of the cursor.</param>
        /// <param name="y">The Y-position of the cursor.</param>
        /// <returns>Whether the event has been handled and shouldn't be propagated further.</returns>
        protected override bool ReceiveLeftClick(int x, int y)
        {
            switch (this.ActiveElement)
            {
            // edit form
            case Element.EditForm:
                // name field
                if (this.EditNameField.GetBounds().Contains(x, y))
                {
                    this.EditNameField.Select();
                }

                // category field
                else if (this.EditCategoryField.GetBounds().Contains(x, y))
                {
                    this.EditCategoryField.Select();
                }

                // order field
                else if (this.EditOrderField.GetBounds().Contains(x, y))
                {
                    this.EditOrderField.Select();
                }

                // 'hide chest' checkbox
                else if (this.EditHideChestField.GetBounds().Contains(x, y))
                {
                    this.EditHideChestField.Toggle();
                }

                // save button
                else if (this.EditSaveButton.containsPoint(x, y))
                {
                    this.SaveEdit();
                    this.ActiveElement = Element.Menu;
                }

                // exit button
                else if (this.EditExitButton.containsPoint(x, y))
                {
                    this.ActiveElement = Element.Menu;
                }

                return(true);    // handle all clicks while open

            // chest list
            case Element.ChestList:
                // close dropdown
                this.ActiveElement = Element.Menu;

                // select chest
                if (this.ChestSelector.containsPoint(x, y))
                {
                    ManagedChest chest = this.ChestSelector.Select(x, y);
                    if (chest != null)
                    {
                        this.SelectChest(chest);
                        this.ReinitialiseComponents();
                    }
                }
                return(true);    // handle all clicks while open

            // group list
            case Element.GroupList:
                // close dropdown
                this.ActiveElement = Element.Menu;

                // select group
                if (this.GroupSelector.containsPoint(x, y))
                {
                    string group = this.GroupSelector.Select(x, y);
                    if (group != null && group != this.SelectedGroup)
                    {
                        this.SelectChest(this.Chests.First(chest => chest.GetGroup() == group));
                        this.ReinitialiseComponents();
                    }
                }
                return(true);    // handle all clicks while open

            // buttons & dropdown
            default:
                if (this.EditButton.containsPoint(x, y))
                {
                    this.OpenEdit();
                }
                else if (this.SortInventoryButton.containsPoint(x, y))
                {
                    this.SortInventory();
                }
                else if (this.ChestTab.containsPoint(x, y))
                {
                    this.ActiveElement = Element.ChestList;
                }
                else if (this.GroupTab?.containsPoint(x, y) == true)
                {
                    this.ActiveElement = Element.GroupList;
                }
                else
                {
                    return(false);
                }
                return(true);
            }
        }
 /// <summary>Switch to the specified chest.</summary>
 /// <param name="chest">The chest to select.</param>
 public void SelectChest(ManagedChest chest)
 {
     this.OnChestSelected?.Invoke(chest);
 }