/// <summary>
        /// Required to draw correct object sprites and strings in crafting menu.
        /// Event handlers on StardewModdingAPI.Events.Display.MenuChanged were inconsistent.
        /// </summary>
        public static void CraftingPage_LayoutRecipes_Postfix(
            CraftingPage __instance)
        {
            int unlockedCount = Game1.player.craftingRecipes.Keys.Count(c => c.StartsWith(OutdoorPot.GenericName));

            int[] matchesPerDict = new int[__instance.pagesOfCraftingRecipes.Count];
            int   i = 0;

            foreach (Dictionary <ClickableTextureComponent, CraftingRecipe> dict in __instance.pagesOfCraftingRecipes)
            {
                List <KeyValuePair <ClickableTextureComponent, CraftingRecipe> > matches = dict
                                                                                           .Where(pair => pair.Value.name.StartsWith(OutdoorPot.GenericName))
                                                                                           .ToList();
                matches.ForEach(pair =>
                {
                    string variantKey = OutdoorPot.GetVariantKeyFromName(name: pair.Value.name);

                    // Sprite
                    pair.Key.texture    = ModEntry.Sprites[ModEntry.ItemDefinitions[variantKey].SpriteKey];
                    pair.Key.sourceRect = OutdoorPot.GetSpriteSourceRectangle(spriteIndex: ModEntry.ItemDefinitions[variantKey].SpriteIndex);

                    // Strings
                    pair.Value.DisplayName = OutdoorPot.GetDisplayNameFromName(pair.Value.name);
                    pair.Value.description = OutdoorPot.GetRawDescription();
                });
                matchesPerDict[i++] = matches.Count();
            }
            Log.T($"Found {string.Join("/", matchesPerDict)} garden beds in crafting menu pages ({unlockedCount} unlocked).");
        }
        public override void draw(SpriteBatch b)
        {
            if (this._timerBeforeStart > 0)
            {
                return;
            }

            // Blackout
            b.Draw(
                texture: Game1.fadeToBlackRect,
                destinationRectangle: new Rectangle(0, 0, Game1.uiViewport.Width, Game1.uiViewport.Height),
                color: Color.Black * 0.5f);

            if (!this._informationUp && this._isActive && this.StarIcon != null)
            {
                this.StarIcon.draw(b);
            }
            else
            {
                if (!this._informationUp)
                {
                    return;
                }

                // Draw popup header
                const int   wh        = 16;
                Vector2     padding   = new Vector2(22, -8) * Game1.pixelZoom;
                const float iconScale = 3f;
                Vector2     iconSize  = new Vector2(26, 20);
                Vector2     textSize  = Game1.dialogueFont.MeasureString(this._titleString);
                textSize = new Vector2(
                    textSize.X + padding.X + (iconSize.X * iconScale),
                    Math.Max(textSize.Y, iconSize.Y * iconScale) + padding.Y);
                Vector2 positionPadded = new Vector2(
                    this.xPositionOnScreen + ((this.width - textSize.X - (wh * Game1.pixelZoom)) / 2),
                    this.yPositionOnScreen - textSize.Y - (wh * Game1.pixelZoom / 2) - (2 * Game1.pixelZoom));
                Point sourceOrigin = new Point(260, 310);
                // background
                b.Draw(
                    texture: Game1.mouseCursors,
                    destinationRectangle: new Rectangle(
                        (int)(positionPadded.X + (wh * Game1.pixelZoom / 2)),
                        (int)(positionPadded.Y + (wh * Game1.pixelZoom / 2)),
                        (int)(textSize.X),
                        (int)(textSize.Y + (wh * Game1.pixelZoom))),
                    sourceRectangle: new Rectangle(360, 437, 1, 8),
                    color: Color.White,
                    rotation: 0f,
                    origin: Vector2.Zero,
                    effects: SpriteEffects.None,
                    layerDepth: 1f);

                // icon
                b.Draw(
                    texture: Game1.mouseCursors,
                    position: positionPadded + new Vector2(wh * Game1.pixelZoom) + new Vector2(0, -8f * iconScale),
                    sourceRectangle: new Rectangle(420, 488, (int)iconSize.X, (int)iconSize.Y),
                    color: Color.White,
                    rotation: 0f,
                    origin: Vector2.Zero,
                    scale: iconScale,
                    effects: SpriteEffects.None,
                    layerDepth: 1f);

                // top-left
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)positionPadded.X, (int)positionPadded.Y, wh * Game1.pixelZoom, wh * Game1.pixelZoom),
                       sourceRectangle: new Rectangle(sourceOrigin.X, sourceOrigin.Y, wh, wh),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // top-right
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)positionPadded.X + (int)textSize.X, (int)positionPadded.Y, wh * Game1.pixelZoom, wh * Game1.pixelZoom),
                       sourceRectangle: new Rectangle(sourceOrigin.X + 28, sourceOrigin.Y, wh, wh),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // bottom-left
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)positionPadded.X, (int)positionPadded.Y + (int)textSize.Y + (wh * Game1.pixelZoom), wh * Game1.pixelZoom, wh * Game1.pixelZoom),
                       sourceRectangle: new Rectangle(sourceOrigin.X, sourceOrigin.Y + 16, wh, wh),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // bottom-right
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)positionPadded.X + (int)textSize.X, (int)positionPadded.Y + (int)textSize.Y + (wh * Game1.pixelZoom), wh * Game1.pixelZoom, wh * Game1.pixelZoom),
                       sourceRectangle: new Rectangle(sourceOrigin.X + 28, sourceOrigin.Y + 16, wh, wh),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // top
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)(positionPadded.X + (wh * Game1.pixelZoom)), (int)positionPadded.Y, (int)textSize.X - (wh * Game1.pixelZoom), wh * Game1.pixelZoom),
                       sourceRectangle: new Rectangle(sourceOrigin.X + wh, sourceOrigin.Y, 1, wh),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // bottom
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)(positionPadded.X + (wh * Game1.pixelZoom)), (int)(positionPadded.Y + textSize.Y + (wh * Game1.pixelZoom)), (int)textSize.X - (wh * Game1.pixelZoom), wh * Game1.pixelZoom),
                       sourceRectangle: new Rectangle(sourceOrigin.X + wh, sourceOrigin.Y + 16, 1, wh),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // left
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)positionPadded.X, (int)positionPadded.Y + (wh * Game1.pixelZoom), wh * Game1.pixelZoom, (int)textSize.Y),
                       sourceRectangle: new Rectangle(sourceOrigin.X, sourceOrigin.Y + wh, wh, 1),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // right
                b.Draw(texture: Game1.mouseCursors,
                       destinationRectangle: new Rectangle((int)positionPadded.X + (int)textSize.X, (int)positionPadded.Y + (wh * Game1.pixelZoom), wh * Game1.pixelZoom, (int)textSize.Y),
                       sourceRectangle: new Rectangle(sourceOrigin.X + 28, sourceOrigin.Y + wh, wh, 1),
                       color: Color.White,
                       rotation: 0f,
                       origin: Vector2.Zero,
                       effects: SpriteEffects.None,
                       layerDepth: 1f);
                // title text
                b.DrawString(
                    spriteFont: Game1.dialogueFont,
                    text: this._titleString,
                    position: positionPadded + new Vector2(iconSize.X * iconScale, 0) + new Vector2(wh * Game1.pixelZoom) + new Vector2(padding.X / 4, -6f * iconScale),
                    color: Game1.textColor);


                // Draw actual popup
                Game1.drawDialogueBox(
                    x: this.xPositionOnScreen,
                    y: this.yPositionOnScreen,
                    width: this.width,
                    height: this.height,
                    speaker: false,
                    drawOnlyBox: true);

                const int paddingY = 3;
                int       x        = this.xPositionOnScreen + (this.width / 2);
                int       y        = this.yPositionOnScreen;
                int       yOffset  = IClickableMenu.spaceToClearTopBorder;

                foreach (string variantKey in this.VariantKeys)
                {
                    yOffset += (int)Game1.dialogueFont.MeasureString(_itemStrings[variantKey]).Y + (paddingY * Game1.pixelZoom)
                               + ((Game1.smallestTileSize + paddingY) * 2);
                    int xOffset = -(int)((Game1.smallFont.MeasureString(_itemStrings[variantKey]).X / 2) - (Game1.smallestTileSize * Game1.pixelZoom));
                    b.DrawString(
                        spriteFont: Game1.smallFont,
                        text: _itemStrings[variantKey],
                        position: new Vector2(
                            x + xOffset,
                            y + yOffset),
                        color: Game1.textColor);

                    yOffset -= (Game1.smallestTileSize * Game1.pixelZoom);
                    b.Draw(
                        texture: ModEntry.Sprites[this._itemSprites[variantKey].Key],
                        sourceRectangle: OutdoorPot.GetSpriteSourceRectangle(spriteIndex: this._itemSprites[variantKey].Value),
                        position: new Vector2(
                            x + xOffset - (Game1.smallestTileSize * 1.5f * Game1.pixelZoom),
                            y + yOffset),
                        color: Color.White,
                        rotation: 0f,
                        origin: Vector2.Zero,
                        scale: Game1.pixelZoom,
                        effects: SpriteEffects.None,
                        layerDepth: 1f);
                    yOffset += (Game1.smallestTileSize * Game1.pixelZoom);
                }
                this.OkButton.draw(b);

                if (!Game1.options.SnappyMenus)
                {
                    Game1.mouseCursorTransparency = 1f;
                    this.drawMouse(b);
                }
            }
        }