From 50aacc79ecc2ceb22e2ab36fbef77e7f3f5ead31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 18 Apr 2017 17:03:46 +0200 Subject: [PATCH] [escher] Create dynamic view controller Change-Id: Ic8fe6d095666aa7af76c8d328b0bf83342a09453 --- escher/Makefile | 1 + escher/include/escher.h | 1 + .../escher/alternate_empty_view_controller.h | 2 + escher/include/escher/button_row_controller.h | 6 +++ .../include/escher/dynamic_view_controller.h | 21 ++++++++++ escher/include/escher/modal_view_controller.h | 2 + escher/include/escher/stack_view_controller.h | 2 + escher/include/escher/tab_view_controller.h | 3 ++ escher/include/escher/view.h | 1 + escher/include/escher/view_controller.h | 2 + .../src/alternate_empty_view_controller.cpp | 8 ++++ escher/src/app.cpp | 7 ++-- escher/src/button_row_controller.cpp | 16 ++++++++ escher/src/dynamic_view_controller.cpp | 32 +++++++++++++++ escher/src/modal_view_controller.cpp | 25 +++++++++-- escher/src/stack_view_controller.cpp | 34 +++++++++++++-- escher/src/tab_view_controller.cpp | 41 ++++++++++++------- escher/src/view.cpp | 3 ++ escher/src/view_controller.cpp | 6 +++ 19 files changed, 188 insertions(+), 25 deletions(-) create mode 100644 escher/include/escher/dynamic_view_controller.h create mode 100644 escher/src/dynamic_view_controller.cpp diff --git a/escher/Makefile b/escher/Makefile index 11ce86c55..c8db11cc4 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -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\ diff --git a/escher/include/escher.h b/escher/include/escher.h index 33e5f8b6f..580ae617b 100644 --- a/escher/include/escher.h +++ b/escher/include/escher.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/escher/include/escher/alternate_empty_view_controller.h b/escher/include/escher/alternate_empty_view_controller.h index 0a3f2da1d..cf676d973 100644 --- a/escher/include/escher/alternate_empty_view_controller.h +++ b/escher/include/escher/alternate_empty_view_controller.h @@ -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: diff --git a/escher/include/escher/button_row_controller.h b/escher/include/escher/button_row_controller.h index ff241c3f3..7cfd26a7f 100644 --- a/escher/include/escher/button_row_controller.h +++ b/escher/include/escher/button_row_controller.h @@ -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; diff --git a/escher/include/escher/dynamic_view_controller.h b/escher/include/escher/dynamic_view_controller.h new file mode 100644 index 000000000..e370ef4f4 --- /dev/null +++ b/escher/include/escher/dynamic_view_controller.h @@ -0,0 +1,21 @@ +#ifndef ESCHER_DYNAMIC_VIEW_CONTROLLER_H +#define ESCHER_DYNAMIC_VIEW_CONTROLLER_H + +#include + +/* 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 diff --git a/escher/include/escher/modal_view_controller.h b/escher/include/escher/modal_view_controller.h index 30361c7a1..84cfad38f 100644 --- a/escher/include/escher/modal_view_controller.h +++ b/escher/include/escher/modal_view_controller.h @@ -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: diff --git a/escher/include/escher/stack_view_controller.h b/escher/include/escher/stack_view_controller.h index 2ab2fb44b..811cfcabb 100644 --- a/escher/include/escher/stack_view_controller.h +++ b/escher/include/escher/stack_view_controller.h @@ -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: diff --git a/escher/include/escher/tab_view_controller.h b/escher/include/escher/tab_view_controller.h index 673040a57..ab94083f1 100644 --- a/escher/include/escher/tab_view_controller.h +++ b/escher/include/escher/tab_view_controller.h @@ -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: diff --git a/escher/include/escher/view.h b/escher/include/escher/view.h index 73f73f1e2..7e021b6b4 100644 --- a/escher/include/escher/view.h +++ b/escher/include/escher/view.h @@ -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 diff --git a/escher/include/escher/view_controller.h b/escher/include/escher/view_controller.h index 47db53b58..db4077fb9 100644 --- a/escher/include/escher/view_controller.h +++ b/escher/include/escher/view_controller.h @@ -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(); }; diff --git a/escher/src/alternate_empty_view_controller.cpp b/escher/src/alternate_empty_view_controller.cpp index 17b6d78fd..147af971e 100644 --- a/escher/src/alternate_empty_view_controller.cpp +++ b/escher/src/alternate_empty_view_controller.cpp @@ -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(); +} diff --git a/escher/src/app.cpp b/escher/src/app.cpp index 21c15be19..73474656d 100644 --- a/escher/src/app.cpp +++ b/escher/src/app.cpp @@ -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(); } diff --git a/escher/src/button_row_controller.cpp b/escher/src/button_row_controller.cpp index 8e2eda5bb..782388166 100644 --- a/escher/src/button_row_controller.cpp +++ b/escher/src/button_row_controller.cpp @@ -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(); +} diff --git a/escher/src/dynamic_view_controller.cpp b/escher/src/dynamic_view_controller.cpp new file mode 100644 index 000000000..39de39315 --- /dev/null +++ b/escher/src/dynamic_view_controller.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +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; +} diff --git a/escher/src/modal_view_controller.cpp b/escher/src/modal_view_controller.cpp index 0768e56d9..3d37517b8 100644 --- a/escher/src/modal_view_controller.cpp +++ b/escher/src/modal_view_controller.cpp @@ -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(); +} diff --git a/escher/src/stack_view_controller.cpp b/escher/src/stack_view_controller.cpp index 6f3237109..ddde1f809 100644 --- a/escher/src/stack_view_controller.cpp +++ b/escher/src/stack_view_controller.cpp @@ -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(); + } +} diff --git a/escher/src/tab_view_controller.cpp b/escher/src/tab_view_controller.cpp index 4e2b5fac0..a90127e3b 100644 --- a/escher/src/tab_view_controller.cpp +++ b/escher/src/tab_view_controller.cpp @@ -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; iviewWillAppear(); } 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; iloadView(); +} + +void TabViewController::unloadView() { + ViewController * activeVC = m_children[m_activeChildIndex]; + activeVC->unloadView(); +} diff --git a/escher/src/view.cpp b/escher/src/view.cpp index bfff7a4d7..5fbfeee81 100644 --- a/escher/src/view.cpp +++ b/escher/src/view.cpp @@ -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! diff --git a/escher/src/view_controller.cpp b/escher/src/view_controller.cpp index 03025cad9..59851acda 100644 --- a/escher/src/view_controller.cpp +++ b/escher/src/view_controller.cpp @@ -14,3 +14,9 @@ void ViewController::viewWillAppear() { void ViewController::viewDidDisappear() { } + +void ViewController::loadView() { +} + +void ViewController::unloadView() { +}