public void Draw() { var darkTitleRect = new Rectangle(MenuRect.X + DbgMenuTopLeftButtonRect.Width, MenuRect.Y, MenuRect.Width - DbgMenuTopLeftButtonRect.Width, DbgMenuTopLeftButtonRect.Height); GFX.SpriteBatchBeginForText(); var clickMeSize = DBG.DEBUG_FONT_SMALL.MeasureString("MENU"); GFX.SpriteBatch.Draw(Main.WHITE_TEXTURE, DbgMenuTopLeftButtonRect, Color.Black * 0.85f); GFX.SpriteBatch.DrawString(DBG.DEBUG_FONT_SMALL, "MENU", DbgMenuTopLeftButtonRect.Center(), Color.White, 0, clickMeSize / 2, 1, SpriteEffects.None, 0); if (MenuOpenState == DbgMenuOpenState.Closed) { GFX.SpriteBatchEnd(); return; } UpdateUI(); float menuBackgroundOpacityMult = MenuOpenState == DbgMenuOpenState.Open ? 1.0f : 0f; // Draw menu background rect //---- Full Background GFX.SpriteBatch.Draw(Main.WHITE_TEXTURE, SubMenuRect, Color.Black * 0.5f * menuBackgroundOpacityMult); //---- Slightly Darker Part On Top GFX.SpriteBatch.Draw(Main.WHITE_TEXTURE, darkTitleRect, Color.Black * 0.75f * menuBackgroundOpacityMult); if (MenuOpenState == DbgMenuOpenState.Open) { var renderPauseStr = $"Render Pause:{(IsPauseRendering ? "Active" : "Inactive")}\n(Click RS / Press Pause Key)"; var renderPauseStrScale = DBG.DEBUG_FONT.MeasureString(renderPauseStr); var renderPauseStrColor = !IsPauseRendering ? Color.White : Color.Yellow; DBG.DrawOutlinedText(renderPauseStr, new Vector2(8, GFX.Device.Viewport.Height - 20), renderPauseStrColor, DBG.DEBUG_FONT, scaleOrigin: new Vector2(0, renderPauseStrScale.Y), //scale: IsPauseRendering ? 1f : 0.75f, startAndEndSpriteBatchForMe: false); } // Draw name on top var sb = new StringBuilder(); //---- If in submenu, append the stack of menues preceding this one if (DbgMenuStack.Count > 0) { bool first = true; foreach (var chain in DbgMenuStack.Reverse()) { if (first) { first = false; } else { sb.Append(" > "); } sb.Append($"{chain.Text}{(chain.Items.Count > 0 ? $" ({chain.Items.Count})" : "")}"); } sb.Append(" > "); } //---- Append the current menu name. sb.Append($"{Text}{(Items.Count > 0 ? $" ({Items.Count})" : "")}"); //---- Draw full menu name DBG.DrawOutlinedText(sb.ToString(), darkTitleRect.TopLeftCorner() + new Vector2(8, 4), CustomColorFunction?.Invoke() ?? Color.White, DBG.DEBUG_FONT, startAndEndSpriteBatchForMe: false); if (Items.Count != 0) { if (SelectedIndex < 0) { SelectedIndex = 0; } else if (SelectedIndex >= Items.Count) { SelectedIndex = Items.Count - 1; } var selectedItemRect = GetItemDisplayRect(SelectedIndex, SubMenuRect); if (Items.Count != prevFrameItemCount) { menuHeight = GetEntireMenuHeight(); } // Only need to calculate scroll stuff if there's text that reaches past the bottom. if (menuHeight > SubMenuRect.Height) { // Scroll selected into view. //---- If item is ABOVE view if (selectedItemRect.Top < SubMenuRect.Top) { int distanceNeededToScroll = SubMenuRect.Top - selectedItemRect.Top; Scroll -= distanceNeededToScroll; } //---- If item is BELOW view if (selectedItemRect.Bottom > SubMenuRect.Bottom) { int distanceNeededToScroll = selectedItemRect.Bottom - SubMenuRect.Bottom; Scroll += distanceNeededToScroll; } } // Clamp scroll MaxScroll = Math.Max(GetEntireMenuHeight() - SubMenuRect.Height, 0); if (Scroll > MaxScroll) { Scroll = MaxScroll; } else if (Scroll < 0) { Scroll = 0; } // Debug display of menu item rectangles: //for (int i = 0; i < Items.Count; i++) //{ // var TEST_DebugDrawItemRect = GetItemDisplayRect(i, SubMenuRect); // GFX.SpriteBatch.Begin(); // GFX.SpriteBatch.Draw(MODEL_VIEWER_MAIN.DEFAULT_TEXTURE_DIFFUSE, TEST_DebugDrawItemRect, Color.Yellow); // GFX.SpriteBatch.End(); //} // ONLY draw the menu items that are in-frame int roughStartDrawIndex = (int)((Scroll / menuHeight) * (Items.Count - 1)) - 1; int roughEndDrawIndex = (int)(((Scroll + MenuRect.Height) / menuHeight) * (Items.Count - 1)) + 1; if (roughStartDrawIndex < 0) { roughStartDrawIndex = 0; } else if (roughStartDrawIndex >= Items.Count) { roughStartDrawIndex = Items.Count - 1; } if (roughEndDrawIndex < 0) { roughEndDrawIndex = 0; } else if (roughEndDrawIndex >= Items.Count) { roughEndDrawIndex = Items.Count - 1; } GFX.SpriteBatchEnd(); // Store current viewport, then switch viewport to JUST the menu rect var oldViewport = GFX.Device.Viewport; GFX.Device.Viewport = new Viewport( oldViewport.X + SubMenuRect.X, oldViewport.Y + SubMenuRect.Y, SubMenuRect.Width, SubMenuRect.Height); GFX.SpriteBatchBegin(); // ---- These braces manually force a smaller scope so we // don't forget to return to the old viewport immediately afterward. { // Draw Items var selectionPrefixTextSize = Vector2.Zero;// FONT.MeasureString($" {UICursorBlinkString} "); for (int i = roughStartDrawIndex; i <= roughEndDrawIndex; i++) { Items[i].UpdateUI(); var entryText = GetActualItemDisplayText(i); var itemRect = GetItemDisplayRect(i, SubMenuRect); // Check if this item is inside the actual menu rectangle. if (SubMenuRect.Intersects(itemRect)) { var itemTextColor = Items[i].CustomColorFunction?.Invoke() ?? ((SelectedIndex == i && MenuOpenState == DbgMenuOpenState.Open) ? Color.LightGreen : Color.White); if (SelectedIndex == i && MenuOpenState == DbgMenuOpenState.Open) { var underlineRect = new Rectangle( itemRect.X - SubMenuRect.X + (int)selectionPrefixTextSize.X - 4, itemRect.Y - SubMenuRect.Y - 1, MenuRect.Width - (int)(selectionPrefixTextSize.X) + 4, itemRect.Height + 2); if (menuHeight > SubMenuRect.Height) { underlineRect = new Rectangle(underlineRect.X + 12, underlineRect.Y, underlineRect.Width - 4, underlineRect.Height); } GFX.SpriteBatch.Draw(Main.WHITE_TEXTURE, underlineRect, Color.Black); } // We have to SUBTRACT the menu top/left coord because the string // drawing is relative to the VIEWPORT, which takes up just the actual menu rect DBG.DrawOutlinedText(entryText, new Vector2(itemRect.X - SubMenuRect.X, itemRect.Y - SubMenuRect.Y), itemTextColor, FONT, startAndEndSpriteBatchForMe: false); } } // Draw Scrollbar // Only if there's stuff that passes the bottom of the menu. if (menuHeight > SubMenuRect.Height) { //---- Draw Scrollbar Background GFX.SpriteBatch.Draw(Main.WHITE_TEXTURE, new Rectangle(0, 0, 8, SubMenuRect.Height), Color.White * 0.5f * menuBackgroundOpacityMult); float curScrollRectTop = (Scroll / menuHeight) * SubMenuRect.Height; float curScrollRectHeight = (SubMenuRect.Height / menuHeight) * SubMenuRect.Height; //---- Scroll Scrollbar current scroll GFX.SpriteBatch.Draw(Main.WHITE_TEXTURE, new Rectangle(0, (int)curScrollRectTop, 8, (int)curScrollRectHeight), Color.White * 0.75f * menuBackgroundOpacityMult); } } //---- Return to old viewport GFX.SpriteBatchEnd(); GFX.Device.Viewport = oldViewport; prevFrameItemCount = Items.Count; } }