[UI] Implemented Host Notifications
Splitted clases into: - ImGuiNotification - ImGuiHostNotification - ImGuiGuestNotification To differentiate between Xenia toast notifications and notifications from titles itself Adhoc: Changed Screenshot into toast notification and switched from being binded to F10 to F12
This commit is contained in:
parent
4abbfa0b04
commit
d6f0ab6a7e
|
@ -41,6 +41,7 @@
|
||||||
#include "xenia/ui/graphics_provider.h"
|
#include "xenia/ui/graphics_provider.h"
|
||||||
#include "xenia/ui/imgui_dialog.h"
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
#include "xenia/ui/imgui_drawer.h"
|
#include "xenia/ui/imgui_drawer.h"
|
||||||
|
#include "xenia/ui/imgui_host_notification.h"
|
||||||
#include "xenia/ui/immediate_drawer.h"
|
#include "xenia/ui/immediate_drawer.h"
|
||||||
#include "xenia/ui/presenter.h"
|
#include "xenia/ui/presenter.h"
|
||||||
#include "xenia/ui/ui_event.h"
|
#include "xenia/ui/ui_event.h"
|
||||||
|
@ -654,7 +655,7 @@ bool EmulatorWindow::Initialize() {
|
||||||
MenuItem::Create(MenuItem::Type::kString, "&Fullscreen", "F11",
|
MenuItem::Create(MenuItem::Type::kString, "&Fullscreen", "F11",
|
||||||
std::bind(&EmulatorWindow::ToggleFullscreen, this)));
|
std::bind(&EmulatorWindow::ToggleFullscreen, this)));
|
||||||
display_menu->AddChild(
|
display_menu->AddChild(
|
||||||
MenuItem::Create(MenuItem::Type::kString, "&Take Screenshot", "F10",
|
MenuItem::Create(MenuItem::Type::kString, "&Take Screenshot", "F12",
|
||||||
std::bind(&EmulatorWindow::TakeScreenshot, this)));
|
std::bind(&EmulatorWindow::TakeScreenshot, this)));
|
||||||
}
|
}
|
||||||
main_menu->AddChild(std::move(display_menu));
|
main_menu->AddChild(std::move(display_menu));
|
||||||
|
@ -840,7 +841,7 @@ void EmulatorWindow::OnKeyDown(ui::KeyEvent& e) {
|
||||||
case ui::VirtualKey::kF11: {
|
case ui::VirtualKey::kF11: {
|
||||||
ToggleFullscreen();
|
ToggleFullscreen();
|
||||||
} break;
|
} break;
|
||||||
case ui::VirtualKey::kF10: {
|
case ui::VirtualKey::kF12: {
|
||||||
TakeScreenshot();
|
TakeScreenshot();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -939,9 +940,13 @@ void EmulatorWindow::ExportScreenshot(const xe::ui::RawImage& image) {
|
||||||
std::string filename = fmt::format("{} - {}.png", title_id, datetime);
|
std::string filename = fmt::format("{} - {}.png", title_id, datetime);
|
||||||
SaveImage(screenshot_path / filename, image);
|
SaveImage(screenshot_path / filename, image);
|
||||||
|
|
||||||
xe::ui::ImGuiDialog::ShowMessageBox(
|
const std::string notification_text =
|
||||||
imgui_drawer_.get(), "Screenshot saved",
|
fmt::format("Screenshot saved: {}", filename);
|
||||||
fmt::format("Screenshot saved to {}", xe::path_to_utf8(screenshot_path)));
|
|
||||||
|
app_context_.CallInUIThread([&, notification_text]() {
|
||||||
|
new xe::ui::HostNotificationWindow(imgui_drawer(), "Screenshot Created!",
|
||||||
|
notification_text, 0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts a RawImage into a PNG file
|
// Converts a RawImage into a PNG file
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "xenia/gpu/graphics_system.h"
|
#include "xenia/gpu/graphics_system.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/ui/imgui_notification.h"
|
#include "xenia/ui/imgui_guest_notification.h"
|
||||||
|
|
||||||
DEFINE_bool(show_achievement_notification, false,
|
DEFINE_bool(show_achievement_notification, false,
|
||||||
"Show achievement notification on screen.", "UI");
|
"Show achievement notification on screen.", "UI");
|
||||||
|
|
|
@ -467,8 +467,22 @@ void ImGuiDrawer::Draw(UIDrawContext& ui_draw_context) {
|
||||||
dialog_loop_next_index_ = SIZE_MAX;
|
dialog_loop_next_index_ = SIZE_MAX;
|
||||||
|
|
||||||
if (!notifications_.empty()) {
|
if (!notifications_.empty()) {
|
||||||
// We only care about drawing next notification.
|
bool was_guest_notification_drawn = false;
|
||||||
notifications_.at(0)->Draw();
|
bool was_host_notification_drawn = false;
|
||||||
|
|
||||||
|
for (const auto& notification : notifications_) {
|
||||||
|
if (notification->GetNotificationType() == NotificationType::Guest &&
|
||||||
|
!was_guest_notification_drawn) {
|
||||||
|
was_guest_notification_drawn = true;
|
||||||
|
notification->Draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification->GetNotificationType() == NotificationType::Host &&
|
||||||
|
!was_host_notification_drawn) {
|
||||||
|
was_host_notification_drawn = true;
|
||||||
|
notification->Draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
|
#include "xenia/ui/imgui_guest_notification.h"
|
||||||
|
#include "xenia/ui/imgui_notification.h"
|
||||||
|
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
|
#include <playsoundapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DEFINE_string(notification_sound_path, "",
|
||||||
|
"Path (including filename) to selected notification sound. Sound "
|
||||||
|
"MUST be in wav format!",
|
||||||
|
"General");
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
ImGuiGuestNotification::ImGuiGuestNotification(ui::ImGuiDrawer* imgui_drawer,
|
||||||
|
std::string& title,
|
||||||
|
std::string& description,
|
||||||
|
uint8_t user_index,
|
||||||
|
uint8_t position_id)
|
||||||
|
: ImGuiNotification(imgui_drawer, NotificationType::Guest, title,
|
||||||
|
description, user_index, position_id),
|
||||||
|
current_stage_(NotificationStage::Awaiting),
|
||||||
|
notification_draw_progress_(0.0f) {
|
||||||
|
imgui_drawer->AddNotification(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiGuestNotification::~ImGuiGuestNotification() {}
|
||||||
|
|
||||||
|
void ImGuiGuestNotification::UpdateNotificationState() {
|
||||||
|
switch (current_stage_) {
|
||||||
|
case NotificationStage::Awaiting:
|
||||||
|
// TODO(Gliniak): Implement delayed notifications.
|
||||||
|
current_stage_ = NotificationStage::FazeIn;
|
||||||
|
notification_draw_progress_ = 0.2f;
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
|
if (!cvars::notification_sound_path.empty()) {
|
||||||
|
auto notification_sound_path = cvars::notification_sound_path;
|
||||||
|
if (std::filesystem::exists(notification_sound_path)) {
|
||||||
|
PlaySound(std::wstring(notification_sound_path.begin(),
|
||||||
|
notification_sound_path.end())
|
||||||
|
.c_str(),
|
||||||
|
NULL,
|
||||||
|
SND_FILENAME | SND_NODEFAULT | SND_NOSTOP | SND_ASYNC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NotificationStage::FazeIn: {
|
||||||
|
SetCreationTime(Clock::QueryHostUptimeMillis());
|
||||||
|
if (notification_draw_progress_ < 1.1f) {
|
||||||
|
notification_draw_progress_ += 0.02f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mimics a bit original console behaviour when it makes window a bit
|
||||||
|
// longer for few frames then decreases size
|
||||||
|
if (notification_draw_progress_ >= 1.1f) {
|
||||||
|
current_stage_ = NotificationStage::Present;
|
||||||
|
notification_draw_progress_ = 1.0f;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NotificationStage::Present:
|
||||||
|
if (IsNotificationClosingTime()) {
|
||||||
|
current_stage_ = NotificationStage::FazeOut;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NotificationStage::FazeOut: {
|
||||||
|
if (notification_draw_progress_ > 0.2f) {
|
||||||
|
notification_draw_progress_ -= 0.02f;
|
||||||
|
} else {
|
||||||
|
current_stage_ = NotificationStage::Finished;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImVec2 ImGuiGuestNotification::CalculateNotificationSize(ImVec2 text_size,
|
||||||
|
float scale) {
|
||||||
|
const ImVec2 result =
|
||||||
|
ImVec2(std::floorf((default_notification_icon_size.x +
|
||||||
|
default_notification_margin_size.x) *
|
||||||
|
scale) +
|
||||||
|
text_size.x,
|
||||||
|
std::floorf((default_notification_icon_size.y +
|
||||||
|
default_notification_margin_size.y) *
|
||||||
|
scale));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AchievementNotificationWindow::OnDraw(ImGuiIO& io) {
|
||||||
|
UpdateNotificationState();
|
||||||
|
|
||||||
|
if (IsNotificationExpired()) {
|
||||||
|
delete this;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string longest_notification_text_line =
|
||||||
|
GetTitle().size() > GetDescription().size() ? GetTitle().c_str()
|
||||||
|
: GetDescription().c_str();
|
||||||
|
|
||||||
|
const ImVec2 screen_size = io.DisplaySize;
|
||||||
|
const float window_scale =
|
||||||
|
std::fminf(screen_size.x / default_drawing_resolution.x,
|
||||||
|
screen_size.y / default_drawing_resolution.y);
|
||||||
|
const float font_scale = default_font_size / io.Fonts->Fonts[0]->FontSize;
|
||||||
|
const ImVec2 text_size = io.Fonts->Fonts[0]->CalcTextSizeA(
|
||||||
|
default_font_size * default_notification_text_scale * window_scale,
|
||||||
|
FLT_MAX, -1.0f, longest_notification_text_line.c_str());
|
||||||
|
|
||||||
|
const ImVec2 final_notification_size =
|
||||||
|
CalculateNotificationSize(text_size, window_scale);
|
||||||
|
|
||||||
|
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||||
|
screen_size, final_notification_size, GetPositionId());
|
||||||
|
|
||||||
|
if (isnan(notification_position.x) || isnan(notification_position.y)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 current_notification_size = final_notification_size;
|
||||||
|
current_notification_size.x *= notification_draw_progress_;
|
||||||
|
current_notification_size.x = std::floorf(current_notification_size.x);
|
||||||
|
|
||||||
|
// Initialize position and window size
|
||||||
|
ImGui::SetNextWindowSize(current_notification_size);
|
||||||
|
ImGui::SetNextWindowPos(notification_position);
|
||||||
|
|
||||||
|
// Set new window style before drawing window
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding,
|
||||||
|
default_notification_rounding * window_scale);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_WindowBg,
|
||||||
|
default_notification_background_color);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Separator,
|
||||||
|
default_notification_background_color);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Border, default_notification_border_color);
|
||||||
|
|
||||||
|
ImGui::Begin("Notification Window", NULL, NOTIFY_TOAST_FLAGS);
|
||||||
|
{
|
||||||
|
ImGui::SetWindowFontScale(default_notification_text_scale * font_scale *
|
||||||
|
window_scale);
|
||||||
|
// Set offset to image to prevent it from being right on border.
|
||||||
|
ImGui::SetCursorPos(ImVec2(final_notification_size.x * 0.005f,
|
||||||
|
final_notification_size.y * 0.05f));
|
||||||
|
// Elements of window
|
||||||
|
ImGui::Image(reinterpret_cast<ImTextureID>(
|
||||||
|
GetDrawer()->GetNotificationIcon(GetUserIndex())),
|
||||||
|
ImVec2(default_notification_icon_size.x * window_scale,
|
||||||
|
default_notification_icon_size.y * window_scale));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (notification_draw_progress_ > 0.5f) {
|
||||||
|
ImGui::TextColored(white_color, GetNotificationText().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Restore previous style
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleColor(3);
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_UI_IMGUI_GUEST_NOTIFICATION_H_
|
||||||
|
#define XENIA_UI_IMGUI_GUEST_NOTIFICATION_H_
|
||||||
|
|
||||||
|
#include "third_party/imgui/imgui.h"
|
||||||
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
|
#include "xenia/ui/imgui_notification.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
class ImGuiGuestNotification : public ImGuiNotification {
|
||||||
|
public:
|
||||||
|
ImGuiGuestNotification(ui::ImGuiDrawer* imgui_drawer, std::string& title,
|
||||||
|
std::string& description, uint8_t user_index,
|
||||||
|
uint8_t position_id = 0);
|
||||||
|
|
||||||
|
~ImGuiGuestNotification();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const ImVec2 default_notification_icon_size = ImVec2(58.0f, 58.0f);
|
||||||
|
const ImVec2 default_notification_margin_size = ImVec2(50.f, 5.f);
|
||||||
|
const float default_notification_text_scale = 2.3f;
|
||||||
|
const float default_notification_rounding = 30.f;
|
||||||
|
const float default_font_size = 12.f;
|
||||||
|
|
||||||
|
const ImVec4 white_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
const ImVec4 default_notification_background_color =
|
||||||
|
ImVec4(0.215f, 0.215f, 0.215f, 1.0f);
|
||||||
|
const ImVec4 default_notification_border_color = white_color;
|
||||||
|
|
||||||
|
enum class NotificationStage : uint8_t {
|
||||||
|
Awaiting = 0,
|
||||||
|
FazeIn = 1,
|
||||||
|
Present = 2,
|
||||||
|
FazeOut = 3,
|
||||||
|
Finished = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool IsNotificationExpired() {
|
||||||
|
return current_stage_ == NotificationStage::Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateNotificationState();
|
||||||
|
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
||||||
|
float scale) override;
|
||||||
|
|
||||||
|
virtual void OnDraw(ImGuiIO& io) {}
|
||||||
|
|
||||||
|
float notification_draw_progress_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NotificationStage current_stage_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AchievementNotificationWindow final : ImGuiGuestNotification {
|
||||||
|
public:
|
||||||
|
AchievementNotificationWindow(ui::ImGuiDrawer* imgui_drawer,
|
||||||
|
std::string title, std::string description,
|
||||||
|
uint8_t user_index, uint8_t position_id = 0)
|
||||||
|
: ImGuiGuestNotification(imgui_drawer, title, description, user_index,
|
||||||
|
position_id) {};
|
||||||
|
|
||||||
|
void OnDraw(ImGuiIO& io) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
|
#include "xenia/ui/imgui_host_notification.h"
|
||||||
|
#include "xenia/ui/imgui_notification.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
ImGuiHostNotification::ImGuiHostNotification(ui::ImGuiDrawer* imgui_drawer,
|
||||||
|
std::string& title,
|
||||||
|
std::string& description,
|
||||||
|
uint8_t user_index,
|
||||||
|
uint8_t position_id)
|
||||||
|
: ImGuiNotification(imgui_drawer, NotificationType::Host, title,
|
||||||
|
description, user_index, position_id) {
|
||||||
|
SetCreationTime(Clock::QueryHostUptimeMillis());
|
||||||
|
imgui_drawer->AddNotification(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHostNotification::~ImGuiHostNotification() {}
|
||||||
|
|
||||||
|
const ImVec2 ImGuiHostNotification::CalculateNotificationSize(ImVec2 text_size,
|
||||||
|
float scale) {
|
||||||
|
const ImVec2 result = ImVec2(text_size.x * scale, text_size.y * scale);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostNotificationWindow::OnDraw(ImGuiIO& io) {
|
||||||
|
if (IsNotificationClosingTime()) {
|
||||||
|
delete this;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string longest_notification_text_line =
|
||||||
|
GetTitle().size() > GetDescription().size() ? GetTitle().c_str()
|
||||||
|
: GetDescription().c_str();
|
||||||
|
|
||||||
|
const ImVec2 screen_size = io.DisplaySize;
|
||||||
|
const float window_scale =
|
||||||
|
std::fminf(screen_size.x / default_drawing_resolution.x,
|
||||||
|
screen_size.y / default_drawing_resolution.y);
|
||||||
|
const float font_size = io.Fonts->Fonts[0]->FontSize;
|
||||||
|
const ImVec2 text_size = io.Fonts->Fonts[0]->CalcTextSizeA(
|
||||||
|
font_size * window_scale, FLT_MAX, -1.0f,
|
||||||
|
longest_notification_text_line.c_str());
|
||||||
|
|
||||||
|
const ImVec2 notification_size =
|
||||||
|
CalculateNotificationSize(text_size, window_scale);
|
||||||
|
|
||||||
|
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
||||||
|
screen_size, notification_size, GetPositionId());
|
||||||
|
|
||||||
|
if (isnan(notification_position.x) || isnan(notification_position.y)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(notification_position);
|
||||||
|
|
||||||
|
ImGui::Begin("Notification Window", NULL, NOTIFY_TOAST_FLAGS);
|
||||||
|
{
|
||||||
|
ImGui::SetWindowFontScale(window_scale);
|
||||||
|
|
||||||
|
ImGui::Text(GetTitle().c_str());
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text(GetDescription().c_str());
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* Xenia : Xbox 360 Emulator Research Project *
|
||||||
|
******************************************************************************
|
||||||
|
* Copyright 2023 Ben Vanik. All rights reserved. *
|
||||||
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XENIA_UI_IMGUI_HOST_NOTIFICATION_H_
|
||||||
|
#define XENIA_UI_IMGUI_HOST_NOTIFICATION_H_
|
||||||
|
|
||||||
|
#include "third_party/imgui/imgui.h"
|
||||||
|
#include "xenia/ui/imgui_dialog.h"
|
||||||
|
#include "xenia/ui/imgui_drawer.h"
|
||||||
|
#include "xenia/ui/imgui_notification.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
class ImGuiHostNotification : public ImGuiNotification {
|
||||||
|
public:
|
||||||
|
ImGuiHostNotification(ui::ImGuiDrawer* imgui_drawer, std::string& title,
|
||||||
|
std::string& description, uint8_t user_index,
|
||||||
|
uint8_t position_id = 10);
|
||||||
|
|
||||||
|
~ImGuiHostNotification();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
||||||
|
float scale) override;
|
||||||
|
|
||||||
|
virtual void OnDraw(ImGuiIO& io) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HostNotificationWindow final : ImGuiHostNotification {
|
||||||
|
public:
|
||||||
|
HostNotificationWindow(ui::ImGuiDrawer* imgui_drawer, std::string title,
|
||||||
|
std::string description, uint8_t user_index,
|
||||||
|
uint8_t position_id = 10)
|
||||||
|
: ImGuiHostNotification(imgui_drawer, title, description, user_index,
|
||||||
|
position_id) {};
|
||||||
|
|
||||||
|
void OnDraw(ImGuiIO& io) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
|
#endif
|
|
@ -14,19 +14,29 @@
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/ui/imgui_notification.h"
|
#include "xenia/ui/imgui_notification.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_WIN32
|
|
||||||
#include <playsoundapi.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DEFINE_string(notification_sound_path, "",
|
|
||||||
"Path (including filename) to selected notification sound. Sound "
|
|
||||||
"MUST be in wav format!",
|
|
||||||
"General");
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
const NotificationAlignment GetNotificationAlignment(
|
ImGuiNotification::ImGuiNotification(ui::ImGuiDrawer* imgui_drawer,
|
||||||
|
NotificationType notification_type,
|
||||||
|
std::string& title,
|
||||||
|
std::string& description,
|
||||||
|
uint8_t user_index, uint8_t position_id)
|
||||||
|
: imgui_drawer_(imgui_drawer),
|
||||||
|
notification_type_(notification_type),
|
||||||
|
title_(title),
|
||||||
|
description_(description),
|
||||||
|
user_index_(user_index),
|
||||||
|
position_(position_id),
|
||||||
|
creation_time_(0) {}
|
||||||
|
|
||||||
|
ImGuiNotification::~ImGuiNotification() {
|
||||||
|
imgui_drawer_->RemoveNotification(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiNotification::Draw() { OnDraw(imgui_drawer_->GetIO()); }
|
||||||
|
|
||||||
|
const NotificationAlignment ImGuiNotification::GetNotificationAlignment(
|
||||||
const uint8_t notification_position_id) {
|
const uint8_t notification_position_id) {
|
||||||
NotificationAlignment alignment = NotificationAlignment::kAlignUnknown;
|
NotificationAlignment alignment = NotificationAlignment::kAlignUnknown;
|
||||||
|
|
||||||
|
@ -49,7 +59,7 @@ const NotificationAlignment GetNotificationAlignment(
|
||||||
return alignment;
|
return alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImVec2 CalculateNotificationScreenPosition(
|
const ImVec2 ImGuiNotification::CalculateNotificationScreenPosition(
|
||||||
ImVec2 screen_size, ImVec2 window_size, uint8_t notification_position_id) {
|
ImVec2 screen_size, ImVec2 window_size, uint8_t notification_position_id) {
|
||||||
ImVec2 result = {NAN, NAN};
|
ImVec2 result = {NAN, NAN};
|
||||||
|
|
||||||
|
@ -88,163 +98,5 @@ const ImVec2 CalculateNotificationScreenPosition(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImVec2 CalculateNotificationSize(ImVec2 text_size, float scale) {
|
|
||||||
const ImVec2 result =
|
|
||||||
ImVec2(std::floorf((default_notification_icon_size.x +
|
|
||||||
default_notification_margin_size.x) *
|
|
||||||
scale) +
|
|
||||||
text_size.x,
|
|
||||||
std::floorf((default_notification_icon_size.y +
|
|
||||||
default_notification_margin_size.y) *
|
|
||||||
scale));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiNotification::ImGuiNotification(ui::ImGuiDrawer* imgui_drawer,
|
|
||||||
std::string title, std::string description,
|
|
||||||
uint8_t user_index, uint8_t position_id)
|
|
||||||
: imgui_drawer_(imgui_drawer),
|
|
||||||
title_(title),
|
|
||||||
description_(description),
|
|
||||||
user_index_(user_index),
|
|
||||||
position_(position_id),
|
|
||||||
creation_time_(0),
|
|
||||||
current_stage_(NotificationStage::kAwaiting),
|
|
||||||
notification_draw_progress_(0.0f) {
|
|
||||||
imgui_drawer->AddNotification(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiNotification::~ImGuiNotification() {
|
|
||||||
imgui_drawer_->RemoveNotification(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGuiNotification::Draw() { OnDraw(imgui_drawer_->GetIO()); }
|
|
||||||
|
|
||||||
void ImGuiNotification::UpdateNotificationState() {
|
|
||||||
switch (current_stage_) {
|
|
||||||
case NotificationStage::kAwaiting:
|
|
||||||
// TODO(Gliniak): Implement delayed notifications.
|
|
||||||
current_stage_ = NotificationStage::kFazeIn;
|
|
||||||
notification_draw_progress_ = 0.2f;
|
|
||||||
#if XE_PLATFORM_WIN32
|
|
||||||
if (!cvars::notification_sound_path.empty()) {
|
|
||||||
auto notification_sound_path = cvars::notification_sound_path;
|
|
||||||
if (std::filesystem::exists(notification_sound_path)) {
|
|
||||||
PlaySound(std::wstring(notification_sound_path.begin(),
|
|
||||||
notification_sound_path.end())
|
|
||||||
.c_str(),
|
|
||||||
NULL,
|
|
||||||
SND_FILENAME | SND_NODEFAULT | SND_NOSTOP | SND_ASYNC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
break;
|
|
||||||
case NotificationStage::kFazeIn: {
|
|
||||||
creation_time_ = Clock::QueryHostUptimeMillis();
|
|
||||||
if (notification_draw_progress_ < 1.1f) {
|
|
||||||
notification_draw_progress_ += 0.02f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mimics a bit original console behaviour when it makes window a bit
|
|
||||||
// longer for few frames then decreases size
|
|
||||||
if (notification_draw_progress_ >= 1.1f) {
|
|
||||||
current_stage_ = NotificationStage::kPresent;
|
|
||||||
notification_draw_progress_ = 1.0f;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NotificationStage::kPresent:
|
|
||||||
if (IsNotificationClosingTime()) {
|
|
||||||
current_stage_ = NotificationStage::kFazeOut;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NotificationStage::kFazeOut: {
|
|
||||||
if (notification_draw_progress_ > 0.2f) {
|
|
||||||
notification_draw_progress_ -= 0.02f;
|
|
||||||
} else {
|
|
||||||
current_stage_ = NotificationStage::kFinished;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AchievementNotificationWindow::OnDraw(ImGuiIO& io) {
|
|
||||||
UpdateNotificationState();
|
|
||||||
|
|
||||||
if (IsNotificationExpired()) {
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string longest_notification_text_line =
|
|
||||||
GetTitle().size() > GetDescription().size() ? GetTitle().c_str()
|
|
||||||
: GetDescription().c_str();
|
|
||||||
|
|
||||||
const ImVec2 screen_size = io.DisplaySize;
|
|
||||||
const float window_scale =
|
|
||||||
std::fminf(screen_size.x / default_drawing_resolution.x,
|
|
||||||
screen_size.y / default_drawing_resolution.y);
|
|
||||||
const float font_scale = default_font_size / io.Fonts->Fonts[0]->FontSize;
|
|
||||||
const ImVec2 text_size = io.Fonts->Fonts[0]->CalcTextSizeA(
|
|
||||||
default_font_size * default_notification_text_scale * window_scale,
|
|
||||||
FLT_MAX, -1.0f, longest_notification_text_line.c_str());
|
|
||||||
|
|
||||||
const ImVec2 final_notification_size =
|
|
||||||
CalculateNotificationSize(text_size, window_scale);
|
|
||||||
|
|
||||||
const ImVec2 notification_position = CalculateNotificationScreenPosition(
|
|
||||||
screen_size, final_notification_size, GetPositionId());
|
|
||||||
|
|
||||||
if (isnan(notification_position.x) || isnan(notification_position.y)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImVec2 current_notification_size = final_notification_size;
|
|
||||||
current_notification_size.x *= notification_draw_progress_;
|
|
||||||
current_notification_size.x = std::floorf(current_notification_size.x);
|
|
||||||
|
|
||||||
// Initialize position and window size
|
|
||||||
ImGui::SetNextWindowSize(current_notification_size);
|
|
||||||
ImGui::SetNextWindowPos(notification_position);
|
|
||||||
|
|
||||||
// Set new window style before drawing window
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding,
|
|
||||||
default_notification_rounding * window_scale);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_WindowBg,
|
|
||||||
default_notification_background_color);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Separator,
|
|
||||||
default_notification_background_color);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Border, default_notification_border_color);
|
|
||||||
|
|
||||||
ImGui::Begin("Notification Window", NULL, NOTIFY_TOAST_FLAGS);
|
|
||||||
{
|
|
||||||
ImGui::SetWindowFontScale(default_notification_text_scale * font_scale *
|
|
||||||
window_scale);
|
|
||||||
// Set offset to image to prevent it from being right on border.
|
|
||||||
ImGui::SetCursorPos(ImVec2(final_notification_size.x * 0.005f,
|
|
||||||
final_notification_size.y * 0.05f));
|
|
||||||
// Elements of window
|
|
||||||
ImGui::Image(reinterpret_cast<ImTextureID>(
|
|
||||||
GetDrawer()->GetNotificationIcon(GetUserIndex())),
|
|
||||||
ImVec2(default_notification_icon_size.x * window_scale,
|
|
||||||
default_notification_icon_size.y * window_scale));
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (notification_draw_progress_ > 0.5f) {
|
|
||||||
ImGui::TextColored(white_color, GetNotificationText().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Restore previous style
|
|
||||||
ImGui::PopStyleVar();
|
|
||||||
ImGui::PopStyleColor(3);
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
|
@ -23,18 +23,6 @@
|
||||||
// Parameters based on 1280x720 resolution
|
// Parameters based on 1280x720 resolution
|
||||||
constexpr ImVec2 default_drawing_resolution = ImVec2(1280.f, 720.f);
|
constexpr ImVec2 default_drawing_resolution = ImVec2(1280.f, 720.f);
|
||||||
|
|
||||||
constexpr ImVec2 default_notification_icon_size = ImVec2(58.0f, 58.0f);
|
|
||||||
constexpr ImVec2 default_notification_margin_size = ImVec2(50.f, 5.f);
|
|
||||||
constexpr float default_notification_text_scale = 2.3f;
|
|
||||||
constexpr float default_notification_rounding = 30.f;
|
|
||||||
constexpr float default_font_size = 12.f;
|
|
||||||
|
|
||||||
constexpr ImVec4 white_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
||||||
|
|
||||||
constexpr ImVec4 default_notification_background_color =
|
|
||||||
ImVec4(0.215f, 0.215f, 0.215f, 1.0f);
|
|
||||||
constexpr ImVec4 default_notification_border_color = white_color;
|
|
||||||
|
|
||||||
enum class NotificationAlignment : uint8_t {
|
enum class NotificationAlignment : uint8_t {
|
||||||
kAlignLeft = 0,
|
kAlignLeft = 0,
|
||||||
kAlignRight = 1,
|
kAlignRight = 1,
|
||||||
|
@ -56,37 +44,32 @@ const static std::vector<ImVec2> notification_position_id_screen_offset = {
|
||||||
{0.93f, 0.80f} // RIGHT-BOTTOM - 10
|
{0.93f, 0.80f} // RIGHT-BOTTOM - 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class NotificationType : uint8_t { Guest = 0, Host = 1 };
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
class ImGuiNotification {
|
class ImGuiNotification {
|
||||||
public:
|
public:
|
||||||
ImGuiNotification(ui::ImGuiDrawer* imgui_drawer, std::string title,
|
ImGuiNotification(ui::ImGuiDrawer* imgui_drawer,
|
||||||
std::string description, uint8_t user_index,
|
NotificationType notification_type, std::string& title,
|
||||||
|
std::string& description, uint8_t user_index,
|
||||||
uint8_t position_id = 0);
|
uint8_t position_id = 0);
|
||||||
|
|
||||||
~ImGuiNotification();
|
~ImGuiNotification();
|
||||||
|
|
||||||
void Draw();
|
void Draw();
|
||||||
|
|
||||||
protected:
|
const NotificationType GetNotificationType() const {
|
||||||
enum class NotificationStage : uint8_t {
|
return notification_type_;
|
||||||
kAwaiting = 0,
|
}
|
||||||
kFazeIn = 1,
|
|
||||||
kPresent = 2,
|
|
||||||
kFazeOut = 3,
|
|
||||||
kFinished = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
|
protected:
|
||||||
ImGuiDrawer* GetDrawer() { return imgui_drawer_; }
|
ImGuiDrawer* GetDrawer() { return imgui_drawer_; }
|
||||||
|
|
||||||
const bool IsNotificationClosingTime() {
|
const bool IsNotificationClosingTime() {
|
||||||
return Clock::QueryHostUptimeMillis() - creation_time_ > time_to_close_;
|
return Clock::QueryHostUptimeMillis() - creation_time_ > time_to_close_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool IsNotificationExpired() {
|
|
||||||
return current_stage_ == NotificationStage::kFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string GetNotificationText() {
|
const std::string GetNotificationText() {
|
||||||
std::string text = title_;
|
std::string text = title_;
|
||||||
|
|
||||||
|
@ -96,20 +79,30 @@ class ImGuiNotification {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetCreationTime(uint64_t new_creation_time) {
|
||||||
|
creation_time_ = new_creation_time;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string GetTitle() { return title_; }
|
const std::string GetTitle() { return title_; }
|
||||||
const std::string GetDescription() { return description_; }
|
const std::string GetDescription() { return description_; }
|
||||||
|
|
||||||
const uint8_t GetPositionId() { return position_; }
|
const uint8_t GetPositionId() { return position_; }
|
||||||
const uint8_t GetUserIndex() { return user_index_; }
|
const uint8_t GetUserIndex() { return user_index_; }
|
||||||
|
|
||||||
void UpdateNotificationState();
|
const NotificationAlignment GetNotificationAlignment(
|
||||||
|
const uint8_t notification_position_id);
|
||||||
|
|
||||||
|
const ImVec2 CalculateNotificationScreenPosition(
|
||||||
|
ImVec2 screen_size, ImVec2 window_size, uint8_t notification_position_id);
|
||||||
|
|
||||||
|
virtual const ImVec2 CalculateNotificationSize(ImVec2 text_size,
|
||||||
|
float scale) = 0;
|
||||||
|
|
||||||
virtual void OnDraw(ImGuiIO& io) {}
|
virtual void OnDraw(ImGuiIO& io) {}
|
||||||
|
|
||||||
float notification_draw_progress_;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NotificationStage current_stage_;
|
NotificationType notification_type_;
|
||||||
|
|
||||||
uint8_t position_;
|
uint8_t position_;
|
||||||
uint8_t user_index_;
|
uint8_t user_index_;
|
||||||
|
|
||||||
|
@ -124,17 +117,6 @@ class ImGuiNotification {
|
||||||
ImGuiDrawer* imgui_drawer_ = nullptr;
|
ImGuiDrawer* imgui_drawer_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AchievementNotificationWindow final : ImGuiNotification {
|
|
||||||
public:
|
|
||||||
AchievementNotificationWindow(ui::ImGuiDrawer* imgui_drawer,
|
|
||||||
std::string title, std::string description,
|
|
||||||
uint8_t user_index, uint8_t position_id = 0)
|
|
||||||
: ImGuiNotification(imgui_drawer, title, description, user_index,
|
|
||||||
position_id) {};
|
|
||||||
|
|
||||||
void OnDraw(ImGuiIO& io) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue