mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-19 05:40:38 +01:00
[apps] split the tool box controller into a table view controller and a
navigation controller Change-Id: I9e1e91ba547df245f22dc44df0637cd7205e3420
This commit is contained in:
@@ -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\
|
||||
)
|
||||
|
||||
|
||||
110
apps/node_list_view_controller.cpp
Normal file
110
apps/node_list_view_controller.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "tool_box_controller.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
45
apps/node_list_view_controller.h
Normal file
45
apps/node_list_view_controller.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef APPS_NODE_LIST_VIEW_CONTROLLER_H
|
||||
#define APPS_NODE_LIST_VIEW_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#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
|
||||
@@ -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) {
|
||||
|
||||
@@ -3,35 +3,43 @@
|
||||
|
||||
#include <escher.h>
|
||||
#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
|
||||
|
||||
Reference in New Issue
Block a user