diff --git a/ext/native/ui/screen.cpp b/ext/native/ui/screen.cpp index 341d9a01ec..91e6fb4537 100644 --- a/ext/native/ui/screen.cpp +++ b/ext/native/ui/screen.cpp @@ -1,230 +1,230 @@ -#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) { - FLOG("Already had a nextScreen_"); - } - 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(InputState &input) { - if (nextScreen_) { - switchToNext(); - } - - if (stack_.size()) { - stack_.back().screen->update(input); - } -} - -void ScreenManager::switchToNext() { - 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) { - if (!stack_.empty()) { - return stack_.back().screen->touch(touch); - } else { - return false; - } -} - -bool ScreenManager::key(const KeyInput &key) { - if (!stack_.empty()) { - return stack_.back().screen->key(key); - } else { - return false; - } -} - -bool ScreenManager::axis(const AxisInput &axis) { - if (!stack_.empty()) { - return stack_.back().screen->axis(axis); - } else { - return false; - } -} - -void ScreenManager::resized() { - // 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; - UIDisableBegin(); - // Also shift to the right somehow... - backback.screen->render(); - UIDisableEnd(); - stack_.back().screen->render(); - break; - } - default: - stack_.back().screen->render(); - 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); -} - -void ScreenManager::deviceLost() { - for (size_t i = 0; i < stack_.size(); i++) { - stack_[i].screen->deviceLost(); - } - // Dialogs too? Nah, they should only use the standard UI texture anyway. - // TODO: Change this when it becomes necessary. -} - -Screen *ScreenManager::topScreen() const { - if (!stack_.empty()) - return stack_.back().screen; - else - return 0; -} - -void ScreenManager::shutdown() { - for (auto x = stack_.begin(); x != stack_.end(); x++) - delete x->screen; - stack_.clear(); - delete nextScreen_; - nextScreen_ = 0; -} - -void ScreenManager::push(Screen *screen, int layerFlags) { - 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() { - 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; -} - -void ScreenManager::processFinishDialog() { - if (dialogFinished_) { - // Another dialog may have been pushed before the render, so search for it. - Screen *caller = 0; - for (size_t i = 0; i < stack_.size(); ++i) { - if (stack_[i].screen != dialogFinished_) { - continue; - } - - stack_.erase(stack_.begin() + i); - // The previous screen was the caller (not necessarily the topmost.) - if (i > 0) { - caller = stack_[i - 1].screen; - } - } - - 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_ = 0; - } -} +#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) { + FLOG("Already had a nextScreen_"); + } + 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(InputState &input) { + if (nextScreen_) { + switchToNext(); + } + + if (stack_.size()) { + stack_.back().screen->update(input); + } +} + +void ScreenManager::switchToNext() { + 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) { + if (!stack_.empty()) { + return stack_.back().screen->touch(touch); + } else { + return false; + } +} + +bool ScreenManager::key(const KeyInput &key) { + if (!stack_.empty()) { + return stack_.back().screen->key(key); + } else { + return false; + } +} + +bool ScreenManager::axis(const AxisInput &axis) { + if (!stack_.empty()) { + return stack_.back().screen->axis(axis); + } else { + return false; + } +} + +void ScreenManager::resized() { + // 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; + UIDisableBegin(); + // Also shift to the right somehow... + backback.screen->render(); + UIDisableEnd(); + stack_.back().screen->render(); + break; + } + default: + stack_.back().screen->render(); + 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); +} + +void ScreenManager::deviceLost() { + for (size_t i = 0; i < stack_.size(); i++) { + stack_[i].screen->deviceLost(); + } + // Dialogs too? Nah, they should only use the standard UI texture anyway. + // TODO: Change this when it becomes necessary. +} + +Screen *ScreenManager::topScreen() const { + if (!stack_.empty()) + return stack_.back().screen; + else + return 0; +} + +void ScreenManager::shutdown() { + for (auto x = stack_.begin(); x != stack_.end(); x++) + delete x->screen; + stack_.clear(); + delete nextScreen_; + nextScreen_ = 0; +} + +void ScreenManager::push(Screen *screen, int layerFlags) { + 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() { + 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; +} + +void ScreenManager::processFinishDialog() { + if (dialogFinished_) { + // Another dialog may have been pushed before the render, so search for it. + Screen *caller = 0; + for (size_t i = 0; i < stack_.size(); ++i) { + if (stack_[i].screen != dialogFinished_) { + continue; + } + + stack_.erase(stack_.begin() + i); + // The previous screen was the caller (not necessarily the topmost.) + if (i > 0) { + caller = stack_[i - 1].screen; + } + } + + 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_ = 0; + } +} diff --git a/ext/native/ui/screen.h b/ext/native/ui/screen.h index 1c8aa7da00..f764f4645f 100644 --- a/ext/native/ui/screen.h +++ b/ext/native/ui/screen.h @@ -1,146 +1,146 @@ -// Super basic screen manager. Let's you, well, switch between screens. Can also be used -// to pop one screen in front for a bit while keeping another one running, it's basically -// a native "activity stack". Well actually that part is still a TODO. -// -// Semantics -// -// switchScreen: When you call this, on a newed screen, the ScreenManager takes ownership. -// On the next update, it switches to the new screen and deletes the previous screen. -// -// TODO: A way to do smooth transitions between screens. Will probably involve screenshotting -// the previous screen and then animating it on top of the current screen with transparency -// and/or other similar effects. - -#pragma once - -#include - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/NativeApp.h" - -namespace UI { - class View; -} - -struct InputState; - -enum DialogResult { - DR_OK, - DR_CANCEL, - DR_YES, - DR_NO, - DR_BACK, -}; - -class ScreenManager; -class UIContext; -class Thin3DContext; - -class Screen { -public: - Screen() : screenManager_(0) { } - virtual ~Screen() { - screenManager_ = 0; - } - - virtual void onFinish(DialogResult reason) {} - virtual void update(InputState &input) {} - virtual void render() {} - virtual void deviceLost() {} - virtual void resized() {} - virtual void dialogFinished(const Screen *dialog, DialogResult result) {} - virtual bool touch(const TouchInput &touch) { return false; } - virtual bool key(const KeyInput &key) { return false; } - virtual bool axis(const AxisInput &touch) { return false; } - virtual void sendMessage(const char *msg, const char *value) {} - - virtual void RecreateViews() {} - - ScreenManager *screenManager() { return screenManager_; } - void setScreenManager(ScreenManager *sm) { screenManager_ = sm; } - - // This one is icky to use because you can't know what's in it until you know - // what screen it is. - virtual void *dialogData() { return 0; } - - virtual std::string tag() const { return std::string(""); } - - virtual bool isTransparent() const { return false; } - virtual bool isTopLevel() const { return false; } - -private: - ScreenManager *screenManager_; - DISALLOW_COPY_AND_ASSIGN(Screen); -}; - -class Transition { -public: - Transition() {} -}; - -enum { - LAYER_SIDEMENU = 1, - LAYER_TRANSPARENT = 2, -}; - -class ScreenManager { -public: - ScreenManager(); - virtual ~ScreenManager(); - - void switchScreen(Screen *screen); - void update(InputState &input); - - void setUIContext(UIContext *context) { uiContext_ = context; } - UIContext *getUIContext() { return uiContext_; } - - void setThin3DContext(Thin3DContext *context) { thin3DContext_ = context; } - Thin3DContext *getThin3DContext() { return thin3DContext_; } - - void render(); - void resized(); - void deviceLost(); - void shutdown(); - - // Push a dialog box in front. Currently 1-level only. - void push(Screen *screen, int layerFlags = 0); - - // Recreate all views - void RecreateAllViews(); - - // Pops the dialog away. - void finishDialog(Screen *dialog, DialogResult result = DR_OK); - - // Instant touch, separate from the update() mechanism. - bool touch(const TouchInput &touch); - bool key(const KeyInput &key); - bool axis(const AxisInput &touch); - - // Generic facility for gross hacks :P - void sendMessage(const char *msg, const char *value); - - Screen *topScreen() const; - -private: - void pop(); - void switchToNext(); - void processFinishDialog(); - - Screen *nextScreen_; - UIContext *uiContext_; - Thin3DContext *thin3DContext_; - - const Screen *dialogFinished_; - DialogResult dialogResult_; - - struct Layer { - Screen *screen; - int flags; // From LAYER_ enum above - UI::View *focusedView; // TODO: save focus here. Going for quick solution now to reset focus. - }; - - // Dialog stack. These are shown "on top" of base screens and the Android back button works as expected. - // Used for options, in-game menus and other things you expect to be able to back out from onto something. - std::vector stack_; -}; +// Super basic screen manager. Let's you, well, switch between screens. Can also be used +// to pop one screen in front for a bit while keeping another one running, it's basically +// a native "activity stack". Well actually that part is still a TODO. +// +// Semantics +// +// switchScreen: When you call this, on a newed screen, the ScreenManager takes ownership. +// On the next update, it switches to the new screen and deletes the previous screen. +// +// TODO: A way to do smooth transitions between screens. Will probably involve screenshotting +// the previous screen and then animating it on top of the current screen with transparency +// and/or other similar effects. + +#pragma once + +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/NativeApp.h" + +namespace UI { + class View; +} + +struct InputState; + +enum DialogResult { + DR_OK, + DR_CANCEL, + DR_YES, + DR_NO, + DR_BACK, +}; + +class ScreenManager; +class UIContext; +class Thin3DContext; + +class Screen { +public: + Screen() : screenManager_(0) { } + virtual ~Screen() { + screenManager_ = 0; + } + + virtual void onFinish(DialogResult reason) {} + virtual void update(InputState &input) {} + virtual void render() {} + virtual void deviceLost() {} + virtual void resized() {} + virtual void dialogFinished(const Screen *dialog, DialogResult result) {} + virtual bool touch(const TouchInput &touch) { return false; } + virtual bool key(const KeyInput &key) { return false; } + virtual bool axis(const AxisInput &touch) { return false; } + virtual void sendMessage(const char *msg, const char *value) {} + + virtual void RecreateViews() {} + + ScreenManager *screenManager() { return screenManager_; } + void setScreenManager(ScreenManager *sm) { screenManager_ = sm; } + + // This one is icky to use because you can't know what's in it until you know + // what screen it is. + virtual void *dialogData() { return 0; } + + virtual std::string tag() const { return std::string(""); } + + virtual bool isTransparent() const { return false; } + virtual bool isTopLevel() const { return false; } + +private: + ScreenManager *screenManager_; + DISALLOW_COPY_AND_ASSIGN(Screen); +}; + +class Transition { +public: + Transition() {} +}; + +enum { + LAYER_SIDEMENU = 1, + LAYER_TRANSPARENT = 2, +}; + +class ScreenManager { +public: + ScreenManager(); + virtual ~ScreenManager(); + + void switchScreen(Screen *screen); + void update(InputState &input); + + void setUIContext(UIContext *context) { uiContext_ = context; } + UIContext *getUIContext() { return uiContext_; } + + void setThin3DContext(Thin3DContext *context) { thin3DContext_ = context; } + Thin3DContext *getThin3DContext() { return thin3DContext_; } + + void render(); + void resized(); + void deviceLost(); + void shutdown(); + + // Push a dialog box in front. Currently 1-level only. + void push(Screen *screen, int layerFlags = 0); + + // Recreate all views + void RecreateAllViews(); + + // Pops the dialog away. + void finishDialog(Screen *dialog, DialogResult result = DR_OK); + + // Instant touch, separate from the update() mechanism. + bool touch(const TouchInput &touch); + bool key(const KeyInput &key); + bool axis(const AxisInput &touch); + + // Generic facility for gross hacks :P + void sendMessage(const char *msg, const char *value); + + Screen *topScreen() const; + +private: + void pop(); + void switchToNext(); + void processFinishDialog(); + + Screen *nextScreen_; + UIContext *uiContext_; + Thin3DContext *thin3DContext_; + + const Screen *dialogFinished_; + DialogResult dialogResult_; + + struct Layer { + Screen *screen; + int flags; // From LAYER_ enum above + UI::View *focusedView; // TODO: save focus here. Going for quick solution now to reset focus. + }; + + // Dialog stack. These are shown "on top" of base screens and the Android back button works as expected. + // Used for options, in-game menus and other things you expect to be able to back out from onto something. + std::vector stack_; +};