system/MsgDialog: User message dialog

This commit is contained in:
Vinicius Rangel 2024-09-04 04:43:12 -03:00
parent 2e4eac3d19
commit 8c1a6fbb5e
No known key found for this signature in database
GPG Key ID: A5B154D904B761D9
2 changed files with 138 additions and 39 deletions

View File

@ -3,6 +3,7 @@
#include <imgui.h> #include <imgui.h>
#include <magic_enum.hpp> #include <magic_enum.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
@ -10,11 +11,10 @@
#include "core/libraries/system/msgdialog.h" #include "core/libraries/system/msgdialog.h"
#include "imgui/imgui_layer.h" #include "imgui/imgui_layer.h"
#include "imgui/imgui_std.h" #include "imgui/imgui_std.h"
#include "imgui_internal.h"
namespace Libraries::MsgDialog { namespace Libraries::MsgDialog {
class MsgDialogGui;
using CommonDialog::Error; using CommonDialog::Error;
using CommonDialog::Result; using CommonDialog::Result;
using CommonDialog::Status; using CommonDialog::Status;
@ -102,26 +102,88 @@ struct Param {
struct MsgDialogResult { struct MsgDialogResult {
FixedValue<u32, 0> mode{}; FixedValue<u32, 0> mode{};
Result result{}; Result result{Result::OK};
ButtonId buttonId{}; ButtonId buttonId{ButtonId::INVALID};
std::array<char, 32> reserved{}; std::array<char, 32> reserved{};
}; };
class MsgDialogGui final : public ImGui::Layer { static auto g_status = Status::NONE;
static MsgDialogResult g_result{};
struct {
int count = 0;
const char* text1;
const char* text2;
} static constexpr button_texts[] = {
{1, "OK"}, // 0 OK
{2, "Yes", "No"}, // 1 YESNO
{0}, // 2 NONE
{2, "OK", "Cancel"}, // 3 OK_CANCEL
{}, // 4 !!NOP
{1, "Wait"}, // 5 WAIT
{2, "Wait", "Cancel"}, // 6 WAIT_CANCEL
{2, "Yes", "No"}, // 7 YESNO_FOCUS_NO
{2, "OK", "Cancel"}, // 8 OK_CANCEL_FOCUS_CANCEL
{0xFF}, // 9 TWO_BUTTONS
};
static_assert(std::size(button_texts) == static_cast<int>(ButtonType::TWO_BUTTONS) + 1);
static void Finish(ButtonId buttonId);
namespace {
using namespace ImGui;
class MsgDialogGui final : public Layer {
const Param* param{}; const Param* param{};
s32 dialog_unique_id{};
void DrawUser(const UserMessageParam& user_message_param) { void DrawUser(const UserMessageParam& user_message_param) {
const auto ws = ImGui::GetWindowSize(); constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
ImGui::SetCursorPosY(ws.y / 2.0f - 30.0f);
ImGui::BeginGroup(); const auto ws = GetWindowSize();
ImGui::PushTextWrapPos(ws.x - 30.0f); SetCursorPosY(ws.y / 2.0f - 50.0f);
ImGui::SetCursorPosX( PushTextWrapPos(ws.x - 30.0f);
(ws.x - ImGui::CalcTextSize(user_message_param.msg, nullptr, false, ws.x - 40.0f).x) / SetCursorPosX(
2.0f); (ws.x - CalcTextSize(user_message_param.msg, nullptr, false, ws.x - 40.0f).x) / 2.0f);
ImGui::Text("%s", user_message_param.msg); Text("%s", user_message_param.msg);
ImGui::PopTextWrapPos(); PopTextWrapPos();
ImGui::EndGroup(); const auto button_type = user_message_param.buttonType;
switch (user_message_param.buttonType) {} ASSERT(user_message_param.buttonType <= ButtonType::TWO_BUTTONS);
auto [count, text1, text2] = button_texts[static_cast<u32>(button_type)];
if (count == 0xFF) { // TWO_BUTTONS -> User defined message
count = 2;
text1 = user_message_param.buttonsParam->msg1;
text2 = user_message_param.buttonsParam->msg2;
}
const bool focus_first = button_type < ButtonType::YESNO_FOCUS_NO;
SetCursorPos({
ws.x / 2.0f - BUTTON_SIZE.x / 2.0f * static_cast<float>(count),
ws.y - 10.0f - BUTTON_SIZE.y,
});
BeginGroup();
if (count > 0) {
// First button at the right, so we render the second button first
if (count == 2) {
PushID(2);
if (Button(text2, BUTTON_SIZE)) {
Finish(ButtonId::BUTTON2);
}
if (!focus_first) {
SetItemDefaultNav();
}
PopID();
SameLine();
}
PushID(1);
if (Button(text1, BUTTON_SIZE)) {
Finish(ButtonId::BUTTON1);
}
if (focus_first) {
SetItemDefaultNav();
}
PopID();
SameLine();
}
EndGroup();
} }
void DrawProgressBar(const ProgressBarParam& progress_bar_param) {} void DrawProgressBar(const ProgressBarParam& progress_bar_param) {}
@ -130,16 +192,29 @@ class MsgDialogGui final : public ImGui::Layer {
public: public:
void Draw() override { void Draw() override {
const auto& io = ImGui::GetIO(); if (g_status != Status::RUNNING) {
return;
}
const auto& io = GetIO();
const ImVec2 window_size{ const ImVec2 window_size{
std::min(io.DisplaySize.x, 500.0f), std::min(io.DisplaySize.x, 500.0f),
std::min(io.DisplaySize.y, 300.0f), std::min(io.DisplaySize.y, 300.0f),
}; };
ImGui::CentralizeWindow(); CentralizeWindow();
ImGui::SetNextWindowSize(window_size); SetNextWindowSize(window_size);
ImGui::Begin("##Message Dialog", nullptr, ImGuiWindowFlags_NoDecoration); SetNextWindowFocus();
SetNextWindowCollapsed(false);
KeepNavHighlight();
// Hack to allow every dialog to have a unique window
#define TITLE "Message Dialog##MD"
char id[sizeof(TITLE) + sizeof(int)] = TITLE "\0\0\0\0";
*reinterpret_cast<int*>(&id[sizeof(TITLE) - 1]) =
dialog_unique_id |
0x80808080; // keep the MSB set to extend the string length (null terminated)
#undef TITLE
if (Begin(id, nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) {
switch (param->mode) { switch (param->mode) {
case MsgDialogMode::USER_MSG: case MsgDialogMode::USER_MSG:
DrawUser(*param->userMsgParam); DrawUser(*param->userMsgParam);
@ -151,25 +226,33 @@ public:
DrawSystemMessage(*param->sysMsgParam); DrawSystemMessage(*param->sysMsgParam);
break; break;
} }
ImGui::End(); End();
}
} }
bool ShouldGrabGamepad() override { bool ShouldGrabGamepad() override {
return true; return true;
} }
explicit MsgDialogGui(const Param* param = nullptr) : param(param) {} explicit MsgDialogGui(const Param* param = nullptr)
} static g_msg_dialog_gui; : param(param), dialog_unique_id([] {
static s32 last_id = 0;
return last_id++;
}()) {}
} g_msg_dialog_gui;
} // namespace
static auto g_status = Status::NONE; static void Finish(ButtonId buttonId) {
static MsgDialogResult g_result{}; g_result.buttonId = buttonId;
g_status = Status::FINISHED;
Layer::RemoveLayer(&g_msg_dialog_gui);
}
Error PS4_SYSV_ABI sceMsgDialogClose() { Error PS4_SYSV_ABI sceMsgDialogClose() {
if (g_status != Status::RUNNING) { if (g_status != Status::RUNNING) {
return Error::NOT_RUNNING; return Error::NOT_RUNNING;
} }
g_status = Status::FINISHED; Finish(ButtonId::INVALID);
ImGui::Layer::RemoveLayer(&g_msg_dialog_gui);
return Error::OK; return Error::OK;
} }
@ -189,7 +272,7 @@ Error PS4_SYSV_ABI sceMsgDialogGetResult(MsgDialogResult* result) {
return Error::OK; return Error::OK;
} }
CommonDialog::Status PS4_SYSV_ABI sceMsgDialogGetStatus() { Status PS4_SYSV_ABI sceMsgDialogGetStatus() {
return g_status; return g_status;
} }
@ -216,8 +299,9 @@ Error PS4_SYSV_ABI sceMsgDialogOpen(const Param* param) {
ASSERT(param->size == sizeof(Param)); ASSERT(param->size == sizeof(Param));
ASSERT(param->baseParam.size == sizeof(CommonDialog::BaseParam)); ASSERT(param->baseParam.size == sizeof(CommonDialog::BaseParam));
g_status = Status::RUNNING; g_status = Status::RUNNING;
g_result = {};
g_msg_dialog_gui = MsgDialogGui(param); g_msg_dialog_gui = MsgDialogGui(param);
ImGui::Layer::AddLayer(&g_msg_dialog_gui); Layer::AddLayer(&g_msg_dialog_gui);
return Error::OK; return Error::OK;
} }

View File

@ -5,10 +5,25 @@
#include <imgui.h> #include <imgui.h>
#include "imgui_internal.h"
namespace ImGui { namespace ImGui {
inline void CentralizeWindow() { inline void CentralizeWindow() {
const auto display_size = GetIO().DisplaySize; const auto display_size = GetIO().DisplaySize;
SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f}); SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f});
} }
inline void KeepNavHighlight() {
GetCurrentContext()->NavDisableHighlight = false;
}
inline void SetItemDefaultNav() {
if (IsWindowAppearing()) {
const auto ctx = GetCurrentContext();
SetFocusID(ctx->LastItemData.ID, ctx->CurrentWindow);
ctx->NavInitResult.Clear();
}
}
} // namespace ImGui } // namespace ImGui