#include "window.h" #include "cfg/configurator.h" #include "util/logging.h" #include "games/io.h" #include "misc/eamuse.h" #include "external/imgui/imgui_internal.h" overlay::Window::Window(SpiceOverlay *overlay) : overlay(overlay) { } overlay::Window::~Window() { // kill children for (auto &child : this->children) { delete child; } } void overlay::Window::update() { // check if toggle is enabled if (this->toggle_button != ~0u) { // get state auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game()); bool toggle_button_new = overlay_buttons && this->overlay->hotkeys_triggered() && GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_button)); if (toggle_button_new && !this->toggle_button_state) { // if the overlay is hidden just reactivate it if (!this->overlay->get_active()) { // but don't let the main menu occlude it if it was previously visible if (this->overlay->window_main_menu) { this->overlay->window_main_menu->set_active(false); } this->active = true; this->overlay->set_active(true); } else { this->toggle_active(); } // raise to the top, but only because the user pressed the hotkey and // the window is now visible if (this->active) { this->bring_to_front(); } } this->toggle_button_state = toggle_button_new; } // update children auto it = this->children.begin(); while (it != this->children.end()) { (*it)->update(); if ((*it)->active) { it++; } else { delete (*it); this->children.erase(it); } } } void overlay::Window::build() { // check if active if (!this->active) { this->mouse_hovered = false; return; } if (this->draws_window) { // automatic max window size if (!cfg::CONFIGURATOR_STANDALONE && (size_max.x < 0 || size_max.y < 0)) { auto &display_size = ImGui::GetIO().DisplaySize; ImVec2 size_max_auto(display_size.x - 100, display_size.y - 100); ImGui::SetNextWindowSizeConstraints(size_min, size_max_auto, resize_callback); } else { ImGui::SetNextWindowSizeConstraints(size_min, size_max, resize_callback); } // background alpha if (this->bg_alpha != 1.f) { ImGui::SetNextWindowBgAlpha(this->bg_alpha); } if (this->remove_window_padding) { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); } const bool custom_window_padding = !this->remove_window_padding && this->window_padding.x >= 0.f; if (custom_window_padding) { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, this->window_padding); } // create window // NoFocusOnAppearing: when the overlay is re-shown every window reappears at // once and ImGui would auto-focus whichever is submitted last, stealing the // top spot. suppress that so only an explicit focus request (see below) // decides what comes to the front. const std::string window_id = this->title + "###" + to_string(this); const bool build_contents = ImGui::Begin( window_id.c_str(), &this->active, this->flags | ImGuiWindowFlags_NoFocusOnAppearing); // keep the window hovered while its invisible input button is held during a drag this->mouse_hovered = ImGui::IsWindowHovered( ImGuiHoveredFlags_RootAndChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); if (build_contents) { // window attributes - init_pos / init_size are only honored once // (ImGuiCond_Once), so compute them a single time instead of every frame if (!this->initial_window_calculated) { this->initial_window_calculated = true; this->calculate_initial_window(); } ImGui::SetWindowPos(this->init_pos, ImGuiCond_Once); ImGui::SetWindowSize(this->init_size, ImGuiCond_Once); // add content this->build_content(); // build children for (auto &child : this->children) { child->build(); } } // end window ImGui::End(); // apply an explicit focus request now that the window exists. FocusWindow // with UnlessBelowModal raises it to the front, but keeps it right below // any blocking popup/modal instead of jumping in front of it (this also // re-orders brand-new windows that ImGui places on top by default). if (this->request_focus) { this->request_focus = false; if (ImGuiWindow *w = ImGui::FindWindowByName(window_id.c_str())) { ImGui::FocusWindow(w, ImGuiFocusRequestFlags_UnlessBelowModal); } } if (this->remove_window_padding) { ImGui::PopStyleVar(); ImGui::PopStyleVar(); ImGui::PopStyleVar(); } if (custom_window_padding) { ImGui::PopStyleVar(); } } else { // raw content does not have an ImGui window to hover this->mouse_hovered = false; // add raw content this->build_content(); } } void overlay::Window::toggle_active() { // flip bool this->active = !this->active; // update children for (auto &child : this->children) { child->toggle_active(); } } void overlay::Window::set_active(bool active) { // toggle if different if (this->get_active() != active) { // flip bool this->active = !this->active; // update children for (auto &child : this->children) { child->set_active(this->active); } } } void overlay::Window::bring_to_front() { this->request_focus = true; } bool overlay::Window::get_active() { // check for active children for (auto &child : this->children) { if (child->get_active()) { return true; } } // now it depends on us return this->active; } bool overlay::Window::is_mouse_hovered() { return this->mouse_hovered; }