mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
183 lines
7.0 KiB
C++
183 lines
7.0 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, char * cacheBuffer, size_t * cacheBufferInformation, HistoryController * historyController, CalculationStore * calculationStore) :
|
|
ViewController(parentResponder),
|
|
m_cacheBuffer(cacheBuffer),
|
|
m_cacheBufferInformation(cacheBufferInformation),
|
|
m_historyController(historyController),
|
|
m_calculationStore(calculationStore),
|
|
m_contentView(this, static_cast<CalculationSelectableTableView *>(m_historyController->view()), inputEventHandlerDelegate, this, this)
|
|
{
|
|
}
|
|
|
|
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::restoreInput() {
|
|
m_contentView.expressionField()->restoreContent(m_cacheBuffer, *m_cacheBufferInformation);
|
|
clearCacheBuffer();
|
|
}
|
|
|
|
void EditExpressionController::memoizeInput() {
|
|
*m_cacheBufferInformation = m_contentView.expressionField()->moveCursorAndDumpContent(m_cacheBuffer, k_cacheBufferSize);
|
|
}
|
|
|
|
bool EditExpressionController::handleEvent(Ion::Events::Event event) {
|
|
if (event == Ion::Events::ShiftBack) {
|
|
m_historyController->reinsertTrash();
|
|
m_historyController->reload();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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(), HistoryViewCell::Height);
|
|
m_historyController->reload();
|
|
return true;
|
|
}
|
|
if (event == Ion::Events::Up) {
|
|
if (m_calculationStore->numberOfCalculations() > 0) {
|
|
clearCacheBuffer();
|
|
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, HistoryViewCell::Height);
|
|
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;
|
|
}
|
|
|
|
}
|