diff --git a/apps/Makefile b/apps/Makefile index a391b59d1..7fefeb149 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -10,6 +10,7 @@ app_objs += $(addprefix apps/,\ expression_text_field_delegate.o\ main.o\ node.o\ + node_list_view_controller.o\ tool_box_controller.o\ ) diff --git a/apps/node_list_view_controller.cpp b/apps/node_list_view_controller.cpp new file mode 100644 index 000000000..793a6cc1e --- /dev/null +++ b/apps/node_list_view_controller.cpp @@ -0,0 +1,110 @@ +#include "tool_box_controller.h" +#include +#include + +NodeListViewController::NodeListViewController(Responder * parentResponder) : + ViewController(parentResponder), + m_selectableTableView(SelectableTableView(this, this)), + m_nodeModel(nullptr), + m_firstSelectedRow(0) +{ +} + +View * NodeListViewController::view() { + return &m_selectableTableView; +} + +const char * NodeListViewController::title() const { + return m_nodeModel->label(); +} + +Node * NodeListViewController::nodeModel() { + return m_nodeModel; +} + +void NodeListViewController::setNodeModel(Node * nodeModel) { + m_nodeModel = nodeModel; +} + +void NodeListViewController::setFirstSelectedRow(int firstSelectedRow) { + m_firstSelectedRow = firstSelectedRow; +} + +int NodeListViewController::selectedRow() { + return m_selectableTableView.selectedRow(); +} + +void NodeListViewController::setVerticalScroll(KDCoordinate verticalScroll) { + KDPoint scroll = m_selectableTableView.contentOffset(); + m_selectableTableView.setContentOffset(KDPoint(scroll.x(), verticalScroll)); +} + +KDCoordinate NodeListViewController::verticalScroll() { + return m_selectableTableView.contentOffset().y(); +} + +void NodeListViewController::deselectTable() { + m_selectableTableView.deselectTable(); +} + +void NodeListViewController::didBecomeFirstResponder() { + m_selectableTableView.reloadData(); + m_selectableTableView.selectCellAtLocation(0, m_firstSelectedRow); + app()->setFirstResponder(&m_selectableTableView); +} + +int NodeListViewController::numberOfRows() { + return m_nodeModel->numberOfChildren(); +} + +TableViewCell * NodeListViewController::reusableCell(int index, int type) { + assert(type < 2); + assert(index >= 0); + assert(index < k_maxNumberOfDisplayedRows); + if (type == 0) { + return &m_commandCells[index]; + } + return &m_menuCells[index]; +} + +int NodeListViewController::reusableCellCount(int type) { + assert(type < 2); + return k_maxNumberOfDisplayedRows; +} + +void NodeListViewController::willDisplayCellForIndex(TableViewCell * cell, int index) { + MenuListCell * myCell = (MenuListCell *)cell; + myCell->setText(m_nodeModel->children(index)->label()); +} + +KDCoordinate NodeListViewController::rowHeight(int j) { + if (typeAtLocation(0, j) == 0) { + return k_leafRowHeight; + } + return k_nodeRowHeight; +} + +KDCoordinate NodeListViewController::cumulatedHeightFromIndex(int j) { + int result = 0; + for (int k = 0; k < j; k++) { + result += rowHeight(k); + } + return result; +} + +int NodeListViewController::indexFromCumulatedHeight(KDCoordinate offsetY) { + int result = 0; + int j = 0; + while (result < offsetY && j < numberOfRows()) { + result += rowHeight(j++); + } + return (result < offsetY || offsetY == 0) ? j : j - 1; +} + +int NodeListViewController::typeAtLocation(int i, int j) { + Node * node = (Node *)m_nodeModel->children(j); + if (node->numberOfChildren() == 0) { + return 0; + } + return 1; +} diff --git a/apps/node_list_view_controller.h b/apps/node_list_view_controller.h new file mode 100644 index 000000000..6d1e2820b --- /dev/null +++ b/apps/node_list_view_controller.h @@ -0,0 +1,45 @@ +#ifndef APPS_NODE_LIST_VIEW_CONTROLLER_H +#define APPS_NODE_LIST_VIEW_CONTROLLER_H + +#include +#include "node.h" + +/* m_nodeModel points at the node of the tree (describing the whole model) + * where we are located. It enables to know which rows are leaves and which are + * subtrees. */ + +class NodeListViewController : public ViewController, public ListViewDataSource { +public: + NodeListViewController(Responder * parentResponder); + View * view() override; + const char * title() const override; + void didBecomeFirstResponder() override; + + int numberOfRows() override; + TableViewCell * reusableCell(int index, int type) override; + int reusableCellCount(int type) override; + void willDisplayCellForIndex(TableViewCell * cell, int index) override; + KDCoordinate rowHeight(int j) override; + KDCoordinate cumulatedHeightFromIndex(int j) override; + int indexFromCumulatedHeight(KDCoordinate offsetY) override; + int typeAtLocation(int i, int j) override; + + void setNodeModel(Node * nodeModel); + Node * nodeModel(); + void setFirstSelectedRow(int firstSelectedRow); + int selectedRow(); + void setVerticalScroll(KDCoordinate verticalScroll); + KDCoordinate verticalScroll(); + void deselectTable(); +private: + constexpr static int k_maxNumberOfDisplayedRows = 6; //240/40 + constexpr static KDCoordinate k_leafRowHeight = 50; + constexpr static KDCoordinate k_nodeRowHeight = 40; + MenuListCell m_commandCells[k_maxNumberOfDisplayedRows]; + ChevronMenuListCell m_menuCells[k_maxNumberOfDisplayedRows]; + SelectableTableView m_selectableTableView; + Node * m_nodeModel; + int m_firstSelectedRow; +}; + +#endif diff --git a/apps/tool_box_controller.cpp b/apps/tool_box_controller.cpp index 267806b82..61f2c9ce8 100644 --- a/apps/tool_box_controller.cpp +++ b/apps/tool_box_controller.cpp @@ -23,10 +23,71 @@ const Node menu[11] = {Node("|x|", "abs()"), Node("root(x)", "root(,)"), Node("l Node("Approximation", nullptr, approximationChildren, 4), Node("Trigonometrie", nullptr, trigonometryChildren, 6)}; const Node toolBoxModel = Node("ToolBox", nullptr, menu, 11); +/* State */ + +ToolBoxController::Stack::State::State(int selectedRow, KDCoordinate verticalScroll) : + m_selectedRow(selectedRow), + m_verticalScroll(verticalScroll) +{ +} + +int ToolBoxController::Stack::State::selectedRow() { + return m_selectedRow; +} + +KDCoordinate ToolBoxController::Stack::State::verticalScroll() { + return m_verticalScroll; +} + +bool ToolBoxController::Stack::State::isNull(){ + if (m_selectedRow == -1) { + return true; + } + return false; +} + +/* Stack */ + +void ToolBoxController::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); +} + +ToolBoxController::Stack::State * ToolBoxController::Stack::stateAtIndex(int index) { + return &m_statesStack[index]; +} + +int ToolBoxController::Stack::depth() { + int depth = 0; + for (int i = 0; i < k_maxModelTreeDepth; i++) { + depth += (!m_statesStack[i].isNull()); + } + return depth; +} + +void ToolBoxController::Stack::pop() { + int stackDepth = depth(); + if (stackDepth == 0) { + return; + } + m_statesStack[stackDepth-1] = State(); +} + +void ToolBoxController::Stack::resetStack() { + for (int i = 0; i < k_maxModelTreeDepth; i++) { + m_statesStack[i] = State(); + } +} + +/* ToolBoxController */ + ToolBoxController::ToolBoxController() : - StackViewController(nullptr, this, true), - m_selectableTableView(SelectableTableView(this, this)), - m_toolBoxModel((Node *)&toolBoxModel) + StackViewController(nullptr, &m_listViewController, true), + m_listViewController(NodeListViewController(this)) { } @@ -35,94 +96,76 @@ const char * ToolBoxController::title() const { } bool ToolBoxController::handleEvent(Ion::Events::Event event) { - if (event != Ion::Events::Event::ENTER) { - return false; - } - int selectedRow = m_selectableTableView.selectedRow(); - Node * selectedNode = (Node *) m_toolBoxModel->children(selectedRow); - if (selectedNode->numberOfChildren() == 0) { - const char * editedText = selectedNode->text(); - m_textFieldCaller->appendText(editedText); - int cursorPosition = 0; - int editedTextLength = strlen(editedText); - for (int i = 0; i < editedTextLength; i++) { - if (editedText[i] == '(') { - cursorPosition = i + 1 - editedTextLength; - break; + switch (event) { + case Ion::Events::Event::ESC: + return returnToPreviousMenu(); + case Ion::Events::Event::ENTER: + { + int selectedRow = m_listViewController.selectedRow(); + Node * selectedNode = (Node *)m_listViewController.nodeModel()->children(selectedRow); + if (selectedNode->numberOfChildren() == 0) { + return editMathFunction(selectedNode); } + return selectSubMenu(selectedNode); } - m_textFieldCaller->moveCursor(cursorPosition); - m_selectableTableView.deselectTable(); + default: + return false; + } +} + +bool ToolBoxController::returnToPreviousMenu() { + m_listViewController.deselectTable(); + int depth = m_stack.depth(); + if (depth == 0) { app()->dismissModalViewController(); return true; } - m_selectableTableView.deselectTable(); - m_toolBoxModel = (Node *)m_toolBoxModel->children(selectedRow); - m_selectableTableView.reloadData(); - m_selectableTableView.selectCellAtLocation(0, 0); + int index = 0; + Node * parentNode = (Node *)&toolBoxModel; + Stack::State * previousState = m_stack.stateAtIndex(index++);; + while (depth-- > 1) { + parentNode = (Node *)parentNode->children(previousState->selectedRow()); + previousState = m_stack.stateAtIndex(index++); + } + m_listViewController.deselectTable(); + m_listViewController.setNodeModel(parentNode); + m_listViewController.setFirstSelectedRow(previousState->selectedRow()); + m_listViewController.setVerticalScroll(previousState->verticalScroll()); + m_stack.pop(); + app()->setFirstResponder(&m_listViewController); + return true; +} + +bool ToolBoxController::selectSubMenu(Node * selectedNode) { + m_stack.push(m_listViewController.selectedRow(), m_listViewController.verticalScroll()); + m_listViewController.deselectTable(); + m_listViewController.setNodeModel(selectedNode); + m_listViewController.setFirstSelectedRow(0); + app()->setFirstResponder(&m_listViewController); + return true; +} + +bool ToolBoxController::editMathFunction(Node * selectedNode){ + const char * editedText = selectedNode->text(); + m_textFieldCaller->appendText(editedText); + int cursorPosition = 0; + int editedTextLength = strlen(editedText); + for (int i = 0; i < editedTextLength; i++) { + if (editedText[i] == '(') { + cursorPosition = i + 1 - editedTextLength; + break; + } + } + m_textFieldCaller->moveCursor(cursorPosition); + app()->dismissModalViewController(); return true; } void ToolBoxController::didBecomeFirstResponder() { - m_toolBoxModel = (Node *)&toolBoxModel; - m_selectableTableView.selectCellAtLocation(0, 0); - app()->setFirstResponder(&m_selectableTableView); -} - -int ToolBoxController::numberOfRows() { - return m_toolBoxModel->numberOfChildren(); -} - -TableViewCell * ToolBoxController::reusableCell(int index, int type) { - assert(type < 2); - assert(index >= 0); - assert(index < k_maxNumberOfDisplayedRows); - if (type == 0) { - return &m_commandCells[index]; - } - return &m_menuCells[index]; -} - -int ToolBoxController::reusableCellCount(int type) { - assert(type < 2); - return k_maxNumberOfDisplayedRows; -} - -void ToolBoxController::willDisplayCellForIndex(TableViewCell * cell, int index) { - MenuListCell * myCell = (MenuListCell *)cell; - myCell->setText(m_toolBoxModel->children(index)->label()); -} - -KDCoordinate ToolBoxController::rowHeight(int j) { - if (typeAtLocation(0, j) == 0) { - return k_commandRowHeight; - } - return k_menuRowHeight; -} - -KDCoordinate ToolBoxController::cumulatedHeightFromIndex(int j) { - int result = 0; - for (int k = 0; k < j; k++) { - result += rowHeight(k); - } - return result; -} - -int ToolBoxController::indexFromCumulatedHeight(KDCoordinate offsetY) { - int result = 0; - int j = 0; - while (result < offsetY && j < numberOfRows()) { - result += rowHeight(j++); - } - return (result < offsetY || offsetY == 0) ? j : j - 1; -} - -int ToolBoxController::typeAtLocation(int i, int j) { - Node * node = (Node *)m_toolBoxModel->children(j); - if (node->numberOfChildren() == 0) { - return 0; - } - return 1; + m_stack.resetStack(); + m_listViewController.setNodeModel((Node *)&toolBoxModel); + m_listViewController.setFirstSelectedRow(0); + app()->setFirstResponder(&m_listViewController); } void ToolBoxController::setTextFieldCaller(TextField * textField) { diff --git a/apps/tool_box_controller.h b/apps/tool_box_controller.h index 1f811090c..8677b46d4 100644 --- a/apps/tool_box_controller.h +++ b/apps/tool_box_controller.h @@ -3,35 +3,43 @@ #include #include "node.h" +#include "node_list_view_controller.h" -/* m_toolBoxModel points at the node of the tree (describing the whole toolbox) - * where we are located. It enables to know which rows are leaves and which are - * subtrees. */ - -class ToolBoxController : public StackViewController, public ListViewDataSource { +class ToolBoxController : public StackViewController { public: ToolBoxController(); const char * title() const override; bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; - int numberOfRows() override; - TableViewCell * reusableCell(int index, int type) override; - int reusableCellCount(int type) override; - void willDisplayCellForIndex(TableViewCell * cell, int index) override; - KDCoordinate rowHeight(int j) override; - KDCoordinate cumulatedHeightFromIndex(int j) override; - int indexFromCumulatedHeight(KDCoordinate offsetY) override; - int typeAtLocation(int i, int j) override; void setTextFieldCaller(TextField * textField); private: - constexpr static int k_maxNumberOfDisplayedRows = 6; //240/40 - constexpr static KDCoordinate k_commandRowHeight = 50; - constexpr static KDCoordinate k_menuRowHeight = 40; - MenuListCell m_commandCells[k_maxNumberOfDisplayedRows]; - ChevronMenuListCell m_menuCells[k_maxNumberOfDisplayedRows]; - SelectableTableView m_selectableTableView; + class Stack { + public: + class State { + public: + State(int selectedRow = -1, KDCoordinate verticalScroll = 0); + bool isNull(); + int selectedRow(); + KDCoordinate 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 = 2; + State m_statesStack[k_maxModelTreeDepth]; + }; + bool editMathFunction(Node * selectedNode); + bool selectSubMenu(Node * selectedNode); + bool returnToPreviousMenu(); + NodeListViewController m_listViewController; TextField * m_textFieldCaller; - Node * m_toolBoxModel; + Stack m_stack; }; #endif