mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
responder, scroll to the bottom of the table (and not the last cell). Indeed, the last cell might be to big to be displayed and scroll to it might scroll to its top. This fixes the following bug: input 1/2/3/4/5/6/7/8, OK, up, down. We did not scroll to the bottom of the table.
164 lines
6.3 KiB
C++
164 lines
6.3 KiB
C++
#include "edit_expression_controller.h"
|
|
#include "app.h"
|
|
#include <ion/display.h>
|
|
#include <poincare/preferences.h>
|
|
#include <assert.h>
|
|
|
|
using namespace Shared;
|
|
using namespace Poincare;
|
|
|
|
namespace Calculation {
|
|
|
|
EditExpressionController::ContentView::ContentView(Responder * parentResponder, CalculationSelectableTableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
|
|
View(),
|
|
m_mainView(subview),
|
|
m_expressionField(parentResponder, inputEventHandlerDelegate, textFieldDelegate, layoutFieldDelegate)
|
|
{
|
|
}
|
|
|
|
View * EditExpressionController::ContentView::subviewAtIndex(int index) {
|
|
assert(index >= 0 && index < numberOfSubviews());
|
|
if (index == 0) {
|
|
return m_mainView;
|
|
}
|
|
assert(index == 1);
|
|
return &m_expressionField;
|
|
}
|
|
|
|
void EditExpressionController::ContentView::layoutSubviews(bool force) {
|
|
KDCoordinate inputViewFrameHeight = m_expressionField.minimalSizeForOptimalDisplay().height();
|
|
KDRect mainViewFrame(0, 0, bounds().width(), bounds().height() - inputViewFrameHeight);
|
|
m_mainView->setFrame(mainViewFrame, force);
|
|
KDRect inputViewFrame(0, bounds().height() - inputViewFrameHeight, bounds().width(), inputViewFrameHeight);
|
|
m_expressionField.setFrame(inputViewFrame, force);
|
|
}
|
|
|
|
void EditExpressionController::ContentView::reload() {
|
|
layoutSubviews();
|
|
markRectAsDirty(bounds());
|
|
}
|
|
|
|
EditExpressionController::EditExpressionController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, HistoryController * historyController, CalculationStore * calculationStore) :
|
|
ViewController(parentResponder),
|
|
m_historyController(historyController),
|
|
m_calculationStore(calculationStore),
|
|
m_contentView(this, static_cast<CalculationSelectableTableView *>(m_historyController->view()), inputEventHandlerDelegate, this, this)
|
|
{
|
|
m_cacheBuffer[0] = 0;
|
|
}
|
|
|
|
void EditExpressionController::insertTextBody(const char * text) {
|
|
Container::activeApp()->setFirstResponder(this);
|
|
m_contentView.expressionField()->handleEventWithText(text, false, true);
|
|
}
|
|
|
|
void EditExpressionController::didBecomeFirstResponder() {
|
|
m_contentView.mainView()->scrollToBottom();
|
|
m_contentView.expressionField()->setEditing(true, false);
|
|
Container::activeApp()->setFirstResponder(m_contentView.expressionField());
|
|
}
|
|
|
|
void EditExpressionController::viewWillAppear() {
|
|
m_historyController->viewWillAppear();
|
|
}
|
|
|
|
bool EditExpressionController::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) {
|
|
bool shouldDuplicateLastCalculation = textField->isEditing() && textField->shouldFinishEditing(event) && textField->draftTextLength() == 0;
|
|
if (inputViewDidReceiveEvent(event, shouldDuplicateLastCalculation)) {
|
|
return true;
|
|
}
|
|
return textFieldDelegateApp()->textFieldDidReceiveEvent(textField, event);
|
|
}
|
|
|
|
bool EditExpressionController::textFieldDidFinishEditing(::TextField * textField, const char * text, Ion::Events::Event event) {
|
|
return inputViewDidFinishEditing(text, nullptr);
|
|
}
|
|
|
|
bool EditExpressionController::textFieldDidAbortEditing(::TextField * textField) {
|
|
return inputViewDidAbortEditing(textField->text());
|
|
}
|
|
|
|
bool EditExpressionController::layoutFieldDidReceiveEvent(::LayoutField * layoutField, Ion::Events::Event event) {
|
|
bool shouldDuplicateLastCalculation = layoutField->isEditing() && layoutField->shouldFinishEditing(event) && !layoutField->hasText();
|
|
if (inputViewDidReceiveEvent(event, shouldDuplicateLastCalculation)) {
|
|
return true;
|
|
}
|
|
return expressionFieldDelegateApp()->layoutFieldDidReceiveEvent(layoutField, event);
|
|
}
|
|
|
|
bool EditExpressionController::layoutFieldDidFinishEditing(::LayoutField * layoutField, Layout layoutR, Ion::Events::Event event) {
|
|
return inputViewDidFinishEditing(nullptr, layoutR);
|
|
}
|
|
|
|
bool EditExpressionController::layoutFieldDidAbortEditing(::LayoutField * layoutField) {
|
|
return inputViewDidAbortEditing(nullptr);
|
|
}
|
|
|
|
void EditExpressionController::layoutFieldDidChangeSize(::LayoutField * layoutField) {
|
|
if (m_contentView.expressionField()->inputViewHeightDidChange()) {
|
|
/* Reload the whole view only if the ExpressionField's height did actually
|
|
* change. */
|
|
reloadView();
|
|
} else {
|
|
/* The input view is already at maximal size so we do not need to relayout
|
|
* the view underneath, but the view inside the input view might still need
|
|
* to be relayouted.
|
|
* We force the relayout because the frame stays the same but we need to
|
|
* propagate a relayout to the content of the field scroll view. */
|
|
m_contentView.expressionField()->layoutSubviews(true);
|
|
}
|
|
}
|
|
|
|
void EditExpressionController::reloadView() {
|
|
m_contentView.reload();
|
|
m_historyController->reload();
|
|
}
|
|
|
|
bool EditExpressionController::inputViewDidReceiveEvent(Ion::Events::Event event, bool shouldDuplicateLastCalculation) {
|
|
if (shouldDuplicateLastCalculation && m_cacheBuffer[0] != 0) {
|
|
/* The input text store in m_cacheBuffer might have been correct the first
|
|
* time but then be too long when replacing ans in another context */
|
|
Shared::TextFieldDelegateApp * myApp = textFieldDelegateApp();
|
|
if (!myApp->isAcceptableText(m_cacheBuffer)) {
|
|
return true;
|
|
}
|
|
m_calculationStore->push(m_cacheBuffer, myApp->localContext());
|
|
m_historyController->reload();
|
|
return true;
|
|
}
|
|
if (event == Ion::Events::Up) {
|
|
if (m_calculationStore->numberOfCalculations() > 0) {
|
|
m_cacheBuffer[0] = 0;
|
|
m_contentView.expressionField()->setEditing(false, false);
|
|
Container::activeApp()->setFirstResponder(m_historyController);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool EditExpressionController::inputViewDidFinishEditing(const char * text, Layout layoutR) {
|
|
Context * context = textFieldDelegateApp()->localContext();
|
|
if (layoutR.isUninitialized()) {
|
|
assert(text);
|
|
strlcpy(m_cacheBuffer, text, k_cacheBufferSize);
|
|
} else {
|
|
layoutR.serializeParsedExpression(m_cacheBuffer, k_cacheBufferSize, context);
|
|
}
|
|
m_calculationStore->push(m_cacheBuffer, context);
|
|
m_historyController->reload();
|
|
m_contentView.expressionField()->setEditing(true, true);
|
|
telemetryReportEvent("Input", m_cacheBuffer);
|
|
return true;
|
|
}
|
|
|
|
bool EditExpressionController::inputViewDidAbortEditing(const char * text) {
|
|
if (text != nullptr) {
|
|
m_contentView.expressionField()->setEditing(true, true);
|
|
m_contentView.expressionField()->setText(text);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|