diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 984f14a43..d64a46f4e 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -9,7 +9,7 @@ App::App(Container * container, Context * context) : m_evaluateContext(EvaluateContext(context, &m_calculationStore)), m_calculationStore(CalculationStore()), m_historyController(HistoryController(&m_editExpressionController, &m_calculationStore)), - m_editExpressionController(EditExpressionController(&m_modalViewController, &m_historyController, &m_calculationStore, this)) + m_editExpressionController(EditExpressionController(&m_modalViewController, &m_historyController, &m_calculationStore)) { } diff --git a/apps/calculation/edit_expression_controller.cpp b/apps/calculation/edit_expression_controller.cpp index 76a593bc3..14dbdb922 100644 --- a/apps/calculation/edit_expression_controller.cpp +++ b/apps/calculation/edit_expression_controller.cpp @@ -4,10 +4,10 @@ namespace Calculation { -EditExpressionController::ContentView::ContentView(TableView * subview, TextFieldDelegate * textFieldDelegate) : +EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate) : View(), m_mainView(subview), - m_textField(nullptr, m_textBody, 255, textFieldDelegate) + m_textField(parentResponder, m_textBody, 255, textFieldDelegate) { m_textBody[0] = 0; } @@ -43,13 +43,12 @@ TableView * EditExpressionController::ContentView::mainView() { return m_mainView; } -EditExpressionController::EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore, TextFieldDelegate * textFieldDelegate) : +EditExpressionController::EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore) : ViewController(parentResponder), - m_contentView((TableView *)historyController->view(), textFieldDelegate), + m_contentView(this, (TableView *)historyController->view(), this), m_historyController(historyController), m_calculationStore(calculationStore) { - m_contentView.textField()->setParentResponder(this); } View * EditExpressionController::view() { @@ -69,19 +68,6 @@ void EditExpressionController::setTextBody(const char * text) { } bool EditExpressionController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK) { - Calculation calculation = Calculation(); - App * calculationApp = (App *)app(); - calculation.setContent(textBody(), calculationApp->evaluateContext()); - m_calculationStore->push(&calculation); - m_historyController->reload(); - m_contentView.mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); - m_contentView.textField()->setText(""); - return true; - } - if (event == Ion::Events::Back) { - return true; - } if (event == Ion::Events::Up) { if (m_calculationStore->numberOfCalculations() > 0) { app()->setFirstResponder(m_historyController); @@ -95,9 +81,20 @@ void EditExpressionController::didBecomeFirstResponder() { app()->setFirstResponder(m_contentView.textField()); } -void EditExpressionController::edit(const char * initialContent) { - setTextBody(initialContent); - app()->setFirstResponder(this); +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) { + Calculation calculation = Calculation(); + App * calculationApp = (App *)app(); + calculation.setContent(textBody(), calculationApp->evaluateContext()); + m_calculationStore->push(&calculation); + m_historyController->reload(); + m_contentView.mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); + m_contentView.textField()->setText(""); + return true; } } diff --git a/apps/calculation/edit_expression_controller.h b/apps/calculation/edit_expression_controller.h index e55200b44..47b817037 100644 --- a/apps/calculation/edit_expression_controller.h +++ b/apps/calculation/edit_expression_controller.h @@ -9,20 +9,21 @@ namespace Calculation { class HistoryController; -class EditExpressionController : public ViewController { +class EditExpressionController : public ViewController, public TextFieldDelegate { public: - EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore, TextFieldDelegate * textFieldDelegate); + EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore); View * view() override; const char * title() const override; void didBecomeFirstResponder() override; bool handleEvent(Ion::Events::Event event) override; - void edit(const char * initialContent); 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 { public: - ContentView(TableView * subview, TextFieldDelegate * textFieldDelegate); + ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate); int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 4931831f1..42bbc5765 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -81,9 +81,9 @@ bool HistoryController::handleEvent(Ion::Events::Event event) { int focusRow = m_selectableTableView.selectedRow(); HistoryViewCell * selectedCell = (HistoryViewCell *)m_selectableTableView.cellAtLocation(0, focusRow); HistoryViewCell::SubviewType subviewType = selectedCell->selectedSubviewType(); + m_selectableTableView.deselectTable(); EditExpressionController * editController = (EditExpressionController *)parentResponder(); m_calculationStore->deleteCalculationAtIndex(focusRow); - m_selectableTableView.deselectTable(); reload(); if (numberOfRows()== 0) { app()->setFirstResponder(editController); diff --git a/apps/calculation/text_field.cpp b/apps/calculation/text_field.cpp index a8159bb02..45d89f7e5 100644 --- a/apps/calculation/text_field.cpp +++ b/apps/calculation/text_field.cpp @@ -3,12 +3,20 @@ namespace Calculation { TextField::TextField(Responder * parentResponder, char * textBuffer, size_t textBufferSize, TextFieldDelegate * delegate) : - ::TextField(parentResponder, textBuffer, textBufferSize, delegate) + ::TextField(parentResponder, textBuffer, textBuffer, textBufferSize, delegate) { + m_isEditing = true; } bool TextField::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::Back) { + return false; + } if (event == Ion::Events::Ans) { + if (!isEditing()) { + setEditing(true); + setText(""); + } insertTextAtLocation("ans", cursorLocation()); setCursorLocation(cursorLocation() + strlen("ans")); return true; @@ -19,10 +27,18 @@ bool TextField::handleEvent(Ion::Events::Event event) { event == Ion::Events::Minus || event == Ion::Events::Dot || event == Ion::Events::Division)) { + if (!isEditing()) { + setEditing(true); + setText(""); + } insertTextAtLocation("ans", cursorLocation()); setCursorLocation(cursorLocation() + strlen("ans")); } return(::TextField::handleEvent(event)); } +void TextField::setEditing(bool isEditing) { + m_isEditing = true; +} + } diff --git a/apps/calculation/text_field.h b/apps/calculation/text_field.h index 6d2f12392..99d11b898 100644 --- a/apps/calculation/text_field.h +++ b/apps/calculation/text_field.h @@ -9,6 +9,7 @@ class TextField : public ::TextField { public: TextField(Responder * parentResponder, char * textBuffer, size_t textBufferSize, TextFieldDelegate * delegate); bool handleEvent(Ion::Events::Event event) override; + void setEditing(bool isEditing) override; }; } diff --git a/apps/expression_text_field_delegate.cpp b/apps/expression_text_field_delegate.cpp index defbaccf0..4dd8f94e7 100644 --- a/apps/expression_text_field_delegate.cpp +++ b/apps/expression_text_field_delegate.cpp @@ -23,7 +23,7 @@ bool ExpressionTextFieldDelegate::cursorInToken(TextField * textField, const cha } bool ExpressionTextFieldDelegate::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { - if (event == Ion::Events::OK) { + if (event == Ion::Events::OK && textField->isEditing()) { Expression * exp = Expression::parse(textField->text()); if (exp == nullptr) { if (textField->textLength() == 0) { @@ -42,14 +42,14 @@ bool ExpressionTextFieldDelegate::textFieldDidReceiveEvent(TextField * textField delete exp; } } - if (event == Ion::Events::Toolbox) { + if (event == Ion::Events::Toolbox && textField->isEditing()) { 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) { + if (event == Ion::Events::Var && textField->isEditing()) { AppsContainer * appsContainer = (AppsContainer *)textField->app()->container(); VariableBoxController * variableBoxController = appsContainer->variableBoxController(); variableBoxController->setTextFieldCaller(textField); @@ -57,6 +57,10 @@ bool ExpressionTextFieldDelegate::textFieldDidReceiveEvent(TextField * textField 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")); diff --git a/apps/float_parameter_controller.cpp b/apps/float_parameter_controller.cpp index 2cd0a1c41..dd3c147d2 100644 --- a/apps/float_parameter_controller.cpp +++ b/apps/float_parameter_controller.cpp @@ -6,7 +6,7 @@ FloatParameterController::FloatParameterController(Responder * parentResponder) : ViewController(parentResponder), m_selectableTableView(SelectableTableView(this, this, Metric::TopMargin, Metric::RightMargin, - Metric::BottomMargin, Metric::LeftMargin)) + Metric::BottomMargin, Metric::LeftMargin, this)) { } @@ -24,52 +24,27 @@ int FloatParameterController::activeCell() { } void FloatParameterController::willDisplayCellForIndex(TableViewCell * cell, int index) { - TextMenuListCell * myCell = (TextMenuListCell *) cell; + EditableTextMenuListCell * myCell = (EditableTextMenuListCell *) cell; char buffer[Constant::FloatBufferSizeInScientificMode]; Float(parameterAtIndex(index)).convertFloatToText(buffer, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); myCell->setAccessoryText(buffer); } -bool FloatParameterController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK) { - editParameter(); - return true; - } - if (event.hasText()) { - editParameter(event.text()); - return true; - } - return false; +bool FloatParameterController::textFieldDidFinishEditing(TextField * textField, const char * text) { + AppsContainer * appsContainer = (AppsContainer *)app()->container(); + Context * globalContext = appsContainer->context(); + float floatBody = Expression::parse(text)->approximate(*globalContext); + setParameterAtIndex(m_selectableTableView.selectedRow(), floatBody); + willDisplayCellForIndex(m_selectableTableView.cellAtLocation(m_selectableTableView.selectedColumn(), + m_selectableTableView.selectedRow()), activeCell()); + return true; } -void FloatParameterController::editParameter(const char * initialText) { - /* This code assumes that the active cell remains the one which is edited - * until the invocation is performed. This could lead to concurrency issue in - * other cases. */ - char initialTextContent[255]; - int cursorDelta = 0; - if (initialText) { - strlcpy(initialTextContent, initialText, sizeof(initialTextContent)); - cursorDelta = strlen(initialText) > 1 ? -1 : 0; - } else { - TextMenuListCell * textMenuListCell = (TextMenuListCell *)reusableCell(activeCell()); - strlcpy(initialTextContent, textMenuListCell->accessoryText(), sizeof(initialTextContent)); - } - int cursorLocation = strlen(initialTextContent) + cursorDelta; - EditableTextMenuListCell * cell = (EditableTextMenuListCell *)m_selectableTableView.cellAtLocation(0, activeCell()); - cell->setParentResponder(&m_selectableTableView); - cell->editValue(textFieldDelegate(), initialTextContent, cursorLocation, this, - [](void * context, void * sender){ - FloatParameterController * floatParameterController = (FloatParameterController *)context; - int activeCell = floatParameterController->activeCell(); - EditableTextMenuListCell * cell = (EditableTextMenuListCell *)sender; - const char * textBody = cell->editedText(); - AppsContainer * appsContainer = (AppsContainer *)floatParameterController->app()->container(); - Context * globalContext = appsContainer->context(); - float floatBody = Expression::parse(textBody)->approximate(*globalContext); - floatParameterController->setParameterAtIndex(activeCell, floatBody); - floatParameterController->willDisplayCellForIndex(cell, activeCell); - }); +void FloatParameterController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) { + EditableTextMenuListCell * myCell = (EditableTextMenuListCell *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY); + myCell->setEditing(false); + EditableTextMenuListCell * myNewCell = (EditableTextMenuListCell *)t->cellAtLocation(t->selectedColumn(), t->selectedRow()); + app()->setFirstResponder(myNewCell); } KDCoordinate FloatParameterController::cellHeight() { diff --git a/apps/float_parameter_controller.h b/apps/float_parameter_controller.h index 2c2cf4922..a5fec9b4c 100644 --- a/apps/float_parameter_controller.h +++ b/apps/float_parameter_controller.h @@ -7,18 +7,18 @@ /* This controller edits float parameter of any model (given through * parameterAtIndex and setParameterAtIndex). */ -class FloatParameterController : public ViewController, public SimpleListViewDataSource { +class FloatParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate { public: FloatParameterController(Responder * parentResponder); - int activeCell(); - void editParameter(const char * initialText = nullptr); virtual ExpressionTextFieldDelegate * textFieldDelegate() = 0; View * view() override; - bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; KDCoordinate cellHeight() override; void willDisplayCellForIndex(TableViewCell * cell, int index) override; + bool textFieldDidFinishEditing(TextField * textField, const char * text) override; + void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) override; protected: + int activeCell(); SelectableTableView m_selectableTableView; private: virtual float parameterAtIndex(int index) = 0; diff --git a/apps/graph/graph/goto_parameter_controller.cpp b/apps/graph/graph/goto_parameter_controller.cpp index e5a6ed10d..910203a9d 100644 --- a/apps/graph/graph/goto_parameter_controller.cpp +++ b/apps/graph/graph/goto_parameter_controller.cpp @@ -1,4 +1,5 @@ #include "goto_parameter_controller.h" +#include "../app.h" #include namespace Graph { @@ -9,6 +10,8 @@ GoToParameterController::GoToParameterController(Responder * parentResponder, Gr m_graphView(graphView), m_function(nullptr) { + m_abscisseCell.setParentResponder(&m_selectableTableView); + m_abscisseCell.setDelegate(this); } ExpressionTextFieldDelegate * GoToParameterController::textFieldDelegate() { @@ -46,4 +49,10 @@ int GoToParameterController::reusableCellCount() { void GoToParameterController::setFunction(Function * function) { m_function = function; } + +bool GoToParameterController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { + App * myApp = (App *)app(); + return myApp->textFieldDidReceiveEvent(textField, event); +} + } diff --git a/apps/graph/graph/goto_parameter_controller.h b/apps/graph/graph/goto_parameter_controller.h index 615f0c2bd..d15055646 100644 --- a/apps/graph/graph/goto_parameter_controller.h +++ b/apps/graph/graph/goto_parameter_controller.h @@ -15,6 +15,7 @@ public: TableViewCell * reusableCell(int index) override; int reusableCellCount() override; void setFunction(Function * function); + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; private: float parameterAtIndex(int index) override; void setParameterAtIndex(int parameterIndex, float f) override; diff --git a/apps/graph/graph/window_parameter_controller.cpp b/apps/graph/graph/window_parameter_controller.cpp index fe4fe4070..b812fb9b6 100644 --- a/apps/graph/graph/window_parameter_controller.cpp +++ b/apps/graph/graph/window_parameter_controller.cpp @@ -1,4 +1,6 @@ #include "window_parameter_controller.h" +#include "../app.h" +#include "../../apps_container.h" #include namespace Graph { @@ -13,6 +15,10 @@ WindowParameterController::WindowParameterController(Responder * parentResponder m_windowCells[1].setText("Xmax"); m_windowCells[2].setText("Ymin"); m_windowCells[3].setText("Ymax"); + for (int k = 0; k < k_numberOfTextCell; k++) { + m_windowCells[k].setParentResponder(&m_selectableTableView); + m_windowCells[k].setDelegate(this); + } } ExpressionTextFieldDelegate * WindowParameterController::textFieldDelegate() { @@ -40,6 +46,31 @@ void WindowParameterController::willDisplayCellForIndex(TableViewCell * cell, in FloatParameterController::willDisplayCellForIndex(cell, index); } +bool WindowParameterController::textFieldDidFinishEditing(TextField * textField, const char * text) { + AppsContainer * appsContainer = (AppsContainer *)app()->container(); + Context * globalContext = appsContainer->context(); + float floatBody = Expression::parse(text)->approximate(*globalContext); + setParameterAtIndex(m_selectableTableView.selectedRow(), floatBody); + willDisplayCellForIndex(m_selectableTableView.cellAtLocation(m_selectableTableView.selectedColumn(), + m_selectableTableView.selectedRow()), activeCell()); + m_selectableTableView.reloadData(); + return true; +} + +void WindowParameterController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) { + if (previousSelectedCellX == 0 && previousSelectedCellY >= 0 && previousSelectedCellY !=2) { + EditableTextMenuListCell * myCell = (EditableTextMenuListCell *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY); + myCell->setEditing(false); + app()->setFirstResponder(t); + } + if (t->selectedColumn() == 0 && t->selectedRow() >= 0 && t->selectedRow() !=2) { + EditableTextMenuListCell * myNewCell = (EditableTextMenuListCell *)t->cellAtLocation(t->selectedColumn(), t->selectedRow()); + if ((t->selectedRow() == 0 || t->selectedRow() == 1) || !m_graphWindow->yAuto()) { + app()->setFirstResponder(myNewCell); + } + } +} + bool WindowParameterController::handleEvent(Ion::Events::Event event) { m_graphView->initCursorPosition(); if (activeCell() == 2) { @@ -53,7 +84,7 @@ bool WindowParameterController::handleEvent(Ion::Events::Event event) { if (m_graphWindow->yAuto() && (activeCell() == 3 || activeCell() == 4)) { return false; } - return FloatParameterController::handleEvent(event); + return false; } float WindowParameterController::parameterAtIndex(int index) { @@ -109,4 +140,9 @@ int WindowParameterController::reusableCellCount() { return k_numberOfTextCell+1; } +bool WindowParameterController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { + App * myApp = (App *)app(); + return myApp->textFieldDidReceiveEvent(textField, event); +} + } diff --git a/apps/graph/graph/window_parameter_controller.h b/apps/graph/graph/window_parameter_controller.h index add2861e8..4398c57bb 100644 --- a/apps/graph/graph/window_parameter_controller.h +++ b/apps/graph/graph/window_parameter_controller.h @@ -16,7 +16,10 @@ public: TableViewCell * reusableCell(int index) override; int reusableCellCount() override; void willDisplayCellForIndex(TableViewCell * cell, int index) override; + bool textFieldDidFinishEditing(TextField * textField, const char * text) override; + void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) override; bool handleEvent(Ion::Events::Event event) override; + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; private: float parameterAtIndex(int index) override; void setParameterAtIndex(int parameterIndex, float f) override; diff --git a/apps/graph/list/list_controller.cpp b/apps/graph/list/list_controller.cpp index 1eb66db5f..4e144a07d 100644 --- a/apps/graph/list/list_controller.cpp +++ b/apps/graph/list/list_controller.cpp @@ -119,19 +119,10 @@ void ListController::configureFunction(Function * function) { stack->push(&m_parameterController); } -void ListController::editExpression(FunctionExpressionView * functionCell, const char * initialText) { - char initialTextContent[255]; - int cursorDelta = 0; - if (initialText) { - strlcpy(initialTextContent, initialText, sizeof(initialTextContent)); - cursorDelta = strlen(initialText) > 1 ? -1 : 0; - } else { - strlcpy(initialTextContent, functionCell->function()->text(), sizeof(initialTextContent)); - } - int cursorLocation = strlen(initialTextContent) + cursorDelta; +void ListController::editExpression(FunctionExpressionView * functionCell, Ion::Events::Event event) { App * myApp = (App *)app(); InputViewController * inputController = myApp->inputViewController(); - inputController->edit(this, initialTextContent, cursorLocation, functionCell, + inputController->edit(this, event, functionCell, [](void * context, void * sender){ FunctionExpressionView * myCell = (FunctionExpressionView *) context; Function * myFunction = myCell->function(); @@ -154,15 +145,11 @@ bool ListController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK) { return handleEnter(); } - if (event == Ion::Events::XNT && m_selectableTableView.selectedColumn() == 1){ - FunctionExpressionView * functionCell = (FunctionExpressionView *)(m_selectableTableView.cellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow())); - editExpression(functionCell, "x"); - } - if (!event.hasText() || m_selectableTableView.selectedColumn() == 0) { + if ((!event.hasText() && event != Ion::Events::XNT) || m_selectableTableView.selectedColumn() == 0) { return false; } FunctionExpressionView * functionCell = (FunctionExpressionView *)(m_selectableTableView.cellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow())); - editExpression(functionCell, event.text()); + editExpression(functionCell, event); return true; } @@ -188,7 +175,7 @@ bool ListController::handleEnter() { return false; } FunctionExpressionView * functionCell = (FunctionExpressionView *)(m_selectableTableView.cellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow())); - editExpression(functionCell); + editExpression(functionCell, Ion::Events::OK); return true; } default: diff --git a/apps/graph/list/list_controller.h b/apps/graph/list/list_controller.h index 90e71b68b..4387f9150 100644 --- a/apps/graph/list/list_controller.h +++ b/apps/graph/list/list_controller.h @@ -32,7 +32,7 @@ public: int reusableCellCount(int type) override; int typeAtLocation(int i, int j) override; void configureFunction(Function * function); - void editExpression(FunctionExpressionView * functionCell, const char * initialText = nullptr); + void editExpression(FunctionExpressionView * functionCell, Ion::Events::Event event); private: static constexpr KDCoordinate k_verticalFunctionMargin = 50-12; diff --git a/apps/graph/values/editable_value_cell.cpp b/apps/graph/values/editable_value_cell.cpp index 2b6fab8e4..0d7a580fb 100644 --- a/apps/graph/values/editable_value_cell.cpp +++ b/apps/graph/values/editable_value_cell.cpp @@ -1,57 +1,53 @@ #include "editable_value_cell.h" +#include "../../apps_container.h" #include #include "../app.h" namespace Graph { EditableValueCell::EditableValueCell() : - ValueCell(), + EvenOddCell(), Responder(nullptr), - m_textField(TextField(this, m_textBody, 255, nullptr)), - m_isEditing(false), - m_successAction(Invocation(nullptr, nullptr)) + m_textField(TextField(this, m_textBody, m_draftTextBody, 255, nullptr, 1.0f, 0.5f, KDColorBlack, KDColorWhite)) { } -const char * EditableValueCell::editedText() const { +void EditableValueCell::setDelegate(TextFieldDelegate * delegate) { + m_textField.setTextFieldDelegate(delegate); +} + +void EditableValueCell::reloadCell() { + EvenOddCell::reloadCell(); + m_textField.setBackgroundColor(backgroundColor()); +} + +const char * EditableValueCell::text() const { return m_textField.text(); } +void EditableValueCell::setText(const char * textContent) { + m_textField.setText(textContent); +} + +int EditableValueCell::numberOfSubviews() const { + return 1; +} + View * EditableValueCell::subviewAtIndex(int index) { assert(index == 0); - if (m_isEditing) { - return &m_textField; - } - return &m_bufferTextView; + return &m_textField; } void EditableValueCell::layoutSubviews() { - m_bufferTextView.setFrame(bounds()); m_textField.setFrame(bounds()); } -bool EditableValueCell::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK) { - m_isEditing = false; - m_successAction.perform(this); - app()->setFirstResponder(parentResponder()); - return true; - } - m_isEditing = false; - app()->setFirstResponder(parentResponder()); - return false; -} - - -void EditableValueCell::editValue(const char * initialText, int cursorPosition, void * context, Invocation::Action successAction) { - markRectAsDirty(bounds()); - App * myApp = (App *)app(); - m_textField.setTextFieldDelegate(myApp); +void EditableValueCell::didBecomeFirstResponder() { app()->setFirstResponder(&m_textField); - m_isEditing = true; - m_textField.setText(initialText); - m_textField.setCursorLocation(cursorPosition); - m_successAction = Invocation(successAction, context); +} + +void EditableValueCell::setEditing(bool isEditing) { + m_textField.setEditing(isEditing); } } diff --git a/apps/graph/values/editable_value_cell.h b/apps/graph/values/editable_value_cell.h index 88811df2f..71b170fda 100644 --- a/apps/graph/values/editable_value_cell.h +++ b/apps/graph/values/editable_value_cell.h @@ -3,22 +3,24 @@ #include #include -#include "value_cell.h" namespace Graph { -class EditableValueCell : public ValueCell, public Responder { +class EditableValueCell : public EvenOddCell, public Responder { public: EditableValueCell(); - const char * editedText() const; + void setDelegate(TextFieldDelegate * delegate); + void reloadCell() override; + const char * text() const; + void setText(const char * textContent); + int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; - bool handleEvent(Ion::Events::Event event) override; - void editValue(const char * initialText, int cursorLocation, void * context, Invocation::Action successAction); + void didBecomeFirstResponder() override; + void setEditing(bool isEditing); private: TextField m_textField; char m_textBody[255]; - bool m_isEditing; - Invocation m_successAction; + char m_draftTextBody[255]; }; } diff --git a/apps/graph/values/interval_parameter_controller.cpp b/apps/graph/values/interval_parameter_controller.cpp index bebccfd2c..d50a81560 100644 --- a/apps/graph/values/interval_parameter_controller.cpp +++ b/apps/graph/values/interval_parameter_controller.cpp @@ -1,4 +1,5 @@ #include "interval_parameter_controller.h" +#include "../app.h" #include namespace Graph { @@ -6,9 +7,9 @@ namespace Graph { IntervalParameterController::IntervalParameterController(Responder * parentResponder, Interval * interval) : FloatParameterController(parentResponder), m_interval(interval), - m_intervalStartCell(EditableTextMenuListCell((char*)"X Debut")), - m_intervalEndCell(EditableTextMenuListCell((char*)"X Fin")), - m_intervalStepCell(EditableTextMenuListCell((char*)"Pas")) + m_intervalStartCell(EditableTextMenuListCell((char*)"X Debut", &m_selectableTableView, this)), + m_intervalEndCell(EditableTextMenuListCell((char*)"X Fin", &m_selectableTableView, this)), + m_intervalStepCell(EditableTextMenuListCell((char*)"Pas", &m_selectableTableView, this)) { } @@ -70,4 +71,9 @@ int IntervalParameterController::reusableCellCount() { return k_totalNumberOfCell; } +bool IntervalParameterController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { + App * myApp = (App *)app(); + return myApp->textFieldDidReceiveEvent(textField, event); +} + } diff --git a/apps/graph/values/interval_parameter_controller.h b/apps/graph/values/interval_parameter_controller.h index eb2f301d8..84ca34d0c 100644 --- a/apps/graph/values/interval_parameter_controller.h +++ b/apps/graph/values/interval_parameter_controller.h @@ -16,6 +16,7 @@ public: int numberOfRows() override; TableViewCell * reusableCell(int index) override; int reusableCellCount() override; + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; private: float parameterAtIndex(int index) override; void setParameterAtIndex(int parameterIndex, float f) override; diff --git a/apps/graph/values/values_controller.cpp b/apps/graph/values/values_controller.cpp index b00612be0..a14bd9a9e 100644 --- a/apps/graph/values/values_controller.cpp +++ b/apps/graph/values/values_controller.cpp @@ -9,7 +9,7 @@ namespace Graph { ValuesController::ValuesController(Responder * parentResponder, FunctionStore * functionStore, HeaderViewController * header) : ViewController(parentResponder), HeaderViewDelegate(header), - m_selectableTableView(SelectableTableView(this, this, k_topMargin, k_rightMargin, k_bottomMargin, k_leftMargin)), + m_selectableTableView(SelectableTableView(this, this, k_topMargin, k_rightMargin, k_bottomMargin, k_leftMargin, this)), m_functionStore(functionStore), m_intervalParameterController(IntervalParameterController(this, &m_interval)), m_abscissaParameterController(AbscissaParameterController(this, &m_intervalParameterController)), @@ -24,6 +24,10 @@ ValuesController::ValuesController(Responder * parentResponder, FunctionStore * m_interval.setStart(0); m_interval.setEnd(10); m_interval.setStep(1); + for (int k = 0; k < k_maxNumberOfAbscissaCells; k++) { + m_abscissaCells[k].setParentResponder(&m_selectableTableView); + m_abscissaCells[k].setDelegate(this); + } } View * ValuesController::view() { @@ -195,26 +199,29 @@ bool ValuesController::handleEvent(Ion::Events::Event event) { } return true; } - if (activeColumn() == 0) { - editValue(); - return true; - } return false; } if (activeRow() == -1) { return headerViewController()->handleEvent(event); } - if (event.hasText()) { - if (activeColumn() == 0 && activeRow() > 0) { - editValue(event.text()); - return true; - } - return false; - } return false; } +bool ValuesController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { + App * myApp = (App *)app(); + return myApp->textFieldDidReceiveEvent(textField, event); +} + +bool ValuesController::textFieldDidFinishEditing(TextField * textField, const char * text) { + AppsContainer * appsContainer = (AppsContainer *)app()->container(); + Context * globalContext = appsContainer->context(); + float floatBody = Expression::parse(text)->approximate(*globalContext); + m_interval.setElement(activeRow()-1, floatBody); + willDisplayCellAtLocation(m_selectableTableView.cellAtLocation(activeColumn(), activeRow()), activeColumn(), activeRow()); + m_selectableTableView.reloadData(); + return true; +} void ValuesController::configureAbscissa() { StackViewController * stack = stackController(); @@ -235,38 +242,16 @@ void ValuesController::configureDerivativeFunction() { stack->push(&m_derivativeParameterController); } -void ValuesController::editValue(const char * initialText) { - /* This code assumes that the active cell remains the one which is edited - * until the invocation is performed. This could lead to concurrency issue in - * other cases. */ - char initialTextContent[255]; - int cursorDelta = 0; - if (initialText) { - strlcpy(initialTextContent, initialText, sizeof(initialTextContent)); - cursorDelta = strlen(initialText) > 1 ? -1 : 0; - } else { - if (activeRow() > m_interval.numberOfElements()) { - initialTextContent[0] = 0; - } else { - Float(m_interval.element(activeRow()-1)).convertFloatToText(initialTextContent, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); - } +void ValuesController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) { + if (previousSelectedCellX == 0 && previousSelectedCellY > 0) { + EditableValueCell * myCell = (EditableValueCell *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY); + myCell->setEditing(false); + app()->setFirstResponder(t); + } + if (t->selectedRow() > 0 && t->selectedColumn() == 0) { + EditableValueCell * myCell = (EditableValueCell *)t->cellAtLocation(t->selectedColumn(), t->selectedRow()); + app()->setFirstResponder(myCell); } - int cursorLocation = strlen(initialTextContent) + cursorDelta; - EditableValueCell * cell = (EditableValueCell *)m_selectableTableView.cellAtLocation(0, activeRow()); - cell->setParentResponder(&m_selectableTableView); - cell->editValue(initialTextContent, cursorLocation, this, - [](void * context, void * sender){ - ValuesController * valuesController = (ValuesController *)context; - int activeRow = valuesController->activeRow(); - int activeColumn = valuesController->activeColumn(); - EditableValueCell * cell = (EditableValueCell *)sender; - const char * textBody = cell->editedText(); - AppsContainer * appsContainer = (AppsContainer *)valuesController->app()->container(); - Context * globalContext = appsContainer->context(); - float floatBody = Expression::parse(textBody)->approximate(*globalContext); - valuesController->interval()->setElement(activeRow-1, floatBody); - valuesController->willDisplayCellAtLocation(cell, activeColumn, activeRow); - }); } int ValuesController::typeAtLocation(int i, int j) { diff --git a/apps/graph/values/values_controller.h b/apps/graph/values/values_controller.h index 9d2eeae82..c356c5af4 100644 --- a/apps/graph/values/values_controller.h +++ b/apps/graph/values/values_controller.h @@ -15,19 +15,17 @@ namespace Graph { -class ValuesController : public ViewController, public HeaderViewDelegate, public TableViewDataSource, public AlternateEmptyViewDelegate { +class ValuesController : public ViewController, public HeaderViewDelegate, public TableViewDataSource, public AlternateEmptyViewDelegate, public SelectableTableViewDelegate, public TextFieldDelegate { public: ValuesController(Responder * parentResponder, FunctionStore * functionStore, HeaderViewController * header); - - int activeRow(); - int activeColumn(); Interval * interval(); - void editValue(const char * initialText = nullptr); - View * view() override; const char * title() const override; bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; + bool textFieldDidFinishEditing(TextField * textField, const char * text) override; + ViewController * intervalParameterController(); int numberOfButtons() const override; Button * buttonAtIndex(int index) override; @@ -48,6 +46,8 @@ public: bool isEmpty() override; const char * emptyMessage() override; Responder * defaultController() override; + void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) override; + static constexpr KDCoordinate k_topMargin = 10; static constexpr KDCoordinate k_bottomMargin = 5; @@ -58,6 +58,8 @@ public: static constexpr KDCoordinate k_ordinateCellWidth = 100; private: + int activeRow(); + int activeColumn(); Function * functionAtColumn(int i); bool isDerivativeColumn(int i); Responder * tabController() const; diff --git a/apps/probability/calculation_controller.cpp b/apps/probability/calculation_controller.cpp index 2de2587f8..2df0d5b9b 100644 --- a/apps/probability/calculation_controller.cpp +++ b/apps/probability/calculation_controller.cpp @@ -12,6 +12,8 @@ CalculationController::ContentView::ContentView(Responder * parentResponder, Law { for (int k = 0; k < k_maxNumberOfEditableFields; k++) { m_calculationCell[k].setParentResponder(parentResponder); + CalculationController * parentController = (CalculationController *)parentResponder; + m_calculationCell[k].setDelegate(parentController); } } @@ -58,14 +60,17 @@ View * CalculationController::ContentView::subviewAtIndex(int index) { return &m_text[2]; } if (index == 3 || index == 5 || index == 7) { - char buffer[Constant::FloatBufferSizeInScientificMode]; - Float(m_law->calculationElementAtIndex((index - 3)/2)).convertFloatToText(buffer, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); - m_calculationCell[(index - 3)/2].setText(buffer); return &m_calculationCell[(index - 3)/2]; } return nullptr; } +void CalculationController::ContentView::willDisplayEditableCellAtIndex(int index) { + char buffer[Constant::FloatBufferSizeInScientificMode]; + Float(m_law->calculationElementAtIndex(index)).convertFloatToText(buffer, Constant::FloatBufferSizeInScientificMode, Constant::NumberOfDigitsInMantissaInScientificMode); + m_calculationCell[index].setText(buffer); +} + void CalculationController::ContentView::layoutSubviews() { markRectAsDirty(bounds()); KDCoordinate xCoordinate = 0; @@ -97,6 +102,10 @@ void CalculationController::ContentView::layoutSubviews() { m_text[2].setFrame(KDRect(xCoordinate, 0, numberOfCharacters*k_charWidth, ImageTableView::k_imageHeight)); xCoordinate += numberOfCharacters*k_charWidth + k_textMargin; m_calculationCell[2].setFrame(KDRect(xCoordinate, 0, k_textFieldWidth, ImageTableView::k_imageHeight)); + + for (int k = 0; k < k_maxNumberOfEditableFields; k++) { + willDisplayEditableCellAtIndex(k); + } } @@ -133,37 +142,12 @@ const char * CalculationController::title() const { } bool CalculationController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK && m_highlightedSubviewIndex == 0) { - m_contentView.imageTableView()->select(true); - app()->setFirstResponder(m_contentView.imageTableView()); - return true; - } - if ((event == Ion::Events::OK || event.hasText()) && m_highlightedSubviewIndex > 0) { - App * myApp = (App *)app(); - EditableTextCell * calculCell = m_contentView.calculationCellAtIndex(m_highlightedSubviewIndex-1); - const char * initialText = calculCell->text(); - if (event.hasText()) { - initialText = event.text(); - } - calculCell->editValue(myApp, initialText, strlen(calculCell->text()), this, - [](void * context, void * sender){ - CalculationController * calculationController = (CalculationController *)context; - Law * law = calculationController->law(); - int highlightedSubviewIndex = calculationController->highlightedSubviewIndex(); - EditableTextCell * cell = (EditableTextCell *)sender; - const char * textBody = cell->editedText(); - AppsContainer * appsContainer = (AppsContainer *)calculationController->app()->container(); - Context * globalContext = appsContainer->context(); - float floatBody = Expression::parse(textBody)->approximate(*globalContext); - law->setCalculationElementAtIndex(floatBody, highlightedSubviewIndex-1); - }); - return true; - } + if ((event == Ion::Events::Left && m_highlightedSubviewIndex > 0) || (event == Ion::Events::Right && m_highlightedSubviewIndex < ContentView::k_maxNumberOfEditableFields - 1)) { if (m_highlightedSubviewIndex == 0) { - app()->setFirstResponder(this); m_contentView.imageTableView()->select(false); m_contentView.imageTableView()->setHighlight(false); + m_contentView.layoutSubviews(); } else { EditableTextCell * calculCell = m_contentView.calculationCellAtIndex(m_highlightedSubviewIndex-1); calculCell->setHighlighted(false); @@ -172,14 +156,38 @@ bool CalculationController::handleEvent(Ion::Events::Event event) { if (m_highlightedSubviewIndex > 0) { EditableTextCell * newCalculCell = m_contentView.calculationCellAtIndex(m_highlightedSubviewIndex-1); newCalculCell->setHighlighted(true); + app()->setFirstResponder(newCalculCell); } else { m_contentView.imageTableView()->setHighlight(true); + app()->setFirstResponder(this); } return true; } + if (event == Ion::Events::OK && m_highlightedSubviewIndex == 0) { + m_contentView.imageTableView()->select(true); + app()->setFirstResponder(m_contentView.imageTableView()); + return true; + } 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) { + AppsContainer * appsContainer = (AppsContainer *)app()->container(); + Context * globalContext = appsContainer->context(); + float floatBody = Expression::parse(text)->approximate(*globalContext); + m_law->setCalculationElementAtIndex(floatBody, m_highlightedSubviewIndex-1); + for (int k = 0; k < ContentView::k_maxNumberOfEditableFields; k++) { + m_contentView.willDisplayEditableCellAtIndex(k); + } + return true; +} + void CalculationController::didBecomeFirstResponder() { m_contentView.layoutSubviews(); for (int subviewIndex = 0; subviewIndex < 2; subviewIndex++) { @@ -190,6 +198,7 @@ void CalculationController::didBecomeFirstResponder() { m_contentView.imageTableView()->setHighlight(false); EditableTextCell * calculCell = m_contentView.calculationCellAtIndex(m_highlightedSubviewIndex-1); calculCell->setHighlighted(true); + app()->setFirstResponder(calculCell); } else { m_contentView.imageTableView()->setHighlight(true); } @@ -200,10 +209,6 @@ Law * CalculationController::law() { return m_law; } -int CalculationController::highlightedSubviewIndex() const { - return m_highlightedSubviewIndex; -} - void CalculationController::selectSubview(int subviewIndex) { m_highlightedSubviewIndex = subviewIndex; } diff --git a/apps/probability/calculation_controller.h b/apps/probability/calculation_controller.h index 5b2525209..15c2779bc 100644 --- a/apps/probability/calculation_controller.h +++ b/apps/probability/calculation_controller.h @@ -8,7 +8,7 @@ namespace Probability { -class CalculationController : public ViewController { +class CalculationController : public ViewController, public TextFieldDelegate { public: CalculationController(Responder * parentResponder, Law * law); View * view() override; @@ -16,8 +16,9 @@ public: bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; Law * law(); - int highlightedSubviewIndex() const; void selectSubview(int subviewIndex); + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; + bool textFieldDidFinishEditing(TextField * textField, const char * text) override; private: class ContentView : public View { public: @@ -27,6 +28,7 @@ private: LawCurveView * lawCurveView(); ImageTableView * imageTableView(); EditableTextCell * calculationCellAtIndex(int index); + void willDisplayEditableCellAtIndex(int index); constexpr static int k_maxNumberOfEditableFields = 3; private: constexpr static KDCoordinate k_textFieldWidth = 35; diff --git a/apps/probability/parameters_controller.cpp b/apps/probability/parameters_controller.cpp index 552427623..78e5eabbd 100644 --- a/apps/probability/parameters_controller.cpp +++ b/apps/probability/parameters_controller.cpp @@ -1,5 +1,6 @@ #include "parameters_controller.h" #include "../constant.h" +#include "app.h" #include #include @@ -74,6 +75,10 @@ ParametersController::ParametersController(Responder * parentResponder, Law * la m_buttonSelected(false), m_calculationController(CalculationController(nullptr, law)) { + for (int k = 0; k < k_maxNumberOfCells; k++) { + m_menuListCell[k].setParentResponder(&m_selectableTableView); + m_menuListCell[k].setDelegate(this); + } } ExpressionTextFieldDelegate * ParametersController::textFieldDelegate() { @@ -150,7 +155,7 @@ int ParametersController::numberOfRows() { }; void ParametersController::willDisplayCellForIndex(TableViewCell * cell, int index) { - TextMenuListCell * myCell = (TextMenuListCell *) cell; + EditableTextMenuListCell * myCell = (EditableTextMenuListCell *) cell; myCell->setText(m_law->parameterNameAtIndex(index)); FloatParameterController::willDisplayCellForIndex(cell, index); } @@ -173,5 +178,10 @@ void ParametersController::setParameterAtIndex(int parameterIndex, float f) { m_law->setParameterAtIndex(f, parameterIndex); } +bool ParametersController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { + App * myApp = (App *)app(); + return myApp->textFieldDidReceiveEvent(textField, event); +} + } diff --git a/apps/probability/parameters_controller.h b/apps/probability/parameters_controller.h index f61ade7a1..a425f3435 100644 --- a/apps/probability/parameters_controller.h +++ b/apps/probability/parameters_controller.h @@ -22,6 +22,7 @@ public: void willDisplayCellForIndex(TableViewCell * cell, int index) override; TableViewCell * reusableCell(int index) override; int reusableCellCount() override; + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; private: float parameterAtIndex(int index) override; void setParameterAtIndex(int parameterIndex, float f) override; @@ -43,7 +44,8 @@ private: PointerTextView m_secondParameterDefinition; SelectableTableView * m_selectableTableView; }; - EditableTextMenuListCell m_menuListCell[2]; + constexpr static int k_maxNumberOfCells = 2; + EditableTextMenuListCell m_menuListCell[k_maxNumberOfCells]; ContentView m_contentView; Law * m_law; bool m_buttonSelected; diff --git a/escher/include/escher/editable_text_cell.h b/escher/include/escher/editable_text_cell.h index 5404d9135..350df0e61 100644 --- a/escher/include/escher/editable_text_cell.h +++ b/escher/include/escher/editable_text_cell.h @@ -1,8 +1,6 @@ #ifndef ESCHER_EDITABLE_TEXT_CELL_H #define ESCHER_EDITABLE_TEXT_CELL_H -#include -#include #include #include #include @@ -10,25 +8,22 @@ class EditableTextCell : public TableViewCell, public Responder { public: - EditableTextCell(Responder * parentResponder = nullptr); + EditableTextCell(Responder * parentResponder = nullptr, TextFieldDelegate * delegate = nullptr); + void setDelegate(TextFieldDelegate * delegate); void reloadCell() override; - const char * editedText() const; const char * text() const; void setText(const char * textContent); int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; - void drawRect(KDContext * ctx, KDRect rect) const override; - void editValue(TextFieldDelegate * textFieldDelegate, const char * initialText, int cursorLocation, void * context, Invocation::Action successAction); - bool handleEvent(Ion::Events::Event event) override; + void didBecomeFirstResponder() override; + void setEditing(bool isEditing); private: constexpr static KDCoordinate k_textHeight = 12; constexpr static KDCoordinate k_separatorThickness = 1; TextField m_textField; char m_textBody[255]; - bool m_isEditing; - Invocation m_successAction; - BufferTextView m_bufferText; + char m_draftTextBody[255]; }; #endif diff --git a/escher/include/escher/editable_text_menu_list_cell.h b/escher/include/escher/editable_text_menu_list_cell.h index 890c2c9c0..65527ade1 100644 --- a/escher/include/escher/editable_text_menu_list_cell.h +++ b/escher/include/escher/editable_text_menu_list_cell.h @@ -1,26 +1,28 @@ #ifndef ESCHER_EDITABLE_TEXT_MENU_LIST_CELL_H #define ESCHER_EDITABLE_TEXT_MENU_LIST_CELL_H -#include -#include -#include +#include +#include #include -class EditableTextMenuListCell : public Responder, public TextMenuListCell { +class EditableTextMenuListCell : public Responder, public MenuListCell { public: - EditableTextMenuListCell(char * label = nullptr); + EditableTextMenuListCell(char * label = nullptr, Responder * parentResponder = nullptr, TextFieldDelegate * textFieldDelegate = nullptr); + void setDelegate(TextFieldDelegate * delegate); View * accessoryView() const override; const char * editedText() const; void layoutSubviews() override; - bool handleEvent(Ion::Events::Event event) override; - void editValue(TextFieldDelegate * textFieldDelegate, const char * initialText, int cursorLocation, void * context, Invocation::Action successAction); + void didBecomeFirstResponder() override; + void setEditing(bool isEditing); + void reloadCell() override; + void setAccessoryText(const char * text); + void setTextColor(KDColor color) override; private: constexpr static KDCoordinate k_textWidth = 7*7; constexpr static KDCoordinate k_textHeight = 12; TextField m_textField; char m_textBody[255]; - bool m_isEditing; - Invocation m_successAction; + char m_draftTextBody[255]; }; #endif diff --git a/escher/include/escher/input_view_controller.h b/escher/include/escher/input_view_controller.h index 63f944cc3..366cdc2e4 100644 --- a/escher/include/escher/input_view_controller.h +++ b/escher/include/escher/input_view_controller.h @@ -6,13 +6,15 @@ #include #include -class InputViewController : public ModalViewController { +class InputViewController : public ModalViewController, TextFieldDelegate { public: - InputViewController(Responder * parentResponder, ViewController * child, TextFieldDelegate * textFieldDelegate = nullptr); + InputViewController(Responder * parentResponder, ViewController * child, TextFieldDelegate * textFieldDelegate); const char * title() const override; - bool handleEvent(Ion::Events::Event event) override; - void edit(Responder * caller, const char * initialContent, int cursorPosition, void * context, Invocation::Action successAction, Invocation::Action failureAction); + void edit(Responder * caller, Ion::Events::Event event, void * context, Invocation::Action successAction, Invocation::Action failureAction); const char * textBody(); + 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; private: class TextFieldController : public ViewController { public: @@ -24,11 +26,10 @@ private: TextField m_textField; char m_textBody[255]; }; - void showInput(); - void setTextBody(const char * text); TextFieldController m_textFieldController; Invocation m_successAction; Invocation m_failureAction; + TextFieldDelegate * m_textFieldDelegate; }; #endif diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index 82481d45a..1acce8481 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -8,7 +8,8 @@ class TextField : public View, public Responder { public: - TextField(Responder * parentResponder, char * textBuffer, size_t textBufferSize, TextFieldDelegate * delegate = nullptr); + TextField(Responder * parentResponder, char * textBuffer, char * draftTextBuffer, size_t textBufferSize, TextFieldDelegate * delegate = nullptr, + float horizontalAlignment = 0.0f, float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite); // View void drawRect(KDContext * ctx, KDRect rect) const override; // Responder @@ -24,17 +25,28 @@ public: void setCursorLocation(int location); void insertTextAtLocation(const char * text, int location); KDSize minimalSizeForOptimalDisplay() override; -protected: + void setBackgroundColor(KDColor backgroundColor); + void setTextColor(KDColor textColor); + void setAlignment(float horizontalAlignment, float verticalAlignment); void reload(); + bool isEditing() const; + virtual void setEditing(bool isEditing); +protected: #if ESCHER_VIEW_LOGGING const char * className() const override; #endif + bool m_isEditing; char * m_textBuffer; + char * m_draftTextBuffer; size_t m_currentTextLength; size_t m_currentCursorLocation; private: size_t m_textBufferSize; TextFieldDelegate * m_delegate; + float m_horizontalAlignment; + float m_verticalAlignment; + KDColor m_textColor; + KDColor m_backgroundColor; }; #endif diff --git a/escher/include/escher/text_field_delegate.h b/escher/include/escher/text_field_delegate.h index 8630e61c7..a44d53def 100644 --- a/escher/include/escher/text_field_delegate.h +++ b/escher/include/escher/text_field_delegate.h @@ -6,6 +6,8 @@ class TextField; class TextFieldDelegate { 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;}; }; #endif diff --git a/escher/src/editable_text_cell.cpp b/escher/src/editable_text_cell.cpp index 3b812b319..f73b95aa3 100644 --- a/escher/src/editable_text_cell.cpp +++ b/escher/src/editable_text_cell.cpp @@ -3,32 +3,29 @@ #include #include -EditableTextCell::EditableTextCell(Responder * parentResponder) : +EditableTextCell::EditableTextCell(Responder * parentResponder, TextFieldDelegate * delegate) : TableViewCell(), Responder(parentResponder), - m_textField(TextField(this, m_textBody, 255, nullptr)), - m_isEditing(false), - m_successAction(Invocation(nullptr, nullptr)), - m_bufferText(BufferTextView()) + m_textField(TextField(this, m_textBody, m_draftTextBody, 255, delegate)) { } +void EditableTextCell::setDelegate(TextFieldDelegate * delegate) { + m_textField.setTextFieldDelegate(delegate); +} + void EditableTextCell::reloadCell() { TableViewCell::reloadCell(); KDColor backgroundColor = isHighlighted()? Palette::FocusCellBackgroundColor : Palette::CellBackgroundColor; - m_bufferText.setBackgroundColor(backgroundColor); -} - -const char * EditableTextCell::editedText() const { - return m_textField.text(); + m_textField.setBackgroundColor(backgroundColor); } const char * EditableTextCell::text() const { - return m_bufferText.text(); + return m_textField.text(); } void EditableTextCell::setText(const char * text) { - m_bufferText.setText(text); + m_textField.setText(text); } int EditableTextCell::numberOfSubviews() const { @@ -37,42 +34,20 @@ int EditableTextCell::numberOfSubviews() const { View * EditableTextCell::subviewAtIndex(int index) { assert(index == 0); - if (m_isEditing) { - return &m_textField; - } - return &m_bufferText; + return &m_textField; } void EditableTextCell::layoutSubviews() { KDCoordinate width = bounds().width(); KDCoordinate height = bounds().height(); m_textField.setFrame(KDRect(k_separatorThickness, (height - k_textHeight)/2, width - k_separatorThickness, k_textHeight)); - m_bufferText.setFrame(KDRect(k_separatorThickness, (height - k_textHeight)/2, width - k_separatorThickness, k_textHeight)); } -void EditableTextCell::drawRect(KDContext * ctx, KDRect rect) const { - KDColor backgroundColor = isHighlighted() ? Palette::FocusCellBackgroundColor : Palette::CellBackgroundColor; - ctx->fillRect(rect, backgroundColor); -} - -bool EditableTextCell::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK) { - m_isEditing = false; - m_successAction.perform(this); - app()->setFirstResponder(parentResponder()); - return true; - } - m_isEditing = false; - app()->setFirstResponder(parentResponder()); - return false; -} - -void EditableTextCell::editValue(TextFieldDelegate * textFieldDelegate, const char * initialText, int cursorPosition, void * context, Invocation::Action successAction) { - markRectAsDirty(bounds()); - m_textField.setTextFieldDelegate(textFieldDelegate); +void EditableTextCell::didBecomeFirstResponder() { app()->setFirstResponder(&m_textField); - m_isEditing = true; - m_textField.setText(initialText); - m_textField.setCursorLocation(cursorPosition); - m_successAction = Invocation(successAction, context); } + +void EditableTextCell::setEditing(bool isEditing) { + m_textField.setEditing(isEditing); +} + diff --git a/escher/src/editable_text_menu_list_cell.cpp b/escher/src/editable_text_menu_list_cell.cpp index 7843cf4b1..6b1de84ac 100644 --- a/escher/src/editable_text_menu_list_cell.cpp +++ b/escher/src/editable_text_menu_list_cell.cpp @@ -1,20 +1,19 @@ #include #include -EditableTextMenuListCell::EditableTextMenuListCell(char * label) : - Responder(nullptr), - TextMenuListCell(label), - m_textField(TextField(this, m_textBody, 255, nullptr)), - m_isEditing(false), - m_successAction(Invocation(nullptr, nullptr)) +EditableTextMenuListCell::EditableTextMenuListCell(char * label, Responder * parentResponder, TextFieldDelegate * textFieldDelegate) : + Responder(parentResponder), + MenuListCell(label), + m_textField(TextField(this, m_textBody, m_draftTextBody, 255, textFieldDelegate)) { } +void EditableTextMenuListCell::setDelegate(TextFieldDelegate * delegate) { + m_textField.setTextFieldDelegate(delegate); +} + View * EditableTextMenuListCell::accessoryView() const { - if (m_isEditing) { - return (View *)&m_textField; - } - return (View *)&m_accessoryView; + return (View *)&m_textField; } const char * EditableTextMenuListCell::editedText() const { @@ -22,32 +21,31 @@ const char * EditableTextMenuListCell::editedText() const { } void EditableTextMenuListCell::layoutSubviews() { - TextMenuListCell::layoutSubviews(); + MenuListCell::layoutSubviews(); KDCoordinate width = bounds().width(); KDCoordinate height = bounds().height(); m_textField.setFrame(KDRect(width - k_textWidth - k_separatorThickness, (height - k_textHeight)/2, k_textWidth - k_separatorThickness, k_textHeight)); - m_accessoryView.setFrame(KDRect(width - k_textWidth - k_separatorThickness, (height - k_textHeight)/2, k_textWidth - k_separatorThickness, k_textHeight)); } -bool EditableTextMenuListCell::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK) { - m_isEditing = false; - m_successAction.perform(this); - app()->setFirstResponder(parentResponder()); - return true; - } - m_isEditing = false; - app()->setFirstResponder(parentResponder()); - return false; -} - - -void EditableTextMenuListCell::editValue(TextFieldDelegate * textFieldDelegate, const char * initialText, int cursorPosition, void * context, Invocation::Action successAction) { - markRectAsDirty(bounds()); - m_textField.setTextFieldDelegate(textFieldDelegate); +void EditableTextMenuListCell::didBecomeFirstResponder() { app()->setFirstResponder(&m_textField); - m_isEditing = true; - m_textField.setText(initialText); - m_textField.setCursorLocation(cursorPosition); - m_successAction = Invocation(successAction, context); -} \ No newline at end of file +} + +void EditableTextMenuListCell::setEditing(bool isEditing) { + m_textField.setEditing(isEditing); +} + +void EditableTextMenuListCell::reloadCell() { + MenuListCell::reloadCell(); + KDColor backgroundColor = isHighlighted()? Palette::FocusCellBackgroundColor : Palette::CellBackgroundColor; + m_textField.setBackgroundColor(backgroundColor); +} + +void EditableTextMenuListCell::setAccessoryText(const char * text) { + m_textField.setText(text); +} + +void EditableTextMenuListCell::setTextColor(KDColor color) { + m_textField.setTextColor(color); + MenuListCell::setTextColor(color); +} diff --git a/escher/src/input_view_controller.cpp b/escher/src/input_view_controller.cpp index 0b81670db..7f9f8233b 100644 --- a/escher/src/input_view_controller.cpp +++ b/escher/src/input_view_controller.cpp @@ -4,7 +4,7 @@ InputViewController::TextFieldController::TextFieldController(Responder * parentResponder, TextFieldDelegate * textFieldDelegate) : ViewController(parentResponder), - m_textField(parentResponder, m_textBody, 255, textFieldDelegate) + m_textField(parentResponder, m_textBody, m_textBody, 255, textFieldDelegate) { m_textBody[0] = 0; } @@ -23,9 +23,10 @@ TextField * InputViewController::TextFieldController::textField() { InputViewController::InputViewController(Responder * parentResponder, ViewController * child, TextFieldDelegate * textFieldDelegate) : ModalViewController(parentResponder, child), - m_textFieldController(TextFieldController(this, textFieldDelegate)), + m_textFieldController(TextFieldController(this, this)), m_successAction(Invocation(nullptr, nullptr)), - m_failureAction(Invocation(nullptr, nullptr)) + m_failureAction(Invocation(nullptr, nullptr)), + m_textFieldDelegate(textFieldDelegate) { } @@ -37,35 +38,27 @@ const char * InputViewController::textBody() { return m_textFieldController.textField()->text(); } -void InputViewController::showInput() { +void InputViewController::edit(Responder * caller, Ion::Events::Event event, void * context, Invocation::Action successAction, Invocation::Action failureAction) { + m_successAction = Invocation(successAction, context); + m_failureAction = Invocation(failureAction, context); + m_textFieldController.textField()->handleEvent(event); displayModalViewController(&m_textFieldController, 1.0f, 1.0f); } -void InputViewController::setTextBody(const char * text) { - m_textFieldController.textField()->setText(text); +bool InputViewController::textFieldDidFinishEditing(TextField * textField, const char * text) { + m_successAction.perform(this); + m_textFieldController.textField()->setText(""); + dismissModalViewController(); + return true; } -bool InputViewController::handleEvent(Ion::Events::Event event) { - if (!isDisplayingModal()) { - return false; - } - if (event == Ion::Events::OK) { - m_successAction.perform(this); - dismissModalViewController(); - return true; - } - if (event == Ion::Events::Back) { - m_failureAction.perform(this); - dismissModalViewController(); - return true; - } - return false; +bool InputViewController::textFieldDidAbortEditing(TextField * textField, const char * text) { + m_failureAction.perform(this); + m_textFieldController.textField()->setText(""); + dismissModalViewController(); + return true; } -void InputViewController::edit(Responder * caller, const char * initialContent, int cursorPosition, void * context, Invocation::Action successAction, Invocation::Action failureAction) { - m_successAction = Invocation(successAction, context); - m_failureAction = Invocation(failureAction, context); - setTextBody(initialContent); - m_textFieldController.textField()->setCursorLocation(cursorPosition); - showInput(); +bool InputViewController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { + return m_textFieldDelegate->textFieldDidReceiveEvent(textField, event); } diff --git a/escher/src/selectable_table_view.cpp b/escher/src/selectable_table_view.cpp index 5b0355d5c..847e7840f 100644 --- a/escher/src/selectable_table_view.cpp +++ b/escher/src/selectable_table_view.cpp @@ -33,8 +33,13 @@ void SelectableTableView::deselectTable() { TableViewCell * previousCell = cellAtLocation(m_selectedCellX, m_selectedCellY); previousCell->setHighlighted(false); } + int previousSelectedCellX = m_selectedCellX; + int previousSelectedCellY = m_selectedCellY; m_selectedCellX = 0; m_selectedCellY = -1; + if (m_delegate) { + m_delegate->tableViewDidChangeSelection(this, previousSelectedCellX, previousSelectedCellY); + } } bool SelectableTableView::selectCellAtLocation(int i, int j) { diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index ff34887f4..9cc682d6a 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -1,22 +1,38 @@ #include #include -TextField::TextField(Responder * parentResponder, char * textBuffer, size_t textBufferSize, TextFieldDelegate * delegate) : +TextField::TextField(Responder * parentResponder, char * textBuffer, char * draftTextBuffer, + size_t textBufferSize, TextFieldDelegate * delegate, + float horizontalAlignment, float verticalAlignment, KDColor textColor, KDColor backgroundColor) : View(), Responder(parentResponder), + m_isEditing(false), m_textBuffer(textBuffer), + m_draftTextBuffer(draftTextBuffer), m_currentTextLength(0), m_currentCursorLocation(0), m_textBufferSize(textBufferSize), - m_delegate(delegate) + m_delegate(delegate), + m_horizontalAlignment(horizontalAlignment), + m_verticalAlignment(verticalAlignment), + m_textColor(textColor), + m_backgroundColor(backgroundColor) { } -/* View */ +const char * TextField::text() const { + if (m_isEditing) { + return (const char *)m_draftTextBuffer; + } + return (const char *)m_textBuffer; +} void TextField::drawRect(KDContext * ctx, KDRect rect) const { - ctx->fillRect(rect, KDColorWhite); - ctx->drawString(m_textBuffer, KDPointZero); + ctx->fillRect(rect, m_backgroundColor); + KDSize textSize = KDText::stringSize(text()); + KDPoint origin(m_horizontalAlignment*(m_frame.width() - textSize.width()), + m_verticalAlignment*(m_frame.height() - textSize.height())); + ctx->drawString(text(), origin, m_textColor, m_backgroundColor); } #if ESCHER_VIEW_LOGGING @@ -25,13 +41,29 @@ const char * TextField::className() const { } #endif -void TextField::reload() { - KDSize sizeText = KDText::stringSize(m_textBuffer); - KDRect dirtyZone(0, 0, sizeText.width(), sizeText.height()); - markRectAsDirty(dirtyZone); +void TextField::setBackgroundColor(KDColor backgroundColor) { + m_backgroundColor = backgroundColor; + markRectAsDirty(bounds()); } -/* Responder */ +void TextField::setTextColor(KDColor textColor) { + m_textColor = textColor; + markRectAsDirty(bounds()); +} + +void TextField::setAlignment(float horizontalAlignment, float verticalAlignment) { + m_horizontalAlignment = horizontalAlignment; + m_verticalAlignment = verticalAlignment; + markRectAsDirty(bounds()); +} + +void TextField::reload() { + KDSize textSize = KDText::stringSize(text()); + KDPoint origin(m_horizontalAlignment*(m_frame.width() - textSize.width()), + m_verticalAlignment*(m_frame.height() - textSize.height())); + KDRect dirtyZone(origin, textSize); + markRectAsDirty(dirtyZone); +} bool TextField::handleEvent(Ion::Events::Event event) { if (m_delegate) { @@ -39,43 +71,63 @@ bool TextField::handleEvent(Ion::Events::Event event) { return true; } } - if (event == Ion::Events::Left) { + if (event == Ion::Events::OK) { + if (m_isEditing) { + strlcpy(m_textBuffer, m_draftTextBuffer, m_textBufferSize); + setEditing(false); + m_delegate->textFieldDidFinishEditing(this, text()); + return true; + } + setEditing(true); + strlcpy(m_draftTextBuffer, m_textBuffer, m_textBufferSize); + setCursorLocation(strlen(m_draftTextBuffer)); + return true; + } + if (event == Ion::Events::Left && m_isEditing) { if (m_currentCursorLocation > 0) { m_currentCursorLocation--; } return true; } - if (event == Ion::Events::Right) { + if (event == Ion::Events::Right && m_isEditing) { if (m_currentCursorLocation < m_currentTextLength) { m_currentCursorLocation++; } return true; } - if (event == Ion::Events::Backspace) { + if (event == Ion::Events::Backspace && m_isEditing) { if (m_currentCursorLocation > 0) { reload(); m_currentTextLength--; m_currentCursorLocation--; for (int k = m_currentCursorLocation; k < m_currentTextLength; k ++) { - m_textBuffer[k] = m_textBuffer[k+1]; + m_draftTextBuffer[k] = m_draftTextBuffer[k+1]; } - m_textBuffer[m_currentTextLength] = 0; + m_draftTextBuffer[m_currentTextLength] = 0; } return true; } if (event.hasText()) { - insertTextAtLocation(event.text(), cursorLocation()); + if (m_isEditing) { + insertTextAtLocation(event.text(), cursorLocation()); + } else { + strlcpy(m_draftTextBuffer, event.text(), m_textBufferSize); + m_currentTextLength = strlen(event.text()); + setCursorLocation(0); + } int cursorDelta = strlen(event.text()) > 1 ? -1 : 0; setCursorLocation(cursorLocation() + strlen(event.text()) + cursorDelta); + setEditing(true); + return true; + } + if (event == Ion::Events::Back && m_isEditing) { + m_delegate->textFieldDidAbortEditing(this, text()); + setEditing(false); return true; } return false; } -const char * TextField::text() const { - return (const char *)m_textBuffer; -} - int TextField::textLength() const { assert(strlen(text()) == m_currentTextLength); return m_currentTextLength; @@ -83,9 +135,13 @@ int TextField::textLength() const { void TextField::setText(const char * text) { reload(); - strlcpy(m_textBuffer, text, m_textBufferSize); - m_currentCursorLocation = strlen(text); - m_currentTextLength = m_currentCursorLocation; + if (m_isEditing) { + strlcpy(m_draftTextBuffer, text, m_textBufferSize); + m_currentCursorLocation = strlen(text); + m_currentTextLength = m_currentCursorLocation; + } else { + strlcpy(m_textBuffer, text, m_textBufferSize); + } reload(); } @@ -105,19 +161,31 @@ void TextField::insertTextAtLocation(const char * text, int location) { return; } for (int k = m_currentTextLength; k >= location && k >= 0; k--) { - m_textBuffer[k+textSize] = m_textBuffer[k]; + m_draftTextBuffer[k+textSize] = m_draftTextBuffer[k]; } - strlcpy(&m_textBuffer[location], text, textSize); - m_textBuffer[location+textSize-1] = text[textSize-1]; + strlcpy(&m_draftTextBuffer[location], text, textSize); + m_draftTextBuffer[location+textSize-1] = text[textSize-1]; m_currentTextLength += textSize; reload(); } KDSize TextField::minimalSizeForOptimalDisplay() { - KDSize textSize = KDText::stringSize(m_textBuffer); + KDSize textSize = KDText::stringSize(m_draftTextBuffer); return KDSize(0, textSize.height()); } void TextField::setTextFieldDelegate(TextFieldDelegate * delegate) { m_delegate = delegate; } + +bool TextField::isEditing() const { + return m_isEditing; +} + +void TextField::setEditing(bool isEditing) { + if (m_isEditing == isEditing) { + return; + } + m_isEditing = isEditing; + markRectAsDirty(bounds()); +}