mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-22 15:20:39 +01:00
Merge changes I180fe53a,Ie273b6d7
* changes: [escher] Add toolbox event handling in responder [apps] Correct the text field delegate app accordingly [apps] Merge toolboxcontroller and node list controller
This commit is contained in:
@@ -16,10 +16,9 @@ app_objs += $(addprefix apps/,\
|
||||
constant.o\
|
||||
global_preferences.o\
|
||||
main.o\
|
||||
math_toolbox.o\
|
||||
node.o\
|
||||
node_list_view_controller.o\
|
||||
title_bar_view.o\
|
||||
toolbox_controller.o\
|
||||
toolbox_leaf_cell.o\
|
||||
toolbox_node.o\
|
||||
variable_box_controller.o\
|
||||
|
||||
@@ -48,8 +48,8 @@ Context * AppsContainer::globalContext() {
|
||||
return &m_globalContext;
|
||||
}
|
||||
|
||||
ToolboxController * AppsContainer::toolboxController() {
|
||||
return &m_toolboxController;
|
||||
MathToolbox * AppsContainer::mathToolbox() {
|
||||
return &m_mathToolbox;
|
||||
}
|
||||
|
||||
VariableBoxController * AppsContainer::variableBoxController() {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "settings/app.h"
|
||||
#include "statistics/app.h"
|
||||
#include "apps_window.h"
|
||||
#include "toolbox_controller.h"
|
||||
#include "math_toolbox.h"
|
||||
#include "variable_box_controller.h"
|
||||
|
||||
#define USE_PIC_VIEW_APP 0
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
int numberOfApps();
|
||||
App * appAtIndex(int index);
|
||||
Poincare::Context * globalContext();
|
||||
ToolboxController * toolboxController();
|
||||
MathToolbox * mathToolbox();
|
||||
VariableBoxController * variableBoxController();
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void switchTo(App * app) override;
|
||||
@@ -46,7 +46,7 @@ private:
|
||||
PicViewApp m_picViewApp;
|
||||
#endif
|
||||
Poincare::GlobalContext m_globalContext;
|
||||
ToolboxController m_toolboxController;
|
||||
MathToolbox m_mathToolbox;
|
||||
VariableBoxController m_variableBoxController;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "../apps_container.h"
|
||||
#include <assert.h>
|
||||
|
||||
using namespace Shared;
|
||||
|
||||
namespace Calculation {
|
||||
|
||||
EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate) :
|
||||
@@ -75,11 +77,6 @@ void EditExpressionController::didBecomeFirstResponder() {
|
||||
app()->setFirstResponder(m_contentView.textField());
|
||||
}
|
||||
|
||||
bool EditExpressionController::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) {
|
||||
App * myApp = (App *)app();
|
||||
return myApp->textFieldDidReceiveEvent(textField, event);
|
||||
}
|
||||
|
||||
bool EditExpressionController::textFieldDidFinishEditing(::TextField * textField, const char * text) {
|
||||
App * calculationApp = (App *)app();
|
||||
m_calculationStore->push(textBody(), calculationApp->localContext());
|
||||
@@ -89,4 +86,8 @@ bool EditExpressionController::textFieldDidFinishEditing(::TextField * textField
|
||||
return true;
|
||||
}
|
||||
|
||||
TextFieldDelegateApp * EditExpressionController::textFieldDelegateApp() {
|
||||
return (App *)app();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#include "history_controller.h"
|
||||
#include "calculation_store.h"
|
||||
#include "text_field.h"
|
||||
#include "../shared/text_field_delegate.h"
|
||||
|
||||
namespace Calculation {
|
||||
class HistoryController;
|
||||
|
||||
class EditExpressionController : public ViewController, public TextFieldDelegate {
|
||||
class EditExpressionController : public ViewController, public Shared::TextFieldDelegate {
|
||||
public:
|
||||
EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore);
|
||||
View * view() override;
|
||||
@@ -18,7 +19,6 @@ public:
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
const char * textBody();
|
||||
void setTextBody(const char * text);
|
||||
bool textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) override;
|
||||
bool textFieldDidFinishEditing(::TextField * textField, const char * text) override;
|
||||
private:
|
||||
class ContentView : public View {
|
||||
@@ -35,6 +35,7 @@ private:
|
||||
TextField m_textField;
|
||||
char m_textBody[255];
|
||||
};
|
||||
Shared::TextFieldDelegateApp * textFieldDelegateApp() override;
|
||||
ContentView m_contentView;
|
||||
HistoryController * m_historyController;
|
||||
CalculationStore * m_calculationStore;
|
||||
|
||||
273
apps/math_toolbox.cpp
Normal file
273
apps/math_toolbox.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
#include "math_toolbox.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* TODO: find a shorter way to initiate tree models
|
||||
* We create one model tree: each node keeps the label of the row it refers to
|
||||
* and the text which would be edited by clicking on the row. When the node is a
|
||||
* subtree, the edited text is set at nullptr. */
|
||||
|
||||
const ToolboxNode calculChildren[4] = {ToolboxNode("diff(,)", "Nombre derive"), ToolboxNode("int(,,)", "Integrale"), ToolboxNode("sum(,,)", "Somme"), ToolboxNode("product(,,)", "Produit")};
|
||||
const ToolboxNode complexChildren[5] = {ToolboxNode("abs()", "Module"), ToolboxNode("arg()", "Argument"), ToolboxNode("re()", "Partie reelle"), ToolboxNode("im()", "Partie imaginaire"), ToolboxNode("conj()", "Conjugue")};
|
||||
const ToolboxNode probabilityChildren[4] = {ToolboxNode("binomial()", "Combinaison"), ToolboxNode("permute(,)", "Arrangement"), ToolboxNode("random(,)", "Nombre aleatoire"), ToolboxNode("gamma()", "Fonction gamma")};
|
||||
const ToolboxNode arithmeticChildren[4] = {ToolboxNode("gcd()", "PGCD"), ToolboxNode("lcm()", "PPCM"), ToolboxNode("rem()", "Reste division euclidienne"), ToolboxNode("quo()","Quotien division euclidienne")};
|
||||
const ToolboxNode matricesChildren[5] = {ToolboxNode("inverse()", "Inverse"), ToolboxNode("det()", "Determinant"), ToolboxNode("transpose()", "Transposee"), ToolboxNode("trace()", "Trace"), ToolboxNode("dim()", "Taille")};
|
||||
const ToolboxNode listesChildren[5] = {ToolboxNode("sort<()", "Tri croissant"), ToolboxNode("sort>()", "Tri decroissant"), ToolboxNode("max()", "Maximum"), ToolboxNode("min()", "Minimum"), ToolboxNode("dim()", "Taille")};
|
||||
const ToolboxNode approximationChildren[4] = {ToolboxNode("floor()", "Partie entiere"), ToolboxNode("frac()", "Partie fractionnaire"), ToolboxNode("ceil()", "Plafond"), ToolboxNode("round(,)", "Arrondi")};
|
||||
const ToolboxNode trigonometryChildren[6] = {ToolboxNode("cosh()", "cosh"), ToolboxNode("sinh()", "sinh"), ToolboxNode("tanh()", "tanh"), ToolboxNode("acosh()", "acosh"), ToolboxNode("asinh()", "asinh"), ToolboxNode("atanh()", "atanh")};
|
||||
|
||||
const ToolboxNode menu[11] = {ToolboxNode("abs()", "Valeur absolue"), ToolboxNode("root(,)", "Racine k-ieme"), ToolboxNode("log(,)", "Logarithme base a"),
|
||||
ToolboxNode("Calcul", nullptr, calculChildren, 4), ToolboxNode("Nombre complexe", nullptr, complexChildren, 5),
|
||||
ToolboxNode("Probabilite", nullptr, probabilityChildren, 4), ToolboxNode("Arithmetique", nullptr, arithmeticChildren, 4),
|
||||
ToolboxNode("Matrice", nullptr, matricesChildren, 5), ToolboxNode("Liste", nullptr, listesChildren, 5),
|
||||
ToolboxNode("Approximation", nullptr, approximationChildren, 4), ToolboxNode("Trigonometrie", nullptr, trigonometryChildren, 6)};
|
||||
const ToolboxNode toolboxModel = ToolboxNode("Toolbox", nullptr, menu, 11);
|
||||
|
||||
/* State */
|
||||
|
||||
MathToolbox::Stack::State::State(int selectedRow, KDCoordinate verticalScroll) :
|
||||
m_selectedRow(selectedRow),
|
||||
m_verticalScroll(verticalScroll)
|
||||
{
|
||||
}
|
||||
|
||||
int MathToolbox::Stack::State::selectedRow() {
|
||||
return m_selectedRow;
|
||||
}
|
||||
|
||||
KDCoordinate MathToolbox::Stack::State::verticalScroll() {
|
||||
return m_verticalScroll;
|
||||
}
|
||||
|
||||
bool MathToolbox::Stack::State::isNull(){
|
||||
if (m_selectedRow == -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Stack */
|
||||
|
||||
void MathToolbox::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);
|
||||
}
|
||||
|
||||
MathToolbox::Stack::State * MathToolbox::Stack::stateAtIndex(int index) {
|
||||
return &m_statesStack[index];
|
||||
}
|
||||
|
||||
int MathToolbox::Stack::depth() {
|
||||
int depth = 0;
|
||||
for (int i = 0; i < k_maxModelTreeDepth; i++) {
|
||||
depth += (!m_statesStack[i].isNull());
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
void MathToolbox::Stack::pop() {
|
||||
int stackDepth = depth();
|
||||
if (stackDepth == 0) {
|
||||
return;
|
||||
}
|
||||
m_statesStack[stackDepth-1] = State();
|
||||
}
|
||||
|
||||
void MathToolbox::Stack::resetStack() {
|
||||
for (int i = 0; i < k_maxModelTreeDepth; i++) {
|
||||
m_statesStack[i] = State();
|
||||
}
|
||||
}
|
||||
|
||||
/* List Controller */
|
||||
|
||||
MathToolbox::ListController::ListController(Responder * parentResponder, SelectableTableView * tableView) :
|
||||
ViewController(parentResponder),
|
||||
m_selectableTableView(tableView),
|
||||
m_firstSelectedRow(0)
|
||||
{
|
||||
}
|
||||
|
||||
View * MathToolbox::ListController::view() {
|
||||
return m_selectableTableView;
|
||||
}
|
||||
|
||||
const char * MathToolbox::ListController::title() const {
|
||||
return toolboxModel.label();
|
||||
}
|
||||
|
||||
void MathToolbox::ListController::didBecomeFirstResponder() {
|
||||
m_selectableTableView->reloadData();
|
||||
m_selectableTableView->selectCellAtLocation(0, m_firstSelectedRow);
|
||||
app()->setFirstResponder(m_selectableTableView);
|
||||
}
|
||||
|
||||
void MathToolbox::ListController::setFirstSelectedRow(int firstSelectedRow) {
|
||||
m_firstSelectedRow = firstSelectedRow;
|
||||
}
|
||||
|
||||
/* MathToolbox */
|
||||
|
||||
MathToolbox::MathToolbox() :
|
||||
Toolbox(nullptr, &m_listController),
|
||||
m_selectableTableView(SelectableTableView(&m_listController, this, 0, 0, 0, 0, nullptr, false)),
|
||||
m_listController(this, &m_selectableTableView),
|
||||
m_nodeModel(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void MathToolbox::didBecomeFirstResponder() {
|
||||
m_nodeModel = (ToolboxNode *)rootModel();
|
||||
StackViewController::didBecomeFirstResponder();
|
||||
m_stack.resetStack();
|
||||
m_listController.setFirstSelectedRow(0);
|
||||
app()->setFirstResponder(&m_listController);
|
||||
}
|
||||
|
||||
bool MathToolbox::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Back) {
|
||||
return returnToPreviousMenu();
|
||||
}
|
||||
if (event == Ion::Events::OK) {
|
||||
int selectedRow = m_selectableTableView.selectedRow();
|
||||
ToolboxNode * selectedNode = (ToolboxNode *)m_nodeModel->children(selectedRow);
|
||||
if (selectedNode->numberOfChildren() == 0) {
|
||||
return selectLeaf(selectedNode);
|
||||
}
|
||||
return selectSubMenu(selectedNode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int MathToolbox::numberOfRows() {
|
||||
return m_nodeModel->numberOfChildren();
|
||||
}
|
||||
|
||||
TableViewCell * MathToolbox::reusableCell(int index, int type) {
|
||||
assert(type < 2);
|
||||
assert(index >= 0);
|
||||
assert(index < k_maxNumberOfDisplayedRows);
|
||||
if (type == 0) {
|
||||
return &m_leafCells[index];
|
||||
}
|
||||
return &m_nodeCells[index];
|
||||
}
|
||||
|
||||
int MathToolbox::reusableCellCount(int type) {
|
||||
assert(type < 2);
|
||||
return k_maxNumberOfDisplayedRows;
|
||||
}
|
||||
|
||||
void MathToolbox::willDisplayCellForIndex(TableViewCell * cell, int index) {
|
||||
ToolboxNode * node = (ToolboxNode *)m_nodeModel->children(index);
|
||||
if (node->numberOfChildren() == 0) {
|
||||
ToolboxLeafCell * myCell = (ToolboxLeafCell *)cell;
|
||||
myCell->setLabel(node->label());
|
||||
myCell->setSubtitle(node->text());
|
||||
return;
|
||||
}
|
||||
MenuListCell * myCell = (MenuListCell *)cell;
|
||||
myCell->setText(node->label());
|
||||
}
|
||||
|
||||
KDCoordinate MathToolbox::rowHeight(int j) {
|
||||
if (typeAtLocation(0, j) == 0) {
|
||||
return k_leafRowHeight;
|
||||
}
|
||||
return k_nodeRowHeight;
|
||||
}
|
||||
|
||||
KDCoordinate MathToolbox::cumulatedHeightFromIndex(int j) {
|
||||
int result = 0;
|
||||
for (int k = 0; k < j; k++) {
|
||||
result += rowHeight(k);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int MathToolbox::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 MathToolbox::typeAtLocation(int i, int j) {
|
||||
Node * node = (Node *)m_nodeModel->children(j);
|
||||
if (node->numberOfChildren() == 0) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void MathToolbox::viewWillDisappear() {
|
||||
m_selectableTableView.deselectTable();
|
||||
}
|
||||
|
||||
TextField * MathToolbox::sender() {
|
||||
return (TextField *)Toolbox::sender();
|
||||
}
|
||||
|
||||
const ToolboxNode * MathToolbox::rootModel() {
|
||||
return &toolboxModel;
|
||||
}
|
||||
|
||||
bool MathToolbox::selectLeaf(ToolboxNode * selectedNode){
|
||||
m_selectableTableView.deselectTable();
|
||||
ToolboxNode * node = selectedNode;
|
||||
const char * editedText = node->label();
|
||||
if (!sender()->isEditing()) {
|
||||
sender()->setEditing(true);
|
||||
}
|
||||
sender()->insertTextAtLocation(editedText, sender()->cursorLocation());
|
||||
int cursorDelta = 0;
|
||||
int editedTextLength = strlen(editedText);
|
||||
for (int i = 0; i < editedTextLength; i++) {
|
||||
if (editedText[i] == '(') {
|
||||
cursorDelta = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sender()->setCursorLocation(sender()->cursorLocation()+cursorDelta);
|
||||
app()->dismissModalViewController();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MathToolbox::returnToPreviousMenu() {
|
||||
m_selectableTableView.deselectTable();
|
||||
int depth = m_stack.depth();
|
||||
if (depth == 0) {
|
||||
app()->dismissModalViewController();
|
||||
return true;
|
||||
}
|
||||
int index = 0;
|
||||
ToolboxNode * parentNode = (ToolboxNode *)rootModel();
|
||||
Stack::State * previousState = m_stack.stateAtIndex(index++);;
|
||||
while (depth-- > 1) {
|
||||
parentNode = (ToolboxNode *)parentNode->children(previousState->selectedRow());
|
||||
previousState = m_stack.stateAtIndex(index++);
|
||||
}
|
||||
m_selectableTableView.deselectTable();
|
||||
m_nodeModel = parentNode;
|
||||
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;
|
||||
}
|
||||
|
||||
bool MathToolbox::selectSubMenu(ToolboxNode * selectedNode) {
|
||||
m_stack.push(m_selectableTableView.selectedRow(), m_selectableTableView.contentOffset().y());
|
||||
m_selectableTableView.deselectTable();
|
||||
m_nodeModel = selectedNode;
|
||||
m_listController.setFirstSelectedRow(0);
|
||||
app()->setFirstResponder(&m_listController);
|
||||
return true;
|
||||
}
|
||||
77
apps/math_toolbox.h
Normal file
77
apps/math_toolbox.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef APPS_MATH_TOOLBOX_H
|
||||
#define APPS_MATH_TOOLBOX_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "toolbox_node.h"
|
||||
#include "toolbox_leaf_cell.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 MathToolbox : public Toolbox, public ListViewDataSource {
|
||||
public:
|
||||
MathToolbox();
|
||||
void didBecomeFirstResponder() override;
|
||||
bool handleEvent(Ion::Events::Event event) 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 viewWillDisappear() override;
|
||||
private:
|
||||
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];
|
||||
};
|
||||
class ListController : public ViewController {
|
||||
public:
|
||||
ListController(Responder * parentResponder, SelectableTableView * tableView);
|
||||
const char * title() const override;
|
||||
View * view() override;
|
||||
void didBecomeFirstResponder() override;
|
||||
void setFirstSelectedRow(int firstSelectedRow);
|
||||
private:
|
||||
SelectableTableView * m_selectableTableView;
|
||||
int m_firstSelectedRow;
|
||||
};
|
||||
constexpr static KDCoordinate k_nodeRowHeight = 40;
|
||||
constexpr static KDCoordinate k_leafRowHeight = 40;
|
||||
constexpr static int k_maxNumberOfDisplayedRows = 6; //240/40
|
||||
TextField * sender() override;
|
||||
const ToolboxNode * rootModel();
|
||||
bool selectLeaf(ToolboxNode * selectedNode);
|
||||
bool selectSubMenu(ToolboxNode * selectedNode);
|
||||
bool returnToPreviousMenu();
|
||||
Stack m_stack;
|
||||
ToolboxLeafCell m_leafCells[k_maxNumberOfDisplayedRows];
|
||||
ChevronMenuListCell m_nodeCells[k_maxNumberOfDisplayedRows];
|
||||
SelectableTableView m_selectableTableView;
|
||||
ListController m_listController;
|
||||
ToolboxNode * m_nodeModel;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,119 +0,0 @@
|
||||
#include "node_list_view_controller.h"
|
||||
#include "toolbox_node.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
NodeListViewController::NodeListViewController(Responder * parentResponder) :
|
||||
ViewController(parentResponder),
|
||||
m_selectableTableView(SelectableTableView(this, this, 0, 0, 0, 0, nullptr, false)),
|
||||
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;
|
||||
m_selectableTableView.reloadData();
|
||||
}
|
||||
|
||||
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_leafCells[index];
|
||||
}
|
||||
return &m_nodeCells[index];
|
||||
}
|
||||
|
||||
int NodeListViewController::reusableCellCount(int type) {
|
||||
assert(type < 2);
|
||||
return k_maxNumberOfDisplayedRows;
|
||||
}
|
||||
|
||||
void NodeListViewController::willDisplayCellForIndex(TableViewCell * cell, int index) {
|
||||
ToolboxNode * node = (ToolboxNode *)m_nodeModel->children(index);
|
||||
if (node->numberOfChildren() == 0) {
|
||||
ToolboxLeafCell * myCell = (ToolboxLeafCell *)cell;
|
||||
myCell->setLabel(node->label());
|
||||
myCell->setSubtitle(node->text());
|
||||
return;
|
||||
}
|
||||
MenuListCell * myCell = (MenuListCell *)cell;
|
||||
myCell->setText(node->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;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#ifndef APPS_NODE_LIST_VIEW_CONTROLLER_H
|
||||
#define APPS_NODE_LIST_VIEW_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "node.h"
|
||||
#include "toolbox_leaf_cell.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 NodeNavigationController;
|
||||
|
||||
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 KDCoordinate k_nodeRowHeight = 40;
|
||||
constexpr static KDCoordinate k_leafRowHeight = 40;
|
||||
constexpr static int k_maxNumberOfDisplayedRows = 6; //240/40
|
||||
ToolboxLeafCell m_leafCells[k_maxNumberOfDisplayedRows];
|
||||
ChevronMenuListCell m_nodeCells[k_maxNumberOfDisplayedRows];
|
||||
SelectableTableView m_selectableTableView;
|
||||
Node * m_nodeModel;
|
||||
int m_firstSelectedRow;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <assert.h>
|
||||
|
||||
using namespace Poincare;
|
||||
using namespace Shared;
|
||||
|
||||
namespace Probability {
|
||||
|
||||
@@ -198,11 +199,6 @@ bool CalculationController::handleEvent(Ion::Events::Event event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CalculationController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
App * myApp = (App *)app();
|
||||
return myApp->textFieldDidReceiveEvent(textField, event);
|
||||
}
|
||||
|
||||
bool CalculationController::textFieldDidFinishEditing(TextField * textField, const char * text) {
|
||||
App * probaApp = (App *)app();
|
||||
Context * globalContext = probaApp->container()->globalContext();
|
||||
@@ -251,4 +247,8 @@ void CalculationController::updateTitle() {
|
||||
m_titleBuffer[currentChar-1] = 0;
|
||||
}
|
||||
|
||||
TextFieldDelegateApp * CalculationController::textFieldDelegateApp() {
|
||||
return (App *)app();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
#include "law_curve_view.h"
|
||||
#include "image_table_view.h"
|
||||
#include "calculation/calculation.h"
|
||||
#include "../shared/text_field_delegate.h"
|
||||
|
||||
namespace Probability {
|
||||
|
||||
class CalculationController : public ViewController, public TextFieldDelegate {
|
||||
class CalculationController : public ViewController, public Shared::TextFieldDelegate {
|
||||
public:
|
||||
CalculationController(Responder * parentResponder);
|
||||
View * view() override;
|
||||
@@ -20,10 +21,10 @@ public:
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
void selectSubview(int subviewIndex);
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
bool textFieldDidFinishEditing(TextField * textField, const char * text) override;
|
||||
private:
|
||||
void updateTitle();
|
||||
Shared::TextFieldDelegateApp * textFieldDelegateApp() override;
|
||||
Calculation * m_calculation;
|
||||
class ContentView : public View {
|
||||
public:
|
||||
|
||||
@@ -5,7 +5,6 @@ app_objs += $(addprefix apps/shared/,\
|
||||
curve_view_cursor.o\
|
||||
curve_view_range.o\
|
||||
editable_cell_table_view_controller.o\
|
||||
expression_text_field_delegate.o\
|
||||
float_pair_store.o\
|
||||
float_parameter_controller.o\
|
||||
function.o\
|
||||
@@ -20,6 +19,7 @@ app_objs += $(addprefix apps/shared/,\
|
||||
range_parameter_controller.o\
|
||||
store_controller.o\
|
||||
store_parameter_controller.o\
|
||||
text_field_delegate.o\
|
||||
text_field_delegate_app.o\
|
||||
zoom_parameter_controller.o\
|
||||
)
|
||||
|
||||
@@ -19,11 +19,6 @@ View * EditableCellTableViewController::view() {
|
||||
return &m_selectableTableView;
|
||||
}
|
||||
|
||||
bool EditableCellTableViewController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
return myApp->textFieldDidReceiveEvent(textField, event);
|
||||
}
|
||||
|
||||
bool EditableCellTableViewController::textFieldDidFinishEditing(TextField * textField, const char * text) {
|
||||
AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container();
|
||||
Context * globalContext = appsContainer->globalContext();
|
||||
@@ -105,9 +100,12 @@ void EditableCellTableViewController::didBecomeFirstResponder() {
|
||||
app()->setFirstResponder(&m_selectableTableView);
|
||||
}
|
||||
|
||||
|
||||
void EditableCellTableViewController::viewWillAppear() {
|
||||
m_selectableTableView.reloadData();
|
||||
}
|
||||
|
||||
TextFieldDelegateApp * EditableCellTableViewController::textFieldDelegateApp() {
|
||||
return (TextFieldDelegateApp *)app();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <escher.h>
|
||||
#include <poincare.h>
|
||||
#include "text_field_delegate.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
@@ -12,7 +13,6 @@ public:
|
||||
KDCoordinate rightMargin, KDCoordinate bottomMargin, KDCoordinate leftMargin);
|
||||
virtual View * view() override;
|
||||
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
bool textFieldDidFinishEditing(TextField * textField, const char * text) override;
|
||||
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) override;
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
protected:
|
||||
SelectableTableView m_selectableTableView;
|
||||
private:
|
||||
TextFieldDelegateApp * textFieldDelegateApp() override;
|
||||
static constexpr KDCoordinate k_cellHeight = 20;
|
||||
virtual bool cellAtLocationIsEditable(int columnIndex, int rowIndex) = 0;
|
||||
virtual void setDataAtLocation(float floatBody, int columnIndex, int rowIndex) = 0;
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
#include "expression_text_field_delegate.h"
|
||||
#include "../apps_container.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
const char * ExpressionTextFieldDelegate::XNT() {
|
||||
return "x";
|
||||
}
|
||||
|
||||
bool ExpressionTextFieldDelegate::cursorInToken(TextField * textField, const char * token) {
|
||||
const char * text = textField->text();
|
||||
int location = textField->cursorLocation();
|
||||
int tokenLength = strlen(token);
|
||||
while (text[location] != '(') {
|
||||
location --;
|
||||
}
|
||||
if (location - tokenLength < 0) {
|
||||
return false;
|
||||
}
|
||||
char previousToken[10];
|
||||
strlcpy(previousToken, text+location-tokenLength, tokenLength+1);
|
||||
if (strcmp(previousToken, token) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExpressionTextFieldDelegate::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
if (event == Ion::Events::OK && textField->isEditing()) {
|
||||
Expression * exp = Expression::parse(textField->text());
|
||||
if (exp == nullptr) {
|
||||
if (textField->textLength() == 0) {
|
||||
return true;
|
||||
}
|
||||
textField->app()->displayWarning("Attention a la syntaxe jeune padawan");
|
||||
return true;
|
||||
}
|
||||
Expression * evaluation = exp->evaluate(*localContext());
|
||||
if (evaluation == nullptr) {
|
||||
delete exp;
|
||||
textField->app()->displayWarning("Relis ton cours de maths, veux tu?");
|
||||
return true;
|
||||
} else {
|
||||
delete evaluation;
|
||||
delete exp;
|
||||
}
|
||||
}
|
||||
if (event == Ion::Events::Toolbox) {
|
||||
AppsContainer * appsContainer = (AppsContainer *)textField->app()->container();
|
||||
ToolboxController * toolboxController = appsContainer->toolboxController();
|
||||
toolboxController->setTextFieldCaller(textField);
|
||||
textField->app()->displayModalViewController(toolboxController, 0.f, 0.f, 50, 50, 0, 50);
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Var) {
|
||||
AppsContainer * appsContainer = (AppsContainer *)textField->app()->container();
|
||||
VariableBoxController * variableBoxController = appsContainer->variableBoxController();
|
||||
variableBoxController->setTextFieldCaller(textField);
|
||||
textField->app()->displayModalViewController(variableBoxController, 0.f, 0.f, 50, 50, 0, 50);
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::XNT) {
|
||||
if (!textField->isEditing()) {
|
||||
textField->setEditing(true);
|
||||
textField->setText("");
|
||||
}
|
||||
if (cursorInToken(textField, "sum") || cursorInToken(textField, "product")) {
|
||||
textField->insertTextAtLocation("n", textField->cursorLocation());
|
||||
textField->setCursorLocation(textField->cursorLocation()+strlen("n"));
|
||||
return true;
|
||||
}
|
||||
textField->insertTextAtLocation(XNT(), textField->cursorLocation());
|
||||
textField->setCursorLocation(textField->cursorLocation()+strlen(XNT()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef SHARED_EXPRESSION_TEXT_FIELD_DELEGATE_H
|
||||
#define SHARED_EXPRESSION_TEXT_FIELD_DELEGATE_H
|
||||
|
||||
#include <escher.h>
|
||||
#include <poincare.h>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class ExpressionTextFieldDelegate : public TextFieldDelegate {
|
||||
public:
|
||||
virtual Poincare::Context * localContext() = 0;
|
||||
virtual const char * XNT();
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
private:
|
||||
bool cursorInToken(TextField * textField, const char * token);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -45,11 +45,6 @@ bool FloatParameterController::textFieldDidFinishEditing(TextField * textField,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FloatParameterController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
return myApp->textFieldDidReceiveEvent(textField, event);
|
||||
}
|
||||
|
||||
void FloatParameterController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) {
|
||||
EditableTextMenuListCell * myCell = (EditableTextMenuListCell *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY);
|
||||
myCell->setEditing(false);
|
||||
@@ -57,6 +52,10 @@ void FloatParameterController::tableViewDidChangeSelection(SelectableTableView *
|
||||
app()->setFirstResponder(myNewCell);
|
||||
}
|
||||
|
||||
TextFieldDelegateApp * FloatParameterController::textFieldDelegateApp() {
|
||||
return (TextFieldDelegateApp *)app();
|
||||
}
|
||||
|
||||
KDCoordinate FloatParameterController::cellHeight() {
|
||||
return Metric::ParameterCellHeight;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define SHARED_FLOAT_PARAMETER_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "expression_text_field_delegate.h"
|
||||
#include "text_field_delegate.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
@@ -17,12 +17,12 @@ public:
|
||||
KDCoordinate cellHeight() override;
|
||||
void willDisplayCellForIndex(TableViewCell * cell, int index) override;
|
||||
bool textFieldDidFinishEditing(TextField * textField, const char * text) override;
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) override;
|
||||
protected:
|
||||
int activeCell();
|
||||
SelectableTableView m_selectableTableView;
|
||||
private:
|
||||
TextFieldDelegateApp * textFieldDelegateApp() override;
|
||||
virtual float parameterAtIndex(int index) = 0;
|
||||
virtual void setParameterAtIndex(int parameterIndex, float f) = 0;
|
||||
};
|
||||
|
||||
15
apps/shared/text_field_delegate.cpp
Normal file
15
apps/shared/text_field_delegate.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "text_field_delegate.h"
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
bool TextFieldDelegate::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) {
|
||||
return textFieldDelegateApp()->textFieldDidReceiveEvent(textField, event);
|
||||
}
|
||||
|
||||
Toolbox * TextFieldDelegate::toolboxForTextField(::TextField * textField) {
|
||||
return textFieldDelegateApp()->toolboxForTextField(textField);
|
||||
}
|
||||
|
||||
}
|
||||
20
apps/shared/text_field_delegate.h
Normal file
20
apps/shared/text_field_delegate.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef SHARED_TEXT_FIELD_DELEGATE_H
|
||||
#define SHARED_TEXT_FIELD_DELEGATE_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "text_field_delegate_app.h"
|
||||
#include <poincare.h>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class TextFieldDelegate : public ::TextFieldDelegate {
|
||||
public:
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
Toolbox * toolboxForTextField(TextField * textField) override;
|
||||
private:
|
||||
virtual TextFieldDelegateApp * textFieldDelegateApp() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "text_field_delegate_app.h"
|
||||
#include "../apps_container.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
@@ -8,7 +10,7 @@ namespace Shared {
|
||||
TextFieldDelegateApp::TextFieldDelegateApp(Container * container, ViewController * rootViewController, const char * name,
|
||||
const char * upperName, const Image * icon) :
|
||||
::App(container, rootViewController, name, upperName, icon),
|
||||
ExpressionTextFieldDelegate()
|
||||
TextFieldDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,4 +22,74 @@ AppsContainer * TextFieldDelegateApp::container() {
|
||||
return (AppsContainer *)app()->container();
|
||||
}
|
||||
|
||||
const char * TextFieldDelegateApp::XNT() {
|
||||
return "x";
|
||||
}
|
||||
|
||||
bool TextFieldDelegateApp::cursorInToken(TextField * textField, const char * token) {
|
||||
const char * text = textField->text();
|
||||
int location = textField->cursorLocation();
|
||||
int tokenLength = strlen(token);
|
||||
while (text[location] != '(') {
|
||||
location --;
|
||||
}
|
||||
if (location - tokenLength < 0) {
|
||||
return false;
|
||||
}
|
||||
char previousToken[10];
|
||||
strlcpy(previousToken, text+location-tokenLength, tokenLength+1);
|
||||
if (strcmp(previousToken, token) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextFieldDelegateApp::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
if (event == Ion::Events::OK && textField->isEditing()) {
|
||||
Expression * exp = Expression::parse(textField->text());
|
||||
if (exp == nullptr) {
|
||||
if (textField->textLength() == 0) {
|
||||
return true;
|
||||
}
|
||||
textField->app()->displayWarning("Attention a la syntaxe jeune padawan");
|
||||
return true;
|
||||
}
|
||||
Expression * evaluation = exp->evaluate(*localContext());
|
||||
if (evaluation == nullptr) {
|
||||
delete exp;
|
||||
textField->app()->displayWarning("Relis ton cours de maths, veux tu?");
|
||||
return true;
|
||||
} else {
|
||||
delete evaluation;
|
||||
delete exp;
|
||||
}
|
||||
}
|
||||
if (event == Ion::Events::Var) {
|
||||
AppsContainer * appsContainer = (AppsContainer *)textField->app()->container();
|
||||
VariableBoxController * variableBoxController = appsContainer->variableBoxController();
|
||||
variableBoxController->setTextFieldCaller(textField);
|
||||
textField->app()->displayModalViewController(variableBoxController, 0.f, 0.f, 50, 50, 0, 50);
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::XNT) {
|
||||
if (!textField->isEditing()) {
|
||||
textField->setEditing(true);
|
||||
textField->setText("");
|
||||
}
|
||||
if (cursorInToken(textField, "sum") || cursorInToken(textField, "product")) {
|
||||
textField->insertTextAtLocation("n", textField->cursorLocation());
|
||||
textField->setCursorLocation(textField->cursorLocation()+strlen("n"));
|
||||
return true;
|
||||
}
|
||||
textField->insertTextAtLocation(XNT(), textField->cursorLocation());
|
||||
textField->setCursorLocation(textField->cursorLocation()+strlen(XNT()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Toolbox * TextFieldDelegateApp::toolboxForTextField(TextField * textField) {
|
||||
return container()->mathToolbox();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
#define SHARED_TEXT_FIELD_DELEGATE_APP_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "expression_text_field_delegate.h"
|
||||
#include <poincare.h>
|
||||
|
||||
class AppsContainer;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class TextFieldDelegateApp : public ::App, public ExpressionTextFieldDelegate {
|
||||
class TextFieldDelegateApp : public ::App, public TextFieldDelegate {
|
||||
public:
|
||||
TextFieldDelegateApp(Container * container, ViewController * rootViewController, const char * name = nullptr, const char * upperName = nullptr, const Image * icon = nullptr);
|
||||
virtual Poincare::Context * localContext() override;
|
||||
virtual Poincare::Context * localContext();
|
||||
AppsContainer * container();
|
||||
virtual const char * XNT();
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
Toolbox * toolboxForTextField(TextField * textField) override;
|
||||
private:
|
||||
bool cursorInToken(TextField * textField, const char * token);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
#include "toolbox_controller.h"
|
||||
#include "toolbox_node.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* TODO: find a shorter way to initiate tree models
|
||||
* We create one model tree: each node keeps the label of the row it refers to
|
||||
* and the text which would be edited by clicking on the row. When the node is a
|
||||
* subtree, the edited text is set at nullptr. */
|
||||
|
||||
const ToolboxNode calculChildren[4] = {ToolboxNode("diff(,)", "Nombre derive"), ToolboxNode("int(,,)", "Integrale"), ToolboxNode("sum(,,)", "Somme"), ToolboxNode("product(,,)", "Produit")};
|
||||
const ToolboxNode complexChildren[5] = {ToolboxNode("abs()", "Module"), ToolboxNode("arg()", "Argument"), ToolboxNode("re()", "Partie reelle"), ToolboxNode("im()", "Partie imaginaire"), ToolboxNode("conj()", "Conjugue")};
|
||||
const ToolboxNode probabilityChildren[4] = {ToolboxNode("binomial()", "Combinaison"), ToolboxNode("permute(,)", "Arrangement"), ToolboxNode("random(,)", "Nombre aleatoire"), ToolboxNode("gamma()", "Fonction gamma")};
|
||||
const ToolboxNode arithmeticChildren[4] = {ToolboxNode("gcd()", "PGCD"), ToolboxNode("lcm()", "PPCM"), ToolboxNode("rem()", "Reste division euclidienne"), ToolboxNode("quo()","Quotien division euclidienne")};
|
||||
const ToolboxNode matricesChildren[5] = {ToolboxNode("inverse()", "Inverse"), ToolboxNode("det()", "Determinant"), ToolboxNode("transpose()", "Transposee"), ToolboxNode("trace()", "Trace"), ToolboxNode("dim()", "Taille")};
|
||||
const ToolboxNode listesChildren[5] = {ToolboxNode("sort<()", "Tri croissant"), ToolboxNode("sort>()", "Tri decroissant"), ToolboxNode("max()", "Maximum"), ToolboxNode("min()", "Minimum"), ToolboxNode("dim()", "Taille")};
|
||||
const ToolboxNode approximationChildren[4] = {ToolboxNode("floor()", "Partie entiere"), ToolboxNode("frac()", "Partie fractionnaire"), ToolboxNode("ceil()", "Plafond"), ToolboxNode("round(,)", "Arrondi")};
|
||||
const ToolboxNode trigonometryChildren[6] = {ToolboxNode("cosh()", "cosh"), ToolboxNode("sinh()", "sinh"), ToolboxNode("tanh()", "tanh"), ToolboxNode("acosh()", "acosh"), ToolboxNode("asinh()", "asinh"), ToolboxNode("atanh()", "atanh")};
|
||||
|
||||
const ToolboxNode menu[11] = {ToolboxNode("abs()", "Valeur absolue"), ToolboxNode("root(,)", "Racine k-ieme"), ToolboxNode("log(,)", "Logarithme base a"),
|
||||
ToolboxNode("Calcul", nullptr, calculChildren, 4), ToolboxNode("Nombre complexe", nullptr, complexChildren, 5),
|
||||
ToolboxNode("Probabilite", nullptr, probabilityChildren, 4), ToolboxNode("Arithmetique", nullptr, arithmeticChildren, 4),
|
||||
ToolboxNode("Matrice", nullptr, matricesChildren, 5), ToolboxNode("Liste", nullptr, listesChildren, 5),
|
||||
ToolboxNode("Approximation", nullptr, approximationChildren, 4), ToolboxNode("Trigonometrie", nullptr, trigonometryChildren, 6)};
|
||||
const ToolboxNode toolboxModel = ToolboxNode("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, &m_listViewController, true, KDColorWhite, Palette::PurpleBright, Palette::PurpleDark),
|
||||
m_listViewController(NodeListViewController(this))
|
||||
{
|
||||
}
|
||||
|
||||
const char * ToolboxController::title() const {
|
||||
return "ToolboxController";
|
||||
}
|
||||
|
||||
bool ToolboxController::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Back) {
|
||||
return returnToPreviousMenu();
|
||||
}
|
||||
if (event == Ion::Events::OK) {
|
||||
int selectedRow = m_listViewController.selectedRow();
|
||||
Node * selectedNode = (Node *)m_listViewController.nodeModel()->children(selectedRow);
|
||||
if (selectedNode->numberOfChildren() == 0) {
|
||||
return selectLeaf(selectedNode);
|
||||
}
|
||||
return selectSubMenu(selectedNode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ToolboxController::didBecomeFirstResponder() {
|
||||
m_listViewController.setNodeModel(rootModel());
|
||||
StackViewController::didBecomeFirstResponder();
|
||||
m_stack.resetStack();
|
||||
m_listViewController.setFirstSelectedRow(0);
|
||||
app()->setFirstResponder(&m_listViewController);
|
||||
}
|
||||
|
||||
void ToolboxController::viewWillDisappear() {
|
||||
m_listViewController.deselectTable();
|
||||
}
|
||||
|
||||
void ToolboxController::setTextFieldCaller(TextField * textField) {
|
||||
m_textFieldCaller = textField;
|
||||
}
|
||||
|
||||
Node * ToolboxController::rootModel() {
|
||||
return (Node *)&toolboxModel;
|
||||
}
|
||||
|
||||
bool ToolboxController::selectLeaf(Node * selectedNode){
|
||||
m_listViewController.deselectTable();
|
||||
ToolboxNode * node = (ToolboxNode *)selectedNode;
|
||||
const char * editedText = node->label();
|
||||
if (!m_textFieldCaller->isEditing()) {
|
||||
m_textFieldCaller->setEditing(true);
|
||||
}
|
||||
m_textFieldCaller->insertTextAtLocation(editedText, m_textFieldCaller->cursorLocation());
|
||||
int cursorDelta = 0;
|
||||
int editedTextLength = strlen(editedText);
|
||||
for (int i = 0; i < editedTextLength; i++) {
|
||||
if (editedText[i] == '(') {
|
||||
cursorDelta = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_textFieldCaller->setCursorLocation(m_textFieldCaller->cursorLocation()+cursorDelta);
|
||||
app()->dismissModalViewController();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ToolboxController::returnToPreviousMenu() {
|
||||
m_listViewController.deselectTable();
|
||||
int depth = m_stack.depth();
|
||||
if (depth == 0) {
|
||||
app()->dismissModalViewController();
|
||||
return true;
|
||||
}
|
||||
int index = 0;
|
||||
Node * parentNode = rootModel();
|
||||
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;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#ifndef APPS_TOOLBOX_CONTROLLER_H
|
||||
#define APPS_TOOLBOX_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "node.h"
|
||||
#include "node_list_view_controller.h"
|
||||
|
||||
class ToolboxController : public StackViewController {
|
||||
public:
|
||||
ToolboxController();
|
||||
const char * title() const override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void didBecomeFirstResponder() override;
|
||||
void viewWillDisappear() override;
|
||||
void setTextFieldCaller(TextField * textField);
|
||||
private:
|
||||
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];
|
||||
};
|
||||
TextField * m_textFieldCaller;
|
||||
NodeListViewController m_listViewController;
|
||||
Node * rootModel();
|
||||
bool selectLeaf(Node * selectedNode);
|
||||
bool selectSubMenu(Node * selectedNode);
|
||||
bool returnToPreviousMenu();
|
||||
Stack m_stack;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -52,6 +52,7 @@ objs += $(addprefix escher/src/,\
|
||||
text_menu_list_cell.o\
|
||||
text_view.o\
|
||||
tiled_view.o\
|
||||
toolbox.o\
|
||||
view.o\
|
||||
view_controller.o\
|
||||
warning_controller.o\
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <escher/table_view_cell.h>
|
||||
#include <escher/table_view_data_source.h>
|
||||
#include <escher/tiled_view.h>
|
||||
#include <escher/toolbox.h>
|
||||
#include <escher/view.h>
|
||||
#include <escher/view_controller.h>
|
||||
#include <escher/warning_controller.h>
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
bool textFieldDidFinishEditing(TextField * textField, const char * text) override;
|
||||
bool textFieldDidAbortEditing(TextField * textField, const char * text) override;
|
||||
Toolbox * toolboxForTextField(TextField * textFied) override;
|
||||
private:
|
||||
class TextFieldController : public ViewController {
|
||||
public:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <ion.h>
|
||||
|
||||
class App;
|
||||
class Toolbox;
|
||||
|
||||
class Responder {
|
||||
public:
|
||||
@@ -15,6 +16,7 @@ public:
|
||||
Responder * parentResponder() const;
|
||||
void setParentResponder(Responder * responder);
|
||||
App * app();
|
||||
virtual Toolbox * toolbox();
|
||||
private:
|
||||
Responder * m_parentResponder;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ public:
|
||||
TextField(Responder * parentResponder, char * textBuffer, char * draftTextBuffer, size_t textBufferSize,
|
||||
TextFieldDelegate * delegate = nullptr, KDText::FontSize size = KDText::FontSize::Large, float horizontalAlignment = 0.0f,
|
||||
float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite);
|
||||
Toolbox * toolbox() override;
|
||||
// View
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
// Responder
|
||||
|
||||
@@ -8,6 +8,7 @@ public:
|
||||
virtual bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) = 0;
|
||||
virtual bool textFieldDidFinishEditing(TextField * textField, const char * text) {return false;};
|
||||
virtual bool textFieldDidAbortEditing(TextField * textField, const char * text) {return false;};
|
||||
virtual Toolbox * toolboxForTextField(TextField * textFied) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
17
escher/include/escher/toolbox.h
Normal file
17
escher/include/escher/toolbox.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef ESCHER_TOOLBOX_H
|
||||
#define ESCHER_TOOLBOX_H
|
||||
|
||||
#include <escher/stack_view_controller.h>
|
||||
|
||||
class Toolbox : public StackViewController {
|
||||
public:
|
||||
Toolbox(Responder * parentResponder, ViewController * rootViewController);
|
||||
void setSender(Responder * sender);
|
||||
protected:
|
||||
virtual Responder * sender();
|
||||
private:
|
||||
Responder * m_sender;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -64,3 +64,7 @@ bool InputViewController::textFieldDidAbortEditing(TextField * textField, const
|
||||
bool InputViewController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
return m_textFieldDelegate->textFieldDidReceiveEvent(textField, event);
|
||||
}
|
||||
|
||||
Toolbox * InputViewController::toolboxForTextField(TextField * textField) {
|
||||
return m_textFieldDelegate->toolboxForTextField(textField);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <escher/responder.h>
|
||||
#include <escher/app.h>
|
||||
#include <escher/toolbox.h>
|
||||
#include <assert.h>
|
||||
|
||||
Responder::Responder(Responder * parentResponder) :
|
||||
@@ -16,6 +17,11 @@ void Responder::setParentResponder(Responder * responder) {
|
||||
}
|
||||
|
||||
bool Responder::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Toolbox && toolbox() != nullptr) {
|
||||
toolbox()->setSender(this);
|
||||
app()->displayModalViewController(toolbox(), 0.f, 0.f, 50, 50, 0, 50);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -38,3 +44,7 @@ App * Responder::app() {
|
||||
assert(result->m_magic == App::Magic); // Poor man's RTTI
|
||||
return result;
|
||||
}
|
||||
|
||||
Toolbox * Responder::toolbox() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,13 @@ TextField::TextField(Responder * parentResponder, char * textBuffer, char * draf
|
||||
{
|
||||
}
|
||||
|
||||
Toolbox * TextField::toolbox() {
|
||||
if (m_delegate) {
|
||||
return m_delegate->toolboxForTextField(this);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char * TextField::text() const {
|
||||
if (m_isEditing) {
|
||||
return (const char *)m_draftTextBuffer;
|
||||
@@ -67,6 +74,9 @@ void TextField::reload() {
|
||||
}
|
||||
|
||||
bool TextField::handleEvent(Ion::Events::Event event) {
|
||||
if (Responder::handleEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
if (m_delegate) {
|
||||
if (m_delegate->textFieldDidReceiveEvent(this, event)) {
|
||||
return true;
|
||||
|
||||
15
escher/src/toolbox.cpp
Normal file
15
escher/src/toolbox.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <escher/toolbox.h>
|
||||
|
||||
Toolbox::Toolbox(Responder * parentResponder, ViewController * rootViewController) :
|
||||
StackViewController(parentResponder, rootViewController, true, KDColorWhite, Palette::PurpleBright, Palette::PurpleDark),
|
||||
m_sender(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void Toolbox::setSender(Responder * sender) {
|
||||
m_sender = sender;
|
||||
}
|
||||
|
||||
Responder * Toolbox::sender() {
|
||||
return m_sender;
|
||||
}
|
||||
Reference in New Issue
Block a user