mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-19 05:40:38 +01:00
[escher] Create a parent class Field of TextField and
LayoutField
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
14
escher/include/escher/field.h
Normal file
14
escher/include/escher/field.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user