diff --git a/apps/calculation/Makefile b/apps/calculation/Makefile index 420391945..226945971 100644 --- a/apps/calculation/Makefile +++ b/apps/calculation/Makefile @@ -5,8 +5,8 @@ app_objs += $(addprefix apps/calculation/,\ app.o\ calculation.o\ calculation_store.o\ - editable_expression_view.o\ edit_expression_controller.o\ + editable_expression_view.o\ history_view_cell.o\ history_controller.o\ local_context.o\ @@ -14,7 +14,6 @@ app_objs += $(addprefix apps/calculation/,\ scrollable_expression_view.o\ scrollable_output_expressions_view.o\ selectable_table_view.o\ - text_field.o\ ) i18n_files += $(addprefix apps/calculation/,\ diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 21056ddb4..f4bb63e6a 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -43,7 +43,7 @@ void App::Snapshot::tidy() { } App::App(Container * container, Snapshot * snapshot) : - TextFieldAndEditableExpressionViewDelegateApp(container, snapshot, &m_editExpressionController), + EditableExpressionViewDelegateApp(container, snapshot, &m_editExpressionController), m_localContext((GlobalContext *)((AppsContainer *)container)->globalContext(), snapshot->calculationStore()), m_historyController(&m_editExpressionController, snapshot->calculationStore()), m_editExpressionController(&m_modalViewController, &m_historyController, snapshot->calculationStore()) @@ -80,21 +80,21 @@ bool App::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event e return false; } -bool App::editableExpressionViewDidReceiveEvent(::EditableExpressionView * editableExpressionView, Ion::Events::Event event) { - if ((event == Ion::Events::Var || event == Ion::Events::XNT) && TextFieldAndEditableExpressionViewDelegateApp::editableExpressionViewDidReceiveEvent(editableExpressionView, event)) { +bool App::scrollableExpressionViewWithCursorDidReceiveEvent(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + if ((event == Ion::Events::Var || event == Ion::Events::XNT) && EditableExpressionViewDelegateApp::scrollableExpressionViewWithCursorDidReceiveEvent(scrollableExpressionViewWithCursor, event)) { return true; } /* Here, we check that the expression entered by the user can be printed with * less than k_printedExpressionLength characters. Otherwise, we prevent the * user from adding this expression to the calculation store. */ - if (editableExpressionView->isEditing() && editableExpressionView->editableExpressionViewShouldFinishEditing(event)) { + if (scrollableExpressionViewWithCursor->isEditing() && scrollableExpressionViewWithCursor->scrollableExpressionViewWithCursorShouldFinishEditing(event)) { int bufferLength = TextField::maxBufferSize(); char bufferForParsing[bufferLength]; - Poincare::ExpressionLayout * expressionLayout = editableExpressionView->expressionViewWithCursor()->expressionView()->expressionLayout(); + Poincare::ExpressionLayout * expressionLayout = scrollableExpressionViewWithCursor->expressionViewWithCursor()->expressionView()->expressionLayout(); expressionLayout->writeTextInBuffer(bufferForParsing, bufferLength); Expression * exp = Expression::parse(bufferForParsing); if (exp == nullptr) { - editableExpressionView->app()->displayWarning(I18n::Message::SyntaxError); + scrollableExpressionViewWithCursor->app()->displayWarning(I18n::Message::SyntaxError); return true; } char buffer[Calculation::k_printedExpressionSize]; diff --git a/apps/calculation/app.h b/apps/calculation/app.h index 4ec807891..56d404178 100644 --- a/apps/calculation/app.h +++ b/apps/calculation/app.h @@ -10,7 +10,7 @@ namespace Calculation { -class App : public Shared::TextFieldAndEditableExpressionViewDelegateApp { +class App : public Shared::EditableExpressionViewDelegateApp { public: class Descriptor : public ::App::Descriptor { public: @@ -30,7 +30,7 @@ public: }; Poincare::Context * localContext() override; bool textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) override; - bool editableExpressionViewDidReceiveEvent(::EditableExpressionView * editableExpressionView, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidReceiveEvent(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; const char * XNT() override; private: App(Container * container, Snapshot * snapshot); diff --git a/apps/calculation/edit_expression_controller.cpp b/apps/calculation/edit_expression_controller.cpp index d1fefa9af..45d9c2474 100644 --- a/apps/calculation/edit_expression_controller.cpp +++ b/apps/calculation/edit_expression_controller.cpp @@ -9,17 +9,11 @@ using namespace Shared; namespace Calculation { -EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate, EditableExpressionViewDelegate * editableExpressionViewDelegate) : +EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate) : View(), m_mainView(subview), - m_textField(parentResponder, m_textBody, TextField::maxBufferSize(), textFieldDelegate), - m_editableExpressionView(parentResponder, new Poincare::HorizontalLayout(), editableExpressionViewDelegate) + m_editableExpressionView(parentResponder, textFieldDelegate, scrollableExpressionViewWithCursorDelegate) { - m_textBody[0] = 0; -} - -int EditExpressionController::ContentView::numberOfSubviews() const { - return 2; } View * EditExpressionController::ContentView::subviewAtIndex(int index) { @@ -28,25 +22,15 @@ View * EditExpressionController::ContentView::subviewAtIndex(int index) { return m_mainView; } assert(index == 1); - if (editionIsInTextField()) { - return &m_textField; - } return &m_editableExpressionView; } void EditExpressionController::ContentView::layoutSubviews() { - KDCoordinate inputViewFrameHeight = inputViewHeight(); - KDRect mainViewFrame(0, 0, bounds().width(), bounds().height() - inputViewFrameHeight-k_separatorThickness); + KDCoordinate inputViewFrameHeight = m_editableExpressionView.minimalSizeForOptimalDisplay().height(); + KDRect mainViewFrame(0, 0, bounds().width(), bounds().height() - inputViewFrameHeight); m_mainView->setFrame(mainViewFrame); - if (editionIsInTextField()) { - KDRect inputViewFrame(k_leftMargin, bounds().height() - inputViewFrameHeight, bounds().width()-k_leftMargin, k_textFieldHeight); - m_textField.setFrame(inputViewFrame); - m_editableExpressionView.setFrame(KDRectZero); - return; - } - KDRect inputViewFrame(k_leftMargin, bounds().height() - inputViewFrameHeight, bounds().width() - k_leftMargin, inputViewFrameHeight); + KDRect inputViewFrame(0, bounds().height() - inputViewFrameHeight, bounds().width(), inputViewFrameHeight); m_editableExpressionView.setFrame(inputViewFrame); - m_textField.setFrame(KDRectZero); } void EditExpressionController::ContentView::reload() { @@ -54,30 +38,6 @@ void EditExpressionController::ContentView::reload() { markRectAsDirty(bounds()); } -void EditExpressionController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { - KDCoordinate inputViewFrameHeight = inputViewHeight(); - // Draw the separator - ctx->fillRect(KDRect(0, bounds().height() -inputViewFrameHeight-k_separatorThickness, bounds().width(), k_separatorThickness), Palette::GreyMiddle); - // Color the left margin - ctx->fillRect(KDRect(0, bounds().height() -inputViewFrameHeight, k_leftMargin, inputViewFrameHeight), m_textField.backgroundColor()); - if (!editionIsInTextField()) { - // Color the upper margin - ctx->fillRect(KDRect(0, bounds().height() -inputViewFrameHeight, bounds().width(), k_verticalEditableExpressionViewMargin), m_textField.backgroundColor()); - } -} - -bool EditExpressionController::ContentView::editionIsInTextField() const { - return Poincare::Preferences::sharedPreferences()->editionMode() == Poincare::Preferences::EditionMode::Edition1D; -} - -KDCoordinate EditExpressionController::ContentView::inputViewHeight() const { - return editionIsInTextField() ? k_textFieldHeight : k_verticalEditableExpressionViewMargin + editableExpressionViewHeight(); -} - -KDCoordinate EditExpressionController::ContentView::editableExpressionViewHeight() const { - return KDCoordinate(min(0.6*Ion::Display::Height, max(k_textFieldHeight, m_editableExpressionView.minimalSizeForOptimalDisplay().height()+k_verticalEditableExpressionViewMargin))); -} - EditExpressionController::EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore) : DynamicViewController(parentResponder), m_historyController(historyController), @@ -86,31 +46,18 @@ EditExpressionController::EditExpressionController(Responder * parentResponder, } const char * EditExpressionController::textBody() { - return ((ContentView *)view())->textField()->text(); + return ((ContentView *)view())->editableExpressionView()->text(); } void EditExpressionController::insertTextBody(const char * text) { - if (((ContentView *)view())->editionIsInTextField()) { - TextField * tf = ((ContentView *)view())->textField(); - tf->setEditing(true, false); - tf->insertTextAtLocation(text, tf->cursorLocation()); - tf->setCursorLocation(tf->cursorLocation() + strlen(text)); - return; - } - EditableExpressionView * editableExpressionView = ((ContentView *)view())->editableExpressionView(); - editableExpressionView->setEditing(true); - editableExpressionView->insertLayoutFromTextAtCursor(text); + ((ContentView *)view())->editableExpressionView()->insertText(text); } bool EditExpressionController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Up) { if (m_calculationStore->numberOfCalculations() > 0) { - if (((ContentView *)view())->editionIsInTextField()) { - ((ContentView *)view())->textField()->setEditing(false, false); - } else { - ((ContentView *)view())->editableExpressionView()->setEditing(false); - } - app()->setFirstResponder(m_historyController); + ((ContentView *)view())->editableExpressionView()->setEditing(false, false); + app()->setFirstResponder(m_historyController); } return true; } @@ -120,74 +67,48 @@ bool EditExpressionController::handleEvent(Ion::Events::Event event) { void EditExpressionController::didBecomeFirstResponder() { int lastRow = m_calculationStore->numberOfCalculations() > 0 ? m_calculationStore->numberOfCalculations()-1 : 0; m_historyController->scrollToCell(0, lastRow); - if (((ContentView *)view())->editionIsInTextField()) { - ((ContentView *)view())->textField()->setEditing(true, false); - app()->setFirstResponder(((ContentView *)view())->textField()); - return; - } - ((ContentView *)view())->editableExpressionView()->setEditing(true); + ((ContentView *)view())->editableExpressionView()->setEditing(true, false); app()->setFirstResponder(((ContentView *)view())->editableExpressionView()); } bool EditExpressionController::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) { + assert(textField == ((ContentView *)view())->editableExpressionView()->textField()); if (textField->isEditing() && textField->textFieldShouldFinishEditing(event) && textField->draftTextLength() == 0 && m_calculationStore->numberOfCalculations() > 0) { - App * calculationApp = (App *)app(); - const char * lastTextBody = m_calculationStore->calculationAtIndex(m_calculationStore->numberOfCalculations()-1)->inputText(); - m_calculationStore->push(lastTextBody, calculationApp->localContext()); - m_historyController->reload(); - ((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); - return true; + return inputViewDidReceiveEvent(event); } return textFieldDelegateApp()->textFieldDidReceiveEvent(textField, event); } bool EditExpressionController::textFieldDidFinishEditing(::TextField * textField, const char * text, Ion::Events::Event event) { - App * calculationApp = (App *)app(); - m_calculationStore->push(textBody(), calculationApp->localContext()); - m_historyController->reload(); - ((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); - ((ContentView *)view())->textField()->setEditing(true); - ((ContentView *)view())->textField()->setText(""); - return true; + assert(textField == ((ContentView *)view())->editableExpressionView()->textField()); + return inputViewDidFinishEditing(text, event); } bool EditExpressionController::textFieldDidAbortEditing(::TextField * textField, const char * text) { - ((ContentView *)view())->textField()->setEditing(true); - ((ContentView *)view())->textField()->setText(text); - return false; + assert(textField == ((ContentView *)view())->editableExpressionView()->textField()); + return inputViewDidAbortEditing(text); } -bool EditExpressionController::editableExpressionViewDidReceiveEvent(::EditableExpressionView * editableExpressionView, Ion::Events::Event event) { - bool layoutIsEmpty = expressionLayout()->isHorizontal() && expressionLayout()->numberOfChildren() == 0; - if (editableExpressionView->isEditing() && editableExpressionView->editableExpressionViewShouldFinishEditing(event) && layoutIsEmpty && m_calculationStore->numberOfCalculations() > 0) { - App * calculationApp = (App *)app(); - const char * lastTextBody = m_calculationStore->calculationAtIndex(m_calculationStore->numberOfCalculations()-1)->inputText(); - m_calculationStore->push(lastTextBody, calculationApp->localContext()); - m_historyController->reload(); - ((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); - return true; +bool EditExpressionController::scrollableExpressionViewWithCursorDidReceiveEvent(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + assert(scrollableExpressionViewWithCursor == ((ContentView *)view())->editableExpressionView()->scrollableExpressionViewWithCursor()); + if (scrollableExpressionViewWithCursor->isEditing() && scrollableExpressionViewWithCursor->scrollableExpressionViewWithCursorShouldFinishEditing(event) && !expressionLayout()->hasText() && m_calculationStore->numberOfCalculations() > 0) { + return inputViewDidReceiveEvent(event); } - return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewDidReceiveEvent(editableExpressionView, event); + return editableExpressionViewDelegateApp()->scrollableExpressionViewWithCursorDidReceiveEvent(scrollableExpressionViewWithCursor, event); } -bool EditExpressionController::editableExpressionViewDidFinishEditing(::EditableExpressionView * editableExpressionView, const char * text, Ion::Events::Event event) { - App * calculationApp = (App *)app(); - expressionLayout()->writeTextInBuffer(const_cast(textBody()), ContentView::k_bufferLength); - m_calculationStore->push(textBody(), calculationApp->localContext()); - (const_cast(((ContentView *)view())->editableExpressionView()->expressionViewWithCursor()->expressionView()))->setExpressionLayout(new Poincare::HorizontalLayout()); - reloadView(); - ((ContentView *)view())->editableExpressionView()->setEditing(true); - return true; +bool EditExpressionController::scrollableExpressionViewWithCursorDidFinishEditing(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text, Ion::Events::Event event) { + assert(scrollableExpressionViewWithCursor == ((ContentView *)view())->editableExpressionView()->scrollableExpressionViewWithCursor()); + return inputViewDidFinishEditing(text, event); } -bool EditExpressionController::editableExpressionViewDidAbortEditing(::EditableExpressionView * editableExpressionView, const char * text) { - ((ContentView *)view())->editableExpressionView()->setEditing(true); - //TODO ((ContentView *)view())->editableExpressionView()->editableExpressionView()->expressionViewWithCursor()->expressionView()->setLayout(; - return false; +bool EditExpressionController::scrollableExpressionViewWithCursorDidAbortEditing(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text) { + assert(scrollableExpressionViewWithCursor == ((ContentView *)view())->editableExpressionView()->scrollableExpressionViewWithCursor()); + return inputViewDidAbortEditing(text); } -void EditExpressionController::editableExpressionViewDidChangeSize(::EditableExpressionView * editableExpressionView) { - assert(editableExpressionView == ((ContentView *)view())->editableExpressionView()); +void EditExpressionController::scrollableExpressionViewWithCursorDidChangeSize(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + assert(scrollableExpressionViewWithCursor == ((ContentView *)view())->editableExpressionView()->scrollableExpressionViewWithCursor()); reloadView(); } @@ -195,7 +116,7 @@ TextFieldDelegateApp * EditExpressionController::textFieldDelegateApp() { return (App *)app(); } -TextFieldAndEditableExpressionViewDelegateApp * EditExpressionController::textFieldAndEditableExpressionViewDelegateApp() { +EditableExpressionViewDelegateApp * EditExpressionController::editableExpressionViewDelegateApp() { return (App *)app(); } @@ -216,13 +137,38 @@ void EditExpressionController::reloadView() { } } +bool EditExpressionController::inputViewDidReceiveEvent(Ion::Events::Event event) { + App * calculationApp = (App *)app(); + const char * lastTextBody = m_calculationStore->calculationAtIndex(m_calculationStore->numberOfCalculations()-1)->inputText(); + m_calculationStore->push(lastTextBody, calculationApp->localContext()); + m_historyController->reload(); + ((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); + return true; +} + +bool EditExpressionController::inputViewDidFinishEditing(const char * text, Ion::Events::Event event) { + App * calculationApp = (App *)app(); + m_calculationStore->push(textBody(), calculationApp->localContext()); + m_historyController->reload(); + ((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); + ((ContentView *)view())->editableExpressionView()->setEditing(true, true); + ((ContentView *)view())->editableExpressionView()->setText(""); + return true; +} + +bool EditExpressionController::inputViewDidAbortEditing(const char * text) { + ((ContentView *)view())->editableExpressionView()->setEditing(true, true); + ((ContentView *)view())->editableExpressionView()->setText(text); + return false; +} + void EditExpressionController::viewDidDisappear() { DynamicViewController::viewDidDisappear(); m_historyController->viewDidDisappear(); } Poincare::ExpressionLayout * EditExpressionController::expressionLayout() { - return ((ContentView *)view())->editableExpressionView()->expressionViewWithCursor()->expressionView()->expressionLayout(); + return ((ContentView *)view())->editableExpressionView()->scrollableExpressionViewWithCursor()->expressionViewWithCursor()->expressionView()->expressionLayout(); } } diff --git a/apps/calculation/edit_expression_controller.h b/apps/calculation/edit_expression_controller.h index 9c3d3d4aa..a2b64b951 100644 --- a/apps/calculation/edit_expression_controller.h +++ b/apps/calculation/edit_expression_controller.h @@ -4,16 +4,15 @@ #include #include "editable_expression_view.h" #include "../shared/text_field_delegate.h" -#include "../shared/editable_expression_view_delegate.h" +#include "../shared/scrollable_expression_view_with_cursor_delegate.h" #include "history_controller.h" #include "calculation_store.h" -#include "text_field.h" namespace Calculation { class HistoryController; /* TODO: implement a split view */ -class EditExpressionController : public DynamicViewController, public Shared::TextFieldDelegate, public Shared::EditableExpressionViewDelegate { +class EditExpressionController : public DynamicViewController, public Shared::TextFieldDelegate, public Shared::ScrollableExpressionViewWithCursorDelegate { public: EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore); void didBecomeFirstResponder() override; @@ -27,43 +26,35 @@ public: bool textFieldDidFinishEditing(::TextField * textField, const char * text, Ion::Events::Event event) override; bool textFieldDidAbortEditing(::TextField * textField, const char * text) override; - /* EditableExpressionViewDelegate */ - bool editableExpressionViewDidReceiveEvent(::EditableExpressionView * editableExpressionView, Ion::Events::Event event) override; - bool editableExpressionViewDidFinishEditing(::EditableExpressionView * editableExpressionView, const char * text, Ion::Events::Event event) override; - bool editableExpressionViewDidAbortEditing(::EditableExpressionView * editableExpressionView, const char * text) override; - void editableExpressionViewDidChangeSize(::EditableExpressionView * editableExpressionView) override; + /* ScrollableExpressionViewWithCursorDelegate */ + bool scrollableExpressionViewWithCursorDidReceiveEvent(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidFinishEditing(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidAbortEditing(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text) override; + void scrollableExpressionViewWithCursorDidChangeSize(::ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) override; private: class ContentView : public View { public: - ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate, EditableExpressionViewDelegate * editableExpressionViewDelegate); - int numberOfSubviews() const override; + ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate); + void reload(); + TableView * mainView() { return m_mainView; } + EditableExpressionView * editableExpressionView() { return &m_editableExpressionView; } + /* View */ + int numberOfSubviews() const override { return 2; } View * subviewAtIndex(int index) override; void layoutSubviews() override; - void reload(); - TextField * textField() { return &m_textField; } - EditableExpressionView * editableExpressionView() { return &m_editableExpressionView; } - TableView * mainView() { return m_mainView; } - void drawRect(KDContext * ctx, KDRect rect) const override; - bool editionIsInTextField() const; - static constexpr int k_bufferLength = TextField::maxBufferSize(); private: - static constexpr KDCoordinate k_textFieldHeight = 37; - static constexpr KDCoordinate k_leftMargin = 5; - static constexpr KDCoordinate k_verticalEditableExpressionViewMargin = 5; - constexpr static int k_separatorThickness = 1; - KDCoordinate inputViewHeight() const; - KDCoordinate editableExpressionViewHeight() const; TableView * m_mainView; - TextField m_textField; EditableExpressionView m_editableExpressionView; - char m_textBody[k_bufferLength]; }; View * loadView() override; void unloadView(View * view) override; void reloadView(); + bool inputViewDidReceiveEvent(Ion::Events::Event event); + bool inputViewDidFinishEditing(const char * text, Ion::Events::Event event); + bool inputViewDidAbortEditing(const char * text); Shared::TextFieldDelegateApp * textFieldDelegateApp() override; - Shared::TextFieldAndEditableExpressionViewDelegateApp * textFieldAndEditableExpressionViewDelegateApp() override; + Shared::EditableExpressionViewDelegateApp * editableExpressionViewDelegateApp() override; Poincare::ExpressionLayout * expressionLayout(); HistoryController * m_historyController; CalculationStore * m_calculationStore; diff --git a/apps/calculation/editable_expression_view.cpp b/apps/calculation/editable_expression_view.cpp index a06287c2c..ac9b2000d 100644 --- a/apps/calculation/editable_expression_view.cpp +++ b/apps/calculation/editable_expression_view.cpp @@ -2,34 +2,30 @@ namespace Calculation { -EditableExpressionView::EditableExpressionView(Responder * parentResponder, Poincare::ExpressionLayout * expressionLayout, EditableExpressionViewDelegate * delegate) : - ::EditableExpressionView(parentResponder, expressionLayout, delegate) +EditableExpressionView::EditableExpressionView(Responder * parentResponder, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate) : + ::EditableExpressionView(parentResponder, textFieldDelegate, scrollableExpressionViewWithCursorDelegate) { setEditing(true); } -bool EditableExpressionView::privateHandleEvent(Ion::Events::Event event) { +bool EditableExpressionView::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Back) { return false; } if (event == Ion::Events::Ans) { - m_expressionViewWithCursor.cursor()->insertText("ans"); + insertText("ans"); return true; } - Poincare::ExpressionLayout * layout = m_expressionViewWithCursor.expressionView()->expressionLayout(); - bool layoutIsEmpty = layout->isEmpty() - || (layout->isHorizontal() - && layout->numberOfChildren() == 0); - if (isEditing() && layoutIsEmpty && + if (isEditing() && isEmpty() && (event == Ion::Events::Multiplication || event == Ion::Events::Plus || event == Ion::Events::Power || event == Ion::Events::Square || event == Ion::Events::Division || event == Ion::Events::Sto)) { - m_expressionViewWithCursor.cursor()->insertText("ans"); + insertText("ans"); } - return(::EditableExpressionView::privateHandleEvent(event)); + return(::EditableExpressionView::handleEvent(event)); } } diff --git a/apps/calculation/editable_expression_view.h b/apps/calculation/editable_expression_view.h index f873e9115..52a1faad3 100644 --- a/apps/calculation/editable_expression_view.h +++ b/apps/calculation/editable_expression_view.h @@ -7,9 +7,9 @@ namespace Calculation { class EditableExpressionView : public ::EditableExpressionView { public: - EditableExpressionView(Responder * parentResponder, Poincare::ExpressionLayout * expressionLayout, EditableExpressionViewDelegate * delegate = nullptr); + EditableExpressionView(Responder * parentResponder, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate); protected: - bool privateHandleEvent(Ion::Events::Event event) override; + bool handleEvent(Ion::Events::Event event) override; }; } diff --git a/apps/calculation/text_field.cpp b/apps/calculation/text_field.cpp deleted file mode 100644 index e9349c201..000000000 --- a/apps/calculation/text_field.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "text_field.h" - -namespace Calculation { - -TextField::TextField(Responder * parentResponder, char * textBuffer, size_t textBufferSize, TextFieldDelegate * delegate) : - ::TextField(parentResponder, textBuffer, textBuffer, textBufferSize, delegate, false) -{ - setEditing(true); -} - -bool TextField::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::Back) { - return false; - } - if (event == Ion::Events::Ans) { - insertTextAtLocation("ans", cursorLocation()); - setCursorLocation(cursorLocation() + strlen("ans")); - return true; - } - if (isEditing() && draftTextLength() == 0 && - (event == Ion::Events::Multiplication || - event == Ion::Events::Plus || - event == Ion::Events::Power || - event == Ion::Events::Square || - event == Ion::Events::Division || - event == Ion::Events::Sto)) { - insertTextAtLocation("ans", cursorLocation()); - setCursorLocation(cursorLocation() + strlen("ans")); - } - return(::TextField::handleEvent(event)); -} - -} diff --git a/apps/calculation/text_field.h b/apps/calculation/text_field.h deleted file mode 100644 index 6d2f12392..000000000 --- a/apps/calculation/text_field.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef CALCULATION_TEXT_FIELD_H -#define CALCULATION_TEXT_FIELD_H - -#include - -namespace Calculation { - -class TextField : public ::TextField { -public: - TextField(Responder * parentResponder, char * textBuffer, size_t textBufferSize, TextFieldDelegate * delegate); - bool handleEvent(Ion::Events::Event event) override; -}; - -} - -#endif diff --git a/apps/graph/app.cpp b/apps/graph/app.cpp index 581877d5f..c1b3d7a0e 100644 --- a/apps/graph/app.cpp +++ b/apps/graph/app.cpp @@ -70,7 +70,7 @@ App::App(Container * container, Snapshot * snapshot) : m_valuesHeader(&m_valuesStackViewController, &m_valuesAlternateEmptyViewController, &m_valuesController), m_valuesStackViewController(&m_tabViewController, &m_valuesHeader), m_tabViewController(&m_inputViewController, snapshot, &m_listStackViewController, &m_graphStackViewController, &m_valuesStackViewController), - m_inputViewController(&m_modalViewController, &m_tabViewController, this) + m_inputViewController(&m_modalViewController, &m_tabViewController, this, this) { } diff --git a/apps/math_toolbox.cpp b/apps/math_toolbox.cpp index a083d5747..63639f81f 100644 --- a/apps/math_toolbox.cpp +++ b/apps/math_toolbox.cpp @@ -115,8 +115,8 @@ void MathToolbox::setSenderAndAction(Responder * sender, Action action) { m_action = action; } -void MathToolbox::actionForEditableExpressionView(void * sender, const char * text, bool removeArguments) { - EditableExpressionView * expressionLayoutEditorSender = static_cast(sender); +void MathToolbox::actionForScrollableExpressionViewWithCursor(void * sender, const char * text, bool removeArguments) { + ScrollableExpressionViewWithCursor * expressionLayoutEditorSender = static_cast(sender); Expression * resultExpression = nullptr; if (removeArguments) { // Replace the arguments with Empty chars. diff --git a/apps/math_toolbox.h b/apps/math_toolbox.h index 913706611..68f38e3e9 100644 --- a/apps/math_toolbox.h +++ b/apps/math_toolbox.h @@ -11,7 +11,7 @@ public: typedef void (*Action)(void * sender, const char * text, bool removeArguments); MathToolbox(); void setSenderAndAction(Responder * sender, Action action); - static void actionForEditableExpressionView(void * sender, const char * text, bool removeArguments = true); + static void actionForScrollableExpressionViewWithCursor(void * sender, const char * text, bool removeArguments = true); static void actionForTextField(void * sender, const char * text, bool removeArguments = true); protected: bool selectLeaf(ToolboxMessageTree * selectedMessageTree) override; diff --git a/apps/sequence/app.cpp b/apps/sequence/app.cpp index de681f165..8c9194f5c 100644 --- a/apps/sequence/app.cpp +++ b/apps/sequence/app.cpp @@ -72,7 +72,7 @@ App::App(Container * container, Snapshot * snapshot) : m_valuesHeader(nullptr, &m_valuesAlternateEmptyViewController, &m_valuesController), m_valuesStackViewController(&m_tabViewController, &m_valuesHeader), m_tabViewController(&m_inputViewController, snapshot, &m_listStackViewController, &m_graphStackViewController, &m_valuesStackViewController), - m_inputViewController(&m_modalViewController, &m_tabViewController, &m_listController) + m_inputViewController(&m_modalViewController, &m_tabViewController, &m_listController, &m_listController) { } diff --git a/apps/sequence/list/list_controller.cpp b/apps/sequence/list/list_controller.cpp index c3a8e6031..b8cc8cdeb 100644 --- a/apps/sequence/list/list_controller.cpp +++ b/apps/sequence/list/list_controller.cpp @@ -38,6 +38,10 @@ TextFieldDelegateApp * ListController::textFieldDelegateApp() { return (App *)app(); } +EditableExpressionViewDelegateApp * ListController::editableExpressionViewDelegateApp() { + return (App *)app(); +} + int ListController::numberOfRows() { int numberOfRows = 0; for (int i = 0; i < m_sequenceStore->numberOfFunctions(); i++) { diff --git a/apps/sequence/list/list_controller.h b/apps/sequence/list/list_controller.h index 2fd3f0f35..a8f34882f 100644 --- a/apps/sequence/list/list_controller.h +++ b/apps/sequence/list/list_controller.h @@ -5,16 +5,17 @@ #include "../sequence_title_cell.h" #include "../sequence_store.h" #include "../../shared/function_expression_cell.h" -#include "type_parameter_controller.h" -#include "../../shared/new_function_cell.h" #include "../../shared/list_controller.h" +#include "../../shared/new_function_cell.h" +#include "../../shared/scrollable_expression_view_with_cursor_delegate.h" #include "../../shared/text_field_delegate.h" #include "list_parameter_controller.h" #include "sequence_toolbox.h" +#include "type_parameter_controller.h" namespace Sequence { -class ListController : public Shared::ListController, public Shared::TextFieldDelegate { +class ListController : public Shared::ListController, public Shared::TextFieldDelegate, public Shared::ScrollableExpressionViewWithCursorDelegate { public: ListController(Responder * parentResponder, SequenceStore * sequenceStore, ButtonRowController * header, ButtonRowController * footer); const char * title() override; @@ -25,6 +26,7 @@ public: void selectPreviousNewSequenceCell(); private: Shared::TextFieldDelegateApp * textFieldDelegateApp() override; + Shared::EditableExpressionViewDelegateApp * editableExpressionViewDelegateApp() override; void editExpression(Sequence * sequence, int sequenceDefinitionIndex, Ion::Events::Event event); ListParameterController * parameterController() override; int maxNumberOfRows() override; diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 40c3a475c..11e481683 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -6,7 +6,7 @@ app_objs += $(addprefix apps/shared/,\ curve_view_cursor.o\ curve_view_range.o\ editable_cell_table_view_controller.o\ - editable_expression_view_delegate.o\ + editable_expression_view_delegate_app.o\ float_pair_store.o\ float_parameter_controller.o\ function.o\ @@ -35,10 +35,10 @@ app_objs += $(addprefix apps/shared/,\ parameter_text_field_delegate.o\ range_parameter_controller.o\ regular_table_view_data_source.o\ + scrollable_expression_view_with_cursor_delegate.o\ store_controller.o\ store_parameter_controller.o\ tab_table_controller.o\ - text_field_and_editable_expression_view_delegate_app.o\ text_field_delegate.o\ text_field_delegate_app.o\ toolbox_helpers.o\ diff --git a/apps/shared/editable_expression_view_delegate.cpp b/apps/shared/editable_expression_view_delegate.cpp deleted file mode 100644 index ff7542ed4..000000000 --- a/apps/shared/editable_expression_view_delegate.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "editable_expression_view_delegate.h" - -using namespace Poincare; - -namespace Shared { - -bool EditableExpressionViewDelegate::editableExpressionViewShouldFinishEditing(::EditableExpressionView * editableExpressionView, Ion::Events::Event event) { - return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewShouldFinishEditing(editableExpressionView, event); -} - -bool EditableExpressionViewDelegate::editableExpressionViewDidReceiveEvent(::EditableExpressionView * editableExpressionView, Ion::Events::Event event) { - return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewDidReceiveEvent(editableExpressionView, event); -} - -bool EditableExpressionViewDelegate::editableExpressionViewDidFinishEditing(EditableExpressionView * editableExpressionView, const char * text, Ion::Events::Event event) { - return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewDidFinishEditing(editableExpressionView, text, event); -} - -bool EditableExpressionViewDelegate::editableExpressionViewDidAbortEditing(EditableExpressionView * editableExpressionView, const char * text) { - return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewDidAbortEditing(editableExpressionView, text); -} - -bool EditableExpressionViewDelegate::editableExpressionViewDidHandleEvent(EditableExpressionView * editableExpressionView, Ion::Events::Event event, bool returnValue, bool expressionHasChanged) { - return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewDidHandleEvent(editableExpressionView, event, returnValue, expressionHasChanged); -} - -void EditableExpressionViewDelegate::editableExpressionViewDidChangeSize(EditableExpressionView * editableExpressionView) { - return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewDidChangeSize(editableExpressionView); -} - -Toolbox * EditableExpressionViewDelegate::toolboxForEditableExpressionView(::EditableExpressionView * editableExpressionView) { - return textFieldAndEditableExpressionViewDelegateApp()->toolboxForEditableExpressionView(editableExpressionView); -} - -} diff --git a/apps/shared/editable_expression_view_delegate.h b/apps/shared/editable_expression_view_delegate.h deleted file mode 100644 index 9c779b642..000000000 --- a/apps/shared/editable_expression_view_delegate.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef SHARED_EDITABLE_EXPRESSION_VIEW_DELEGATE_H -#define SHARED_EDITABLE_EXPRESSION_VIEW_DELEGATE_H - -#include -#include "text_field_and_editable_expression_view_delegate_app.h" - -namespace Shared { - -class EditableExpressionViewDelegate : public ::EditableExpressionViewDelegate { -public: - bool editableExpressionViewShouldFinishEditing(EditableExpressionView * editableExpressionView, Ion::Events::Event event) override; - bool editableExpressionViewDidReceiveEvent(EditableExpressionView * editableExpressionView, Ion::Events::Event event) override; - bool editableExpressionViewDidFinishEditing(EditableExpressionView * editableExpressionView, const char * text, Ion::Events::Event event) override; - bool editableExpressionViewDidAbortEditing(EditableExpressionView * editableExpressionView, const char * text) override; - bool editableExpressionViewDidHandleEvent(EditableExpressionView * editableExpressionView, Ion::Events::Event event, bool returnValue, bool expressionHasChanged) override; - void editableExpressionViewDidChangeSize(EditableExpressionView * editableExpressionView) override; - Toolbox * toolboxForEditableExpressionView(EditableExpressionView * editableExpressionView) override; -private: - virtual TextFieldAndEditableExpressionViewDelegateApp * textFieldAndEditableExpressionViewDelegateApp() = 0; -}; - -} - -#endif diff --git a/apps/shared/editable_expression_view_delegate_app.cpp b/apps/shared/editable_expression_view_delegate_app.cpp new file mode 100644 index 000000000..3ce1f53cb --- /dev/null +++ b/apps/shared/editable_expression_view_delegate_app.cpp @@ -0,0 +1,56 @@ +#include "editable_expression_view_delegate_app.h" +#include "../i18n.h" +#include "../apps_container.h" + +using namespace Poincare; + +namespace Shared { + +EditableExpressionViewDelegateApp::EditableExpressionViewDelegateApp(Container * container, Snapshot * snapshot, ViewController * rootViewController) : + TextFieldDelegateApp(container, snapshot, rootViewController), + ScrollableExpressionViewWithCursorDelegate() +{ +} + +bool EditableExpressionViewDelegateApp::scrollableExpressionViewWithCursorShouldFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + return event == Ion::Events::OK || event == Ion::Events::EXE; +} + +bool EditableExpressionViewDelegateApp::scrollableExpressionViewWithCursorDidReceiveEvent(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + if (scrollableExpressionViewWithCursor->isEditing() && scrollableExpressionViewWithCursor->scrollableExpressionViewWithCursorShouldFinishEditing(event)) { + if (!scrollableExpressionViewWithCursor->expressionViewWithCursor()->expressionView()->expressionLayout()->hasText()) { + scrollableExpressionViewWithCursor->app()->displayWarning(I18n::Message::SyntaxError); + return true; + } + int bufferSize = 256; + char buffer[bufferSize]; + scrollableExpressionViewWithCursor->expressionViewWithCursor()->expressionView()->expressionLayout()->writeTextInBuffer(buffer, bufferSize); + Expression * exp = Expression::parse(buffer); + if (exp != nullptr) { + delete exp; + } + if (exp == nullptr) { + scrollableExpressionViewWithCursor->app()->displayWarning(I18n::Message::SyntaxError); + return true; + } + } + if (event == Ion::Events::Var) { + if (!scrollableExpressionViewWithCursor->isEditing()) { + scrollableExpressionViewWithCursor->setEditing(true); + } + AppsContainer * appsContainer = (AppsContainer *)scrollableExpressionViewWithCursor->app()->container(); + VariableBoxController * variableBoxController = appsContainer->variableBoxController(); + variableBoxController->setScrollableExpressionViewWithCursorSender(scrollableExpressionViewWithCursor); + scrollableExpressionViewWithCursor->app()->displayModalViewController(variableBoxController, 0.f, 0.f, Metric::PopUpTopMargin, Metric::PopUpLeftMargin, 0, Metric::PopUpRightMargin); + return true; + } + return false; +} + +Toolbox * EditableExpressionViewDelegateApp::toolboxForScrollableExpressionViewWithCursor(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + Toolbox * toolbox = container()->mathToolbox(); + static_cast(toolbox)->setSenderAndAction(scrollableExpressionViewWithCursor, MathToolbox::actionForScrollableExpressionViewWithCursor); + return toolbox; +} + +} diff --git a/apps/shared/editable_expression_view_delegate_app.h b/apps/shared/editable_expression_view_delegate_app.h new file mode 100644 index 000000000..626b3fac7 --- /dev/null +++ b/apps/shared/editable_expression_view_delegate_app.h @@ -0,0 +1,21 @@ +#ifndef SHARED_EDITABLE_EXPRESSION_VIEW_DELEGATE_APP_H +#define SHARED_EDITABLE_EXPRESSION_VIEW_DELEGATE_APP_H + +#include "text_field_delegate_app.h" +#include + +namespace Shared { + +class EditableExpressionViewDelegateApp : public TextFieldDelegateApp, public ScrollableExpressionViewWithCursorDelegate { +public: + virtual ~EditableExpressionViewDelegateApp() = default; + bool scrollableExpressionViewWithCursorShouldFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; + virtual bool scrollableExpressionViewWithCursorDidReceiveEvent(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; + Toolbox * toolboxForScrollableExpressionViewWithCursor(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) override; +protected: + EditableExpressionViewDelegateApp(Container * container, Snapshot * snapshot, ViewController * rootViewController); +}; + +} + +#endif diff --git a/apps/shared/function_app.cpp b/apps/shared/function_app.cpp index 4e01d51d4..f805efe48 100644 --- a/apps/shared/function_app.cpp +++ b/apps/shared/function_app.cpp @@ -47,7 +47,7 @@ void FunctionApp::Snapshot::reset() { } FunctionApp::FunctionApp(Container * container, Snapshot * snapshot, ViewController * rootViewController) : - TextFieldDelegateApp(container, snapshot, rootViewController) + EditableExpressionViewDelegateApp(container, snapshot, rootViewController) { } @@ -56,7 +56,7 @@ void FunctionApp::willBecomeInactive() { m_modalViewController.dismissModalViewController(); } if (inputViewController()->isDisplayingModal()) { - inputViewController()->abortTextFieldEditionAndDismiss(); + inputViewController()->abortEditionAndDismiss(); } ::App::willBecomeInactive(); } diff --git a/apps/shared/function_app.h b/apps/shared/function_app.h index 0c73d582f..b4b59503d 100644 --- a/apps/shared/function_app.h +++ b/apps/shared/function_app.h @@ -2,7 +2,7 @@ #define SHARED_FUNCTION_APP_H #include -#include "text_field_delegate_app.h" +#include "editable_expression_view_delegate_app.h" #include "curve_view_cursor.h" #include "interval.h" @@ -10,7 +10,7 @@ class AppsContainer; namespace Shared { -class FunctionApp : public TextFieldDelegateApp { +class FunctionApp : public EditableExpressionViewDelegateApp { public: class Snapshot : public ::App::Snapshot, public TabViewDataSource { public: diff --git a/apps/shared/scrollable_expression_view_with_cursor_delegate.cpp b/apps/shared/scrollable_expression_view_with_cursor_delegate.cpp new file mode 100644 index 000000000..5174721ff --- /dev/null +++ b/apps/shared/scrollable_expression_view_with_cursor_delegate.cpp @@ -0,0 +1,31 @@ +#include "scrollable_expression_view_with_cursor_delegate.h" + +using namespace Poincare; + +namespace Shared { + +bool ScrollableExpressionViewWithCursorDelegate::scrollableExpressionViewWithCursorShouldFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + return editableExpressionViewDelegateApp()->scrollableExpressionViewWithCursorShouldFinishEditing(scrollableExpressionViewWithCursor, event); +} + +bool ScrollableExpressionViewWithCursorDelegate::scrollableExpressionViewWithCursorDidReceiveEvent(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + return editableExpressionViewDelegateApp()->scrollableExpressionViewWithCursorDidReceiveEvent(scrollableExpressionViewWithCursor, event); +} + +bool ScrollableExpressionViewWithCursorDelegate::scrollableExpressionViewWithCursorDidFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text, Ion::Events::Event event) { + return editableExpressionViewDelegateApp()->scrollableExpressionViewWithCursorDidFinishEditing(scrollableExpressionViewWithCursor, text, event); +} + +bool ScrollableExpressionViewWithCursorDelegate::scrollableExpressionViewWithCursorDidAbortEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text) { + return editableExpressionViewDelegateApp()->scrollableExpressionViewWithCursorDidAbortEditing(scrollableExpressionViewWithCursor, text); +} + +void ScrollableExpressionViewWithCursorDelegate::scrollableExpressionViewWithCursorDidChangeSize(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + return editableExpressionViewDelegateApp()->scrollableExpressionViewWithCursorDidChangeSize(scrollableExpressionViewWithCursor); +} + +Toolbox * ScrollableExpressionViewWithCursorDelegate::toolboxForScrollableExpressionViewWithCursor(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + return editableExpressionViewDelegateApp()->toolboxForScrollableExpressionViewWithCursor(scrollableExpressionViewWithCursor); +} + +} diff --git a/apps/shared/scrollable_expression_view_with_cursor_delegate.h b/apps/shared/scrollable_expression_view_with_cursor_delegate.h new file mode 100644 index 000000000..589f6a8db --- /dev/null +++ b/apps/shared/scrollable_expression_view_with_cursor_delegate.h @@ -0,0 +1,23 @@ +#ifndef SHARED_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H +#define SHARED_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H + +#include +#include "editable_expression_view_delegate_app.h" + +namespace Shared { + +class ScrollableExpressionViewWithCursorDelegate : public ::ScrollableExpressionViewWithCursorDelegate { +public: + bool scrollableExpressionViewWithCursorShouldFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidReceiveEvent(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidAbortEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text) override; + void scrollableExpressionViewWithCursorDidChangeSize(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) override; + Toolbox * toolboxForScrollableExpressionViewWithCursor(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) override; +private: + virtual EditableExpressionViewDelegateApp * editableExpressionViewDelegateApp() = 0; +}; + +} + +#endif diff --git a/apps/shared/text_field_and_editable_expression_view_delegate_app.cpp b/apps/shared/text_field_and_editable_expression_view_delegate_app.cpp deleted file mode 100644 index 516c4a466..000000000 --- a/apps/shared/text_field_and_editable_expression_view_delegate_app.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "text_field_and_editable_expression_view_delegate_app.h" -#include "../i18n.h" -#include "../apps_container.h" - -using namespace Poincare; - -namespace Shared { - -TextFieldAndEditableExpressionViewDelegateApp::TextFieldAndEditableExpressionViewDelegateApp(Container * container, Snapshot * snapshot, ViewController * rootViewController) : - TextFieldDelegateApp(container, snapshot, rootViewController), - EditableExpressionViewDelegate() -{ -} - -bool TextFieldAndEditableExpressionViewDelegateApp::editableExpressionViewShouldFinishEditing(EditableExpressionView * editableExpressionView, Ion::Events::Event event) { - return event == Ion::Events::OK || event == Ion::Events::EXE; -} - -bool TextFieldAndEditableExpressionViewDelegateApp::editableExpressionViewDidReceiveEvent(EditableExpressionView * editableExpressionView, Ion::Events::Event event) { - if (editableExpressionView->isEditing() && editableExpressionView->editableExpressionViewShouldFinishEditing(event)) { - int bufferSize = 256; - char buffer[bufferSize]; - editableExpressionView->expressionViewWithCursor()->expressionView()->expressionLayout()->writeTextInBuffer(buffer, bufferSize); - Expression * exp = Expression::parse(buffer); - if (exp != nullptr) { - delete exp; - } - if (exp == nullptr) { - editableExpressionView->app()->displayWarning(I18n::Message::SyntaxError); - return true; - } - } - if (event == Ion::Events::Var) { - if (!editableExpressionView->isEditing()) { - editableExpressionView->setEditing(true); - } - AppsContainer * appsContainer = (AppsContainer *)editableExpressionView->app()->container(); - VariableBoxController * variableBoxController = appsContainer->variableBoxController(); - variableBoxController->setEditableExpressionViewSender(editableExpressionView); - editableExpressionView->app()->displayModalViewController(variableBoxController, 0.f, 0.f, Metric::PopUpTopMargin, Metric::PopUpLeftMargin, 0, Metric::PopUpRightMargin); - return true; - } - return false; -} - -Toolbox * TextFieldAndEditableExpressionViewDelegateApp::toolboxForEditableExpressionView(EditableExpressionView * editableExpressionView) { - Toolbox * toolbox = container()->mathToolbox(); - static_cast(toolbox)->setSenderAndAction(editableExpressionView, MathToolbox::actionForEditableExpressionView); - return toolbox; -} - -} diff --git a/apps/shared/text_field_and_editable_expression_view_delegate_app.h b/apps/shared/text_field_and_editable_expression_view_delegate_app.h deleted file mode 100644 index 85bc85fc2..000000000 --- a/apps/shared/text_field_and_editable_expression_view_delegate_app.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef SHARED_TEXT_FIELD_AND_EDITABLE_EXPRESSION_VIEW_DELEGATE_APP_H -#define SHARED_TEXT_FIELD_AND_EDITABLE_EXPRESSION_VIEW_DELEGATE_APP_H - -#include "text_field_delegate_app.h" -#include - -namespace Shared { - -class TextFieldAndEditableExpressionViewDelegateApp : public TextFieldDelegateApp, public EditableExpressionViewDelegate { -public: - virtual ~TextFieldAndEditableExpressionViewDelegateApp() = default; - bool editableExpressionViewShouldFinishEditing(EditableExpressionView * editableExpressionView, Ion::Events::Event event) override; - virtual bool editableExpressionViewDidReceiveEvent(EditableExpressionView * editableExpressionView, Ion::Events::Event event) override; - Toolbox * toolboxForEditableExpressionView(EditableExpressionView * editableExpressionView) override; -protected: - TextFieldAndEditableExpressionViewDelegateApp(Container * container, Snapshot * snapshot, ViewController * rootViewController); -}; - -} - -#endif diff --git a/apps/variable_box_controller.cpp b/apps/variable_box_controller.cpp index 3f14cc49c..253a38ea9 100644 --- a/apps/variable_box_controller.cpp +++ b/apps/variable_box_controller.cpp @@ -202,9 +202,9 @@ void VariableBoxController::ContentViewController::setTextFieldSender(TextField m_insertTextAction = &insertTextInTextField; } -void VariableBoxController::ContentViewController::setEditableExpressionViewSender(EditableExpressionView * editableExpressionView) { - m_sender = editableExpressionView; - m_insertTextAction = &insertTextInEditableExpressionView; +void VariableBoxController::ContentViewController::setScrollableExpressionViewWithCursorSender(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + m_sender = scrollableExpressionViewWithCursor; + m_insertTextAction = &insertTextInScrollableExpressionViewWithCursor; } void VariableBoxController::ContentViewController::reloadData() { @@ -288,12 +288,12 @@ void VariableBoxController::ContentViewController::insertTextInTextField(void * textField->setCursorLocation(textField->cursorLocation() + strlen(textToInsert)); } -void VariableBoxController::ContentViewController::insertTextInEditableExpressionView(void * sender, const char * textToInsert) { - EditableExpressionView * editableExpressionView = static_cast(sender); - if (!editableExpressionView->isEditing()) { - editableExpressionView->setEditing(true); +void VariableBoxController::ContentViewController::insertTextInScrollableExpressionViewWithCursor(void * sender, const char * textToInsert) { + ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor = static_cast(sender); + if (!scrollableExpressionViewWithCursor->isEditing()) { + scrollableExpressionViewWithCursor->setEditing(true); } - editableExpressionView->insertLayoutFromTextAtCursor(textToInsert); + scrollableExpressionViewWithCursor->insertLayoutFromTextAtCursor(textToInsert); } VariableBoxController::VariableBoxController(GlobalContext * context) : @@ -310,8 +310,8 @@ void VariableBoxController::setTextFieldSender(TextField * textField) { m_contentViewController.setTextFieldSender(textField); } -void VariableBoxController::setEditableExpressionViewSender(EditableExpressionView * editableExpressionView) { - m_contentViewController.setEditableExpressionViewSender(editableExpressionView); +void VariableBoxController::setScrollableExpressionViewWithCursorSender(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + m_contentViewController.setScrollableExpressionViewWithCursorSender(scrollableExpressionViewWithCursor); } void VariableBoxController::viewWillAppear() { diff --git a/apps/variable_box_controller.h b/apps/variable_box_controller.h index daa75938e..512ef2798 100644 --- a/apps/variable_box_controller.h +++ b/apps/variable_box_controller.h @@ -14,7 +14,7 @@ public: VariableBoxController(Poincare::GlobalContext * context); void didBecomeFirstResponder() override; void setTextFieldSender(TextField * textField); - void setEditableExpressionViewSender(EditableExpressionView * editableExpressionView); + void setScrollableExpressionViewWithCursorSender(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor); void viewWillAppear() override; void viewDidDisappear() override; private: @@ -34,7 +34,7 @@ private: int indexFromCumulatedHeight(KDCoordinate offsetY) override; int typeAtLocation(int i, int j) override; void setTextFieldSender(TextField * textField); - void setEditableExpressionViewSender(EditableExpressionView * editableExpressionView); + void setScrollableExpressionViewWithCursorSender(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor); void reloadData(); void resetPage(); void viewDidDisappear() override; @@ -59,7 +59,7 @@ private: I18n::Message nodeLabelAtIndex(int index); const Poincare::Expression * expressionForIndex(int index); static void insertTextInTextField(void * sender, const char * textToInsert); - static void insertTextInEditableExpressionView(void * sender, const char * textToInsert); + static void insertTextInScrollableExpressionViewWithCursor(void * sender, const char * textToInsert); Poincare::GlobalContext * m_context; Responder * m_sender; Action m_insertTextAction; diff --git a/escher/Makefile b/escher/Makefile index bf54220fe..274900e46 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -49,6 +49,7 @@ objs += $(addprefix escher/src/,\ scroll_view_data_source.o\ scroll_view_indicator.o\ scrollable_view.o\ + scrollable_expression_view_with_cursor.o\ selectable_table_view.o\ selectable_table_view_data_source.o\ selectable_table_view_delegate.o\ diff --git a/escher/include/escher.h b/escher/include/escher.h index 50dc65933..9db562c60 100644 --- a/escher/include/escher.h +++ b/escher/include/escher.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -53,6 +52,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/escher/include/escher/editable_expression_view.h b/escher/include/escher/editable_expression_view.h index 84dcd8d9b..d0c6e88b8 100644 --- a/escher/include/escher/editable_expression_view.h +++ b/escher/include/escher/editable_expression_view.h @@ -1,39 +1,49 @@ #ifndef ESCHER_EDITABLE_EXPRESSION_VIEW_H #define ESCHER_EDITABLE_EXPRESSION_VIEW_H -#include -#include -#include -#include +#include +#include +#include +#include -class EditableExpressionView : public ScrollableView, public ScrollViewDataSource { +class EditableExpressionView : public Responder, public View { public: - EditableExpressionView(Responder * parentResponder, Poincare::ExpressionLayout * expressionLayout, EditableExpressionViewDelegate * delegate = nullptr); - void setDelegate(EditableExpressionViewDelegate * delegate) { m_delegate = delegate; } - ExpressionViewWithCursor * expressionViewWithCursor() { return &m_expressionViewWithCursor; } + EditableExpressionView(Responder * parentResponder, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate); + + void setEditing(bool isEditing, bool reinitDraftBuffer = true); bool isEditing() const; - void setEditing(bool isEditing); - void scrollToCursor(); + const char * text(); + void setText(const char * text); + void insertText(const char * text); void reload(); - - /* Responder */ - Toolbox * toolbox() override; - bool handleEvent(Ion::Events::Event event) override; - - bool editableExpressionViewShouldFinishEditing(Ion::Events::Event event); - - void insertLayoutAtCursor(Poincare::ExpressionLayout * layout, Poincare::ExpressionLayout * pointedLayout); - void insertLayoutFromTextAtCursor(const char * text); + TextField * textField() { return &m_textField; } + ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor() { return &m_scrollableExpressionViewWithCursor; } + bool editionIsInTextField() const; + bool isEmpty() const; + bool heightIsMaximal() const; /* View */ + int numberOfSubviews() const override { return 1; } + View * subviewAtIndex(int index) override; + void layoutSubviews() override; + void drawRect(KDContext * ctx, KDRect rect) const override; KDSize minimalSizeForOptimalDisplay() const override; -protected: - virtual bool privateHandleEvent(Ion::Events::Event event); - bool privateHandleMoveEvent(Ion::Events::Event event, bool * shouldRecomputeLayout); - ExpressionViewWithCursor m_expressionViewWithCursor; + /* Responder */ + void didBecomeFirstResponder() override; + bool handleEvent(Ion::Events::Event event) override; + + static constexpr int k_bufferLength = TextField::maxBufferSize(); private: - EditableExpressionViewDelegate * m_delegate; + static constexpr KDCoordinate k_textFieldHeight = 37; + static constexpr KDCoordinate k_leftMargin = 5; + static constexpr KDCoordinate k_verticalExpressionViewMargin = 5; + constexpr static int k_separatorThickness = 1; + KDCoordinate inputViewHeight() const; + KDCoordinate maximalHeight() const; + TextField m_textField; + ScrollableExpressionViewWithCursor m_scrollableExpressionViewWithCursor; + char m_textBody[k_bufferLength]; }; #endif diff --git a/escher/include/escher/editable_expression_view_delegate.h b/escher/include/escher/editable_expression_view_delegate.h deleted file mode 100644 index a6728caed..000000000 --- a/escher/include/escher/editable_expression_view_delegate.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ESCHER_EDITABLE_EXPRESSION_VIEW_DELEGATE_H -#define ESCHER_EDITABLE_EXPRESSION_VIEW_DELEGATE_H - -#include -#include - -class EditableExpressionView; - -class EditableExpressionViewDelegate { -public: - virtual bool editableExpressionViewShouldFinishEditing(EditableExpressionView * editableExpressionView, Ion::Events::Event event) = 0; - virtual bool editableExpressionViewDidReceiveEvent(EditableExpressionView * editableExpressionView, Ion::Events::Event event) = 0; - virtual bool editableExpressionViewDidFinishEditing(EditableExpressionView * editableExpressionView, const char * text, Ion::Events::Event event) { return false; } - virtual bool editableExpressionViewDidAbortEditing(EditableExpressionView * editableExpressionView, const char * text) { return false; } - virtual bool editableExpressionViewDidHandleEvent(EditableExpressionView * editableExpressionView, Ion::Events::Event event, bool returnValue, bool expressionHasChanged) { return returnValue; } - virtual void editableExpressionViewDidChangeSize(EditableExpressionView * editableExpressionView) {} - virtual Toolbox * toolboxForEditableExpressionView(EditableExpressionView * editableExpressionView) = 0; -}; - -#endif diff --git a/escher/include/escher/input_view_controller.h b/escher/include/escher/input_view_controller.h index 52169e9b6..a2f037758 100644 --- a/escher/include/escher/input_view_controller.h +++ b/escher/include/escher/input_view_controller.h @@ -1,53 +1,53 @@ #ifndef ESCHER_INPUT_VIEW_CONTROLLER_H #define ESCHER_INPUT_VIEW_CONTROLLER_H +#include +#include #include #include #include #include -class InputViewController : public ModalViewController, TextFieldDelegate { +class InputViewController : public ModalViewController, TextFieldDelegate, ScrollableExpressionViewWithCursorDelegate { public: - InputViewController(Responder * parentResponder, ViewController * child, TextFieldDelegate * textFieldDelegate); + InputViewController(Responder * parentResponder, ViewController * child, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate); void edit(Responder * caller, Ion::Events::Event event, void * context, const char * initialText, Invocation::Action successAction, Invocation::Action failureAction); const char * textBody(); + void abortEditionAndDismiss(); + + /* TextFieldDelegate */ bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; - void abortTextFieldEditionAndDismiss(); bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; bool textFieldDidAbortEditing(TextField * textField, const char * text) override; Toolbox * toolboxForTextField(TextField * textFied) override; + + /* ScrollableExpressionViewWithCursorDelegate */ + bool scrollableExpressionViewWithCursorShouldFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidReceiveEvent(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text, Ion::Events::Event event) override; + bool scrollableExpressionViewWithCursorDidAbortEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text) override; + void scrollableExpressionViewWithCursorDidChangeSize(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) override; + Toolbox * toolboxForScrollableExpressionViewWithCursor(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) override; + private: - class TextFieldController : public ViewController { + class EditableExpressionViewController : public ViewController { public: - TextFieldController(Responder * parentResponder, TextFieldDelegate * textFieldDelegate); + EditableExpressionViewController(Responder * parentResponder, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate); void didBecomeFirstResponder() override; - View * view() override; - TextField * textField(); + View * view() override { return &m_editableExpressionView; } + EditableExpressionView * editableExpressionView() { return &m_editableExpressionView; } private: - class ContentView : public Responder, public View { - public: - ContentView(Responder * parentResponder, TextFieldDelegate * textFieldDelegate); - void didBecomeFirstResponder() override; - TextField * textField(); - void drawRect(KDContext * ctx, KDRect rect) const override; - KDSize minimalSizeForOptimalDisplay() const override; - private: - View * subviewAtIndex(int index) override; - int numberOfSubviews() const override; - void layoutSubviews() override; - constexpr static KDCoordinate k_inputHeight = 37; - constexpr static KDCoordinate k_separatorThickness = 1; - constexpr static KDCoordinate k_textMargin = 5; - TextField m_textField; - char m_textBody[TextField::maxBufferSize()]; - }; - ContentView m_view; + EditableExpressionView m_editableExpressionView; }; - TextFieldController m_textFieldController; + bool inputViewDidFinishEditing(); + bool inputViewDidAbortEditing(); + EditableExpressionViewController m_editableExpressionViewController; Invocation m_successAction; Invocation m_failureAction; TextFieldDelegate * m_textFieldDelegate; + ScrollableExpressionViewWithCursorDelegate * m_scrollableExpressionViewWithCursorDelegate; + bool m_inputViewHeightIsMaximal; }; #endif diff --git a/escher/include/escher/modal_view_controller.h b/escher/include/escher/modal_view_controller.h index ae466b820..3a326a12d 100644 --- a/escher/include/escher/modal_view_controller.h +++ b/escher/include/escher/modal_view_controller.h @@ -17,6 +17,8 @@ public: bool isDisplayingModal(); void viewWillAppear() override; void viewDidDisappear() override; +protected: + void reloadView(); private: class ContentView : public View { public: @@ -29,6 +31,7 @@ private: KDCoordinate topMargin, KDCoordinate leftMargin, KDCoordinate bottomMargin, KDCoordinate rightMargin); void dismissModalView(); bool isDisplayingModal() const; + void reload(); private: KDRect frame() const; View * m_regularView; diff --git a/escher/include/escher/scrollable_expression_view_with_cursor.h b/escher/include/escher/scrollable_expression_view_with_cursor.h new file mode 100644 index 000000000..7983e0ab7 --- /dev/null +++ b/escher/include/escher/scrollable_expression_view_with_cursor.h @@ -0,0 +1,40 @@ +#ifndef ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_H +#define ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_H + +#include +#include +#include +#include + +class ScrollableExpressionViewWithCursor : public ScrollableView, public ScrollViewDataSource { +public: + ScrollableExpressionViewWithCursor(Responder * parentResponder, Poincare::ExpressionLayout * expressionLayout, ScrollableExpressionViewWithCursorDelegate * delegate = nullptr); + void setDelegate(ScrollableExpressionViewWithCursorDelegate * delegate) { m_delegate = delegate; } + ExpressionViewWithCursor * expressionViewWithCursor() { return &m_expressionViewWithCursor; } + bool isEditing() const; + void setEditing(bool isEditing); + void clearLayout(); + void scrollToCursor(); + void reload(); + + /* Responder */ + Toolbox * toolbox() override; + bool handleEvent(Ion::Events::Event event) override; + + bool scrollableExpressionViewWithCursorShouldFinishEditing(Ion::Events::Event event); + + void insertLayoutAtCursor(Poincare::ExpressionLayout * layout, Poincare::ExpressionLayout * pointedLayout); + void insertLayoutFromTextAtCursor(const char * text); + + /* View */ + KDSize minimalSizeForOptimalDisplay() const override; + +protected: + virtual bool privateHandleEvent(Ion::Events::Event event); + bool privateHandleMoveEvent(Ion::Events::Event event, bool * shouldRecomputeLayout); + ExpressionViewWithCursor m_expressionViewWithCursor; +private: + ScrollableExpressionViewWithCursorDelegate * m_delegate; +}; + +#endif diff --git a/escher/include/escher/scrollable_expression_view_with_cursor_delegate.h b/escher/include/escher/scrollable_expression_view_with_cursor_delegate.h new file mode 100644 index 000000000..56b22b337 --- /dev/null +++ b/escher/include/escher/scrollable_expression_view_with_cursor_delegate.h @@ -0,0 +1,19 @@ +#ifndef ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H +#define ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H + +#include +#include + +class ScrollableExpressionViewWithCursor; + +class ScrollableExpressionViewWithCursorDelegate { +public: + virtual bool scrollableExpressionViewWithCursorShouldFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) = 0; + virtual bool scrollableExpressionViewWithCursorDidReceiveEvent(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) = 0; + virtual bool scrollableExpressionViewWithCursorDidFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text, Ion::Events::Event event) { return false; } + virtual bool scrollableExpressionViewWithCursorDidAbortEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text) { return false; } + virtual void scrollableExpressionViewWithCursorDidChangeSize(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) {} + virtual Toolbox * toolboxForScrollableExpressionViewWithCursor(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) = 0; +}; + +#endif diff --git a/escher/src/editable_expression_view.cpp b/escher/src/editable_expression_view.cpp index a4de32044..359d129cf 100644 --- a/escher/src/editable_expression_view.cpp +++ b/escher/src/editable_expression_view.cpp @@ -1,212 +1,137 @@ #include -#include -#include -#include +#include #include -EditableExpressionView::EditableExpressionView(Responder * parentResponder, Poincare::ExpressionLayout * expressionLayout, EditableExpressionViewDelegate * delegate) : - ScrollableView(parentResponder, &m_expressionViewWithCursor, this), - m_expressionViewWithCursor(expressionLayout), - m_delegate(delegate) +EditableExpressionView::EditableExpressionView(Responder * parentResponder, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate) : + Responder(parentResponder), + View(), + m_textField(parentResponder, m_textBody, m_textBody, k_bufferLength, textFieldDelegate, false), + m_scrollableExpressionViewWithCursor(parentResponder, new Poincare::HorizontalLayout(), scrollableExpressionViewWithCursorDelegate) { + m_textBody[0] = 0; +} + +void EditableExpressionView::setEditing(bool isEditing, bool reinitDraftBuffer) { + if (editionIsInTextField()) { + m_textField.setEditing(isEditing, reinitDraftBuffer); + } + if (reinitDraftBuffer) { + m_scrollableExpressionViewWithCursor.clearLayout(); + } + m_scrollableExpressionViewWithCursor.setEditing(isEditing); } bool EditableExpressionView::isEditing() const { - return m_expressionViewWithCursor.isEditing(); + return editionIsInTextField() ? m_textField.isEditing() : m_scrollableExpressionViewWithCursor.isEditing(); } -void EditableExpressionView::setEditing(bool isEditing) { - m_expressionViewWithCursor.setEditing(isEditing); +const char * EditableExpressionView::text() { + if (!editionIsInTextField()) { + m_scrollableExpressionViewWithCursor.expressionViewWithCursor()->expressionView()->expressionLayout()->writeTextInBuffer(m_textBody, k_bufferLength); + } + return m_textBody; } -void EditableExpressionView::scrollToCursor() { - scrollToContentRect(m_expressionViewWithCursor.cursorRect(), true); +void EditableExpressionView::setText(const char * text) { + if (editionIsInTextField()) { + m_textField.setText(text); + } + m_scrollableExpressionViewWithCursor.clearLayout(); + if (strlen(text) > 0) { + m_scrollableExpressionViewWithCursor.insertLayoutFromTextAtCursor(text); + } } -Toolbox * EditableExpressionView::toolbox() { - if (m_delegate) { - return m_delegate->toolboxForEditableExpressionView(this); - } - return nullptr; -} - -bool EditableExpressionView::handleEvent(Ion::Events::Event event) { - KDSize previousSize = minimalSizeForOptimalDisplay(); - bool shouldRecomputeLayout = false; - if (privateHandleMoveEvent(event, &shouldRecomputeLayout)) { - if (!shouldRecomputeLayout) { - m_expressionViewWithCursor.cursorPositionChanged(); - scrollToCursor(); - return true; - } - reload(); - KDSize newSize = minimalSizeForOptimalDisplay(); - if (m_delegate && previousSize.height() != newSize.height()) { - m_delegate->editableExpressionViewDidChangeSize(this); - reload(); - } - return true; - } - if (privateHandleEvent(event)) { - reload(); - KDSize newSize = minimalSizeForOptimalDisplay(); - if (m_delegate && previousSize.height() != newSize.height()) { - m_delegate->editableExpressionViewDidChangeSize(this); - reload(); - } - return true; - } - return false; -} - -bool EditableExpressionView::editableExpressionViewShouldFinishEditing(Ion::Events::Event event) { - return m_delegate->editableExpressionViewShouldFinishEditing(this, event); -} - -KDSize EditableExpressionView::minimalSizeForOptimalDisplay() const { - return m_expressionViewWithCursor.minimalSizeForOptimalDisplay(); -} - -bool EditableExpressionView::privateHandleMoveEvent(Ion::Events::Event event, bool * shouldRecomputeLayout) { - if (event == Ion::Events::Left) { - return m_expressionViewWithCursor.cursor()->moveLeft(shouldRecomputeLayout); - } - if (event == Ion::Events::Right) { - return m_expressionViewWithCursor.cursor()->moveRight(shouldRecomputeLayout); - } - if (event == Ion::Events::Up) { - return m_expressionViewWithCursor.cursor()->moveUp(shouldRecomputeLayout); - } - if (event == Ion::Events::Down) { - return m_expressionViewWithCursor.cursor()->moveDown(shouldRecomputeLayout); - } - if (event == Ion::Events::ShiftLeft) { - m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(m_expressionViewWithCursor.expressionView()->expressionLayout()); - m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Left); - return true; - } - if (event == Ion::Events::ShiftRight) { - m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(m_expressionViewWithCursor.expressionView()->expressionLayout()); - m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Right); - return true; - } - return false; -} - -bool EditableExpressionView::privateHandleEvent(Ion::Events::Event event) { - if (m_delegate && m_delegate->editableExpressionViewDidReceiveEvent(this, event)) { - return true; - } - if (Responder::handleEvent(event)) { - /* The only event Responder handles is 'Toolbox' displaying. In that case, - * the EditableExpressionView is forced into editing mode. */ - if (!isEditing()) { - setEditing(true); - } - return true; - } - if (isEditing() && editableExpressionViewShouldFinishEditing(event)) { - setEditing(false); - int bufferSize = TextField::maxBufferSize(); - char buffer[bufferSize]; - m_expressionViewWithCursor.expressionView()->expressionLayout()->writeTextInBuffer(buffer, bufferSize); - if (m_delegate->editableExpressionViewDidFinishEditing(this, buffer, event)) { - delete m_expressionViewWithCursor.expressionView()->expressionLayout(); - Poincare::ExpressionLayout * newLayout = new Poincare::HorizontalLayout(); - m_expressionViewWithCursor.expressionView()->setExpressionLayout(newLayout); - m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(newLayout); - } - return true; - } - if (event == Ion::Events::Division) { - m_expressionViewWithCursor.cursor()->addFractionLayoutAndCollapseBrothers(); - return true; - } - if (event == Ion::Events::XNT) { - m_expressionViewWithCursor.cursor()->addXNTCharLayout(); - return true; - } - if (event == Ion::Events::Exp) { - m_expressionViewWithCursor.cursor()->addEmptyExponentialLayout(); - return true; - } - if (event == Ion::Events::Power) { - m_expressionViewWithCursor.cursor()->addEmptyPowerLayout(); - return true; - } - if (event == Ion::Events::Sqrt) { - m_expressionViewWithCursor.cursor()->addEmptySquareRootLayout(); - return true; - } - if (event == Ion::Events::Square) { - m_expressionViewWithCursor.cursor()->addEmptySquarePowerLayout(); - return true; - } - if (event.hasText()) { - const char * textToInsert = event.text(); - if (textToInsert[1] == 0) { - if (textToInsert[0] == Ion::Charset::MultiplicationSign) { - const char middleDotString[] = {Ion::Charset::MiddleDot, 0}; - m_expressionViewWithCursor.cursor()->insertText(middleDotString); - return true; - } - if (textToInsert[0] == '[' || textToInsert[0] == ']') { - m_expressionViewWithCursor.cursor()->addEmptyMatrixLayout(); - return true; - } - } - m_expressionViewWithCursor.cursor()->insertText(textToInsert); - return true; - } - if (event == Ion::Events::Backspace) { - m_expressionViewWithCursor.cursor()->performBackspace(); - return true; - } - if (event == Ion::Events::Paste) { - if (!isEditing()) { - setEditing(true); - } - insertLayoutFromTextAtCursor(Clipboard::sharedClipboard()->storedText()); - return true; - } - return false; -} - -void EditableExpressionView::insertLayoutAtCursor(Poincare::ExpressionLayout * layout, Poincare::ExpressionLayout * pointedLayout) { - if (layout == nullptr) { +void EditableExpressionView::insertText(const char * text) { + if (editionIsInTextField()) { + m_textField.setEditing(true, false); + m_textField.insertTextAtLocation(text, m_textField.cursorLocation()); + m_textField.setCursorLocation(m_textField.cursorLocation() + strlen(text)); return; } - KDSize previousSize = minimalSizeForOptimalDisplay(); - m_expressionViewWithCursor.cursor()->addLayout(layout); - if (layout->isMatrix() && pointedLayout->hasAncestor(layout)) { - static_cast(layout)->addGreySquares(); - } - m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(pointedLayout); - m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Right); - reload(); - KDSize newSize = minimalSizeForOptimalDisplay(); - if (m_delegate && previousSize.height() != newSize.height()) { - m_delegate->editableExpressionViewDidChangeSize(this); - } + m_scrollableExpressionViewWithCursor.setEditing(true); + m_scrollableExpressionViewWithCursor.insertLayoutFromTextAtCursor(text); } -void EditableExpressionView::insertLayoutFromTextAtCursor(const char * text) { - Poincare::Expression * expression = Poincare::Expression::parse(text); - if (expression != nullptr) { - Poincare::ExpressionLayout * layout = expression->createLayout(); - delete expression; - insertLayoutAtCursor(layout, layout); - reload(); +View * EditableExpressionView::subviewAtIndex(int index) { + assert(index == 0); + if (editionIsInTextField()) { + return &m_textField; + } + return &m_scrollableExpressionViewWithCursor; +} + +void EditableExpressionView::layoutSubviews() { + KDRect inputViewFrame(k_leftMargin, k_separatorThickness, bounds().width() - k_leftMargin, bounds().height() - k_separatorThickness); + if (editionIsInTextField()) { + m_textField.setFrame(inputViewFrame); + m_scrollableExpressionViewWithCursor.setFrame(KDRectZero); return; } - m_expressionViewWithCursor.cursor()->insertText(text); - reload(); + m_scrollableExpressionViewWithCursor.setFrame(inputViewFrame); + m_textField.setFrame(KDRectZero); } void EditableExpressionView::reload() { - m_expressionViewWithCursor.expressionView()->expressionLayout()->invalidAllSizesPositionsAndBaselines(); - m_expressionViewWithCursor.cursorPositionChanged(); layoutSubviews(); - scrollToCursor(); markRectAsDirty(bounds()); } + +void EditableExpressionView::drawRect(KDContext * ctx, KDRect rect) const { + // Draw the separator + ctx->fillRect(KDRect(0, 0, bounds().width(), k_separatorThickness), Palette::GreyMiddle); + // Color the left margin + ctx->fillRect(KDRect(0, k_separatorThickness, k_leftMargin, bounds().height() - k_separatorThickness), m_textField.backgroundColor()); + if (!editionIsInTextField()) { + // Color the upper margin + ctx->fillRect(KDRect(0, k_separatorThickness, bounds().width(), k_verticalExpressionViewMargin), m_textField.backgroundColor()); + } +} + +void EditableExpressionView::didBecomeFirstResponder() { + if (editionIsInTextField()) { + app()->setFirstResponder(&m_textField); + return; + } + app()->setFirstResponder(&m_scrollableExpressionViewWithCursor); +} + +bool EditableExpressionView::handleEvent(Ion::Events::Event event) { + return editionIsInTextField() ? m_textField.handleEvent(event) : m_scrollableExpressionViewWithCursor.handleEvent(event); +} + +KDSize EditableExpressionView::minimalSizeForOptimalDisplay() const { + return KDSize(0, inputViewHeight()); +} + +bool EditableExpressionView::editionIsInTextField() const { + return Poincare::Preferences::sharedPreferences()->editionMode() == Poincare::Preferences::EditionMode::Edition1D; +} + +bool EditableExpressionView::isEmpty() const { + if (editionIsInTextField()) { + return m_textField.draftTextLength() == 0; + } + Poincare::ExpressionLayout * layout = const_cast(&m_scrollableExpressionViewWithCursor)->expressionViewWithCursor()->expressionView()->expressionLayout(); + return !layout->hasText(); +} + +bool EditableExpressionView::heightIsMaximal() const { + return inputViewHeight() == k_separatorThickness + k_verticalExpressionViewMargin + maximalHeight(); +} + +KDCoordinate EditableExpressionView::inputViewHeight() const { + if (editionIsInTextField()) { + return k_separatorThickness + k_textFieldHeight; + } + return k_separatorThickness + + k_verticalExpressionViewMargin + + min(maximalHeight(), + max(k_textFieldHeight, + m_scrollableExpressionViewWithCursor.minimalSizeForOptimalDisplay().height() + + k_verticalExpressionViewMargin)); +} + +KDCoordinate EditableExpressionView::maximalHeight() const { + return 0.6*Ion::Display::Height; +} diff --git a/escher/src/input_view_controller.cpp b/escher/src/input_view_controller.cpp index 22fd62fa1..fb89c3d62 100644 --- a/escher/src/input_view_controller.cpp +++ b/escher/src/input_view_controller.cpp @@ -1,89 +1,47 @@ #include #include #include +#include #include -InputViewController::TextFieldController::ContentView::ContentView(Responder * parentResponder, TextFieldDelegate * textFieldDelegate) : - Responder(parentResponder), - View(), - m_textField(this, m_textBody, m_textBody, TextField::maxBufferSize(), textFieldDelegate, false) -{ - m_textBody[0] = 0; -} - -void InputViewController::TextFieldController::ContentView::didBecomeFirstResponder() { - app()->setFirstResponder(&m_textField); -} - -TextField * InputViewController::TextFieldController::ContentView::textField() { - return &m_textField; -} - -void InputViewController::TextFieldController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { - ctx->fillRect(KDRect(0, 0, bounds().width(), k_separatorThickness), Palette::GreyMiddle); - ctx->fillRect(KDRect(0, k_separatorThickness, k_textMargin, bounds().height()-k_separatorThickness), m_textField.backgroundColor()); -} - -KDSize InputViewController::TextFieldController::ContentView::minimalSizeForOptimalDisplay() const { - return KDSize(0, k_inputHeight); -} - -int InputViewController::TextFieldController::ContentView::numberOfSubviews() const { - return 1; -} - -View * InputViewController::TextFieldController::ContentView::subviewAtIndex(int index) { - return &m_textField; -} - -void InputViewController::TextFieldController::ContentView::layoutSubviews() { - m_textField.setFrame(KDRect(k_textMargin, k_separatorThickness, bounds().width()-k_textMargin, bounds().height())); -} - -InputViewController::TextFieldController::TextFieldController(Responder * parentResponder, TextFieldDelegate * textFieldDelegate) : +InputViewController::EditableExpressionViewController::EditableExpressionViewController(Responder * parentResponder, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate) : ViewController(parentResponder), - m_view(this, textFieldDelegate) + m_editableExpressionView(this, textFieldDelegate, scrollableExpressionViewWithCursorDelegate) { } -View * InputViewController::TextFieldController::view() { - return &m_view; +void InputViewController::EditableExpressionViewController::didBecomeFirstResponder() { + app()->setFirstResponder(&m_editableExpressionView); } -void InputViewController::TextFieldController::didBecomeFirstResponder() { - app()->setFirstResponder(&m_view); -} - -TextField * InputViewController::TextFieldController::textField() { - return m_view.textField(); -} - -InputViewController::InputViewController(Responder * parentResponder, ViewController * child, TextFieldDelegate * textFieldDelegate) : +InputViewController::InputViewController(Responder * parentResponder, ViewController * child, TextFieldDelegate * textFieldDelegate, ScrollableExpressionViewWithCursorDelegate * scrollableExpressionViewWithCursorDelegate) : ModalViewController(parentResponder, child), - m_textFieldController(this, this), + m_editableExpressionViewController(this, this, this), m_successAction(Invocation(nullptr, nullptr)), m_failureAction(Invocation(nullptr, nullptr)), - m_textFieldDelegate(textFieldDelegate) + m_textFieldDelegate(textFieldDelegate), + m_scrollableExpressionViewWithCursorDelegate(scrollableExpressionViewWithCursorDelegate), + m_inputViewHeightIsMaximal(false) { } const char * InputViewController::textBody() { - return m_textFieldController.textField()->text(); + return m_editableExpressionViewController.editableExpressionView()->text(); } void InputViewController::edit(Responder * caller, Ion::Events::Event event, void * context, const char * initialText, Invocation::Action successAction, Invocation::Action failureAction) { m_successAction = Invocation(successAction, context); m_failureAction = Invocation(failureAction, context); - displayModalViewController(&m_textFieldController, 1.0f, 1.0f); - m_textFieldController.textField()->handleEvent(event); + displayModalViewController(&m_editableExpressionViewController, 1.0f, 1.0f); if (initialText != nullptr) { - m_textFieldController.textField()->insertTextAtLocation(initialText, 0); - m_textFieldController.textField()->setCursorLocation(strlen(initialText)); + m_editableExpressionViewController.editableExpressionView()->setText(initialText); + //TODO is the editableExpressionView always clean before we set the text? Otherwise, problem if there is no initial text. } + m_editableExpressionViewController.editableExpressionView()->handleEvent(event); } -void InputViewController::abortTextFieldEditionAndDismiss() { - m_textFieldController.textField()->setEditing(false); +void InputViewController::abortEditionAndDismiss() { + m_editableExpressionViewController.editableExpressionView()->setEditing(false); dismissModalViewController(); } @@ -92,15 +50,11 @@ bool InputViewController::textFieldShouldFinishEditing(TextField * textField, Io } bool InputViewController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { - m_successAction.perform(this); - dismissModalViewController(); - return true; + return inputViewDidFinishEditing(); } bool InputViewController::textFieldDidAbortEditing(TextField * textField, const char * text) { - m_failureAction.perform(this); - dismissModalViewController(); - return true; + return inputViewDidAbortEditing(); } bool InputViewController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { @@ -110,3 +64,44 @@ bool InputViewController::textFieldDidReceiveEvent(TextField * textField, Ion::E Toolbox * InputViewController::toolboxForTextField(TextField * textField) { return m_textFieldDelegate->toolboxForTextField(textField); } +bool InputViewController::scrollableExpressionViewWithCursorShouldFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + return event == Ion::Events::OK || event == Ion::Events::EXE; +} + +bool InputViewController::scrollableExpressionViewWithCursorDidReceiveEvent(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, Ion::Events::Event event) { + return m_scrollableExpressionViewWithCursorDelegate->scrollableExpressionViewWithCursorDidReceiveEvent(scrollableExpressionViewWithCursor, event); +} + +bool InputViewController::scrollableExpressionViewWithCursorDidFinishEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text, Ion::Events::Event event) { + return inputViewDidFinishEditing(); +} + +bool InputViewController::scrollableExpressionViewWithCursorDidAbortEditing(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor, const char * text) { + return inputViewDidAbortEditing(); +} + +void InputViewController::scrollableExpressionViewWithCursorDidChangeSize(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + // Reload the view only if the EditableExpressionView height actually changes, + // i.e. not if the height is already maximal and stays maximal. + bool newInputViewHeightIsMaximal = m_editableExpressionViewController.editableExpressionView()->heightIsMaximal(); + if (!m_inputViewHeightIsMaximal || !newInputViewHeightIsMaximal) { + m_inputViewHeightIsMaximal = newInputViewHeightIsMaximal; + reloadView(); + } +} + +Toolbox * InputViewController::toolboxForScrollableExpressionViewWithCursor(ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor) { + return m_scrollableExpressionViewWithCursorDelegate->toolboxForScrollableExpressionViewWithCursor(scrollableExpressionViewWithCursor); +} + +bool InputViewController::inputViewDidFinishEditing() { + m_successAction.perform(this); + dismissModalViewController(); + return true; +} + +bool InputViewController::inputViewDidAbortEditing() { + m_failureAction.perform(this); + dismissModalViewController(); + return true; +} diff --git a/escher/src/modal_view_controller.cpp b/escher/src/modal_view_controller.cpp index 5780b448f..1f7aaed18 100644 --- a/escher/src/modal_view_controller.cpp +++ b/escher/src/modal_view_controller.cpp @@ -95,6 +95,11 @@ bool ModalViewController::ContentView::isDisplayingModal() const { return m_isDisplayingModal; } +void ModalViewController::ContentView::reload() { + markRectAsDirty(frame()); + layoutSubviews(); +} + ModalViewController::ModalViewController(Responder * parentResponder, ViewController * child) : ViewController(parentResponder), m_contentView(), @@ -162,3 +167,7 @@ void ModalViewController::viewDidDisappear() { } m_regularViewController->viewDidDisappear(); } + +void ModalViewController::reloadView() { + m_contentView.reload(); +} diff --git a/escher/src/scrollable_expression_view_with_cursor.cpp b/escher/src/scrollable_expression_view_with_cursor.cpp new file mode 100644 index 000000000..4ce4d5c6a --- /dev/null +++ b/escher/src/scrollable_expression_view_with_cursor.cpp @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include + +ScrollableExpressionViewWithCursor::ScrollableExpressionViewWithCursor(Responder * parentResponder, Poincare::ExpressionLayout * expressionLayout, ScrollableExpressionViewWithCursorDelegate * delegate) : + ScrollableView(parentResponder, &m_expressionViewWithCursor, this), + m_expressionViewWithCursor(expressionLayout), + m_delegate(delegate) +{ +} + +bool ScrollableExpressionViewWithCursor::isEditing() const { + return m_expressionViewWithCursor.isEditing(); +} + +void ScrollableExpressionViewWithCursor::setEditing(bool isEditing) { + m_expressionViewWithCursor.setEditing(isEditing); +} + +void ScrollableExpressionViewWithCursor::clearLayout() { + delete m_expressionViewWithCursor.expressionView()->expressionLayout(); + Poincare::ExpressionLayout * newLayout = new Poincare::HorizontalLayout(); + m_expressionViewWithCursor.expressionView()->setExpressionLayout(newLayout); + m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(newLayout); +} + +void ScrollableExpressionViewWithCursor::scrollToCursor() { + scrollToContentRect(m_expressionViewWithCursor.cursorRect(), true); +} + +Toolbox * ScrollableExpressionViewWithCursor::toolbox() { + if (m_delegate) { + return m_delegate->toolboxForScrollableExpressionViewWithCursor(this); + } + return nullptr; +} + +bool ScrollableExpressionViewWithCursor::handleEvent(Ion::Events::Event event) { + KDSize previousSize = minimalSizeForOptimalDisplay(); + bool shouldRecomputeLayout = false; + if (privateHandleMoveEvent(event, &shouldRecomputeLayout)) { + if (!shouldRecomputeLayout) { + m_expressionViewWithCursor.cursorPositionChanged(); + scrollToCursor(); + return true; + } + reload(); + KDSize newSize = minimalSizeForOptimalDisplay(); + if (m_delegate && previousSize.height() != newSize.height()) { + m_delegate->scrollableExpressionViewWithCursorDidChangeSize(this); + reload(); + } + return true; + } + if (privateHandleEvent(event)) { + reload(); + KDSize newSize = minimalSizeForOptimalDisplay(); + if (m_delegate && previousSize.height() != newSize.height()) { + m_delegate->scrollableExpressionViewWithCursorDidChangeSize(this); + reload(); + } + return true; + } + return false; +} + +bool ScrollableExpressionViewWithCursor::scrollableExpressionViewWithCursorShouldFinishEditing(Ion::Events::Event event) { + return m_delegate->scrollableExpressionViewWithCursorShouldFinishEditing(this, event); +} + +KDSize ScrollableExpressionViewWithCursor::minimalSizeForOptimalDisplay() const { + return m_expressionViewWithCursor.minimalSizeForOptimalDisplay(); +} + +bool ScrollableExpressionViewWithCursor::privateHandleMoveEvent(Ion::Events::Event event, bool * shouldRecomputeLayout) { + if (event == Ion::Events::Left) { + return m_expressionViewWithCursor.cursor()->moveLeft(shouldRecomputeLayout); + } + if (event == Ion::Events::Right) { + return m_expressionViewWithCursor.cursor()->moveRight(shouldRecomputeLayout); + } + if (event == Ion::Events::Up) { + return m_expressionViewWithCursor.cursor()->moveUp(shouldRecomputeLayout); + } + if (event == Ion::Events::Down) { + return m_expressionViewWithCursor.cursor()->moveDown(shouldRecomputeLayout); + } + if (event == Ion::Events::ShiftLeft) { + m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(m_expressionViewWithCursor.expressionView()->expressionLayout()); + m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Left); + return true; + } + if (event == Ion::Events::ShiftRight) { + m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(m_expressionViewWithCursor.expressionView()->expressionLayout()); + m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Right); + return true; + } + return false; +} + +bool ScrollableExpressionViewWithCursor::privateHandleEvent(Ion::Events::Event event) { + if (m_delegate && m_delegate->scrollableExpressionViewWithCursorDidReceiveEvent(this, event)) { + return true; + } + if (Responder::handleEvent(event)) { + /* The only event Responder handles is 'Toolbox' displaying. In that case, + * the ScrollableExpressionViewWithCursor is forced into editing mode. */ + if (!isEditing()) { + setEditing(true); + } + return true; + } + if (isEditing() && scrollableExpressionViewWithCursorShouldFinishEditing(event)) { + setEditing(false); + int bufferSize = TextField::maxBufferSize(); + char buffer[bufferSize]; + m_expressionViewWithCursor.expressionView()->expressionLayout()->writeTextInBuffer(buffer, bufferSize); + if (m_delegate->scrollableExpressionViewWithCursorDidFinishEditing(this, buffer, event)) { + delete m_expressionViewWithCursor.expressionView()->expressionLayout(); + Poincare::ExpressionLayout * newLayout = new Poincare::HorizontalLayout(); + m_expressionViewWithCursor.expressionView()->setExpressionLayout(newLayout); + m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(newLayout); + } + return true; + } + if ((event == Ion::Events::OK || event == Ion::Events::EXE) && !isEditing()) { + setEditing(true); + m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(m_expressionViewWithCursor.expressionView()->expressionLayout()); + m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Right); + return true; + } + if (event == Ion::Events::Division) { + m_expressionViewWithCursor.cursor()->addFractionLayoutAndCollapseBrothers(); + return true; + } + if (event == Ion::Events::XNT) { + m_expressionViewWithCursor.cursor()->addXNTCharLayout(); + return true; + } + if (event == Ion::Events::Exp) { + m_expressionViewWithCursor.cursor()->addEmptyExponentialLayout(); + return true; + } + if (event == Ion::Events::Power) { + m_expressionViewWithCursor.cursor()->addEmptyPowerLayout(); + return true; + } + if (event == Ion::Events::Sqrt) { + m_expressionViewWithCursor.cursor()->addEmptySquareRootLayout(); + return true; + } + if (event == Ion::Events::Square) { + m_expressionViewWithCursor.cursor()->addEmptySquarePowerLayout(); + return true; + } + if (event.hasText()) { + const char * textToInsert = event.text(); + if (textToInsert[1] == 0) { + if (textToInsert[0] == Ion::Charset::MultiplicationSign) { + const char middleDotString[] = {Ion::Charset::MiddleDot, 0}; + m_expressionViewWithCursor.cursor()->insertText(middleDotString); + return true; + } + if (textToInsert[0] == '[' || textToInsert[0] == ']') { + m_expressionViewWithCursor.cursor()->addEmptyMatrixLayout(); + return true; + } + } + m_expressionViewWithCursor.cursor()->insertText(textToInsert); + return true; + } + if (event == Ion::Events::Backspace) { + m_expressionViewWithCursor.cursor()->performBackspace(); + return true; + } + if (event == Ion::Events::Paste) { + if (!isEditing()) { + setEditing(true); + } + insertLayoutFromTextAtCursor(Clipboard::sharedClipboard()->storedText()); + return true; + } + return false; +} + +void ScrollableExpressionViewWithCursor::insertLayoutAtCursor(Poincare::ExpressionLayout * layout, Poincare::ExpressionLayout * pointedLayout) { + if (layout == nullptr) { + return; + } + KDSize previousSize = minimalSizeForOptimalDisplay(); + m_expressionViewWithCursor.cursor()->addLayout(layout); + if (layout->isMatrix() && pointedLayout->hasAncestor(layout)) { + static_cast(layout)->addGreySquares(); + } + m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(pointedLayout); + m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Right); + reload(); + KDSize newSize = minimalSizeForOptimalDisplay(); + if (m_delegate && previousSize.height() != newSize.height()) { + m_delegate->scrollableExpressionViewWithCursorDidChangeSize(this); + } +} + +void ScrollableExpressionViewWithCursor::insertLayoutFromTextAtCursor(const char * text) { + Poincare::Expression * expression = Poincare::Expression::parse(text); + if (expression != nullptr) { + Poincare::ExpressionLayout * layout = expression->createLayout(); + delete expression; + insertLayoutAtCursor(layout, layout); + reload(); + return; + } + m_expressionViewWithCursor.cursor()->insertText(text); + reload(); +} + +void ScrollableExpressionViewWithCursor::reload() { + m_expressionViewWithCursor.expressionView()->expressionLayout()->invalidAllSizesPositionsAndBaselines(); + m_expressionViewWithCursor.cursorPositionChanged(); + layoutSubviews(); + scrollToCursor(); + markRectAsDirty(bounds()); +} diff --git a/poincare/include/poincare/expression_layout.h b/poincare/include/poincare/expression_layout.h index 38ef59c82..e97f4e73d 100644 --- a/poincare/include/poincare/expression_layout.h +++ b/poincare/include/poincare/expression_layout.h @@ -94,6 +94,7 @@ public: virtual int writeTextInBuffer(char * buffer, int bufferSize) const = 0; /* Other */ + bool hasText() const; virtual bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { return true; } /* isCollapsable is used when adding a brother fraction: should the layout be * inserted in the numerator (or denominator)? For instance, 1+2|3-4 should diff --git a/poincare/src/layout/expression_layout.cpp b/poincare/src/layout/expression_layout.cpp index 9faf5b7e0..1b22d51b7 100644 --- a/poincare/src/layout/expression_layout.cpp +++ b/poincare/src/layout/expression_layout.cpp @@ -283,6 +283,12 @@ bool ExpressionLayout::moveDownInside(ExpressionLayoutCursor * cursor, bool * sh return moveInside(VerticalDirection::Down, cursor, shouldRecomputeLayout); } +bool ExpressionLayout::hasText() const { + // A layout has text if it is not empty and it is not an horizontal layout + // with no child or with one child with no text. + return !isEmpty() && !(isHorizontal() && (numberOfChildren() == 0 || (numberOfChildren() == 1 && !child(0)->hasText()))); +} + bool ExpressionLayout::canBeOmittedMultiplicationLeftFactor() const { // WARNING: canBeOmittedMultiplicationLeftFactor is true when and only when // isCollapsable is true too. If isCollapsable changes, it might not be the