ppsspp/ext/native/ui/screen.cpp

250 lines
6.1 KiB
C++

#include "base/display.h"
#include "base/logging.h"
#include "input/input_state.h"
#include "ui/screen.h"
#include "ui/ui.h"
#include "ui/view.h"
ScreenManager::ScreenManager() {
nextScreen_ = 0;
uiContext_ = 0;
dialogFinished_ = 0;
}
ScreenManager::~ScreenManager() {
shutdown();
}
void ScreenManager::switchScreen(Screen *screen) {
if (screen == nextScreen_) {
ELOG("Already switching to this screen");
return;
}
// Note that if a dialog is found, this will be a silent background switch that
// will only become apparent if the dialog is closed. The previous screen will stick around
// until that switch.
// TODO: is this still true?
if (nextScreen_ != 0) {
ELOG("Already had a nextScreen_! Asynchronous open while doing something? Deleting the new screen.");
delete screen;
return;
}
if (screen == 0) {
WLOG("Swiching to a zero screen, this can't be good");
}
if (stack_.empty() || screen != stack_.back().screen) {
nextScreen_ = screen;
nextScreen_->setScreenManager(this);
}
}
void ScreenManager::update() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (nextScreen_) {
switchToNext();
}
if (stack_.size()) {
stack_.back().screen->update();
}
}
void ScreenManager::switchToNext() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (!nextScreen_) {
ELOG("switchToNext: No nextScreen_!");
}
Layer temp = {0, 0};
if (!stack_.empty()) {
temp = stack_.back();
stack_.pop_back();
}
Layer newLayer = {nextScreen_, 0};
stack_.push_back(newLayer);
if (temp.screen) {
delete temp.screen;
}
nextScreen_ = 0;
UI::SetFocusedView(0);
}
bool ScreenManager::touch(const TouchInput &touch) {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (!stack_.empty()) {
Screen *screen = stack_.back().screen;
return screen->touch(screen->transformTouch(touch));
} else {
return false;
}
}
bool ScreenManager::key(const KeyInput &key) {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (!stack_.empty()) {
return stack_.back().screen->key(key);
} else {
return false;
}
}
bool ScreenManager::axis(const AxisInput &axis) {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (!stack_.empty()) {
return stack_.back().screen->axis(axis);
} else {
return false;
}
}
void ScreenManager::resized() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
// Have to notify the whole stack, otherwise there will be problems when going back
// to non-top screens.
for (auto iter = stack_.begin(); iter != stack_.end(); ++iter) {
iter->screen->resized();
}
}
void ScreenManager::render() {
if (!stack_.empty()) {
switch (stack_.back().flags) {
case LAYER_SIDEMENU:
case LAYER_TRANSPARENT:
if (stack_.size() == 1) {
ELOG("Can't have sidemenu over nothing");
break;
} else {
auto iter = stack_.end();
iter--;
iter--;
Layer backback = *iter;
// TODO: Make really sure that this "mismatched" pre/post only happens
// when screens are "compatible" (both are UIScreens, for example).
backback.screen->preRender();
backback.screen->render();
stack_.back().screen->render();
if (postRenderCb_)
postRenderCb_(getUIContext(), postRenderUserdata_);
backback.screen->postRender();
break;
}
default:
stack_.back().screen->preRender();
stack_.back().screen->render();
if (postRenderCb_)
postRenderCb_(getUIContext(), postRenderUserdata_);
stack_.back().screen->postRender();
break;
}
} else {
ELOG("No current screen!");
}
processFinishDialog();
}
void ScreenManager::sendMessage(const char *msg, const char *value) {
if (!strcmp(msg, "recreateviews"))
RecreateAllViews();
if (!stack_.empty())
stack_.back().screen->sendMessage(msg, value);
}
Screen *ScreenManager::topScreen() const {
if (!stack_.empty())
return stack_.back().screen;
else
return 0;
}
void ScreenManager::shutdown() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
for (auto x = stack_.begin(); x != stack_.end(); x++)
delete x->screen;
stack_.clear();
delete nextScreen_;
nextScreen_ = nullptr;
}
void ScreenManager::push(Screen *screen, int layerFlags) {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (nextScreen_ && stack_.empty()) {
// we're during init, this is OK
switchToNext();
}
screen->setScreenManager(this);
if (screen->isTransparent()) {
layerFlags |= LAYER_TRANSPARENT;
}
UI::SetFocusedView(0);
Layer layer = {screen, layerFlags};
stack_.push_back(layer);
}
void ScreenManager::pop() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (stack_.size()) {
delete stack_.back().screen;
stack_.pop_back();
} else {
ELOG("Can't pop when stack empty");
}
}
void ScreenManager::RecreateAllViews() {
for (auto it = stack_.begin(); it != stack_.end(); ++it) {
it->screen->RecreateViews();
}
}
void ScreenManager::finishDialog(Screen *dialog, DialogResult result) {
if (stack_.empty()) {
ELOG("Must be in a dialog to finishDialog");
return;
}
if (dialog != stack_.back().screen) {
ELOG("Wrong dialog being finished!");
return;
}
dialog->onFinish(result);
dialogFinished_ = dialog;
dialogResult_ = result;
}
Screen *ScreenManager::dialogParent(const Screen *dialog) const {
for (size_t i = 1; i < stack_.size(); ++i) {
if (stack_[i].screen == dialog) {
// The previous screen was the caller (not necessarily the topmost.)
return stack_[i - 1].screen;
}
}
return nullptr;
}
void ScreenManager::processFinishDialog() {
if (dialogFinished_) {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
// Another dialog may have been pushed before the render, so search for it.
Screen *caller = dialogParent(dialogFinished_);
for (size_t i = 0; i < stack_.size(); ++i) {
if (stack_[i].screen == dialogFinished_) {
stack_.erase(stack_.begin() + i);
}
}
if (!caller) {
ELOG("ERROR: no top screen when finishing dialog");
} else if (caller != topScreen()) {
// The caller may get confused if we call dialogFinished() now.
WLOG("Skipping non-top dialog when finishing dialog.");
} else {
caller->dialogFinished(dialogFinished_, dialogResult_);
}
delete dialogFinished_;
dialogFinished_ = nullptr;
}
}