[escher] Create dynamic view controller

Change-Id: Ic8fe6d095666aa7af76c8d328b0bf83342a09453
This commit is contained in:
Émilie Feral
2017-04-18 17:03:46 +02:00
parent 097ecc8b89
commit 50aacc79ec
19 changed files with 188 additions and 25 deletions

View File

@@ -8,6 +8,7 @@ objs += $(addprefix escher/src/,\
button_row_controller.o\
chevron_view.o\
container.o\
dynamic_view_controller.o\
editable_text_cell.o\
even_odd_cell.o\
even_odd_buffer_text_cell.o\

View File

@@ -9,6 +9,7 @@
#include <escher/button_row_controller.h>
#include <escher/chevron_view.h>
#include <escher/container.h>
#include <escher/dynamic_view_controller.h>
#include <escher/editable_text_cell.h>
#include <escher/even_odd_cell.h>
#include <escher/even_odd_buffer_text_cell.h>

View File

@@ -15,6 +15,8 @@ public:
void didBecomeFirstResponder() override;
void viewWillAppear() override;
void viewDidDisappear() override;
void loadView() override;
void unloadView() override;
private:
class ContentView : public View {
public:

View File

@@ -28,6 +28,8 @@ public:
bool setSelectedButton(int selectedButton);
void viewWillAppear() override;
void viewDidDisappear() override;
void loadView() override;
void unloadView() override;
private:
class ContentView : public View {
public:
@@ -64,6 +66,10 @@ public:
virtual Button * buttonAtIndex(int index, ButtonRowController::Position position) const;
ButtonRowController * header();
ButtonRowController * footer();
/* By default these two functions do not load anything. They should be
* overrided if the delegate dynamically allocate the button views. */
virtual void loadButtonView();
virtual void unloadButtonView();
private:
ButtonRowController * m_header;
ButtonRowController * m_footer;

View File

@@ -0,0 +1,21 @@
#ifndef ESCHER_DYNAMIC_VIEW_CONTROLLER_H
#define ESCHER_DYNAMIC_VIEW_CONTROLLER_H
#include <escher/view_controller.h>
/* Dynamic view controllers dynamically construct their views when appearring and
* delete them when disappearing. */
class DynamicViewController : public ViewController {
public:
DynamicViewController(Responder * parentResponder);
~DynamicViewController();
View * view() override;
virtual void loadView() override;
virtual void unloadView() override;
private:
virtual View * createView() = 0;
View * m_view;
};
#endif

View File

@@ -17,6 +17,8 @@ public:
bool isDisplayingModal();
void viewWillAppear() override;
void viewDidDisappear() override;
void loadView() override;
void unloadView() override;
private:
class ContentView : public View {
public:

View File

@@ -23,6 +23,8 @@ public:
void didBecomeFirstResponder() override;
void viewWillAppear() override;
void viewDidDisappear() override;
void loadView() override;
void unloadView() override;
private:
class ControllerView : public View {
public:

View File

@@ -16,9 +16,12 @@ public:
const char * tabName(uint8_t index);
bool handleEvent(Ion::Events::Event event) override;
void didBecomeFirstResponder() override;
void didEnterResponderChain(Responder * previousResponder) override;
void willResignFirstResponder() override;
void viewWillAppear() override;
void viewDidDisappear() override;
void loadView() override;
void unloadView() override;
private:
class ContentView : public View {
public:

View File

@@ -25,6 +25,7 @@ class View {
friend class Window;
public:
View();
virtual ~View();
/* The drawRect method should be implemented by each View subclass. In a
* typical drawRect implementation, a subclass will make drawing calls to the

View File

@@ -32,6 +32,8 @@ public:
ViewController(Responder * parentResponder);
virtual const char * title();
virtual View * view() = 0;
virtual void loadView();
virtual void unloadView();
virtual void viewWillAppear();
virtual void viewDidDisappear();
};

View File

@@ -86,3 +86,11 @@ void AlternateEmptyViewController::viewDidDisappear() {
m_contentView.mainViewController()->viewDidDisappear();
}
}
void AlternateEmptyViewController::loadView() {
m_contentView.mainViewController()->loadView();
}
void AlternateEmptyViewController::unloadView() {
m_contentView.mainViewController()->unloadView();
}

View File

@@ -91,13 +91,12 @@ const Container * App::container() const {
}
void App::didBecomeActive(Window * window) {
m_modalViewController.loadView();
View * view = m_modalViewController.view();
assert(m_modalViewController.app() == this);
window->setContentView(view);
m_modalViewController.viewWillAppear();
if (m_firstResponder == nullptr) {
setFirstResponder(&m_modalViewController);
}
setFirstResponder(&m_modalViewController);
window->redraw();
}
@@ -105,5 +104,7 @@ void App::willBecomeInactive() {
if (m_modalViewController.isDisplayingModal()) {
dismissModalViewController();
}
setFirstResponder(nullptr);
m_modalViewController.viewDidDisappear();
m_modalViewController.unloadView();
}

View File

@@ -26,6 +26,12 @@ ButtonRowController * ButtonRowDelegate::header() {
return m_header;
}
void ButtonRowDelegate::loadButtonView() {
}
void ButtonRowDelegate::unloadButtonView() {
}
ButtonRowController::ContentView::ContentView(ViewController * mainViewController, ButtonRowDelegate * delegate, Position position, Style style) :
View(),
m_mainViewController(mainViewController),
@@ -231,3 +237,13 @@ void ButtonRowController::viewWillAppear() {
void ButtonRowController::viewDidDisappear() {
m_contentView.mainViewController()->viewDidDisappear();
}
void ButtonRowController::loadView() {
m_contentView.buttonRowDelegate()->loadButtonView();
m_contentView.mainViewController()->loadView();
}
void ButtonRowController::unloadView() {
m_contentView.buttonRowDelegate()->unloadButtonView();
m_contentView.mainViewController()->unloadView();
}

View File

@@ -0,0 +1,32 @@
#include <escher/dynamic_view_controller.h>
#include <escher/app.h>
#include <assert.h>
DynamicViewController::DynamicViewController(Responder * parentResponder) :
ViewController(parentResponder),
m_view(nullptr)
{
}
DynamicViewController::~DynamicViewController() {
if (m_view) {
delete m_view;
m_view = nullptr;
}
}
View * DynamicViewController::view() {
assert(m_view != nullptr);
return m_view;
}
void DynamicViewController::loadView() {
assert(m_view == nullptr);
m_view = createView();
}
void DynamicViewController::unloadView() {
assert(m_view != nullptr);
delete m_view;
m_view = nullptr;
}

View File

@@ -17,7 +17,9 @@ ModalViewController::ContentView::ContentView() :
}
void ModalViewController::ContentView::setMainView(View * regularView) {
m_regularView = regularView;
if (m_regularView != regularView) {
m_regularView = regularView;
}
}
int ModalViewController::ContentView::numberOfSubviews() const {
@@ -96,9 +98,6 @@ ModalViewController::ModalViewController(Responder * parentResponder, ViewContro
}
View * ModalViewController::view() {
if (m_contentView.subviewAtIndex(0) == nullptr) {
m_contentView.setMainView(m_regularViewController->view());
}
return &m_contentView;
}
@@ -112,6 +111,7 @@ void ModalViewController::displayModalViewController(ViewController * vc, float
vc->setParentResponder(this);
m_previousResponder = app()->firstResponder();
m_regularViewController->viewDidDisappear();
vc->loadView();
m_contentView.presentModalView(vc->view(), verticalAlignment, horizontalAlignment, topMargin, leftMargin, bottomMargin, rightMargin);
m_currentModalViewController->viewWillAppear();
app()->setFirstResponder(vc);
@@ -119,6 +119,7 @@ void ModalViewController::displayModalViewController(ViewController * vc, float
void ModalViewController::dismissModalViewController() {
m_currentModalViewController->viewDidDisappear();
m_currentModalViewController->unloadView();
m_currentModalViewController = nullptr;
m_regularViewController->viewWillAppear();
app()->setFirstResponder(m_previousResponder);
@@ -156,3 +157,19 @@ void ModalViewController::viewDidDisappear() {
}
m_regularViewController->viewDidDisappear();
}
void ModalViewController::loadView() {
if (m_contentView.isDisplayingModal()) {
m_currentModalViewController->loadView();
}
m_regularViewController->loadView();
m_contentView.setMainView(m_regularViewController->view());
m_contentView.layoutSubviews();
}
void ModalViewController::unloadView() {
if (m_contentView.isDisplayingModal()) {
m_currentModalViewController->unloadView();
}
m_regularViewController->unloadView();
}

View File

@@ -90,8 +90,9 @@ const char * StackViewController::title() {
void StackViewController::push(ViewController * vc, KDColor textColor, KDColor backgroundColor, KDColor separatorColor) {
m_view.pushStack(vc, textColor, backgroundColor, separatorColor);
m_children[m_numberOfChildren++] = vc;
if (m_numberOfChildren > 1) {
if (m_numberOfChildren > 2) {
m_children[m_numberOfChildren-2]->viewDidDisappear();
m_children[m_numberOfChildren-2]->unloadView();
}
setupActiveViewController();
}
@@ -100,15 +101,19 @@ void StackViewController::pop() {
m_view.popStack();
assert(m_numberOfChildren > 0);
ViewController * vc = m_children[m_numberOfChildren-1];
vc->setParentResponder(nullptr);
m_numberOfChildren--;
vc->viewDidDisappear();
setupActiveViewController();
vc->setParentResponder(nullptr);
vc->unloadView();
}
void StackViewController::setupActiveViewController() {
ViewController * vc = m_children[m_numberOfChildren-1];
vc->setParentResponder(this);
if (m_numberOfChildren > 1) {
vc->loadView();
}
m_view.setContentView(vc->view());
vc->viewWillAppear();
app()->setFirstResponder(vc);
@@ -133,7 +138,12 @@ View * StackViewController::view() {
void StackViewController::viewWillAppear() {
if (m_rootViewController != nullptr) {
push(m_rootViewController, m_textColor, m_backgroundColor, m_separatorColor);
/* push the m_rootViewController without setting it as first responder
* (which will be done in did become first responder */
m_view.pushStack(m_rootViewController, m_textColor, m_backgroundColor, m_separatorColor);
m_children[m_numberOfChildren++] = m_rootViewController;
m_rootViewController->setParentResponder(this);
m_view.setContentView(m_rootViewController->view());
m_rootViewController = nullptr;
}
ViewController * vc = m_children[m_numberOfChildren-1];
@@ -148,3 +158,21 @@ void StackViewController::viewDidDisappear() {
vc->viewDidDisappear();
}
}
void StackViewController::loadView() {
if (m_rootViewController) {
m_rootViewController->loadView();
} else {
ViewController * vc = m_children[m_numberOfChildren-1];
if (m_numberOfChildren > 0 && vc) {
vc->loadView();
}
}
}
void StackViewController::unloadView() {
ViewController * vc = m_children[m_numberOfChildren-1];
if (m_numberOfChildren > 0 && vc) {
vc->unloadView();
}
}

View File

@@ -111,10 +111,14 @@ void TabViewController::setActiveTab(int8_t i, bool forceReactive) {
ViewController * activeVC = m_children[i];
if (i != m_activeChildIndex || forceReactive) {
assert(i <= m_numberOfChildren);
if (i != m_activeChildIndex) {
activeVC->loadView();
}
m_view.setActiveView(activeVC->view());
m_view.m_tabView.setActiveIndex(i);
if (m_activeChildIndex >= 0) {
if (m_activeChildIndex >= 0 && m_activeChildIndex != i) {
m_children[m_activeChildIndex]->viewDidDisappear();
m_children[m_activeChildIndex]->unloadView();
}
m_activeChildIndex = i;
if (i >= 0) {
@@ -132,6 +136,10 @@ void TabViewController::setSelectedTab(int8_t i) {
m_selectedChildIndex = i;
}
void TabViewController::didEnterResponderChain(Responder * previousResponder) {
setActiveTab(m_activeChildIndex, true);
}
void TabViewController::didBecomeFirstResponder() {
setSelectedTab(m_activeChildIndex);
}
@@ -142,13 +150,6 @@ void TabViewController::willResignFirstResponder() {
View * TabViewController::view() {
// We're asked for a view!
// Let's populate our tabview
if (m_view.m_tabView.numberOfTabs() != m_numberOfChildren) {
for (int i=0; i<m_numberOfChildren; i++) {
m_view.m_tabView.addTab(m_children[i]);
}
}
return &m_view;
}
@@ -161,16 +162,26 @@ const char * TabViewController::tabName(uint8_t index) {
}
void TabViewController::viewWillAppear() {
if (m_activeChildIndex < 0) {
setActiveTab(0, true);
} else {
setActiveTab(m_activeChildIndex, true);
}
ViewController * activeVC = m_children[m_activeChildIndex];
activeVC->viewWillAppear();
}
void TabViewController::viewDidDisappear() {
ViewController * activeVC = m_children[m_activeChildIndex];
activeVC->viewDidDisappear();
}
void TabViewController::loadView() {
if (m_view.m_tabView.numberOfTabs() != m_numberOfChildren) {
for (int i=0; i<m_numberOfChildren; i++) {
m_view.m_tabView.addTab(m_children[i]);
}
}
if (m_activeChildIndex < 0) {
m_activeChildIndex = 0;
}
m_children[m_activeChildIndex]->loadView();
}
void TabViewController::unloadView() {
ViewController * activeVC = m_children[m_activeChildIndex];
activeVC->unloadView();
}

View File

@@ -10,6 +10,9 @@ View::View() :
{
}
View::~View() {
}
void View::drawRect(KDContext * ctx, KDRect rect) const {
// By default, a view doesn't do anything
// It's transparent!

View File

@@ -14,3 +14,9 @@ void ViewController::viewWillAppear() {
void ViewController::viewDidDisappear() {
}
void ViewController::loadView() {
}
void ViewController::unloadView() {
}