diff --git a/lib/libimhex/CMakeLists.txt b/lib/libimhex/CMakeLists.txt index 03c4e7517..f9de79686 100644 --- a/lib/libimhex/CMakeLists.txt +++ b/lib/libimhex/CMakeLists.txt @@ -45,6 +45,7 @@ set(LIBIMHEX_SOURCES source/ui/imgui_imhex_extensions.cpp source/ui/view.cpp source/ui/popup.cpp + source/ui/toast.cpp source/subcommands/subcommands.cpp ) diff --git a/lib/libimhex/include/hex/ui/popup.hpp b/lib/libimhex/include/hex/ui/popup.hpp index 58a4b5079..afaee1edf 100644 --- a/lib/libimhex/include/hex/ui/popup.hpp +++ b/lib/libimhex/include/hex/ui/popup.hpp @@ -56,8 +56,9 @@ namespace hex { return m_close; } + protected: + static std::mutex& getMutex(); private: - UnlocalizedString m_unlocalizedName; bool m_closeButton, m_modal; std::atomic m_close = false; @@ -74,8 +75,7 @@ namespace hex { public: template static void open(Args && ... args) { - static std::mutex mutex; - std::lock_guard lock(mutex); + std::lock_guard lock(getMutex()); auto popup = std::make_unique(std::forward(args)...); diff --git a/lib/libimhex/include/hex/ui/toast.hpp b/lib/libimhex/include/hex/ui/toast.hpp new file mode 100644 index 000000000..1d99a8801 --- /dev/null +++ b/lib/libimhex/include/hex/ui/toast.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include +#include + +namespace hex { + + namespace impl { + + class ToastBase { + public: + ToastBase(ImColor color) : m_color(color) {} + virtual ~ToastBase() = default; + + virtual void draw() { drawContent(); } + virtual void drawContent() = 0; + + [[nodiscard]] static std::list> &getQueuedToasts(); + + [[nodiscard]] const ImColor& getColor() const { + return m_color; + } + + void setAppearTime(double appearTime) { + m_appearTime = appearTime; + } + + [[nodiscard]] double getAppearTime() const { + return m_appearTime; + } + + constexpr static double VisibilityTime = 4.0; + + protected: + static std::mutex& getMutex(); + + double m_appearTime = 0; + ImColor m_color; + }; + + } + + template + class Toast : public impl::ToastBase { + public: + using impl::ToastBase::ToastBase; + + template + static void open(Args && ... args) { + std::lock_guard lock(getMutex()); + + auto toast = std::make_unique(std::forward(args)...); + getQueuedToasts().emplace_back(std::move(toast)); + } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/source/ui/popup.cpp b/lib/libimhex/source/ui/popup.cpp index 6d70c7669..a3f28eb0f 100644 --- a/lib/libimhex/source/ui/popup.cpp +++ b/lib/libimhex/source/ui/popup.cpp @@ -9,5 +9,12 @@ namespace hex::impl { return openPopups; } + std::mutex& PopupBase::getMutex() { + static std::mutex mutex; + + return mutex; + } + + } \ No newline at end of file diff --git a/lib/libimhex/source/ui/toast.cpp b/lib/libimhex/source/ui/toast.cpp new file mode 100644 index 000000000..55560a857 --- /dev/null +++ b/lib/libimhex/source/ui/toast.cpp @@ -0,0 +1,17 @@ +#include + +namespace hex::impl { + + [[nodiscard]] std::list> &ToastBase::getQueuedToasts() { + static std::list> queuedToasts; + + return queuedToasts; + } + + std::mutex& ToastBase::getMutex() { + static std::mutex mutex; + + return mutex; + } + +} \ No newline at end of file diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 82bf3bc92..87fde1ac6 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -39,6 +39,7 @@ #include #include +#include namespace hex { @@ -753,6 +754,45 @@ namespace hex { } } + // Draw Toasts + { + static std::unique_ptr currToast; + if (currToast == nullptr) { + if (auto &queuedToasts = impl::ToastBase::getQueuedToasts(); !queuedToasts.empty()) { + currToast = std::move(queuedToasts.front()); + queuedToasts.pop_front(); + + currToast->setAppearTime(ImGui::GetTime()); + } + } else { + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5_scaled); + ImGui::SetNextWindowSize(scaled({ 280, 60 })); + ImGui::SetNextWindowPos((ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize()) - scaled({ 10, 10 }), ImGuiCond_Always, ImVec2(1, 1)); + if (ImGui::Begin("##Toast", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoInputs)) { + auto drawList = ImGui::GetWindowDrawList(); + + const auto min = ImGui::GetWindowPos(); + const auto max = min + ImGui::GetWindowSize(); + + drawList->PushClipRect(min, min + scaled({ 5, 60 })); + drawList->AddRectFilled(min, max, currToast->getColor(), 5_scaled); + drawList->PopClipRect(); + + ImGui::Indent(); + currToast->draw(); + ImGui::Unindent(); + } + ImGui::End(); + ImGui::PopStyleVar(); + + if ((currToast->getAppearTime() + impl::ToastBase::VisibilityTime) < ImGui::GetTime()) { + currToast.reset(); + } + } + + + } + // Run all deferred calls TaskManager::runDeferredCalls(); diff --git a/plugins/builtin/include/content/toasts/toast_notification.hpp b/plugins/builtin/include/content/toasts/toast_notification.hpp new file mode 100644 index 000000000..80ecd07d0 --- /dev/null +++ b/plugins/builtin/include/content/toasts/toast_notification.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace hex::plugin::builtin::ui { + + namespace impl { + + template + struct ToastNotification : Toast { + ToastNotification(ImColor color, const char *icon, UnlocalizedString title, UnlocalizedString message) + : Toast(color), m_icon(icon), m_title(std::move(title)), m_message(std::move(message)) {} + + void drawContent() final { + ImGuiExt::TextFormattedColored(this->getColor(), "{}", m_icon); + ImGui::SameLine(); + ImGui::PushFont(ImHexApi::Fonts::Bold()); + { + ImGuiExt::TextFormatted("{}", hex::limitStringLength(Lang(m_title).get(), 30)); + } + ImGui::PopFont(); + + ImGui::Separator(); + + ImGuiExt::TextFormattedWrapped("{}", hex::limitStringLength(Lang(m_message).get(), 60)); + } + + private: + const char *m_icon; + UnlocalizedString m_title, m_message; + }; + + } + + struct ToastInfo : impl::ToastNotification { + ToastInfo(UnlocalizedString title, UnlocalizedString message) + : ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerInfo), ICON_VS_INFO, std::move(title), std::move(message)) {} + }; + + struct ToastWarn : impl::ToastNotification { + ToastWarn(UnlocalizedString title, UnlocalizedString message) + : ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerWarning), ICON_VS_WARNING, std::move(title), std::move(message)) {} + }; + + struct ToastError : impl::ToastNotification { + ToastError(UnlocalizedString title, UnlocalizedString message) + : ToastNotification(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerError), ICON_VS_ERROR, std::move(title), std::move(message)) {} + }; + +}