overlay: refactor overlay layering (#739)

## Link to GitHub Issue or related Pull Request, if one exists
n/a

## Description of change

Create three distinct layers for the overlay:

1. Bottommost persistent layer - non-interactable layer that is always
on. This was only for notifications, but now the FPS widget lives here
as well.
2. Overlay windows layer - most interactable windows go here.
3. Topmost main menu - this is reserved for the main menu (escape key)
and this is a modal dialog that occludes the layers below.

Why? 

- `toggle overlay` behavior with FPS widget *also* toggling on/off was a
bit confusing (now they're two separate keys)
- FPS widget is popular, but it caused the entire overlay to be active,
which affects how input is handled
- the main menu being a standalone window was a little awkward (now it's
a modal)

## Testing
This commit is contained in:
bicarus
2026-06-07 17:23:08 -07:00
committed by GitHub
parent bb87aa2944
commit 100caa0f5c
16 changed files with 348 additions and 102 deletions
+58 -15
View File
@@ -1,5 +1,6 @@
#include "exitprompt.h"
#include "avs/game.h"
#include "build/defs.h"
#include "misc/eamuse.h"
#include "util/logging.h"
#include "games/iidx/iidx.h"
@@ -9,7 +10,7 @@
namespace overlay::windows {
ExitPrompt::ExitPrompt(SpiceOverlay *overlay) : Window(overlay) {
this->title = "spice2x";
this->title = "spice2x (" + to_string(VERSION_STRING_CFG) + ")";
this->init_size = ImVec2(
(ImGui::GetFontSize() * 14) + (ImGui::GetStyle().ItemSpacing.x * 2),
overlay::apply_scaling(120));
@@ -20,22 +21,35 @@ namespace overlay::windows {
this->flags = ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_AlwaysAutoResize
| ImGuiWindowFlags_NoDocking;
| ImGuiWindowFlags_NoDocking
| ImGuiWindowFlags_NoMove;
// the menu renders itself as a popup, not a normal overlay window
this->draws_window = false;
}
void ExitPrompt::update() {
Window::update();
// allow the popup to be reopened the next time the menu is shown
if (!this->active) {
this->popup_opened = false;
}
}
void ExitPrompt::build_button(
Window *window, std::string label, const ImVec2 &size, NextItem next, bool is_toggle) {
Window *window, std::string label, const ImVec2 &size, NextItem next) {
if (window == nullptr) {
return;
}
if (ImGui::Button(label.c_str(), size)) {
if (is_toggle) {
window->toggle_active();
} else {
window->set_active(true);
this->set_active(false);
window->toggle_active();
// raise to the top when the user toggled it visible from the menu
if (window->get_active()) {
window->bring_to_front();
}
}
if (next == NextItem::NEW_LINE) {
@@ -46,6 +60,31 @@ namespace overlay::windows {
}
void ExitPrompt::build_content() {
// use ### so the visible label is the full title (with version) while the
// popup keeps a stable identifier regardless of the title text
const std::string popup_id_str = this->title + "###mainmenu";
const char *popup_id = popup_id_str.c_str();
// open the popup once when the menu becomes active; popup_opened is reset
// in update() while inactive so it reopens the next time it is shown
if (!this->popup_opened) {
ImGui::OpenPopup(popup_id);
this->popup_opened = true;
}
// position at the top center of the screen
const ImVec2 menu_pos(
ImGui::GetIO().DisplaySize.x / 2 - this->init_size.x / 2,
overlay::apply_scaling(10));
ImGui::SetNextWindowPos(menu_pos, ImGuiCond_Appearing);
bool open = true;
if (!ImGui::BeginPopupModal(popup_id, &open, this->flags)) {
// dismissed via escape - close the menu
this->set_active(false);
return;
}
const ImVec2 size(ImGui::GetFontSize() * 14, ImGui::GetFontSize() * 1.9f);
const ImVec2 size_half(
(size.x - ImGui::GetStyle().ItemSpacing.x) / 2,
@@ -54,11 +93,7 @@ namespace overlay::windows {
(size.x - (ImGui::GetStyle().ItemSpacing.x * 2)) / 3,
ImGui::GetFontSize() * 2.5f);
if (ImGui::Button("Hide overlay", size)) {
overlay::OVERLAY->set_active(false);
}
ImGui::Spacing();
build_button(this->overlay->window_config, "Show Config", size, NextItem::NEW_LINE, false);
build_button(this->overlay->window_config, "Options", size, NextItem::NEW_LINE);
std::string sub = "Show Subscreen";
if (avs::game::is_model("LDJ")) {
@@ -77,11 +112,12 @@ namespace overlay::windows {
sub = "Show Pop'n Subscreen";
}
build_button(this->overlay->window_sub, sub, size, NextItem::NEW_LINE, false);
build_button(this->overlay->window_sub, sub, size, NextItem::NEW_LINE);
ImGui::TextDisabled("Graphics");
build_button(this->overlay->window_camera, "Camera control", size, NextItem::NEW_LINE);
build_button(this->overlay->window_fps, "FPS", size_half, NextItem::SAME_LINE);
build_button(this->overlay->window_fps.get(), "FPS", size_half, NextItem::SAME_LINE);
build_button(this->overlay->window_resize, "Resize", size_half, NextItem::NEW_LINE);
ImGui::TextDisabled("I/O");
@@ -157,5 +193,12 @@ namespace overlay::windows {
ImGui::EndPopup();
}
ImGui::Spacing();
ImGui::EndPopup();
// title-bar X was clicked - close the menu
if (!open) {
this->set_active(false);
}
}
}