mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[apps/escher/poincare] Use 2D Edition in Sequence and Graph apps.
EditableExpressionView is renamed ScrollableViewWithCursor. EditableExpressionView is now a View that displays either a TextField or a ScrollableViewWithCursor, depending on Poincare::Preferences. Change-Id: Id44ddcd9a83f5fd17d65753ca4c94c5c7cda9b8a
This commit is contained in:
@@ -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/,\
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<char *>(textBody()), ContentView::k_bufferLength);
|
||||
m_calculationStore->push(textBody(), calculationApp->localContext());
|
||||
(const_cast<ExpressionView *>(((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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,16 +4,15 @@
|
||||
#include <escher.h>
|
||||
#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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef CALCULATION_TEXT_FIELD_H
|
||||
#define CALCULATION_TEXT_FIELD_H
|
||||
|
||||
#include <escher.h>
|
||||
|
||||
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
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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<EditableExpressionView *>(sender);
|
||||
void MathToolbox::actionForScrollableExpressionViewWithCursor(void * sender, const char * text, bool removeArguments) {
|
||||
ScrollableExpressionViewWithCursor * expressionLayoutEditorSender = static_cast<ScrollableExpressionViewWithCursor *>(sender);
|
||||
Expression * resultExpression = nullptr;
|
||||
if (removeArguments) {
|
||||
// Replace the arguments with Empty chars.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#ifndef SHARED_EDITABLE_EXPRESSION_VIEW_DELEGATE_H
|
||||
#define SHARED_EDITABLE_EXPRESSION_VIEW_DELEGATE_H
|
||||
|
||||
#include <escher/editable_expression_view_delegate.h>
|
||||
#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
|
||||
56
apps/shared/editable_expression_view_delegate_app.cpp
Normal file
56
apps/shared/editable_expression_view_delegate_app.cpp
Normal file
@@ -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<MathToolbox *>(toolbox)->setSenderAndAction(scrollableExpressionViewWithCursor, MathToolbox::actionForScrollableExpressionViewWithCursor);
|
||||
return toolbox;
|
||||
}
|
||||
|
||||
}
|
||||
21
apps/shared/editable_expression_view_delegate_app.h
Normal file
21
apps/shared/editable_expression_view_delegate_app.h
Normal file
@@ -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 <escher/scrollable_expression_view_with_cursor_delegate.h>
|
||||
|
||||
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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define SHARED_FUNCTION_APP_H
|
||||
|
||||
#include <poincare.h>
|
||||
#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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef SHARED_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H
|
||||
#define SHARED_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H
|
||||
|
||||
#include <escher/scrollable_expression_view_with_cursor_delegate.h>
|
||||
#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
|
||||
@@ -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<MathToolbox *>(toolbox)->setSenderAndAction(editableExpressionView, MathToolbox::actionForEditableExpressionView);
|
||||
return toolbox;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <escher/editable_expression_view_delegate.h>
|
||||
|
||||
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
|
||||
@@ -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<EditableExpressionView *>(sender);
|
||||
if (!editableExpressionView->isEditing()) {
|
||||
editableExpressionView->setEditing(true);
|
||||
void VariableBoxController::ContentViewController::insertTextInScrollableExpressionViewWithCursor(void * sender, const char * textToInsert) {
|
||||
ScrollableExpressionViewWithCursor * scrollableExpressionViewWithCursor = static_cast<ScrollableExpressionViewWithCursor *>(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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <escher/container.h>
|
||||
#include <escher/dynamic_view_controller.h>
|
||||
#include <escher/editable_expression_view.h>
|
||||
#include <escher/editable_expression_view_delegate.h>
|
||||
#include <escher/editable_text_cell.h>
|
||||
#include <escher/ellipsis_view.h>
|
||||
#include <escher/even_odd_cell.h>
|
||||
@@ -53,6 +52,8 @@
|
||||
#include <escher/scroll_view_data_source.h>
|
||||
#include <escher/scroll_view_indicator.h>
|
||||
#include <escher/scrollable_view.h>
|
||||
#include <escher/scrollable_expression_view_with_cursor.h>
|
||||
#include <escher/scrollable_expression_view_with_cursor_delegate.h>
|
||||
#include <escher/selectable_table_view.h>
|
||||
#include <escher/selectable_table_view_data_source.h>
|
||||
#include <escher/selectable_table_view_delegate.h>
|
||||
|
||||
@@ -1,39 +1,49 @@
|
||||
#ifndef ESCHER_EDITABLE_EXPRESSION_VIEW_H
|
||||
#define ESCHER_EDITABLE_EXPRESSION_VIEW_H
|
||||
|
||||
#include <escher/scrollable_view.h>
|
||||
#include <escher/expression_view_with_cursor.h>
|
||||
#include <escher/editable_expression_view_delegate.h>
|
||||
#include <poincare/expression_layout_cursor.h>
|
||||
#include <escher/scrollable_expression_view_with_cursor.h>
|
||||
#include <escher/scrollable_expression_view_with_cursor_delegate.h>
|
||||
#include <escher/text_field.h>
|
||||
#include <escher/text_field_delegate.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef ESCHER_EDITABLE_EXPRESSION_VIEW_DELEGATE_H
|
||||
#define ESCHER_EDITABLE_EXPRESSION_VIEW_DELEGATE_H
|
||||
|
||||
#include <escher/toolbox.h>
|
||||
#include <ion/events.h>
|
||||
|
||||
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
|
||||
@@ -1,53 +1,53 @@
|
||||
#ifndef ESCHER_INPUT_VIEW_CONTROLLER_H
|
||||
#define ESCHER_INPUT_VIEW_CONTROLLER_H
|
||||
|
||||
#include <escher/editable_expression_view.h>
|
||||
#include <escher/scrollable_expression_view_with_cursor_delegate.h>
|
||||
#include <escher/modal_view_controller.h>
|
||||
#include <escher/invocation.h>
|
||||
#include <escher/text_field.h>
|
||||
#include <escher/text_field_delegate.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_H
|
||||
#define ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_H
|
||||
|
||||
#include <escher/scrollable_view.h>
|
||||
#include <escher/expression_view_with_cursor.h>
|
||||
#include <escher/scrollable_expression_view_with_cursor_delegate.h>
|
||||
#include <poincare/expression_layout_cursor.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H
|
||||
#define ESCHER_SCROLLABLE_EXPRESSION_VIEW_WITH_CURSOR_DELEGATE_H
|
||||
|
||||
#include <escher/toolbox.h>
|
||||
#include <ion/events.h>
|
||||
|
||||
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
|
||||
@@ -1,212 +1,137 @@
|
||||
#include <escher/editable_expression_view.h>
|
||||
#include <escher/clipboard.h>
|
||||
#include <escher/text_field.h>
|
||||
#include <poincare/src/layout/matrix_layout.h>
|
||||
#include <poincare/preferences.h>
|
||||
#include <assert.h>
|
||||
|
||||
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<Poincare::MatrixLayout *>(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<ScrollableExpressionViewWithCursor *>(&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;
|
||||
}
|
||||
|
||||
@@ -1,89 +1,47 @@
|
||||
#include <escher/input_view_controller.h>
|
||||
#include <escher/app.h>
|
||||
#include <escher/palette.h>
|
||||
#include <poincare/src/layout/horizontal_layout.h>
|
||||
#include <assert.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
225
escher/src/scrollable_expression_view_with_cursor.cpp
Normal file
225
escher/src/scrollable_expression_view_with_cursor.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include <escher/scrollable_expression_view_with_cursor.h>
|
||||
#include <escher/clipboard.h>
|
||||
#include <escher/text_field.h>
|
||||
#include <poincare/src/layout/matrix_layout.h>
|
||||
#include <assert.h>
|
||||
|
||||
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<Poincare::MatrixLayout *>(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());
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user