[escher] Create nested_menu_controller to factorize toolbox and future

variable_box_controller
This commit is contained in:
Émilie Feral
2018-10-04 22:57:50 +02:00
parent 2e4af43fe3
commit 08439b5c54
9 changed files with 266 additions and 223 deletions

View File

@@ -288,9 +288,9 @@ KDCoordinate PythonToolbox::rowHeight(int j) {
return Toolbox::rowHeight(j);
}
bool PythonToolbox::selectLeaf(ToolboxMessageTree * selectedMessageTree) {
bool PythonToolbox::selectLeaf(int selectedRow) {
m_selectableTableView.deselectTable();
ToolboxMessageTree * node = selectedMessageTree;
ToolboxMessageTree * node = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow);
const char * editedText = I18n::translate(node->insertedText());
int strippedEditedTextMaxLength = strlen(editedText)+1;
char strippedEditedText[k_maxMessageSize];

View File

@@ -14,7 +14,7 @@ public:
bool handleEvent(Ion::Events::Event event) override;
protected:
KDCoordinate rowHeight(int j) override;
bool selectLeaf(ToolboxMessageTree * selectedMessageTree) override;
bool selectLeaf(int selectedRow) override;
const ToolboxMessageTree * rootModel() override;
MessageTableCellWithMessage * leafCellAtIndex(int index) override;
MessageTableCellWithChevron* nodeCellAtIndex(int index) override;

View File

@@ -111,8 +111,8 @@ MathToolbox::MathToolbox() :
{
}
bool MathToolbox::selectLeaf(ToolboxMessageTree * selectedMessageTree) {
ToolboxMessageTree * messageTree = selectedMessageTree;
bool MathToolbox::selectLeaf(int selectedRow) {
ToolboxMessageTree * messageTree = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow);
m_selectableTableView.deselectTable();
// Translate the message and remove the arguments

View File

@@ -9,7 +9,7 @@ class MathToolbox : public Toolbox {
public:
MathToolbox();
protected:
bool selectLeaf(ToolboxMessageTree * selectedMessageTree) override;
bool selectLeaf(int selectedRow) override;
const ToolboxMessageTree * rootModel() override;
MessageTableCellWithMessage * leafCellAtIndex(int index) override;
MessageTableCellWithChevron* nodeCellAtIndex(int index) override;

View File

@@ -43,6 +43,7 @@ objs += $(addprefix escher/src/,\
message_text_view.o\
message_tree.o\
modal_view_controller.o\
nested_menu_controller.o\
palette.o\
pointer_text_view.o\
responder.o\

View File

@@ -0,0 +1,76 @@
#ifndef ESCHER_NESTED_MENU_CONTROLLER_H
#define ESCHER_NESTED_MENU_CONTROLLER_H
#include <escher/highlight_cell.h>
#include <escher/list_view_data_source.h>
#include <escher/selectable_table_view.h>
#include <escher/stack_view_controller.h>
class NestedMenuController : public StackViewController, public ListViewDataSource, public SelectableTableViewDataSource {
public:
NestedMenuController(Responder * parentResponder, const char * title = 0);
void setSender(Responder * sender) { m_sender = sender; }
// StackViewController
bool handleEvent(Ion::Events::Event event) override;
void didBecomeFirstResponder() override;
void viewWillAppear() override;
void viewDidDisappear() override;
//ListViewDataSource
virtual KDCoordinate rowHeight(int j) override;
HighlightCell * reusableCell(int index, int type) override;
protected:
class Stack {
public:
class State {
public:
State(int selectedRow = -1, KDCoordinate verticalScroll = 0);
bool isNull();
int selectedRow() { return m_selectedRow; }
KDCoordinate verticalScroll() { return m_verticalScroll; }
private:
int m_selectedRow;
KDCoordinate m_verticalScroll;
};
void push(int selectedRow, KDCoordinate verticalScroll);
State * stateAtIndex(int index);
State pop();
int depth();
void resetStack();
private:
constexpr static int k_maxModelTreeDepth = 3;
State m_statesStack[k_maxModelTreeDepth];
};
class ListController : public ViewController {
public:
ListController(Responder * parentResponder, SelectableTableView * tableView, const char * title);
const char * title() override;
View * view() override;
void didBecomeFirstResponder() override;
void setFirstSelectedRow(int firstSelectedRow);
private:
SelectableTableView * m_selectableTableView;
int m_firstSelectedRow;
const char * m_title;
};
static constexpr int LeafCellType = 0;
static constexpr int NodeCellType = 1;
int stackDepth();
bool handleEventForRow(Ion::Events::Event event, int selectedRow);
virtual bool selectSubMenu(int selectedRow);
virtual bool returnToPreviousMenu();
virtual bool selectLeaf(int selectedRow) = 0;
Responder * sender() { return m_sender; }
virtual HighlightCell * leafCellAtIndex(int index) = 0;
virtual HighlightCell * nodeCellAtIndex(int index) = 0;
SelectableTableView m_selectableTableView;
Stack m_stack;
ListController m_listController;
private:
Responder * m_sender;
};
#endif

View File

@@ -1,92 +1,37 @@
#ifndef ESCHER_TOOLBOX_H
#define ESCHER_TOOLBOX_H
#include <escher/highlight_cell.h>
#include <escher/list_view_data_source.h>
#include <escher/message_table_cell_with_message.h>
#include <escher/message_table_cell_with_chevron.h>
#include <escher/selectable_table_view.h>
#include <escher/stack_view_controller.h>
#include <escher/nested_menu_controller.h>
#include <escher/toolbox_message_tree.h>
class Toolbox : public StackViewController, public ListViewDataSource, public SelectableTableViewDataSource {
class Toolbox : public NestedMenuController {
public:
Toolbox(Responder * parentResponder, const char * title = 0);
void setSender(Responder * sender) { m_sender = sender; }
// StackViewController
bool handleEvent(Ion::Events::Event event) override;
void didBecomeFirstResponder() override;
void viewWillAppear() override;
void viewDidDisappear() override;
//ListViewDataSource
virtual KDCoordinate rowHeight(int j) override;
int numberOfRows() override;
HighlightCell * reusableCell(int index, int type) override;
int reusableCellCount(int type) override;
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
int typeAtLocation(int i, int j) override;
protected:
constexpr static int k_maxMessageSize = 100;
class Stack {
public:
class State {
public:
State(int selectedRow = -1, KDCoordinate verticalScroll = 0);
bool isNull();
int selectedRow() { return m_selectedRow; }
KDCoordinate verticalScroll() { return m_verticalScroll; }
private:
int m_selectedRow;
KDCoordinate m_verticalScroll;
};
void push(int selectedRow, KDCoordinate verticalScroll);
void pop();
State * stateAtIndex(int index);
int depth();
void resetStack();
private:
constexpr static int k_maxModelTreeDepth = 3;
State m_statesStack[k_maxModelTreeDepth];
};
class ListController : public ViewController {
public:
ListController(Responder * parentResponder, SelectableTableView * tableView, const char * title);
const char * title() override;
View * view() override;
void didBecomeFirstResponder() override;
void setFirstSelectedRow(int firstSelectedRow);
private:
SelectableTableView * m_selectableTableView;
int m_firstSelectedRow;
const char * m_title;
};
static constexpr int LeafCellType = 0;
static constexpr int NodeCellType = 1;
int stackDepth();
bool handleEventForRow(Ion::Events::Event event, int selectedRow);
bool selectSubMenu(ToolboxMessageTree * selectedMessageTree);
bool returnToPreviousMenu();
Responder * sender() { return m_sender; }
virtual bool selectLeaf(ToolboxMessageTree * selectedMessageTree) = 0;
virtual const ToolboxMessageTree * rootModel() = 0;
virtual MessageTableCellWithMessage * leafCellAtIndex(int index) = 0;
virtual MessageTableCellWithChevron * nodeCellAtIndex(int index) = 0;
bool selectSubMenu(int selectedRow) override;
bool returnToPreviousMenu() override;
virtual int maxNumberOfDisplayedRows() = 0;
SelectableTableView m_selectableTableView;
virtual const ToolboxMessageTree * rootModel() = 0;
virtual MessageTableCellWithMessage * leafCellAtIndex(int index) override = 0;
virtual MessageTableCellWithChevron * nodeCellAtIndex(int index) override = 0;
Stack m_stack;
ListController m_listController;
ToolboxMessageTree * m_messageTreeModel;
/* m_messageTreeModel points at the messageTree of the tree (describing the
* whole model) where we are located. It enables to know which rows are leaves
* and which are subtrees. */
private:
Responder * m_sender;
};
#endif

View File

@@ -0,0 +1,166 @@
#include <escher/nested_menu_controller.h>
#include <escher/metric.h>
#include <assert.h>
#include <string.h>
/* State */
NestedMenuController::Stack::State::State(int selectedRow, KDCoordinate verticalScroll) :
m_selectedRow(selectedRow),
m_verticalScroll(verticalScroll)
{
}
bool NestedMenuController::Stack::State::isNull(){
if (m_selectedRow == -1) {
return true;
}
return false;
}
/* Stack */
void NestedMenuController::Stack::push(int selectedRow, KDCoordinate verticalScroll) {
int i = 0;
while (!m_statesStack[i].isNull() && i < k_maxModelTreeDepth) {
i++;
}
assert(m_statesStack[i].isNull());
m_statesStack[i] = State(selectedRow, verticalScroll);
}
NestedMenuController::Stack::State * NestedMenuController::Stack::stateAtIndex(int index) {
return &m_statesStack[index];
}
int NestedMenuController::Stack::depth() {
int depth = 0;
for (int i = 0; i < k_maxModelTreeDepth; i++) {
depth += (!m_statesStack[i].isNull());
}
return depth;
}
NestedMenuController::Stack::State NestedMenuController::Stack::pop() {
int stackDepth = depth();
if (stackDepth == 0) {
return State();
}
NestedMenuController::Stack::State state = m_statesStack[stackDepth-1];
m_statesStack[stackDepth-1] = State();
return state;
}
void NestedMenuController::Stack::resetStack() {
for (int i = 0; i < k_maxModelTreeDepth; i++) {
m_statesStack[i] = State();
}
}
/* List Controller */
NestedMenuController::ListController::ListController(Responder * parentResponder, SelectableTableView * tableView, const char * title) :
ViewController(parentResponder),
m_selectableTableView(tableView),
m_firstSelectedRow(0),
m_title(title)
{
}
const char * NestedMenuController::ListController::title() {
return m_title;
}
View * NestedMenuController::ListController::view() {
return m_selectableTableView;
}
void NestedMenuController::ListController::didBecomeFirstResponder() {
m_selectableTableView->reloadData();
m_selectableTableView->selectCellAtLocation(0, m_firstSelectedRow);
app()->setFirstResponder(m_selectableTableView);
}
void NestedMenuController::ListController::setFirstSelectedRow(int firstSelectedRow) {
m_firstSelectedRow = firstSelectedRow;
}
/* NestedMenuController */
NestedMenuController::NestedMenuController(Responder * parentResponder, const char * title) :
StackViewController(parentResponder, &m_listController, KDColorWhite, Palette::PurpleBright, Palette::PurpleDark),
m_selectableTableView(&m_listController, this, this),
m_listController(this, &m_selectableTableView, title),
m_sender(nullptr)
{
m_selectableTableView.setMargins(0);
m_selectableTableView.setShowsIndicators(false);
}
bool NestedMenuController::handleEvent(Ion::Events::Event event) {
return handleEventForRow(event, selectedRow());
}
void NestedMenuController::didBecomeFirstResponder() {
app()->setFirstResponder(&m_listController);
}
void NestedMenuController::viewWillAppear() {
StackViewController::viewWillAppear();
m_selectableTableView.reloadData();
m_stack.resetStack();
m_listController.setFirstSelectedRow(0);
}
void NestedMenuController::viewDidDisappear() {
StackViewController::viewDidDisappear();
m_selectableTableView.deselectTable();
}
KDCoordinate NestedMenuController::rowHeight(int j) {
return Metric::ToolboxRowHeight;
}
HighlightCell * NestedMenuController::reusableCell(int index, int type) {
assert(type < 2);
assert(index >= 0);
if (type == LeafCellType) {
return leafCellAtIndex(index);
}
return nodeCellAtIndex(index);
}
int NestedMenuController::stackDepth() {
return m_stack.depth();
}
bool NestedMenuController::handleEventForRow(Ion::Events::Event event, int selectedRow) {
int depth = m_stack.depth();
if ((event == Ion::Events::Back || event == Ion::Events::Left) && depth > 0) {
return returnToPreviousMenu();
}
if ((event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) && typeAtLocation(0, selectedRow) == NodeCellType) {
return selectSubMenu(selectedRow);
}
if ((event == Ion::Events::OK || event == Ion::Events::EXE) && typeAtLocation(0, selectedRow) == LeafCellType) {
return selectLeaf(selectedRow);
}
return false;
}
bool NestedMenuController::selectSubMenu(int selectedRow) {
m_stack.push(selectedRow, m_selectableTableView.contentOffset().y());
m_listController.setFirstSelectedRow(0);
app()->setFirstResponder(&m_listController);
return true;
}
bool NestedMenuController::returnToPreviousMenu() {
assert(m_stack.depth() > 0);
NestedMenuController::Stack::State state = m_stack.pop();
m_listController.setFirstSelectedRow(state.selectedRow());
KDPoint scroll = m_selectableTableView.contentOffset();
m_selectableTableView.setContentOffset(KDPoint(scroll.x(), state.verticalScroll()));
app()->setFirstResponder(&m_listController);
return true;
}

View File

@@ -3,122 +3,14 @@
#include <assert.h>
#include <string.h>
/* State */
Toolbox::Stack::State::State(int selectedRow, KDCoordinate verticalScroll) :
m_selectedRow(selectedRow),
m_verticalScroll(verticalScroll)
{
}
bool Toolbox::Stack::State::isNull(){
if (m_selectedRow == -1) {
return true;
}
return false;
}
/* Stack */
void Toolbox::Stack::push(int selectedRow, KDCoordinate verticalScroll) {
int i = 0;
while (!m_statesStack[i].isNull() && i < k_maxModelTreeDepth) {
i++;
}
assert(m_statesStack[i].isNull());
m_statesStack[i] = State(selectedRow, verticalScroll);
}
Toolbox::Stack::State * Toolbox::Stack::stateAtIndex(int index) {
return &m_statesStack[index];
}
int Toolbox::Stack::depth() {
int depth = 0;
for (int i = 0; i < k_maxModelTreeDepth; i++) {
depth += (!m_statesStack[i].isNull());
}
return depth;
}
void Toolbox::Stack::pop() {
int stackDepth = depth();
if (stackDepth == 0) {
return;
}
m_statesStack[stackDepth-1] = State();
}
void Toolbox::Stack::resetStack() {
for (int i = 0; i < k_maxModelTreeDepth; i++) {
m_statesStack[i] = State();
}
}
/* List Controller */
Toolbox::ListController::ListController(Responder * parentResponder, SelectableTableView * tableView, const char * title) :
ViewController(parentResponder),
m_selectableTableView(tableView),
m_firstSelectedRow(0),
m_title(title)
{
}
const char * Toolbox::ListController::title() {
return m_title;
}
View * Toolbox::ListController::view() {
return m_selectableTableView;
}
void Toolbox::ListController::didBecomeFirstResponder() {
m_selectableTableView->reloadData();
m_selectableTableView->selectCellAtLocation(0, m_firstSelectedRow);
app()->setFirstResponder(m_selectableTableView);
}
void Toolbox::ListController::setFirstSelectedRow(int firstSelectedRow) {
m_firstSelectedRow = firstSelectedRow;
}
/* Toolbox */
Toolbox::Toolbox(Responder * parentResponder, const char * title) :
StackViewController(parentResponder, &m_listController, KDColorWhite, Palette::PurpleBright, Palette::PurpleDark),
m_selectableTableView(&m_listController, this, this),
m_listController(this, &m_selectableTableView, title),
m_messageTreeModel(nullptr),
m_sender(nullptr)
{
m_selectableTableView.setMargins(0);
m_selectableTableView.setShowsIndicators(false);
}
bool Toolbox::handleEvent(Ion::Events::Event event) {
return handleEventForRow(event, selectedRow());
}
void Toolbox::didBecomeFirstResponder() {
app()->setFirstResponder(&m_listController);
}
NestedMenuController(parentResponder, title),
m_messageTreeModel(nullptr)
{}
void Toolbox::viewWillAppear() {
StackViewController::viewWillAppear();
m_messageTreeModel = (ToolboxMessageTree *)rootModel();
m_selectableTableView.reloadData();
m_stack.resetStack();
m_listController.setFirstSelectedRow(0);
}
void Toolbox::viewDidDisappear() {
StackViewController::viewDidDisappear();
m_selectableTableView.deselectTable();
}
KDCoordinate Toolbox::rowHeight(int j) {
return Metric::ToolboxRowHeight;
NestedMenuController::viewWillAppear();
}
int Toolbox::numberOfRows() {
@@ -128,16 +20,6 @@ int Toolbox::numberOfRows() {
return m_messageTreeModel->numberOfChildren();
}
HighlightCell * Toolbox::reusableCell(int index, int type) {
assert(type < 2);
assert(index >= 0);
assert(index < maxNumberOfDisplayedRows());
if (type == LeafCellType) {
return leafCellAtIndex(index);
}
return nodeCellAtIndex(index);
}
int Toolbox::reusableCellCount(int type) {
return maxNumberOfDisplayedRows();
}
@@ -163,35 +45,14 @@ int Toolbox::typeAtLocation(int i, int j) {
return NodeCellType;
}
int Toolbox::stackDepth() {
return m_stack.depth();
}
bool Toolbox::handleEventForRow(Ion::Events::Event event, int selectedRow) {
int depth = m_stack.depth();
if ((event == Ion::Events::Back || event == Ion::Events::Left) && depth > 0) {
return returnToPreviousMenu();
}
ToolboxMessageTree * selectedMessageTree = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow);
if ((event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) && selectedMessageTree->numberOfChildren() > 0) {
return selectSubMenu(selectedMessageTree);
}
if ((event == Ion::Events::OK || event == Ion::Events::EXE) && selectedMessageTree->numberOfChildren() == 0) {
return selectLeaf(selectedMessageTree);
}
return false;
}
bool Toolbox::selectSubMenu(ToolboxMessageTree * selectedMessageTree) {
m_stack.push(selectedRow(), m_selectableTableView.contentOffset().y());
bool Toolbox::selectSubMenu(int selectedRow) {
m_selectableTableView.deselectTable();
m_messageTreeModel = selectedMessageTree;
m_listController.setFirstSelectedRow(0);
app()->setFirstResponder(&m_listController);
return true;
m_messageTreeModel = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow);
return NestedMenuController::selectSubMenu(selectedRow);
}
bool Toolbox::returnToPreviousMenu() {
m_selectableTableView.deselectTable();
int currentDepth = m_stack.depth();
int index = 0;
// We want to get the current ToolboxMessageTree's parent ToolboxMessageTree,
@@ -199,17 +60,11 @@ bool Toolbox::returnToPreviousMenu() {
// start from the root ToolboxMessageTree and sequentially get the selected
// child until it has the wanted depth.
ToolboxMessageTree * parentMessageTree = (ToolboxMessageTree *)rootModel();
Stack::State * previousState = m_stack.stateAtIndex(index++);;
Stack::State * previousState = m_stack.stateAtIndex(index++);
while (currentDepth-- > 1) {
parentMessageTree = (ToolboxMessageTree *)parentMessageTree->children(previousState->selectedRow());
previousState = m_stack.stateAtIndex(index++);
}
m_selectableTableView.deselectTable();
m_messageTreeModel = parentMessageTree;
m_listController.setFirstSelectedRow(previousState->selectedRow());
KDPoint scroll = m_selectableTableView.contentOffset();
m_selectableTableView.setContentOffset(KDPoint(scroll.x(), previousState->verticalScroll()));
m_stack.pop();
app()->setFirstResponder(&m_listController);
return true;
return NestedMenuController::returnToPreviousMenu();
}