mirror of
https://github.com/stenzek/duckstation.git
synced 2025-07-20 17:10:08 +00:00
FullscreenUI: Add modal progress callback
This commit is contained in:
parent
c922893b6f
commit
a9862461f3
@ -19,6 +19,7 @@
|
||||
#include "common/timer.h"
|
||||
|
||||
#include "core/fullscreen_ui.h" // For updating run idle state.
|
||||
#include "core/gpu_thread.h" // For kicking to GPU thread.
|
||||
#include "core/host.h"
|
||||
#include "core/system.h" // For async workers, should be in general host.
|
||||
|
||||
@ -192,6 +193,38 @@ private:
|
||||
InputStringDialogCallback m_callback;
|
||||
};
|
||||
|
||||
class ProgressDialog : public PopupDialog
|
||||
{
|
||||
public:
|
||||
ProgressDialog();
|
||||
~ProgressDialog();
|
||||
|
||||
std::unique_ptr<ProgressCallback> GetProgressCallback(std::string title, float window_unscaled_width);
|
||||
|
||||
void Draw();
|
||||
|
||||
private:
|
||||
class ProgressCallbackImpl : public ProgressCallback
|
||||
{
|
||||
public:
|
||||
ProgressCallbackImpl();
|
||||
~ProgressCallbackImpl() override;
|
||||
|
||||
void SetStatusText(std::string_view text) override;
|
||||
void SetProgressRange(u32 range) override;
|
||||
void SetProgressValue(u32 value) override;
|
||||
void SetCancellable(bool cancellable) override;
|
||||
bool IsCancelled() const override;
|
||||
};
|
||||
|
||||
std::string m_status_text;
|
||||
float m_last_frac = 0.0f;
|
||||
float m_width = 0.0f;
|
||||
u32 m_progress_value = 0;
|
||||
u32 m_progress_range = 0;
|
||||
std::atomic_bool m_cancelled{false};
|
||||
};
|
||||
|
||||
class FixedPopupDialog : public PopupDialog
|
||||
{
|
||||
public:
|
||||
@ -238,6 +271,7 @@ struct ALIGN_TO_CACHE_LINE UIState
|
||||
FileSelectorDialog file_selector_dialog;
|
||||
InputStringDialog input_string_dialog;
|
||||
FixedPopupDialog fixed_popup_dialog;
|
||||
ProgressDialog progress_dialog;
|
||||
|
||||
ImAnimatedVec2 menu_button_frame_min_animated;
|
||||
ImAnimatedVec2 menu_button_frame_max_animated;
|
||||
@ -709,6 +743,7 @@ void ImGuiFullscreen::EndLayout()
|
||||
s_state.choice_dialog.Draw();
|
||||
s_state.file_selector_dialog.Draw();
|
||||
s_state.input_string_dialog.Draw();
|
||||
s_state.progress_dialog.Draw();
|
||||
|
||||
DrawFullscreenFooter();
|
||||
|
||||
@ -2863,7 +2898,7 @@ bool ImGuiFullscreen::PopupDialog::BeginRender(float scaled_window_padding /* =
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
(m_title.starts_with("##") ? ImGuiWindowFlags_NoTitleBar : 0);
|
||||
bool is_open = true;
|
||||
if (popup_open && !ImGui::Begin(m_title.c_str(), &is_open, window_flags))
|
||||
if (popup_open && !ImGui::Begin(m_title.c_str(), m_user_closeable ? &is_open : nullptr, window_flags))
|
||||
is_open = false;
|
||||
|
||||
if (popup_open && !is_open && m_state != State::ClosingTrigger)
|
||||
@ -3499,6 +3534,180 @@ void ImGuiFullscreen::CloseMessageDialog()
|
||||
s_state.message_dialog.StartClose();
|
||||
}
|
||||
|
||||
ImGuiFullscreen::ProgressDialog::ProgressDialog() = default;
|
||||
ImGuiFullscreen::ProgressDialog::~ProgressDialog() = default;
|
||||
|
||||
void ImGuiFullscreen::ProgressDialog::Draw()
|
||||
{
|
||||
if (!IsOpen())
|
||||
return;
|
||||
|
||||
const float window_padding = LayoutScale(20.0f);
|
||||
|
||||
if (!BeginRender(window_padding, window_padding, ImVec2(m_width, 0.0f)))
|
||||
{
|
||||
if (m_user_closeable)
|
||||
m_cancelled.store(true, std::memory_order_release);
|
||||
|
||||
m_status_text = {};
|
||||
m_last_frac = 0.0f;
|
||||
ClearState();
|
||||
return;
|
||||
}
|
||||
|
||||
const float spacing = LayoutScale(5.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, LayoutScale(6.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, spacing));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, DarkerColor(UIStyle.PopupBackgroundColor));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, UIStyle.SecondaryColor);
|
||||
ImGui::PushFont(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.NormalFontWeight);
|
||||
|
||||
const bool has_progress = (m_progress_range > 0);
|
||||
float wrap_width = ImGui::GetContentRegionAvail().x;
|
||||
if (has_progress)
|
||||
{
|
||||
// reserve space for text
|
||||
TinyString text;
|
||||
text.format("{}/{}", m_progress_value, m_progress_range);
|
||||
|
||||
const ImVec2 text_width = ImGui::CalcTextSize(IMSTR_START_END(text));
|
||||
const ImVec2 screen_pos = ImGui::GetCursorScreenPos();
|
||||
const ImVec2 text_pos = ImVec2(screen_pos.x + wrap_width - text_width.x, screen_pos.y);
|
||||
ImGui::GetWindowDrawList()->AddText(UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, text_pos,
|
||||
ImGui::GetColorU32(ImGuiCol_Text), IMSTR_START_END(text));
|
||||
wrap_width -= text_width.x + spacing;
|
||||
}
|
||||
|
||||
if (!m_status_text.empty())
|
||||
ImGuiFullscreen::TextAlignedMultiLine(0.0f, IMSTR_START_END(m_status_text), wrap_width);
|
||||
|
||||
const float bar_height = LayoutScale(20.0f);
|
||||
|
||||
float frac;
|
||||
if (has_progress)
|
||||
{
|
||||
const float max_frac = (static_cast<float>(m_progress_value) / static_cast<float>(m_progress_range));
|
||||
const float dt = ImGui::GetIO().DeltaTime;
|
||||
frac = std::min(m_last_frac + dt, max_frac);
|
||||
m_last_frac = frac;
|
||||
}
|
||||
else
|
||||
{
|
||||
frac = static_cast<float>(-ImGui::GetTime());
|
||||
}
|
||||
ImGui::ProgressBar(frac, ImVec2(-1.0f, bar_height), "");
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, LayoutScale(5.0f)));
|
||||
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
if (m_user_closeable)
|
||||
{
|
||||
BeginHorizontalMenuButtons(1, 150.0f);
|
||||
if (HorizontalMenuButton("Cancel"))
|
||||
StartClose();
|
||||
EndHorizontalMenuButtons();
|
||||
}
|
||||
|
||||
EndRender();
|
||||
}
|
||||
|
||||
std::unique_ptr<ProgressCallback> ImGuiFullscreen::ProgressDialog::GetProgressCallback(std::string title, float window_unscaled_width)
|
||||
{
|
||||
if (m_state == PopupDialog::State::Open)
|
||||
{
|
||||
// return dummy callback so the op can still go through
|
||||
ERROR_LOG("Progress dialog is already open, cannot create dialog for '{}'.", std::move(title));
|
||||
return std::make_unique<ProgressCallback>();
|
||||
}
|
||||
|
||||
SetTitleAndOpen(std::move(title));
|
||||
m_width = LayoutScale(window_unscaled_width);
|
||||
m_last_frac = 0.0f;
|
||||
m_cancelled.store(false, std::memory_order_release);
|
||||
return std::make_unique<ProgressCallbackImpl>();
|
||||
}
|
||||
|
||||
ImGuiFullscreen::ProgressDialog::ProgressCallbackImpl::ProgressCallbackImpl() = default;
|
||||
|
||||
ImGuiFullscreen::ProgressDialog::ProgressCallbackImpl::~ProgressCallbackImpl()
|
||||
{
|
||||
Host::RunOnCPUThread([]() mutable {
|
||||
GPUThread::RunOnThread([]() mutable {
|
||||
if (!s_state.progress_dialog.IsOpen())
|
||||
return;
|
||||
s_state.progress_dialog.StartClose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::ProgressDialog::ProgressCallbackImpl::SetStatusText(std::string_view text)
|
||||
{
|
||||
Host::RunOnCPUThread([text = std::string(text)]() mutable {
|
||||
GPUThread::RunOnThread([text = std::move(text)]() mutable {
|
||||
if (!s_state.progress_dialog.IsOpen())
|
||||
return;
|
||||
|
||||
s_state.progress_dialog.m_status_text = std::move(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::ProgressDialog::ProgressCallbackImpl::SetProgressRange(u32 range)
|
||||
{
|
||||
ProgressCallback::SetProgressRange(range);
|
||||
|
||||
Host::RunOnCPUThread([range]() {
|
||||
GPUThread::RunOnThread([range]() {
|
||||
if (!s_state.progress_dialog.IsOpen())
|
||||
return;
|
||||
|
||||
s_state.progress_dialog.m_progress_range = range;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::ProgressDialog::ProgressCallbackImpl::SetProgressValue(u32 value)
|
||||
{
|
||||
ProgressCallback::SetProgressValue(value);
|
||||
|
||||
Host::RunOnCPUThread([value]() {
|
||||
GPUThread::RunOnThread([value]() {
|
||||
if (!s_state.progress_dialog.IsOpen())
|
||||
return;
|
||||
|
||||
s_state.progress_dialog.m_progress_value = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::ProgressDialog::ProgressCallbackImpl::SetCancellable(bool cancellable)
|
||||
{
|
||||
ProgressCallback::SetCancellable(cancellable);
|
||||
|
||||
Host::RunOnCPUThread([cancellable]() {
|
||||
GPUThread::RunOnThread([cancellable]() {
|
||||
if (!s_state.progress_dialog.IsOpen())
|
||||
return;
|
||||
|
||||
s_state.progress_dialog.m_user_closeable = cancellable;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool ImGuiFullscreen::ProgressDialog::ProgressCallbackImpl::IsCancelled() const
|
||||
{
|
||||
return s_state.progress_dialog.m_cancelled.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
std::unique_ptr<ProgressCallback> ImGuiFullscreen::OpenModalProgressDialog(std::string title, float window_unscaled_width)
|
||||
{
|
||||
return s_state.progress_dialog.GetProgressCallback(std::move(title), window_unscaled_width);
|
||||
}
|
||||
|
||||
static float s_notification_vertical_position = 0.15f;
|
||||
static float s_notification_vertical_direction = 1.0f;
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
class Image;
|
||||
class GPUTexture;
|
||||
class SmallStringBase;
|
||||
class ProgressCallback;
|
||||
|
||||
namespace ImGuiFullscreen {
|
||||
|
||||
@ -410,6 +411,8 @@ void OpenMessageDialog(std::string_view title, std::string message, MessageDialo
|
||||
std::string first_button_text, std::string second_button_text, std::string third_button_text);
|
||||
void CloseMessageDialog();
|
||||
|
||||
std::unique_ptr<ProgressCallback> OpenModalProgressDialog(std::string title, float window_unscaled_width = 500.0f);
|
||||
|
||||
float GetNotificationVerticalPosition();
|
||||
float GetNotificationVerticalDirection();
|
||||
void SetNotificationVerticalPosition(float position, float direction);
|
||||
@ -459,7 +462,7 @@ public:
|
||||
void ClearState();
|
||||
|
||||
protected:
|
||||
enum class State
|
||||
enum class State : u8
|
||||
{
|
||||
Inactive,
|
||||
ClosingTrigger,
|
||||
@ -482,6 +485,7 @@ protected:
|
||||
std::string m_title;
|
||||
float m_animation_time_remaining = 0.0f;
|
||||
State m_state = State::Inactive;
|
||||
bool m_user_closeable = true;
|
||||
};
|
||||
|
||||
// Wrapper for computing menu button bounds.
|
||||
|
Loading…
x
Reference in New Issue
Block a user