diff --git a/src/port/notification/notification.cpp b/src/port/notification/notification.cpp new file mode 100644 index 00000000..94f8ea59 --- /dev/null +++ b/src/port/notification/notification.cpp @@ -0,0 +1,132 @@ + +#include "notification.h" +#include + +namespace Notification { + +static uint32_t nextId = 0; +static std::vector notifications = {}; + +void Window::Draw() { + auto vp = ImGui::GetMainViewport(); + + const float margin = 30.0f; + const float padding = 10.0f; + + int position = CVarGetInteger("gNotifications.Position", 0); + + // Top Left + ImVec2 basePosition; + switch (position) { + case 0: // Top Left + basePosition = ImVec2(vp->Pos.x + margin, vp->Pos.y + margin); + break; + case 1: // Top Right + basePosition = ImVec2(vp->Pos.x + vp->Size.x - margin, vp->Pos.y + margin); + break; + case 2: // Bottom Left + basePosition = ImVec2(vp->Pos.x + margin, vp->Pos.y + vp->Size.y - margin); + break; + case 3: // Bottom Right + basePosition = ImVec2(vp->Pos.x + vp->Size.x - margin, vp->Pos.y + vp->Size.y - margin); + break; + case 4: // Hidden + return; + } + + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, CVarGetFloat("gNotifications.BgOpacity", 0.5f))); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + + for (int index = 0; index < notifications.size(); ++index) { + auto& notification = notifications[index]; + int inverseIndex = -ABS(index - (notifications.size() - 1)); + + ImGui::SetNextWindowViewport(vp->ID); + if (notification.remainingTime < 4.0f) { + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, (notification.remainingTime - 1) / 3.0f); + } else { + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 1.0f); + } + + ImGui::Begin(("notification#" + std::to_string(notification.id)).c_str(), nullptr, + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoDocking | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoScrollWithMouse | + ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoScrollbar + ); + + ImGui::SetWindowFontScale(CVarGetFloat("gNotifications.Size", 1.8f)); // Make this adjustable + + ImVec2 notificationPos; + switch (position) { + case 0: // Top Left + notificationPos = ImVec2(basePosition.x, basePosition.y + ((ImGui::GetWindowSize().y + padding) * inverseIndex)); + break; + case 1: // Top Right + notificationPos = ImVec2(basePosition.x - ImGui::GetWindowSize().x, basePosition.y + ((ImGui::GetWindowSize().y + padding) * inverseIndex)); + break; + case 2: // Bottom Left + notificationPos = ImVec2(basePosition.x, basePosition.y - ((ImGui::GetWindowSize().y + padding) * (inverseIndex + 1))); + break; + case 3: // Bottom Right + notificationPos = ImVec2(basePosition.x - ImGui::GetWindowSize().x, basePosition.y - ((ImGui::GetWindowSize().y + padding) * (inverseIndex + 1))); + break; + } + + ImGui::SetWindowPos(notificationPos); + + if (notification.itemIcon != nullptr) { + ImGui::Image(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(notification.itemIcon), ImVec2(24, 24)); + ImGui::SameLine(); + } + if (!notification.prefix.empty()) { + ImGui::TextColored(notification.prefixColor, "%s", notification.prefix.c_str()); + ImGui::SameLine(); + } + ImGui::TextColored(notification.messageColor, "%s", notification.message.c_str()); + if (!notification.suffix.empty()) { + ImGui::SameLine(); + ImGui::TextColored(notification.suffixColor, "%s", notification.suffix.c_str()); + } + + ImGui::End(); + ImGui::PopStyleVar(); + } + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(2); +} + + +void Window::UpdateElement() { + for (int index = 0; index < notifications.size(); ++index) { + auto& notification = notifications[index]; + + // decrement remainingTime + notification.remainingTime -= ImGui::GetIO().DeltaTime; + + // remove notification if it has expired + if (notification.remainingTime <= 0) { + notifications.erase(notifications.begin() + index); + --index; + } + } +} + +void Emit(Options notification) { + notification.id = nextId++; + if (notification.remainingTime == 0.0f) { + notification.remainingTime = CVarGetFloat("gNotifications.Duration", 10.0f); + } + notifications.push_back(notification); + // Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +} // namespace Notification diff --git a/src/port/notification/notification.h b/src/port/notification/notification.h new file mode 100644 index 00000000..dbcece01 --- /dev/null +++ b/src/port/notification/notification.h @@ -0,0 +1,35 @@ +#pragma once + +#ifdef __cplusplus + +#include +#include + +namespace Notification { + +struct Options { + uint32_t id = 0; + const char* itemIcon = nullptr; + std::string prefix = ""; + ImVec4 prefixColor = ImVec4(0.5f, 0.5f, 1.0f, 1.0f); + std::string message = ""; + ImVec4 messageColor = ImVec4(0.7f, 0.7f, 0.7f, 1.0f); + std::string suffix = ""; + ImVec4 suffixColor = ImVec4(1.0f, 0.5f, 0.5f, 1.0f); + float remainingTime = 0.0f; // Seconds +}; + +class Window : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + + void InitElement() override {}; + void DrawElement() override {}; + void Draw() override; + void UpdateElement() override; +}; + +void Emit(Options notification); + +} // namespace Notification +#endif // __cplusplus diff --git a/src/port/ui/ImguiUI.cpp b/src/port/ui/ImguiUI.cpp index eb9a62c8..5c77bf14 100644 --- a/src/port/ui/ImguiUI.cpp +++ b/src/port/ui/ImguiUI.cpp @@ -11,6 +11,7 @@ #include #include #include "port/Engine.h" +#include "port/notification/notification.h" extern "C" { #include "sys.h" @@ -24,6 +25,7 @@ std::shared_ptr mConsoleWindow; std::shared_ptr mStatsWindow; std::shared_ptr mInputEditorWindow; std::shared_ptr mGfxDebuggerWindow; +std::shared_ptr mNotificationWindow; std::shared_ptr mAdvancedResolutionSettingsWindow; void SetupGuiElements() { @@ -59,13 +61,20 @@ void SetupGuiElements() { mAdvancedResolutionSettingsWindow = std::make_shared("gAdvancedResolutionEditorEnabled", "Advanced Resolution Settings"); gui->AddGuiWindow(mAdvancedResolutionSettingsWindow); + mNotificationWindow = std::make_shared("gNotifications", "Notifications Window"); + gui->AddGuiWindow(mNotificationWindow); + mNotificationWindow->Show(); } void Destroy() { + auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); + gui->RemoveAllGuiWindows(); + mAdvancedResolutionSettingsWindow = nullptr; mConsoleWindow = nullptr; mStatsWindow = nullptr; mInputEditorWindow = nullptr; + mNotificationWindow = nullptr; } std::string GetWindowButtonText(const char* text, bool menuOpen) {