[escher] Create a parent class Field of TextField and

LayoutField
This commit is contained in:
Émilie Feral
2018-09-28 11:12:18 +02:00
parent ce14c5d694
commit a93e9566ad
11 changed files with 95 additions and 70 deletions

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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]);
}
}

View File

@@ -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);
};
}

View File

@@ -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)) {

View File

@@ -0,0 +1,14 @@
#ifndef ESCHER_FIELD_H
#define ESCHER_FIELD_H
#include <ion.h>
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

View File

@@ -1,6 +1,7 @@
#ifndef ESCHER_LAYOUT_FIELD_H
#define ESCHER_LAYOUT_FIELD_H
#include <escher/field.h>
#include <escher/expression_view.h>
#include <escher/layout_field_delegate.h>
#include <escher/scrollable_view.h>
@@ -10,7 +11,7 @@
#include <poincare/layout.h>
#include <poincare/layout_cursor.h>
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);
}

View File

@@ -1,11 +1,12 @@
#ifndef ESCHER_TEXT_FIELD_H
#define ESCHER_TEXT_FIELD_H
#include <escher/field.h>
#include <escher/text_input.h>
#include <escher/text_field_delegate.h>
#include <string.h>
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:

View File

@@ -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();

View File

@@ -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)) {