diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 701ce5926..21056ddb4 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) : - TextFieldDelegateApp(container, snapshot, &m_editExpressionController), + TextFieldAndEditableExpressionViewDelegateApp(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,6 +80,36 @@ 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)) { + 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)) { + int bufferLength = TextField::maxBufferSize(); + char bufferForParsing[bufferLength]; + Poincare::ExpressionLayout * expressionLayout = editableExpressionView->expressionViewWithCursor()->expressionView()->expressionLayout(); + expressionLayout->writeTextInBuffer(bufferForParsing, bufferLength); + Expression * exp = Expression::parse(bufferForParsing); + if (exp == nullptr) { + editableExpressionView->app()->displayWarning(I18n::Message::SyntaxError); + return true; + } + char buffer[Calculation::k_printedExpressionSize]; + int length = exp->writeTextInBuffer(buffer, sizeof(buffer)); + delete exp; + /* if the buffer is totally full, it is VERY likely that writeTextInBuffer + * escaped before printing utterly the expression. */ + if (length >= Calculation::k_printedExpressionSize-1) { + displayWarning(I18n::Message::SyntaxError); + return true; + } + } + return false; +} + const char * App::XNT() { return "x"; } diff --git a/apps/calculation/app.h b/apps/calculation/app.h index a9b616599..4ec807891 100644 --- a/apps/calculation/app.h +++ b/apps/calculation/app.h @@ -10,7 +10,7 @@ namespace Calculation { -class App : public Shared::TextFieldDelegateApp { +class App : public Shared::TextFieldAndEditableExpressionViewDelegateApp { public: class Descriptor : public ::App::Descriptor { public: @@ -30,6 +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; 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 0df8a0f17..90d8cd36d 100644 --- a/apps/calculation/edit_expression_controller.cpp +++ b/apps/calculation/edit_expression_controller.cpp @@ -1,5 +1,6 @@ #include "edit_expression_controller.h" #include "../apps_container.h" +#include "ion/display.h" #include "app.h" #include @@ -7,10 +8,11 @@ using namespace Shared; namespace Calculation { -EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate) : +EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate, EditableExpressionViewDelegate * editableExpressionViewDelegate) : View(), m_mainView(subview), - m_textField(parentResponder, m_textBody, TextField::maxBufferSize(), textFieldDelegate) + m_textField(parentResponder, m_textBody, TextField::maxBufferSize(), textFieldDelegate), + m_editableExpressionView(parentResponder, new Poincare::HorizontalLayout(), editableExpressionViewDelegate) { m_textBody[0] = 0; } @@ -20,30 +22,55 @@ int EditExpressionController::ContentView::numberOfSubviews() const { } View * EditExpressionController::ContentView::subviewAtIndex(int index) { - View * views[2] = {m_mainView, &m_textField}; - return views[index]; + assert(index >= 0 && index < numberOfSubviews()); + if (index == 0) { + return m_mainView; + } + assert(index == 1); + if (editionIsInTextField()) { + return &m_textField; + } + return &m_editableExpressionView; } void EditExpressionController::ContentView::layoutSubviews() { - KDRect mainViewFrame(0, 0, bounds().width(), bounds().height() - k_textFieldHeight-k_separatorThickness); + KDCoordinate inputViewFrameHeight = inputViewHeight(); + KDRect mainViewFrame(0, 0, bounds().width(), bounds().height() - inputViewFrameHeight-k_separatorThickness); m_mainView->setFrame(mainViewFrame); - KDRect inputViewFrame(k_textMargin, bounds().height() - k_textFieldHeight, bounds().width()-k_textMargin, k_textFieldHeight); - m_textField.setFrame(inputViewFrame); + if (editionIsInTextField()) { + KDRect inputViewFrame(k_leftMargin, bounds().height() - inputViewFrameHeight + k_verticalMargin, bounds().width()-k_leftMargin, k_textFieldHeight - k_verticalMargin); + m_textField.setFrame(inputViewFrame); + m_editableExpressionView.setFrame(KDRectZero); + return; + } + KDRect inputViewFrame(k_leftMargin, bounds().height() - inputViewFrameHeight, bounds().width() - k_leftMargin, inputViewFrameHeight); + m_editableExpressionView.setFrame(inputViewFrame); + m_textField.setFrame(KDRectZero); } -void EditExpressionController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { +void EditExpressionController::ContentView::reload() { + layoutSubviews(); + markRectAsDirty(bounds()); +} + +void EditExpressionController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { + KDCoordinate inputViewFrameHeight = inputViewHeight(); // Draw the separator - ctx->fillRect(KDRect(0, bounds().height() -k_textFieldHeight-k_separatorThickness, bounds().width(), k_separatorThickness), Palette::GreyMiddle); - // Color the margin - ctx->fillRect(KDRect(0, bounds().height() -k_textFieldHeight, k_textMargin, k_textFieldHeight), m_textField.backgroundColor()); + 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, k_textFieldHeight), m_textField.backgroundColor()); + if (!editionIsInTextField()) { + // Color the upper margin + ctx->fillRect(KDRect(0, bounds().height() -inputViewFrameHeight, bounds().width(), k_verticalMargin), m_textField.backgroundColor()); + } } -TextField * EditExpressionController::ContentView::textField() { - return &m_textField; +KDCoordinate EditExpressionController::ContentView::inputViewHeight() const { + return k_verticalMargin + (editionIsInTextField() ? k_textFieldHeight : editableExpressionViewHeight()); } -TableView * EditExpressionController::ContentView::mainView() { - return m_mainView; +KDCoordinate EditExpressionController::ContentView::editableExpressionViewHeight() const { + return KDCoordinate(min(0.6*Ion::Display::Height, max(k_textFieldHeight, m_editableExpressionView.minimalSizeForOptimalDisplay().height()+k_verticalMargin))); } EditExpressionController::EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore) : @@ -67,7 +94,11 @@ void EditExpressionController::insertTextBody(const char * text) { bool EditExpressionController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Up) { if (m_calculationStore->numberOfCalculations() > 0) { - ((ContentView *)view())->textField()->setEditing(false, false); + if (((ContentView *)view())->editionIsInTextField()) { + ((ContentView *)view())->textField()->setEditing(false, false); + } else { + ((ContentView *)view())->editableExpressionView()->setEditing(false); + } app()->setFirstResponder(m_historyController); } return true; @@ -78,8 +109,13 @@ 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); - ((ContentView *)view())->textField()->setEditing(true, false); - app()->setFirstResponder(((ContentView *)view())->textField()); + if (((ContentView *)view())->editionIsInTextField()) { + ((ContentView *)view())->textField()->setEditing(true, false); + app()->setFirstResponder(((ContentView *)view())->textField()); + return; + } + ((ContentView *)view())->editableExpressionView()->setEditing(true); + app()->setFirstResponder(((ContentView *)view())->editableExpressionView()); } bool EditExpressionController::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) { @@ -110,21 +146,71 @@ bool EditExpressionController::textFieldDidAbortEditing(::TextField * textField, return false; } +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; + } + return textFieldAndEditableExpressionViewDelegateApp()->editableExpressionViewDidReceiveEvent(editableExpressionView, 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::editableExpressionViewDidAbortEditing(::EditableExpressionView * editableExpressionView, const char * text) { + ((ContentView *)view())->editableExpressionView()->setEditing(true); + //TODO ((ContentView *)view())->editableExpressionView()->editableExpressionView()->expressionViewWithCursor()->expressionView()->setLayout(; + return false; +} + +void EditExpressionController::editableExpressionViewDidChangeSize(EditableExpressionView * editableExpressionView) { + assert(editableExpressionView == ((ContentView *)view())->editableExpressionView()); + reloadView(); +} + TextFieldDelegateApp * EditExpressionController::textFieldDelegateApp() { return (App *)app(); } +TextFieldAndEditableExpressionViewDelegateApp * EditExpressionController::textFieldAndEditableExpressionViewDelegateApp() { + return (App *)app(); +} + View * EditExpressionController::loadView() { - return new ContentView(this, (TableView *)m_historyController->view(), this); + return new ContentView(this, (TableView *)m_historyController->view(), this, this); } void EditExpressionController::unloadView(View * view) { delete view; } +void EditExpressionController::reloadView() { + ((ContentView *)view())->reload(); + m_historyController->reload(); + if (m_historyController->numberOfRows() > 0) { + ((ContentView *)view())->mainView()->scrollToCell(0, m_historyController->numberOfRows()-1); + } +} + void EditExpressionController::viewDidDisappear() { DynamicViewController::viewDidDisappear(); m_historyController->viewDidDisappear(); } +Poincare::ExpressionLayout * EditExpressionController::expressionLayout() { + return ((ContentView *)view())->editableExpressionView()->expressionViewWithCursor()->expressionView()->expressionLayout(); +} + } diff --git a/apps/calculation/edit_expression_controller.h b/apps/calculation/edit_expression_controller.h index 9c019fe4b..b9c8f4514 100644 --- a/apps/calculation/edit_expression_controller.h +++ b/apps/calculation/edit_expression_controller.h @@ -3,6 +3,7 @@ #include #include "../shared/text_field_delegate.h" +#include "../shared/editable_expression_view_delegate.h" #include "history_controller.h" #include "calculation_store.h" #include "text_field.h" @@ -11,7 +12,7 @@ namespace Calculation { class HistoryController; /* TODO: implement a split view */ -class EditExpressionController : public DynamicViewController, public Shared::TextFieldDelegate { +class EditExpressionController : public DynamicViewController, public Shared::TextFieldDelegate, public Shared::EditableExpressionViewDelegate { public: EditExpressionController(Responder * parentResponder, HistoryController * historyController, CalculationStore * calculationStore); void didBecomeFirstResponder() override; @@ -19,30 +20,50 @@ public: bool handleEvent(Ion::Events::Event event) override; const char * textBody(); void insertTextBody(const char * text); -bool textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) override; + + /* TextFieldDelegate */ + bool textFieldDidReceiveEvent(::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; + + /* 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; + private: class ContentView : public View { public: - ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate); + ContentView(Responder * parentResponder, TableView * subview, TextFieldDelegate * textFieldDelegate, EditableExpressionViewDelegate * editableExpressionViewDelegate); int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; - TextField * textField(); - TableView * mainView(); + 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 { return true; } //TODO + static constexpr int k_bufferLength = TextField::maxBufferSize(); private: - static constexpr KDCoordinate k_textFieldHeight = 37; - static constexpr KDCoordinate k_textMargin= 5; + static constexpr KDCoordinate k_textFieldHeight = 32; //37 + static constexpr KDCoordinate k_leftMargin = 5; + static constexpr KDCoordinate k_verticalMargin = 9; constexpr static int k_separatorThickness = 1; + KDCoordinate inputViewHeight() const; + KDCoordinate editableExpressionViewHeight() const; TableView * m_mainView; TextField m_textField; - char m_textBody[TextField::maxBufferSize()]; + EditableExpressionView m_editableExpressionView; + char m_textBody[k_bufferLength]; }; View * loadView() override; void unloadView(View * view) override; + void reloadView(); Shared::TextFieldDelegateApp * textFieldDelegateApp() override; + Shared::TextFieldAndEditableExpressionViewDelegateApp * textFieldAndEditableExpressionViewDelegateApp() override; + Poincare::ExpressionLayout * expressionLayout(); HistoryController * m_historyController; CalculationStore * m_calculationStore; }; diff --git a/apps/shared/editable_expression_view_delegate.cpp b/apps/shared/editable_expression_view_delegate.cpp index ab502a270..ff7542ed4 100644 --- a/apps/shared/editable_expression_view_delegate.cpp +++ b/apps/shared/editable_expression_view_delegate.cpp @@ -12,6 +12,22 @@ bool EditableExpressionViewDelegate::editableExpressionViewDidReceiveEvent(::Edi 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 index 438ae1acc..9c779b642 100644 --- a/apps/shared/editable_expression_view_delegate.h +++ b/apps/shared/editable_expression_view_delegate.h @@ -10,6 +10,10 @@ 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; diff --git a/escher/include/escher/editable_expression_view.h b/escher/include/escher/editable_expression_view.h index 5d9f967a6..8041440ce 100644 --- a/escher/include/escher/editable_expression_view.h +++ b/escher/include/escher/editable_expression_view.h @@ -14,6 +14,7 @@ public: bool isEditing() const; void setEditing(bool isEditing); void scrollToCursor(); + void reload(); /* Responder */ Toolbox * toolbox() override; @@ -29,7 +30,6 @@ public: private: bool privateHandleEvent(Ion::Events::Event event); - void cursorOrLayoutChanged(); ExpressionViewWithCursor m_expressionViewWithCursor; EditableExpressionViewDelegate * m_delegate; }; diff --git a/escher/include/escher/editable_expression_view_delegate.h b/escher/include/escher/editable_expression_view_delegate.h index 6eb66bcdd..a6728caed 100644 --- a/escher/include/escher/editable_expression_view_delegate.h +++ b/escher/include/escher/editable_expression_view_delegate.h @@ -10,9 +10,10 @@ 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 textHasChanged) { return returnValue; };*/ // TODO? + 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; }; diff --git a/escher/include/escher/expression_view.h b/escher/include/escher/expression_view.h index 25861e61f..cd3577f19 100644 --- a/escher/include/escher/expression_view.h +++ b/escher/include/escher/expression_view.h @@ -23,6 +23,7 @@ public: void setAlignment(float horizontalAlignment, float verticalAlignment); KDSize minimalSizeForOptimalDisplay() const override; KDPoint drawingOrigin() const; + KDPoint absoluteDrawingOrigin() const; private: /* Warning: we do not need to delete the previous expression layout when * deleting object or setting a new expression layout. Indeed, the expression diff --git a/escher/include/escher/expression_view_with_cursor.h b/escher/include/escher/expression_view_with_cursor.h index b362bb5f3..7588769bb 100644 --- a/escher/include/escher/expression_view_with_cursor.h +++ b/escher/include/escher/expression_view_with_cursor.h @@ -10,12 +10,12 @@ class ExpressionViewWithCursor : public View { public: ExpressionViewWithCursor(Poincare::ExpressionLayout * expressionLayout); - Poincare::ExpressionLayoutCursor * cursor() { return &m_cursor; } bool isEditing() const { return m_isEditing; } void setEditing(bool isEditing) { m_isEditing = isEditing; } void cursorPositionChanged(); KDRect cursorRect(); - const ExpressionView * expressionView() const { return &m_expressionView; } + Poincare::ExpressionLayoutCursor * cursor() { return &m_cursor; } + ExpressionView * expressionView() { return &m_expressionView; } /* View */ KDSize minimalSizeForOptimalDisplay() const override; private: diff --git a/escher/src/editable_expression_view.cpp b/escher/src/editable_expression_view.cpp index ef3082d2c..023028d0f 100644 --- a/escher/src/editable_expression_view.cpp +++ b/escher/src/editable_expression_view.cpp @@ -1,4 +1,5 @@ #include +#include #include EditableExpressionView::EditableExpressionView(Responder * parentResponder, Poincare::ExpressionLayout * expressionLayout, EditableExpressionViewDelegate * delegate) : @@ -28,8 +29,13 @@ Toolbox * EditableExpressionView::toolbox() { } bool EditableExpressionView::handleEvent(Ion::Events::Event event) { + KDSize previousSize = minimalSizeForOptimalDisplay(); if (privateHandleEvent(event)) { - cursorOrLayoutChanged(); + reload(); + KDSize newSize = minimalSizeForOptimalDisplay(); + if (m_delegate && previousSize.height() != newSize.height()) { + m_delegate->editableExpressionViewDidChangeSize(this); + } return true; } return false; @@ -44,6 +50,30 @@ KDSize EditableExpressionView::minimalSizeForOptimalDisplay() const { } 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::Left) { return m_expressionViewWithCursor.cursor()->moveLeft(); } @@ -121,14 +151,22 @@ void EditableExpressionView::insertLayoutAtCursor(Poincare::ExpressionLayout * l if (layout == nullptr) { return; } + KDSize previousSize = minimalSizeForOptimalDisplay(); m_expressionViewWithCursor.cursor()->addLayout(layout); m_expressionViewWithCursor.cursor()->setPointedExpressionLayout(pointedLayout); m_expressionViewWithCursor.cursor()->setPosition(Poincare::ExpressionLayoutCursor::Position::Right); m_expressionViewWithCursor.expressionView()->expressionLayout()->invalidAllSizesPositionsAndBaselines(); - cursorOrLayoutChanged(); + KDSize newSize = minimalSizeForOptimalDisplay(); + reload(); + if (m_delegate && previousSize.height() != newSize.height()) { + m_delegate->editableExpressionViewDidChangeSize(this); + } } -void EditableExpressionView::cursorOrLayoutChanged() { +void EditableExpressionView::reload() { m_expressionViewWithCursor.expressionView()->expressionLayout()->invalidAllSizesPositionsAndBaselines(); + m_expressionViewWithCursor.cursorPositionChanged(); layoutSubviews(); + scrollToCursor(); + markRectAsDirty(bounds()); } diff --git a/escher/src/expression_view.cpp b/escher/src/expression_view.cpp index 0fa196d63..dfd23e3b3 100644 --- a/escher/src/expression_view.cpp +++ b/escher/src/expression_view.cpp @@ -49,8 +49,11 @@ KDSize ExpressionView::minimalSizeForOptimalDisplay() const { KDPoint ExpressionView::drawingOrigin() const { KDSize expressionSize = m_expressionLayout->size(); - return KDPoint(m_horizontalAlignment*(m_frame.width() - expressionSize.width()), - 0.5f*(m_frame.height() - expressionSize.height())); + return KDPoint(m_horizontalAlignment*(m_frame.width() - expressionSize.width()), 0.5f*(m_frame.height() - expressionSize.height())); +} + +KDPoint ExpressionView::absoluteDrawingOrigin() const { + return drawingOrigin().translatedBy(m_frame.topLeft()); } void ExpressionView::drawRect(KDContext * ctx, KDRect rect) const { diff --git a/escher/src/expression_view_with_cursor.cpp b/escher/src/expression_view_with_cursor.cpp index b231aea65..b5889eb06 100644 --- a/escher/src/expression_view_with_cursor.cpp +++ b/escher/src/expression_view_with_cursor.cpp @@ -25,7 +25,10 @@ KDRect ExpressionViewWithCursor::cursorRect() { KDSize ExpressionViewWithCursor::minimalSizeForOptimalDisplay() const { KDSize expressionViewSize = m_expressionView.minimalSizeForOptimalDisplay(); - return KDSize(expressionViewSize.width()+1, m_expressionView.minimalSizeForOptimalDisplay().height()); // +1 for the cursor + KDSize cursorSize = isEditing() ? m_cursorView.minimalSizeForOptimalDisplay() : KDSizeZero; + KDCoordinate resultWidth = expressionViewSize.width() + cursorSize.width(); + KDCoordinate resultHeight = expressionViewSize.height() + cursorSize.height()/2; + return KDSize(resultWidth, resultHeight); } View * ExpressionViewWithCursor::subviewAtIndex(int index) { @@ -40,7 +43,7 @@ void ExpressionViewWithCursor::layoutSubviews() { } void ExpressionViewWithCursor::layoutCursorSubview() { - KDPoint expressionViewOrigin = m_expressionView.drawingOrigin(); + KDPoint expressionViewOrigin = m_expressionView.absoluteDrawingOrigin(); KDPoint cursoredExpressionViewOrigin = m_cursor.pointedExpressionLayout()->absoluteOrigin(); KDCoordinate cursorX = expressionViewOrigin.x() + cursoredExpressionViewOrigin.x(); if (m_cursor.position() == ExpressionLayoutCursor::Position::Right) {