Changeset View
Changeset View
Standalone View
Standalone View
source/gui/GUIManager.cpp
Show First 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | |||||
CGUIManager::~CGUIManager() | CGUIManager::~CGUIManager() | ||||
{ | { | ||||
UnregisterFileReloadFunc(ReloadChangedFileCB, this); | UnregisterFileReloadFunc(ReloadChangedFileCB, this); | ||||
} | } | ||||
size_t CGUIManager::GetPageCount() const | size_t CGUIManager::GetPageCount() const | ||||
{ | { | ||||
return m_PageStack.size(); | if (m_NextPages.has_value()) | ||||
return m_NextPages->size(); | |||||
return m_CurrentPages.size(); | |||||
} | } | ||||
void CGUIManager::SwitchPage(const CStrW& pageName, const ScriptInterface* srcScriptInterface, JS::HandleValue initData) | void CGUIManager::SwitchPage(const CStrW& pageName, const ScriptInterface* srcScriptInterface, JS::HandleValue initData) | ||||
{ | { | ||||
MakeNextPages(); | |||||
// The page stack is cleared (including the script context where initData came from), | // The page stack is cleared (including the script context where initData came from), | ||||
// therefore we have to clone initData. | // therefore we have to clone initData. | ||||
Script::StructuredClone initDataClone; | Script::StructuredClone initDataClone; | ||||
if (!initData.isUndefined()) | if (!initData.isUndefined()) | ||||
{ | { | ||||
ScriptRequest rq(srcScriptInterface); | ScriptRequest rq(srcScriptInterface); | ||||
initDataClone = Script::WriteStructuredClone(rq, initData); | initDataClone = Script::WriteStructuredClone(rq, initData); | ||||
} | } | ||||
if (!m_PageStack.empty()) | if (!m_NextPages->empty()) | ||||
{ | { | ||||
// Make sure we unfocus anything on the current page. | // Make sure we unfocus anything on the current page. | ||||
m_PageStack.back().gui->SendFocusMessage(GUIM_LOST_FOCUS); | m_NextPages->back().gui->SendFocusMessage(GUIM_LOST_FOCUS); | ||||
m_PageStack.clear(); | m_NextPages->clear(); | ||||
} | } | ||||
PushPage(pageName, initDataClone, JS::UndefinedHandleValue); | PushPage(pageName, initDataClone, JS::UndefinedHandleValue); | ||||
} | } | ||||
void CGUIManager::PushPage(const CStrW& pageName, Script::StructuredClone initData, JS::HandleValue callbackFunction) | void CGUIManager::PushPage(const CStrW& pageName, Script::StructuredClone initData, JS::HandleValue callbackFunction) | ||||
{ | { | ||||
MakeNextPages(); | |||||
// Store the callback handler in the current GUI page before opening the new one | // Store the callback handler in the current GUI page before opening the new one | ||||
if (!m_PageStack.empty() && !callbackFunction.isUndefined()) | if (!m_NextPages->empty() && !callbackFunction.isUndefined()) | ||||
{ | { | ||||
m_PageStack.back().SetCallbackFunction(*m_ScriptInterface, callbackFunction); | m_NextPages->back().SetCallbackFunction(*m_ScriptInterface, callbackFunction); | ||||
// Make sure we unfocus anything on the current page. | // Make sure we unfocus anything on the current page. | ||||
m_PageStack.back().gui->SendFocusMessage(GUIM_LOST_FOCUS); | m_NextPages->back().gui->SendFocusMessage(GUIM_LOST_FOCUS); | ||||
} | } | ||||
// Push the page prior to loading its contents, because that may push | // Push the page prior to loading its contents, because that may push | ||||
// another GUI page on init which should be pushed on top of this new page. | // another GUI page on init which should be pushed on top of this new page. | ||||
m_PageStack.emplace_back(pageName, initData); | m_NextPages->emplace_back(pageName, initData); | ||||
m_PageStack.back().LoadPage(m_ScriptContext); | m_NextPages->back().LoadPage(m_ScriptContext); | ||||
} | } | ||||
void CGUIManager::PopPage(Script::StructuredClone args) | void CGUIManager::PopPage(Script::StructuredClone args) | ||||
{ | { | ||||
if (m_PageStack.size() < 2) | MakeNextPages(); | ||||
if (m_NextPages->size() < 2) | |||||
{ | { | ||||
debug_warn(L"Tried to pop GUI page when there's < 2 in the stack"); | debug_warn(L"Tried to pop GUI page when there's < 2 in the stack"); | ||||
return; | return; | ||||
} | } | ||||
// Make sure we unfocus anything on the current page. | // Make sure we unfocus anything on the current page. | ||||
m_PageStack.back().gui->SendFocusMessage(GUIM_LOST_FOCUS); | m_NextPages->back().gui->SendFocusMessage(GUIM_LOST_FOCUS); | ||||
m_PageStack.pop_back(); | m_NextPages->pop_back(); | ||||
m_PageStack.back().PerformCallbackFunction(args); | m_NextPages->back().PerformCallbackFunction(args); | ||||
// We return to a page where some object might have been focused. | // We return to a page where some object might have been focused. | ||||
m_PageStack.back().gui->SendFocusMessage(GUIM_GOT_FOCUS); | m_NextPages->back().gui->SendFocusMessage(GUIM_GOT_FOCUS); | ||||
} | } | ||||
CGUIManager::SGUIPage::SGUIPage(const CStrW& pageName, const Script::StructuredClone initData) | CGUIManager::SGUIPage::SGUIPage(const CStrW& pageName, const Script::StructuredClone initData) | ||||
: m_Name(pageName), initData(initData), inputs(), gui(), callbackFunction() | : m_Name(pageName), initData(initData), inputs(), gui(), callbackFunction() | ||||
{ | { | ||||
} | } | ||||
void CGUIManager::SGUIPage::LoadPage(std::shared_ptr<ScriptContext> scriptContext) | void CGUIManager::SGUIPage::LoadPage(std::shared_ptr<ScriptContext> scriptContext) | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | void CGUIManager::SGUIPage::PerformCallbackFunction(Script::StructuredClone args) | ||||
JS::RootedValue result(rq.cx); | JS::RootedValue result(rq.cx); | ||||
if(!JS_CallFunctionValue(rq.cx, globalObj, funcVal, paramData, &result)) | if(!JS_CallFunctionValue(rq.cx, globalObj, funcVal, paramData, &result)) | ||||
ScriptException::CatchPending(rq); | ScriptException::CatchPending(rq); | ||||
} | } | ||||
Status CGUIManager::ReloadChangedFile(const VfsPath& path) | Status CGUIManager::ReloadChangedFile(const VfsPath& path) | ||||
{ | { | ||||
for (SGUIPage& p : m_PageStack) | UpdatePages(); | ||||
for (SGUIPage& p : m_CurrentPages) | |||||
if (p.inputs.find(path) != p.inputs.end()) | if (p.inputs.find(path) != p.inputs.end()) | ||||
{ | { | ||||
LOGMESSAGE("GUI file '%s' changed - reloading page '%s'", path.string8(), utf8_from_wstring(p.m_Name)); | LOGMESSAGE("GUI file '%s' changed - reloading page '%s'", path.string8(), utf8_from_wstring(p.m_Name)); | ||||
p.LoadPage(m_ScriptContext); | p.LoadPage(m_ScriptContext); | ||||
// TODO: this can crash if LoadPage runs an init script which modifies the page stack and breaks our iterators | |||||
} | } | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
Status CGUIManager::ReloadAllPages() | Status CGUIManager::ReloadAllPages() | ||||
{ | { | ||||
// TODO: this can crash if LoadPage runs an init script which modifies the page stack and breaks our iterators | UpdatePages(); | ||||
for (SGUIPage& p : m_PageStack) | |||||
for (SGUIPage& p : m_CurrentPages) | |||||
p.LoadPage(m_ScriptContext); | p.LoadPage(m_ScriptContext); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
InReaction CGUIManager::HandleEvent(const SDL_Event_* ev) | InReaction CGUIManager::HandleEvent(const SDL_Event_* ev) | ||||
{ | { | ||||
// We want scripts to have access to the raw input events, so they can do complex | // We want scripts to have access to the raw input events, so they can do complex | ||||
Show All 31 Lines | bool handled = false; | ||||
if (ScriptFunction::Call(rq, global, "handleInputAfterGui", handled, *ev)) | if (ScriptFunction::Call(rq, global, "handleInputAfterGui", handled, *ev)) | ||||
if (handled) | if (handled) | ||||
return IN_HANDLED; | return IN_HANDLED; | ||||
} | } | ||||
return IN_PASS; | return IN_PASS; | ||||
} | } | ||||
void CGUIManager::SendEventToAll(const CStr& eventName) const | void CGUIManager::SendEventToAll(const CStr& eventName) | ||||
{ | { | ||||
// Save an immutable copy so iterators aren't invalidated by handlers | UpdatePages(); | ||||
PageStackType pageStack = m_PageStack; | |||||
for (const SGUIPage& p : pageStack) | for (const SGUIPage& p : m_CurrentPages) | ||||
p.gui->SendEventToAll(eventName); | p.gui->SendEventToAll(eventName); | ||||
} | } | ||||
void CGUIManager::SendEventToAll(const CStr& eventName, JS::HandleValueArray paramData) const | void CGUIManager::SendEventToAll(const CStr& eventName, JS::HandleValueArray paramData) | ||||
{ | { | ||||
// Save an immutable copy so iterators aren't invalidated by handlers | UpdatePages(); | ||||
PageStackType pageStack = m_PageStack; | |||||
for (const SGUIPage& p : pageStack) | for (const SGUIPage& p : m_CurrentPages) | ||||
p.gui->SendEventToAll(eventName, paramData); | p.gui->SendEventToAll(eventName, paramData); | ||||
} | } | ||||
void CGUIManager::TickObjects() | void CGUIManager::TickObjects() | ||||
{ | { | ||||
PROFILE3("gui tick"); | PROFILE3("gui tick"); | ||||
// We share the script context with everything else that runs in the same thread. | // We share the script context with everything else that runs in the same thread. | ||||
// This call makes sure we trigger GC regularly even if the simulation is not running. | // This call makes sure we trigger GC regularly even if the simulation is not running. | ||||
m_ScriptInterface->GetContext()->MaybeIncrementalGC(1.0f); | m_ScriptInterface->GetContext()->MaybeIncrementalGC(1.0f); | ||||
// Save an immutable copy so iterators aren't invalidated by tick handlers | UpdatePages(); | ||||
PageStackType pageStack = m_PageStack; | |||||
for (const SGUIPage& p : pageStack) | for (const SGUIPage& p : m_CurrentPages) | ||||
p.gui->TickObjects(); | p.gui->TickObjects(); | ||||
} | } | ||||
void CGUIManager::Draw(CCanvas2D& canvas) const | void CGUIManager::Draw(CCanvas2D& canvas) const | ||||
{ | { | ||||
PROFILE3_GPU("gui"); | PROFILE3_GPU("gui"); | ||||
for (const SGUIPage& p : m_PageStack) | for (const SGUIPage& p : m_CurrentPages) | ||||
p.gui->Draw(canvas); | p.gui->Draw(canvas); | ||||
} | } | ||||
void CGUIManager::UpdateResolution() | void CGUIManager::UpdateResolution() | ||||
{ | { | ||||
// Save an immutable copy so iterators aren't invalidated by event handlers | UpdatePages(); | ||||
PageStackType pageStack = m_PageStack; | |||||
for (const SGUIPage& p : pageStack) | for (const SGUIPage& p : m_CurrentPages) | ||||
{ | { | ||||
p.gui->UpdateResolution(); | p.gui->UpdateResolution(); | ||||
p.gui->SendEventToAll(EVENT_NAME_WINDOW_RESIZED); | p.gui->SendEventToAll(EVENT_NAME_WINDOW_RESIZED); | ||||
} | } | ||||
} | } | ||||
bool CGUIManager::TemplateExists(const std::string& templateName) const | bool CGUIManager::TemplateExists(const std::string& templateName) const | ||||
{ | { | ||||
Show All 25 Lines | void CGUIManager::DisplayLoadProgress(int percent, const wchar_t* pending_task) | ||||
SendEventToAll(EVENT_NAME_GAME_LOAD_PROGRESS, paramData); | SendEventToAll(EVENT_NAME_GAME_LOAD_PROGRESS, paramData); | ||||
} | } | ||||
// This returns a shared_ptr to make sure the CGUI doesn't get deallocated | // This returns a shared_ptr to make sure the CGUI doesn't get deallocated | ||||
// while we're in the middle of calling a function on it (e.g. if a GUI script | // while we're in the middle of calling a function on it (e.g. if a GUI script | ||||
// calls SwitchPage) | // calls SwitchPage) | ||||
std::shared_ptr<CGUI> CGUIManager::top() const | std::shared_ptr<CGUI> CGUIManager::top() const | ||||
{ | { | ||||
ENSURE(m_PageStack.size()); | if (m_NextPages.has_value()) | ||||
return m_PageStack.back().gui; | { | ||||
ENSURE(!m_NextPages->empty()); | |||||
return m_NextPages->back().gui; | |||||
} | |||||
ENSURE(!m_CurrentPages.empty()); | |||||
return m_CurrentPages.back().gui; | |||||
} | |||||
void CGUIManager::MakeNextPages() | |||||
{ | |||||
if (!m_NextPages.has_value()) | |||||
m_NextPages.emplace(m_CurrentPages); | |||||
} | |||||
void CGUIManager::UpdatePages() | |||||
{ | |||||
if (m_NextPages.has_value()) | |||||
m_CurrentPages = *std::exchange(m_NextPages, std::nullopt); | |||||
} | } |
Wildfire Games · Phabricator