From a93e9566ada10a4173cf36beeb1e091eb490ab0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 28 Sep 2018 11:12:18 +0200 Subject: [PATCH] [escher] Create a parent class Field of TextField and LayoutField --- apps/calculation/app.cpp | 4 +- .../edit_expression_controller.cpp | 4 +- apps/shared/expression_field_delegate_app.cpp | 8 +-- apps/shared/text_field_delegate_app.cpp | 51 ++----------------- apps/shared/text_field_delegate_app.h | 2 +- apps/solver/list_controller.cpp | 4 +- escher/include/escher/field.h | 14 +++++ escher/include/escher/layout_field.h | 11 ++-- escher/include/escher/text_field.h | 10 ++-- escher/src/layout_field.cpp | 8 +++ escher/src/text_field.cpp | 49 +++++++++++++++++- 11 files changed, 95 insertions(+), 70 deletions(-) create mode 100644 escher/include/escher/field.h diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index b3b5a21c3..f17fe35e1 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -55,7 +55,7 @@ bool App::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event e if ((event == Ion::Events::Var || event == Ion::Events::XNT) && TextFieldDelegateApp::textFieldDidReceiveEvent(textField, event)) { return true; } - if (textField->isEditing() && textField->textFieldShouldFinishEditing(event)) { + if (textField->isEditing() && textField->shouldFinishEditing(event)) { if (textField->text()[0] == 0) { return true; } @@ -71,7 +71,7 @@ bool App::layoutFieldDidReceiveEvent(::LayoutField * layoutField, Ion::Events::E if ((event == Ion::Events::Var || event == Ion::Events::XNT) && ExpressionFieldDelegateApp::layoutFieldDidReceiveEvent(layoutField, event)) { return true; } - if (layoutField->isEditing() && layoutField->layoutFieldShouldFinishEditing(event)) { + if (layoutField->isEditing() && layoutField->shouldFinishEditing(event)) { if (!layoutField->hasText()) { return true; } diff --git a/apps/calculation/edit_expression_controller.cpp b/apps/calculation/edit_expression_controller.cpp index 86931135e..a1920bc01 100644 --- a/apps/calculation/edit_expression_controller.cpp +++ b/apps/calculation/edit_expression_controller.cpp @@ -78,7 +78,7 @@ void EditExpressionController::didBecomeFirstResponder() { } bool EditExpressionController::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) { - if (textField->isEditing() && textField->textFieldShouldFinishEditing(event) && textField->draftTextLength() == 0 && m_cacheBuffer[0] != 0) { + if (textField->isEditing() && textField->shouldFinishEditing(event) && textField->draftTextLength() == 0 && m_cacheBuffer[0] != 0) { return inputViewDidReceiveEvent(event); } return textFieldDelegateApp()->textFieldDidReceiveEvent(textField, event); @@ -93,7 +93,7 @@ bool EditExpressionController::textFieldDidAbortEditing(::TextField * textField) } bool EditExpressionController::layoutFieldDidReceiveEvent(::LayoutField * layoutField, Ion::Events::Event event) { - if (layoutField->isEditing() && layoutField->layoutFieldShouldFinishEditing(event) && !layoutField->hasText() && m_calculationStore->numberOfCalculations() > 0) { + if (layoutField->isEditing() && layoutField->shouldFinishEditing(event) && !layoutField->hasText() && m_calculationStore->numberOfCalculations() > 0) { return inputViewDidReceiveEvent(event); } return expressionFieldDelegateApp()->layoutFieldDidReceiveEvent(layoutField, event); diff --git a/apps/shared/expression_field_delegate_app.cpp b/apps/shared/expression_field_delegate_app.cpp index 77c8c1c4b..6b737e92e 100644 --- a/apps/shared/expression_field_delegate_app.cpp +++ b/apps/shared/expression_field_delegate_app.cpp @@ -14,11 +14,7 @@ ExpressionFieldDelegateApp::ExpressionFieldDelegateApp(Container * container, Sn } char ExpressionFieldDelegateApp::privateXNT(LayoutField * layoutField) { - char xntCharFromLayout = layoutField->XNTChar(); - if (xntCharFromLayout != Ion::Charset::Empty) { - return xntCharFromLayout; - } - return XNT()[0]; + return layoutField->XNTChar(XNT()[0]); } bool ExpressionFieldDelegateApp::layoutFieldShouldFinishEditing(LayoutField * layoutField, Ion::Events::Event event) { @@ -26,7 +22,7 @@ bool ExpressionFieldDelegateApp::layoutFieldShouldFinishEditing(LayoutField * la } bool ExpressionFieldDelegateApp::layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) { - if (layoutField->isEditing() && layoutField->layoutFieldShouldFinishEditing(event)) { + if (layoutField->isEditing() && layoutField->shouldFinishEditing(event)) { if (!layoutField->hasText()) { layoutField->app()->displayWarning(I18n::Message::SyntaxError); return true; diff --git a/apps/shared/text_field_delegate_app.cpp b/apps/shared/text_field_delegate_app.cpp index 88a3ea30e..c2ed14bbd 100644 --- a/apps/shared/text_field_delegate_app.cpp +++ b/apps/shared/text_field_delegate_app.cpp @@ -30,7 +30,7 @@ bool TextFieldDelegateApp::textFieldShouldFinishEditing(TextField * textField, I } bool TextFieldDelegateApp::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { - if (textField->isEditing() && textField->textFieldShouldFinishEditing(event)) { + if (textField->isEditing() && textField->shouldFinishEditing(event)) { if (unparsableText(textField->text(), textField)) { return true; } @@ -41,7 +41,7 @@ bool TextFieldDelegateApp::textFieldDidReceiveEvent(TextField * textField, Ion:: } if (event == Ion::Events::XNT) { forceEdition(textField); - const char * xnt = privateXNT(textField); + const char xnt[2] = {privateXNT(textField), 0}; return textField->handleEventWithText(xnt); } return false; @@ -84,51 +84,8 @@ bool TextFieldDelegateApp::displayVariableBoxController(Responder * sender) { /* Private */ -const char * TextFieldDelegateApp::privateXNT(TextField * textField) { - static constexpr struct { const char *name, *xnt; } sFunctions[] = { - { "diff", "x" }, { "int", "x" }, - { "product", "n" }, { "sum", "n" } - }; - // Let's assume everything before the cursor is nested correctly, which is reasonable if the expression is being entered left-to-right. - const char * text = textField->text(); - size_t location = textField->cursorLocation(); - unsigned level = 0; - while (location >= 1) { - location--; - switch (text[location]) { - case '(': - // Check if we are skipping to the next matching '('. - if (level) { - level--; - break; - } - // Skip over whitespace. - while (location >= 1 && text[location-1] == ' ') { - location--; - } - // We found the next innermost function we are currently in. - for (size_t i = 0; i < sizeof(sFunctions)/sizeof(sFunctions[0]); i++) { - const char * name = sFunctions[i].name; - size_t length = strlen(name); - if (location >= length && memcmp(&text[location-length], name, length) == 0) { - return sFunctions[i].xnt; - } - } - break; - case ',': - // Commas encountered while skipping to the next matching '(' should be ignored. - if (level) { - break; - } - // FALLTHROUGH - case ')': - // Skip to the next matching '('. - level++; - break; - } - } - // Fallback to the default - return XNT(); +char TextFieldDelegateApp::privateXNT(TextField * textField) { + return textField->XNTChar(XNT()[0]); } } diff --git a/apps/shared/text_field_delegate_app.h b/apps/shared/text_field_delegate_app.h index 3c7534804..85a2ac13a 100644 --- a/apps/shared/text_field_delegate_app.h +++ b/apps/shared/text_field_delegate_app.h @@ -26,7 +26,7 @@ protected: bool unparsableText(const char * text, Responder * responder); bool displayVariableBoxController(Responder * sender); private: - const char * privateXNT(TextField * textField); + char privateXNT(TextField * textField); }; } diff --git a/apps/solver/list_controller.cpp b/apps/solver/list_controller.cpp index 31b9d4930..191551ebf 100644 --- a/apps/solver/list_controller.cpp +++ b/apps/solver/list_controller.cpp @@ -120,7 +120,7 @@ bool textRepresentsAnEquality(const char * text) { } bool ListController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { - if (textField->isEditing() && textField->textFieldShouldFinishEditing(event)) { + if (textField->isEditing() && textField->shouldFinishEditing(event)) { if (!textRepresentsAnEquality(textField->text())) { textField->handleEvent(Ion::Events::ShiftRight); textField->handleEventWithText("=0"); @@ -137,7 +137,7 @@ bool ListController::textFieldDidReceiveEvent(TextField * textField, Ion::Events } bool ListController::layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) { - if (layoutField->isEditing() && layoutField->layoutFieldShouldFinishEditing(event)) { + if (layoutField->isEditing() && layoutField->shouldFinishEditing(event)) { char buffer[TextField::maxBufferSize()]; layoutField->serialize(buffer, TextField::maxBufferSize()); if (!textRepresentsAnEquality(buffer)) { diff --git a/escher/include/escher/field.h b/escher/include/escher/field.h new file mode 100644 index 000000000..ed8e9bf7e --- /dev/null +++ b/escher/include/escher/field.h @@ -0,0 +1,14 @@ +#ifndef ESCHER_FIELD_H +#define ESCHER_FIELD_H + +#include + +class Field { +public: + virtual bool isEditing() const = 0; + virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true) = 0; + virtual char XNTChar(char defaultXNTChar) = 0; + virtual bool shouldFinishEditing(Ion::Events::Event event) = 0; +}; + +#endif diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 67deede6a..cb6489a28 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -1,6 +1,7 @@ #ifndef ESCHER_LAYOUT_FIELD_H #define ESCHER_LAYOUT_FIELD_H +#include #include #include #include @@ -10,7 +11,7 @@ #include #include -class LayoutField : public ScrollableView, public ScrollViewDataSource { +class LayoutField : public ScrollableView, public ScrollViewDataSource, public Field { public: LayoutField(Responder * parentResponder, LayoutFieldDelegate * delegate = nullptr) : ScrollableView(parentResponder, &m_contentView, this), @@ -18,8 +19,8 @@ public: m_delegate(delegate) {} void setDelegate(LayoutFieldDelegate * delegate) { m_delegate = delegate; } - bool isEditing() const { return m_contentView.isEditing(); } - void setEditing(bool isEditing) { m_contentView.setEditing(isEditing); } + bool isEditing() const override { return m_contentView.isEditing(); } + void setEditing(bool isEditing, bool reinitDraftBuffer = false) override { m_contentView.setEditing(isEditing); } void clearLayout() { m_contentView.clearLayout(); } void scrollToCursor() { scrollToBaselinedRect(m_contentView.cursorRect(), m_contentView.cursor()->baseline()); @@ -27,7 +28,7 @@ public: bool hasText() const { return layout().hasText(); } int serialize(char * buffer, int bufferLength) { return layout().serialize(buffer, bufferLength); } Poincare::Layout layout() const { return m_contentView.expressionView()->layout(); } - char XNTChar() { return m_contentView.cursor()->layoutReference().XNTChar(); } + char XNTChar(char defaultXNTChar) override; // ScrollableView void setBackgroundColor(KDColor c) override { @@ -41,7 +42,7 @@ public: Toolbox * toolbox() override { return m_delegate != nullptr ? m_delegate->toolboxForLayoutField(this) : nullptr; } - bool layoutFieldShouldFinishEditing(Ion::Events::Event event) { // TODO REMOVE ? + bool shouldFinishEditing(Ion::Events::Event event) override { // TODO REMOVE ? return m_delegate->layoutFieldShouldFinishEditing(this, event); } diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index d3fe2f821..d7d2401a5 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -1,11 +1,12 @@ #ifndef ESCHER_TEXT_FIELD_H #define ESCHER_TEXT_FIELD_H +#include #include #include #include -class TextField : public TextInput { +class TextField : public TextInput, public Field { public: TextField(Responder * parentResponder, char * textBuffer, char * draftTextBuffer, size_t textBufferSize, TextFieldDelegate * delegate = nullptr, bool hasTwoBuffers = true, const KDFont * font = KDFont::LargeFont, @@ -14,19 +15,20 @@ public: void setTextColor(KDColor textColor); void setDelegate(TextFieldDelegate * delegate) { m_delegate = delegate; } void setDraftTextBuffer(char * draftTextBuffer); - bool isEditing() const; + bool isEditing() const override; size_t draftTextLength() const; void setText(const char * text); void setAlignment(float horizontalAlignment, float verticalAlignment); - virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true); + virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true) override; KDSize minimalSizeForOptimalDisplay() const override; + char XNTChar(char defaultXNTChar) override; bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override; bool handleEvent(Ion::Events::Event event) override; constexpr static int maxBufferSize() { return ContentView::k_maxBufferSize; } void scrollToCursor() override; - bool textFieldShouldFinishEditing(Ion::Events::Event event) { return m_delegate->textFieldShouldFinishEditing(this, event); } + bool shouldFinishEditing(Ion::Events::Event event) override { return m_delegate->textFieldShouldFinishEditing(this, event); } protected: class ContentView : public TextInput::ContentView { public: diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index ad17094dc..6400e8562 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -68,6 +68,14 @@ void LayoutField::ContentView::layoutCursorSubview() { m_cursorView.setFrame(KDRect(cursorTopLeftPosition, LayoutCursor::k_cursorWidth, m_cursor.cursorHeight())); } +char LayoutField::XNTChar(char defaultXNTChar) { + char xnt = m_contentView.cursor()->layoutReference().XNTChar(); + if (xnt != Ion::Charset::Empty) { + return xnt; + } + return defaultXNTChar; +} + void LayoutField::reload(KDSize previousSize) { layout().invalidAllSizesPositionsAndBaselines(); KDSize newSize = minimalSizeForOptimalDisplay(); diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index dca7fa9cc..7aba2bac3 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -247,7 +247,7 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { if (event == Ion::Events::ShiftRight && isEditing()) { return setCursorLocation(draftTextLength()); } - if (isEditing() && textFieldShouldFinishEditing(event)) { + if (isEditing() && shouldFinishEditing(event)) { char bufferText[ContentView::k_maxBufferSize]; strlcpy(bufferText, m_contentView.textBuffer(), ContentView::k_maxBufferSize); strlcpy(m_contentView.textBuffer(), m_contentView.draftTextBuffer(), m_contentView.bufferSize()); @@ -300,6 +300,53 @@ KDSize TextField::minimalSizeForOptimalDisplay() const { return m_contentView.minimalSizeForOptimalDisplay(); } +char TextField::XNTChar(char defaultXNTChar) { + static constexpr struct { const char *name; char xnt; } sFunctions[] = { + { "diff", 'x' }, { "int", 'x' }, + { "product", 'n' }, { "sum", 'n' } + }; + // Let's assume everything before the cursor is nested correctly, which is reasonable if the expression is being entered left-to-right. + const char * text = this->text(); + size_t location = cursorLocation(); + unsigned level = 0; + while (location >= 1) { + location--; + switch (text[location]) { + case '(': + // Check if we are skipping to the next matching '('. + if (level) { + level--; + break; + } + // Skip over whitespace. + while (location >= 1 && text[location-1] == ' ') { + location--; + } + // We found the next innermost function we are currently in. + for (size_t i = 0; i < sizeof(sFunctions)/sizeof(sFunctions[0]); i++) { + const char * name = sFunctions[i].name; + size_t length = strlen(name); + if (location >= length && memcmp(&text[location-length], name, length) == 0) { + return sFunctions[i].xnt; + } + } + break; + case ',': + // Commas encountered while skipping to the next matching '(' should be ignored. + if (level) { + break; + } + // FALLTHROUGH + case ')': + // Skip to the next matching '('. + level++; + break; + } + } + // Fallback to the default + return defaultXNTChar; +} + bool TextField::handleEvent(Ion::Events::Event event) { assert(m_delegate != nullptr); if (m_delegate->textFieldDidReceiveEvent(this, event)) {