Add ToastQueue for in-app notifications.

This commit is contained in:
Guillaume Jacquemin 2021-07-25 10:52:01 +02:00
parent 445d7323b3
commit d9efe8191e
3 changed files with 244 additions and 0 deletions

View file

@ -46,6 +46,8 @@ add_executable(MassBuilderSaveTool WIN32
Mass/Mass.cpp
Maps/LastMissionId.h
Maps/StoryProgress.h
ToastQueue/ToastQueue.h
ToastQueue/ToastQueue.cpp
FontAwesome/IconsFontAwesome5.h
FontAwesome/IconsFontAwesome5Brands.h
resource.rc

View file

@ -0,0 +1,160 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Utility/FormatStl.h>
#include <Magnum/Math/Functions.h>
#include <imgui.h>
#include "../FontAwesome/IconsFontAwesome5.h"
#include "ToastQueue.h"
using namespace Corrade;
constexpr UnsignedInt success_colour = 0xff67d23bu;
constexpr UnsignedInt info_colour = 0xffcc832fu;
constexpr UnsignedInt warning_colour = 0xff2fcfc7u;
constexpr UnsignedInt error_colour = 0xff3134cdu;
constexpr UnsignedInt fade_time = 150;
constexpr Float base_opacity = 1.0f;
constexpr Vector2 padding{20.0f, 20.0f};
constexpr Float toast_spacing = 10.0f;
Toast::Toast(Type type, const std::string& message, std::chrono::milliseconds timeout):
_type{type}, _message{message}, _timeout{timeout}, _creationTime{std::chrono::steady_clock::now()}
{
_phaseTrack = Animation::Track<UnsignedInt, Phase>{{
{0, Phase::FadeIn},
{fade_time, Phase::Wait},
{fade_time + timeout.count(), Phase::FadeOut},
{(fade_time * 2) + timeout.count(), Phase::TimedOut}
}, Math::select, Animation::Extrapolation::Constant};
}
auto Toast::type() -> Type {
return _type;
}
auto Toast::message() -> const std::string& {
return _message;
}
auto Toast::timeout() -> std::chrono::milliseconds {
return _timeout;
}
auto Toast::creationTime() -> std::chrono::steady_clock::time_point {
return _creationTime;
}
auto Toast::elapsedTime() -> std::chrono::milliseconds {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - _creationTime);
}
auto Toast::phase() -> Phase {
return _phaseTrack.at(elapsedTime().count());
}
auto Toast::opacity() -> Float {
Phase phase = this->phase();
Long elapsed_time = elapsedTime().count();
if(phase == Phase::FadeIn) {
return Float(elapsed_time) / Float(fade_time);
}
else if(phase == Phase::FadeOut) {
return 1.0f - ((Float(elapsed_time) - Float(fade_time) - Float(_timeout.count())) / Float(fade_time));
}
return 1.0f;
}
void ToastQueue::addToast(Toast&& toast) {
_toasts.push_back(std::move(toast));
}
void ToastQueue::addToast(Toast::Type type, const std::string& message, std::chrono::milliseconds timeout) {
_toasts.emplace_back(type, message, timeout);
}
void ToastQueue::draw(Vector2i viewport_size) {
Float height = 0.0f;
for(UnsignedInt i = 0; i < _toasts.size(); i++) {
Toast* current = &_toasts[i];
if(current->phase() == Toast::Phase::TimedOut) {
removeToast(i);
continue;
}
std::string win_id = Utility::formatString("##Toast{}", i);
Float opacity = base_opacity * current->opacity();
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, opacity);
ImGui::SetNextWindowPos({viewport_size.x() - padding.x(), viewport_size.y() - padding.y() - height}, ImGuiCond_Always, {1.0f, 1.0f});
if(ImGui::Begin(win_id.c_str(), nullptr,
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoDecoration|
ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoNav|ImGuiWindowFlags_NoFocusOnAppearing))
{
ImColor colour = 0xffffffff;
switch(current->type()) {
case Toast::Type::Default:
break;
case Toast::Type::Success:
colour = success_colour;
ImGui::TextColored(colour, ICON_FA_CHECK_CIRCLE);
break;
case Toast::Type::Info:
colour = info_colour;
ImGui::TextColored(colour, ICON_FA_INFO_CIRCLE);
break;
case Toast::Type::Warning:
colour = warning_colour;
ImGui::TextColored(colour, ICON_FA_EXCLAMATION_TRIANGLE);
break;
case Toast::Type::Error:
colour = error_colour;
ImGui::TextColored(colour, ICON_FA_TIMES_CIRCLE);
break;
}
ImGui::SameLine();
if(current->message().length() > 127) {
ImGui::TextColored(colour, "%.*s...", 127, current->message().c_str());
}
else {
ImGui::TextColored(colour, current->message().c_str());
}
height += ImGui::GetWindowHeight() + toast_spacing;
}
ImGui::End();
ImGui::PopStyleVar();
}
}
void ToastQueue::removeToast(Long index) {
_toasts.erase(_toasts.begin() + index);
}

View file

@ -0,0 +1,82 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <chrono>
#include <string>
#include <vector>
#include <Magnum/Magnum.h>
#include <Magnum/Animation/Track.h>
using namespace Magnum;
class Toast {
public:
enum class Type : UnsignedByte {
Default, Success, Info, Warning, Error
};
enum class Phase : UnsignedByte {
FadeIn, Wait, FadeOut, TimedOut
};
explicit Toast(Type type, const std::string& message,
std::chrono::milliseconds timeout = std::chrono::milliseconds{3000});
Toast(const Toast& other) = delete;
Toast& operator=(const Toast& other) = delete;
Toast(Toast&& other) = default;
Toast& operator=(Toast&& other) = default;
auto type() -> Type;
auto message() -> std::string const&;
auto timeout() -> std::chrono::milliseconds;
auto creationTime() -> std::chrono::steady_clock::time_point;
auto elapsedTime() -> std::chrono::milliseconds;
auto phase() -> Phase;
auto opacity() -> Float;
private:
Type _type{Type::Default};
std::string _message;
std::chrono::milliseconds _timeout;
std::chrono::steady_clock::time_point _creationTime;
Animation::Track<UnsignedInt, Phase> _phaseTrack;
};
class ToastQueue {
public:
void addToast(Toast&& toast);
void addToast(Toast::Type type, const std::string& message,
std::chrono::milliseconds timeout = std::chrono::milliseconds{3000});
void draw(Vector2i viewport_size);
private:
void removeToast(Long index);
std::vector<Toast> _toasts;
};