mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[shared] Temporarily duplicate shared controllers to take a
StorageFunction instead of a Function The original controllers are to be deleted when Sequence uses Storage
This commit is contained in:
@@ -53,9 +53,17 @@ app_objs += $(addprefix apps/shared/,\
|
||||
storage_cartesian_function.o\
|
||||
storage_expression_model.o\
|
||||
storage_expression_model_store.o\
|
||||
storage_expression_model_list_controller.o\
|
||||
storage_function.o\
|
||||
storage_function_banner_delegate.o\
|
||||
storage_function_curve_parameter_controller.o\
|
||||
storage_function_go_to_parameter_controller.o\
|
||||
storage_function_graph_view.o\
|
||||
storage_function_graph_controller.o\
|
||||
storage_function_list_controller.o\
|
||||
storage_function_store.o\
|
||||
storage_list_parameter_controller.o\
|
||||
storage_sum_graph_controller.o\
|
||||
store_cell.o\
|
||||
store_context.o\
|
||||
store_controller.o\
|
||||
|
||||
35
apps/shared/storage_function_banner_delegate.cpp
Normal file
35
apps/shared/storage_function_banner_delegate.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "storage_function_banner_delegate.h"
|
||||
#include "poincare_helpers.h"
|
||||
#include "../constant.h"
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
void StorageFunctionBannerDelegate::reloadBannerViewForCursorOnFunction(CurveViewCursor * cursor, StorageFunction * function, char symbol) {
|
||||
constexpr int bufferSize = k_maxNumberOfCharacters+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits);
|
||||
char buffer[bufferSize];
|
||||
const char * space = " ";
|
||||
int spaceLength = strlen(space);
|
||||
const char * legend = "0=";
|
||||
int legendLength = strlen(legend);
|
||||
int numberOfChar = 0;
|
||||
strlcpy(buffer, legend, legendLength+1);
|
||||
numberOfChar += legendLength;
|
||||
buffer[0] = symbol;
|
||||
numberOfChar += PoincareHelpers::ConvertFloatToText<double>(cursor->x(), buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits);
|
||||
strlcpy(buffer+numberOfChar, space, spaceLength+1);
|
||||
buffer[k_maxDigitLegendLength+2] = 0;
|
||||
bannerView()->setLegendAtIndex(buffer, 0);
|
||||
|
||||
numberOfChar = 0;
|
||||
numberOfChar += function->nameWithArgument(buffer, bufferSize, symbol);
|
||||
legend = "=";
|
||||
numberOfChar += strlcpy(buffer+numberOfChar, legend, bufferSize-numberOfChar);
|
||||
numberOfChar += PoincareHelpers::ConvertFloatToText<double>(cursor->y(), buffer+numberOfChar, bufferSize-numberOfChar, Constant::MediumNumberOfSignificantDigits);
|
||||
strlcpy(buffer+numberOfChar, space, bufferSize-numberOfChar);
|
||||
buffer[k_maxDigitLegendLength+5] = 0;
|
||||
bannerView()->setLegendAtIndex(buffer, 1);
|
||||
}
|
||||
|
||||
}
|
||||
21
apps/shared/storage_function_banner_delegate.h
Normal file
21
apps/shared/storage_function_banner_delegate.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef SHARED_STORAGE_FUNCTION_BANNER_DELEGATE_H
|
||||
#define SHARED_STORAGE_FUNCTION_BANNER_DELEGATE_H
|
||||
|
||||
#include "storage_function_store.h"
|
||||
#include "banner_view.h"
|
||||
#include "curve_view_cursor.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class StorageFunctionBannerDelegate {
|
||||
public:
|
||||
constexpr static int k_maxNumberOfCharacters = 50;
|
||||
constexpr static int k_maxDigitLegendLength = 11;
|
||||
protected:
|
||||
void reloadBannerViewForCursorOnFunction(CurveViewCursor * cursor, StorageFunction * function, char symbol);
|
||||
virtual BannerView * bannerView() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
43
apps/shared/storage_function_curve_parameter_controller.cpp
Normal file
43
apps/shared/storage_function_curve_parameter_controller.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "storage_function_curve_parameter_controller.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
StorageFunctionCurveParameterController::StorageFunctionCurveParameterController(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor) :
|
||||
ViewController(nullptr),
|
||||
m_goToCell(I18n::Message::Goto),
|
||||
m_selectableTableView(this, this, this),
|
||||
m_function(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
View * StorageFunctionCurveParameterController::view() {
|
||||
return &m_selectableTableView;
|
||||
}
|
||||
|
||||
void StorageFunctionCurveParameterController::didBecomeFirstResponder() {
|
||||
if (selectedRow() < 0) {
|
||||
selectCellAtLocation(0, 0);
|
||||
}
|
||||
app()->setFirstResponder(&m_selectableTableView);
|
||||
}
|
||||
|
||||
bool StorageFunctionCurveParameterController::handleGotoSelection() {
|
||||
if (m_function == nullptr) {
|
||||
return false;
|
||||
}
|
||||
goToParameterController()->setFunction(m_function);
|
||||
StackViewController * stack = (StackViewController *)parentResponder();
|
||||
stack->push(goToParameterController());
|
||||
return true;
|
||||
}
|
||||
|
||||
KDCoordinate StorageFunctionCurveParameterController::cellHeight() {
|
||||
return Metric::ParameterCellHeight;
|
||||
}
|
||||
|
||||
void StorageFunctionCurveParameterController::setFunction(StorageFunction * function) {
|
||||
m_function = function;
|
||||
}
|
||||
|
||||
}
|
||||
30
apps/shared/storage_function_curve_parameter_controller.h
Normal file
30
apps/shared/storage_function_curve_parameter_controller.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef SHARED_STORAGE_FUNCTION_CURVE_PARAMETER_CONTROLLER_H
|
||||
#define SHARED_STORAGE_FUNCTION_CURVE_PARAMETER_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "storage_function_go_to_parameter_controller.h"
|
||||
#include "function.h"
|
||||
#include "curve_view_cursor.h"
|
||||
#include "interactive_curve_view_range.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class StorageFunctionCurveParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource {
|
||||
public:
|
||||
StorageFunctionCurveParameterController(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor);
|
||||
View * view() override;
|
||||
void didBecomeFirstResponder() override;
|
||||
KDCoordinate cellHeight() override;
|
||||
void setFunction(StorageFunction * function);
|
||||
protected:
|
||||
bool handleGotoSelection();
|
||||
MessageTableCellWithChevron m_goToCell;
|
||||
SelectableTableView m_selectableTableView;
|
||||
StorageFunction * m_function;
|
||||
private:
|
||||
virtual StorageFunctionGoToParameterController * goToParameterController() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
45
apps/shared/storage_function_go_to_parameter_controller.cpp
Normal file
45
apps/shared/storage_function_go_to_parameter_controller.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "storage_function_go_to_parameter_controller.h"
|
||||
#include "text_field_delegate_app.h"
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
StorageFunctionGoToParameterController::StorageFunctionGoToParameterController(Responder * parentResponder, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, I18n::Message symbol) :
|
||||
GoToParameterController(parentResponder, graphRange, cursor, symbol),
|
||||
m_function(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
const char * StorageFunctionGoToParameterController::title() {
|
||||
return I18n::translate(I18n::Message::Goto);
|
||||
}
|
||||
|
||||
double StorageFunctionGoToParameterController::parameterAtIndex(int index) {
|
||||
assert(index == 0);
|
||||
return m_cursor->x();
|
||||
}
|
||||
|
||||
bool StorageFunctionGoToParameterController::setParameterAtIndex(int parameterIndex, double f) {
|
||||
assert(parameterIndex == 0);
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
float y = m_function->evaluateAtAbscissa(f, myApp->localContext());
|
||||
if (std::fabs(f) > k_maxDisplayableFloat || std::fabs(y) > k_maxDisplayableFloat) {
|
||||
app()->displayWarning(I18n::Message::ForbiddenValue);
|
||||
return false;
|
||||
}
|
||||
if (std::isnan(y) || std::isinf(y)) {
|
||||
app()->displayWarning(I18n::Message::ValueNotReachedByFunction);
|
||||
return false;
|
||||
}
|
||||
m_cursor->moveTo(f, y);
|
||||
m_graphRange->centerAxisAround(CurveViewRange::Axis::X, m_cursor->x());
|
||||
m_graphRange->centerAxisAround(CurveViewRange::Axis::Y, m_cursor->y());
|
||||
return true;
|
||||
}
|
||||
|
||||
void StorageFunctionGoToParameterController::setFunction(StorageFunction * function) {
|
||||
m_function = function;
|
||||
}
|
||||
|
||||
}
|
||||
23
apps/shared/storage_function_go_to_parameter_controller.h
Normal file
23
apps/shared/storage_function_go_to_parameter_controller.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef SHARED_STORAGE_FUNCTION_GO_TO_PARAMETER_CONTROLLER_H
|
||||
#define SHARED_STORAGE_FUNCTION_GO_TO_PARAMETER_CONTROLLER_H
|
||||
|
||||
#include "go_to_parameter_controller.h"
|
||||
#include "storage_function.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class StorageFunctionGoToParameterController : public GoToParameterController {
|
||||
public:
|
||||
StorageFunctionGoToParameterController(Responder * parentResponder, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, I18n::Message symbol);
|
||||
const char * title() override;
|
||||
void setFunction(StorageFunction * function);
|
||||
protected:
|
||||
bool setParameterAtIndex(int parameterIndex, double f) override;
|
||||
StorageFunction * m_function;
|
||||
private:
|
||||
double parameterAtIndex(int index) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
165
apps/shared/storage_function_graph_controller.cpp
Normal file
165
apps/shared/storage_function_graph_controller.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
#include "storage_function_graph_controller.h"
|
||||
#include "text_field_delegate_app.h"
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
StorageFunctionGraphController::StorageFunctionGraphController(Responder * parentResponder, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion) :
|
||||
InteractiveCurveViewController(parentResponder, header, interactiveRange, curveView, cursor, modelVersion, rangeVersion),
|
||||
m_initialisationParameterController(this, interactiveRange),
|
||||
m_angleUnitVersion(angleUnitVersion),
|
||||
m_indexFunctionSelectedByCursor(indexFunctionSelectedByCursor)
|
||||
{
|
||||
}
|
||||
|
||||
bool StorageFunctionGraphController::isEmpty() const {
|
||||
if (functionStore()->numberOfActiveFunctions() == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewController * StorageFunctionGraphController::initialisationParameterController() {
|
||||
return &m_initialisationParameterController;
|
||||
}
|
||||
|
||||
void StorageFunctionGraphController::viewWillAppear() {
|
||||
functionGraphView()->setCursorView(cursorView());
|
||||
functionGraphView()->setBannerView(bannerView());
|
||||
functionGraphView()->setAreaHighlight(NAN,NAN);
|
||||
|
||||
if (functionGraphView()->context() == nullptr) {
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
functionGraphView()->setContext(myApp->localContext());
|
||||
}
|
||||
Preferences::AngleUnit newAngleUnitVersion = Preferences::sharedPreferences()->angleUnit();
|
||||
if (*m_angleUnitVersion != newAngleUnitVersion) {
|
||||
*m_angleUnitVersion = newAngleUnitVersion;
|
||||
initCursorParameters();
|
||||
}
|
||||
InteractiveCurveViewController::viewWillAppear();
|
||||
}
|
||||
|
||||
bool StorageFunctionGraphController::handleEnter() {
|
||||
StorageFunction * f = functionStore()->activeFunctionAtIndex(indexFunctionSelectedByCursor());
|
||||
curveParameterController()->setFunction(f);
|
||||
StackViewController * stack = stackController();
|
||||
stack->push(curveParameterController());
|
||||
return true;
|
||||
}
|
||||
|
||||
void StorageFunctionGraphController::selectFunctionWithCursor(int functionIndex) {
|
||||
*m_indexFunctionSelectedByCursor = functionIndex;
|
||||
}
|
||||
|
||||
void StorageFunctionGraphController::reloadBannerView() {
|
||||
if (functionStore()->numberOfActiveFunctions() == 0) {
|
||||
return;
|
||||
}
|
||||
StorageFunction * f = functionStore()->activeFunctionAtIndex(indexFunctionSelectedByCursor());
|
||||
reloadBannerViewForCursorOnFunction(m_cursor, f, functionStore()->symbol());
|
||||
}
|
||||
|
||||
InteractiveCurveViewRangeDelegate::Range StorageFunctionGraphController::computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) {
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
float min = FLT_MAX;
|
||||
float max = -FLT_MAX;
|
||||
float xMin = interactiveCurveViewRange->xMin();
|
||||
float xMax = interactiveCurveViewRange->xMax();
|
||||
if (functionStore()->numberOfActiveFunctions() <= 0) {
|
||||
InteractiveCurveViewRangeDelegate::Range range;
|
||||
range.min = xMin;
|
||||
range.max = xMax;
|
||||
return range;
|
||||
}
|
||||
for (int i=0; i<functionStore()->numberOfActiveFunctions(); i++) {
|
||||
StorageFunction * f = functionStore()->activeFunctionAtIndex(i);
|
||||
float y = 0.0f;
|
||||
float res = curveView()->resolution();
|
||||
/* Scan x-range from the middle to the extrema in order to get balanced
|
||||
* y-range for even functions (y = 1/x). */
|
||||
for (int j = -res/2; j <= res/2; j++) {
|
||||
float x = (xMin+xMax)/2.0+(xMax-xMin)*j/res;
|
||||
y = f->evaluateAtAbscissa(x, myApp->localContext());
|
||||
if (!std::isnan(y) && !std::isinf(y)) {
|
||||
min = min < y ? min : y;
|
||||
max = max > y ? max : y;
|
||||
}
|
||||
}
|
||||
}
|
||||
InteractiveCurveViewRangeDelegate::Range range;
|
||||
range.min = min;
|
||||
range.max = max;
|
||||
return range;
|
||||
}
|
||||
|
||||
void StorageFunctionGraphController::initRangeParameters() {
|
||||
interactiveCurveViewRange()->setDefault();
|
||||
initCursorParameters();
|
||||
selectFunctionWithCursor(0);
|
||||
}
|
||||
|
||||
double StorageFunctionGraphController::defaultCursorAbscissa() {
|
||||
return (interactiveCurveViewRange()->xMin()+interactiveCurveViewRange()->xMax())/2.0f;
|
||||
}
|
||||
|
||||
void StorageFunctionGraphController::initCursorParameters() {
|
||||
double x = defaultCursorAbscissa();
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
int functionIndex = 0;
|
||||
double y = 0;
|
||||
do {
|
||||
StorageFunction * firstFunction = functionStore()->activeFunctionAtIndex(functionIndex++);
|
||||
y = firstFunction->evaluateAtAbscissa(x, myApp->localContext());
|
||||
} while ((std::isnan(y) || std::isinf(y)) && functionIndex < functionStore()->numberOfActiveFunctions());
|
||||
m_cursor->moveTo(x, y);
|
||||
functionIndex = (std::isnan(y) || std::isinf(y)) ? 0 : functionIndex - 1;
|
||||
selectFunctionWithCursor(functionIndex);
|
||||
interactiveCurveViewRange()->panToMakePointVisible(x, y, k_displayTopMarginRatio, k_cursorRightMarginRatio, k_displayBottomMarginRatio, k_cursorLeftMarginRatio);
|
||||
}
|
||||
|
||||
bool StorageFunctionGraphController::moveCursorVertically(int direction) {
|
||||
StorageFunction * actualFunction = functionStore()->activeFunctionAtIndex(indexFunctionSelectedByCursor());
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
double y = actualFunction->evaluateAtAbscissa(m_cursor->x(), myApp->localContext());
|
||||
StorageFunction * nextFunction = actualFunction;
|
||||
double nextY = direction > 0 ? DBL_MAX : -DBL_MAX;
|
||||
for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) {
|
||||
StorageFunction * f = functionStore()->activeFunctionAtIndex(i);
|
||||
double newY = f->evaluateAtAbscissa(m_cursor->x(), myApp->localContext());
|
||||
bool isNextFunction = direction > 0 ? (newY > y && newY < nextY) : (newY < y && newY > nextY);
|
||||
if (isNextFunction) {
|
||||
selectFunctionWithCursor(i);
|
||||
nextY = newY;
|
||||
nextFunction = f;
|
||||
}
|
||||
}
|
||||
if (nextFunction == actualFunction) {
|
||||
return false;
|
||||
}
|
||||
m_cursor->moveTo(m_cursor->x(), nextY);
|
||||
interactiveCurveViewRange()->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
CurveView * StorageFunctionGraphController::curveView() {
|
||||
return functionGraphView();
|
||||
}
|
||||
|
||||
uint32_t StorageFunctionGraphController::modelVersion() {
|
||||
return functionStore()->storeChecksum();
|
||||
}
|
||||
|
||||
uint32_t StorageFunctionGraphController::rangeVersion() {
|
||||
return interactiveCurveViewRange()->rangeChecksum();
|
||||
}
|
||||
|
||||
bool StorageFunctionGraphController::isCursorVisible() {
|
||||
return interactiveCurveViewRange()->isCursorVisible(k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
|
||||
}
|
||||
|
||||
}
|
||||
61
apps/shared/storage_function_graph_controller.h
Normal file
61
apps/shared/storage_function_graph_controller.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef SHARED_STORAGE_FUNCTION_GRAPH_CONTROLLER_H
|
||||
#define SHARED_STORAGE_FUNCTION_GRAPH_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "initialisation_parameter_controller.h"
|
||||
#include "storage_function_banner_delegate.h"
|
||||
#include "interactive_curve_view_controller.h"
|
||||
#include "storage_function_store.h"
|
||||
#include "storage_function_graph_view.h"
|
||||
#include "storage_function_curve_parameter_controller.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class StorageFunctionGraphController : public InteractiveCurveViewController, public StorageFunctionBannerDelegate {
|
||||
public:
|
||||
StorageFunctionGraphController(Responder * parentResponder, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion);
|
||||
bool isEmpty() const override;
|
||||
ViewController * initialisationParameterController() override;
|
||||
void viewWillAppear() override;
|
||||
|
||||
protected:
|
||||
constexpr static float k_cursorTopMarginRatio = 0.068f; // (cursorHeight/2)/graphViewHeight
|
||||
constexpr static float k_cursorBottomMarginRatio = 0.15f; // (cursorHeight/2+bannerHeigh)/graphViewHeight
|
||||
void reloadBannerView() override;
|
||||
bool handleEnter() override;
|
||||
int indexFunctionSelectedByCursor() const {
|
||||
return *m_indexFunctionSelectedByCursor;
|
||||
}
|
||||
virtual void selectFunctionWithCursor(int functionIndex);
|
||||
virtual double defaultCursorAbscissa();
|
||||
private:
|
||||
/* When y auto is ticked, we use a display margin to be ensure that the user
|
||||
* can move the cursor along the curve without panning the window */
|
||||
constexpr static float k_displayTopMarginRatio = 0.09f;
|
||||
constexpr static float k_displayBottomMarginRatio = 0.2f;
|
||||
|
||||
// InteractiveCurveViewController
|
||||
float displayTopMarginRatio() override { return k_displayTopMarginRatio; }
|
||||
float displayBottomMarginRatio() override { return k_displayBottomMarginRatio; }
|
||||
// InteractiveCurveViewRangeDelegate
|
||||
InteractiveCurveViewRangeDelegate::Range computeYRange(InteractiveCurveViewRange * interactiveCurveViewRange) override;
|
||||
|
||||
void initRangeParameters() override;
|
||||
void initCursorParameters() override;
|
||||
bool moveCursorVertically(int direction) override;
|
||||
CurveView * curveView() override;
|
||||
uint32_t modelVersion() override;
|
||||
uint32_t rangeVersion() override;
|
||||
bool isCursorVisible() override;
|
||||
virtual StorageFunctionGraphView * functionGraphView() = 0;
|
||||
virtual View * cursorView() = 0;
|
||||
virtual StorageFunctionStore * functionStore() const = 0;
|
||||
virtual StorageFunctionCurveParameterController * curveParameterController() = 0;
|
||||
InitialisationParameterController m_initialisationParameterController;
|
||||
Poincare::Preferences::AngleUnit * m_angleUnitVersion;
|
||||
int * m_indexFunctionSelectedByCursor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
90
apps/shared/storage_function_graph_view.cpp
Normal file
90
apps/shared/storage_function_graph_view.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "storage_function_graph_view.h"
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
StorageFunctionGraphView::StorageFunctionGraphView(InteractiveCurveViewRange * graphRange,
|
||||
CurveViewCursor * cursor, BannerView * bannerView, View * cursorView) :
|
||||
CurveView(graphRange, cursor, bannerView, cursorView),
|
||||
m_selectedFunction(nullptr),
|
||||
m_highlightedStart(NAN),
|
||||
m_highlightedEnd(NAN),
|
||||
m_shouldColorHighlighted(false),
|
||||
m_xLabels{},
|
||||
m_yLabels{},
|
||||
m_context(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void StorageFunctionGraphView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
ctx->fillRect(rect, KDColorWhite);
|
||||
drawGrid(ctx, rect);
|
||||
drawAxes(ctx, rect, Axis::Horizontal);
|
||||
drawAxes(ctx, rect, Axis::Vertical);
|
||||
drawLabels(ctx, rect, Axis::Horizontal, true);
|
||||
drawLabels(ctx, rect, Axis::Vertical, true);
|
||||
}
|
||||
|
||||
void StorageFunctionGraphView::setContext(Context * context) {
|
||||
m_context = context;
|
||||
}
|
||||
|
||||
Context * StorageFunctionGraphView::context() const {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
void StorageFunctionGraphView::selectFunction(StorageFunction * function) {
|
||||
if (m_selectedFunction != function) {
|
||||
m_selectedFunction = function;
|
||||
reloadBetweenBounds(m_highlightedStart, m_highlightedEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void StorageFunctionGraphView::setAreaHighlight(float start, float end) {
|
||||
if (m_highlightedStart != start || m_highlightedEnd != end) {
|
||||
float dirtyStart = start > m_highlightedStart ? m_highlightedStart : start;
|
||||
float dirtyEnd = start > m_highlightedStart ? start : m_highlightedStart;
|
||||
reloadBetweenBounds(dirtyStart, dirtyEnd);
|
||||
dirtyStart = end > m_highlightedEnd ? m_highlightedEnd : end;
|
||||
dirtyEnd = end > m_highlightedEnd ? end : m_highlightedEnd;
|
||||
reloadBetweenBounds(dirtyStart, dirtyEnd);
|
||||
if (std::isnan(start) || std::isnan(end)) {
|
||||
reloadBetweenBounds(m_highlightedStart, m_highlightedEnd);
|
||||
}
|
||||
if (std::isnan(m_highlightedStart) || std::isnan(m_highlightedEnd)) {
|
||||
reloadBetweenBounds(start, end);
|
||||
}
|
||||
m_highlightedStart = start;
|
||||
m_highlightedEnd = end;
|
||||
}
|
||||
}
|
||||
|
||||
void StorageFunctionGraphView::setAreaHighlightColor(bool highlightColor) {
|
||||
if (m_shouldColorHighlighted != highlightColor) {
|
||||
reloadBetweenBounds(m_highlightedStart, m_highlightedEnd);
|
||||
m_shouldColorHighlighted = highlightColor;
|
||||
}
|
||||
}
|
||||
|
||||
char * StorageFunctionGraphView::label(Axis axis, int index) const {
|
||||
return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]);
|
||||
}
|
||||
|
||||
void StorageFunctionGraphView::reloadBetweenBounds(float start, float end) {
|
||||
if (start == end) {
|
||||
return;
|
||||
}
|
||||
float pixelLowerBound = floatToPixel(Axis::Horizontal, start)-2.0;
|
||||
float pixelUpperBound = floatToPixel(Axis::Horizontal, end)+4.0;
|
||||
/* We exclude the banner frame from the dirty zone to avoid unnecessary
|
||||
* redrawing */
|
||||
KDRect dirtyZone(KDRect(pixelLowerBound, 0, pixelUpperBound-pixelLowerBound,
|
||||
bounds().height()-m_bannerView->bounds().height()));
|
||||
markRectAsDirty(dirtyZone);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
37
apps/shared/storage_function_graph_view.h
Normal file
37
apps/shared/storage_function_graph_view.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef SHARED_STORAGE_FUNCTION_GRAPH_VIEW_H
|
||||
#define SHARED_STORAGE_FUNCTION_GRAPH_VIEW_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "curve_view.h"
|
||||
#include "storage_function.h"
|
||||
#include "../constant.h"
|
||||
#include "interactive_curve_view_range.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class StorageFunctionGraphView : public CurveView {
|
||||
public:
|
||||
StorageFunctionGraphView(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor,
|
||||
BannerView * bannerView, View * cursorView);
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
void setContext(Poincare::Context * context);
|
||||
Poincare::Context * context() const;
|
||||
void selectFunction(StorageFunction * function);
|
||||
void setAreaHighlight(float start, float end);
|
||||
virtual void setAreaHighlightColor(bool highlightColor);
|
||||
protected:
|
||||
void reloadBetweenBounds(float start, float end);
|
||||
StorageFunction * m_selectedFunction;
|
||||
float m_highlightedStart;
|
||||
float m_highlightedEnd;
|
||||
bool m_shouldColorHighlighted;
|
||||
private:
|
||||
char * label(Axis axis, int index) const override;
|
||||
char m_xLabels[k_maxNumberOfXLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)];
|
||||
char m_yLabels[k_maxNumberOfYLabels][Poincare::PrintFloat::bufferSizeForFloatsWithPrecision(Constant::ShortNumberOfSignificantDigits)];
|
||||
Poincare::Context * m_context;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
317
apps/shared/storage_sum_graph_controller.cpp
Normal file
317
apps/shared/storage_sum_graph_controller.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
#include "storage_sum_graph_controller.h"
|
||||
#include "../apps_container.h"
|
||||
#include <poincare_layouts.h>
|
||||
#include <poincare/layout_helper.h>
|
||||
#include "poincare_helpers.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace Poincare;
|
||||
|
||||
namespace Shared {
|
||||
|
||||
StorageSumGraphController::StorageSumGraphController(Responder * parentResponder, StorageFunctionGraphView * graphView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, char sumSymbol) :
|
||||
SimpleInteractiveCurveViewController(parentResponder, range, graphView, cursor),
|
||||
m_step(Step::FirstParameter),
|
||||
m_startSum(NAN),
|
||||
m_endSum(NAN),
|
||||
m_function(nullptr),
|
||||
m_graphRange(range),
|
||||
m_graphView(graphView),
|
||||
m_legendView(this, sumSymbol),
|
||||
m_cursorView()
|
||||
{
|
||||
}
|
||||
|
||||
void StorageSumGraphController::viewWillAppear() {
|
||||
m_graphRange->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
|
||||
m_graphView->setBannerView(&m_legendView);
|
||||
m_graphView->setCursorView(&m_cursorView);
|
||||
m_graphView->setOkView(nullptr);
|
||||
m_graphView->selectMainView(true);
|
||||
m_graphView->setAreaHighlightColor(false);
|
||||
m_graphView->setAreaHighlight(NAN, NAN);
|
||||
m_graphView->reload();
|
||||
|
||||
m_startSum = m_cursor->x();
|
||||
m_endSum = NAN;
|
||||
m_step = Step::FirstParameter;
|
||||
m_legendView.setLegendMessage(legendMessageAtStep(Step::FirstParameter), Step::FirstParameter);
|
||||
m_legendView.setEditableZone(m_startSum);
|
||||
m_legendView.setSumSymbol(m_step);
|
||||
}
|
||||
|
||||
|
||||
void StorageSumGraphController::didEnterResponderChain(Responder * previousFirstResponder) {
|
||||
app()->setFirstResponder(m_legendView.textField());
|
||||
}
|
||||
|
||||
bool StorageSumGraphController::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Plus || event == Ion::Events::Minus) {
|
||||
return handleZoom(event);
|
||||
}
|
||||
if ((int)m_step > 1 && event != Ion::Events::OK && event != Ion::Events::EXE && event != Ion::Events::Back) {
|
||||
return false;
|
||||
}
|
||||
if (event == Ion::Events::Left && !m_legendView.textField()->isEditing()) {
|
||||
if ((int)m_step > 0 && m_startSum >= m_cursor->x()) {
|
||||
return false;
|
||||
}
|
||||
if (moveCursorHorizontallyToPosition(cursorNextStep(m_cursor->x(), -1))) {
|
||||
m_graphView->reload();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (event == Ion::Events::Right && !m_legendView.textField()->isEditing()) {
|
||||
if (moveCursorHorizontallyToPosition(cursorNextStep(m_cursor->x(), 1))) {
|
||||
m_graphView->reload();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
|
||||
return handleEnter();
|
||||
}
|
||||
if (event == Ion::Events::Back && (int)m_step > 0) {
|
||||
m_step = (Step)((int)m_step-1);
|
||||
m_legendView.setLegendMessage(legendMessageAtStep(m_step), m_step);
|
||||
if (m_step == Step::SecondParameter) {
|
||||
app()->setFirstResponder(m_legendView.textField());
|
||||
m_graphView->setAreaHighlightColor(false);
|
||||
m_graphView->setCursorView(&m_cursorView);
|
||||
m_endSum = m_cursor->x();
|
||||
m_legendView.setEditableZone(m_endSum);
|
||||
m_legendView.setSumSymbol(m_step, m_startSum);
|
||||
}
|
||||
if (m_step == Step::FirstParameter) {
|
||||
m_graphView->setAreaHighlight(NAN,NAN);
|
||||
moveCursorHorizontallyToPosition(m_startSum);
|
||||
m_legendView.setLegendMessage(legendMessageAtStep(m_step), m_step);
|
||||
m_legendView.setEditableZone(m_startSum);
|
||||
m_legendView.setSumSymbol(m_step);
|
||||
m_graphView->reload();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StorageSumGraphController::moveCursorHorizontallyToPosition(double x) {
|
||||
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
|
||||
if (m_function == nullptr) {
|
||||
return false;
|
||||
}
|
||||
double y = m_function->evaluateAtAbscissa(x, myApp->localContext());
|
||||
m_cursor->moveTo(x, y);
|
||||
if (m_step == Step::FirstParameter) {
|
||||
m_startSum = m_cursor->x();
|
||||
m_legendView.setEditableZone(m_startSum);
|
||||
}
|
||||
if (m_step == Step::SecondParameter) {
|
||||
m_graphView->setAreaHighlight(m_startSum, m_cursor->x());
|
||||
m_endSum = m_cursor->x();
|
||||
m_legendView.setEditableZone(m_endSum);
|
||||
}
|
||||
m_graphRange->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StorageSumGraphController::setFunction(StorageFunction * function) {
|
||||
m_graphView->selectFunction(function);
|
||||
m_function = function;
|
||||
}
|
||||
|
||||
bool StorageSumGraphController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
|
||||
AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container();
|
||||
Context * globalContext = appsContainer->globalContext();
|
||||
double floatBody = PoincareHelpers::ApproximateToScalar<double>(text, *globalContext);
|
||||
if (std::isnan(floatBody) || std::isinf(floatBody)) {
|
||||
app()->displayWarning(I18n::Message::UndefinedValue);
|
||||
return false;
|
||||
}
|
||||
if (m_step == Step::SecondParameter && floatBody < m_startSum) {
|
||||
app()->displayWarning(I18n::Message::ForbiddenValue);
|
||||
return false;
|
||||
}
|
||||
if (moveCursorHorizontallyToPosition(floatBody)) {
|
||||
handleEnter();
|
||||
m_graphView->reload();
|
||||
return true;
|
||||
}
|
||||
app()->displayWarning(I18n::Message::ForbiddenValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StorageSumGraphController::textFieldDidAbortEditing(TextField * textField) {
|
||||
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits)];
|
||||
double parameter = NAN;
|
||||
switch(m_step) {
|
||||
case Step::FirstParameter:
|
||||
parameter = m_startSum;
|
||||
break;
|
||||
case Step::SecondParameter:
|
||||
parameter = m_endSum;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
PrintFloat::convertFloatToText<double>(parameter, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal);
|
||||
textField->setText(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StorageSumGraphController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
if ((event == Ion::Events::OK || event == Ion::Events::EXE) && !textField->isEditing()) {
|
||||
return handleEnter();
|
||||
}
|
||||
if (m_step == Step::Result) {
|
||||
return handleEvent(event);
|
||||
}
|
||||
return TextFieldDelegate::textFieldDidReceiveEvent(textField, event);
|
||||
}
|
||||
|
||||
bool StorageSumGraphController::handleEnter() {
|
||||
if (m_step == Step::Result) {
|
||||
StackViewController * stack = (StackViewController *)parentResponder();
|
||||
stack->pop();
|
||||
return true;
|
||||
}
|
||||
if (m_step == Step::FirstParameter) {
|
||||
m_step = Step::SecondParameter;
|
||||
m_graphView->setAreaHighlight(m_startSum,m_startSum);
|
||||
m_endSum = m_cursor->x();
|
||||
m_legendView.setEditableZone(m_endSum);
|
||||
m_legendView.setSumSymbol(m_step, m_startSum);
|
||||
m_legendView.setLegendMessage(legendMessageAtStep(m_step), m_step);
|
||||
return true;
|
||||
}
|
||||
m_step = (Step)((int)m_step+1);
|
||||
TextFieldDelegateApp * myApp = static_cast<TextFieldDelegateApp *>(app());
|
||||
double sum = m_function->sumBetweenBounds(m_startSum, m_endSum, myApp->localContext());
|
||||
m_legendView.setSumSymbol(m_step, m_startSum, m_endSum, sum, createFunctionLayout(m_function));
|
||||
m_legendView.setLegendMessage(I18n::Message::Default, m_step);
|
||||
m_graphView->setAreaHighlightColor(true);
|
||||
m_graphView->setCursorView(nullptr);
|
||||
myApp->setFirstResponder(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Legend View */
|
||||
|
||||
StorageSumGraphController::LegendView::LegendView(StorageSumGraphController * controller, char sumSymbol) :
|
||||
m_sum(0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle),
|
||||
m_sumLayout(),
|
||||
m_legend(KDText::FontSize::Small, I18n::Message::Default, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle),
|
||||
m_editableZone(controller, m_draftText, m_draftText, TextField::maxBufferSize(), controller, false, KDText::FontSize::Small, 0.0f, 0.5f, KDColorBlack, Palette::GreyMiddle),
|
||||
m_sumSymbol(sumSymbol)
|
||||
{
|
||||
m_draftText[0] = 0;
|
||||
}
|
||||
|
||||
void StorageSumGraphController::LegendView::drawRect(KDContext * ctx, KDRect rect) const {
|
||||
ctx->fillRect(bounds(), Palette::GreyMiddle);
|
||||
}
|
||||
|
||||
KDSize StorageSumGraphController::LegendView::minimalSizeForOptimalDisplay() const {
|
||||
return KDSize(0, k_legendHeight);
|
||||
}
|
||||
|
||||
void StorageSumGraphController::LegendView::setLegendMessage(I18n::Message message, Step step) {
|
||||
m_legend.setMessage(message);
|
||||
layoutSubviews(step);
|
||||
}
|
||||
|
||||
void StorageSumGraphController::LegendView::setEditableZone(double d) {
|
||||
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits)];
|
||||
PrintFloat::convertFloatToText<double>(d, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal);
|
||||
m_editableZone.setText(buffer);
|
||||
}
|
||||
|
||||
void StorageSumGraphController::LegendView::setSumSymbol(Step step, double start, double end, double result, Layout functionLayout) {
|
||||
assert(step == Step::Result || functionLayout.isUninitialized());
|
||||
const char sigma[] = {' ', m_sumSymbol};
|
||||
if (step == Step::FirstParameter) {
|
||||
m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma));
|
||||
} else if (step == Step::SecondParameter) {
|
||||
char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits)];
|
||||
PrintFloat::convertFloatToText<double>(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal);
|
||||
m_sumLayout = CondensedSumLayout(
|
||||
LayoutHelper::String(sigma, sizeof(sigma)),
|
||||
LayoutHelper::String(buffer, strlen(buffer), KDText::FontSize::Small),
|
||||
EmptyLayout(EmptyLayoutNode::Color::Yellow, false, KDText::FontSize::Small, false));
|
||||
} else {
|
||||
m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma));
|
||||
char buffer[2+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)];
|
||||
PrintFloat::convertFloatToText<double>(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal);
|
||||
Layout start = LayoutHelper::String(buffer, strlen(buffer), KDText::FontSize::Small);
|
||||
PrintFloat::convertFloatToText<double>(end, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal);
|
||||
Layout end = LayoutHelper::String(buffer, strlen(buffer), KDText::FontSize::Small);
|
||||
m_sumLayout = CondensedSumLayout(
|
||||
LayoutHelper::String(sigma, sizeof(sigma)),
|
||||
start,
|
||||
end);
|
||||
strlcpy(buffer, "= ", 3);
|
||||
PoincareHelpers::ConvertFloatToText<double>(result, buffer+2, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits);
|
||||
m_sumLayout = HorizontalLayout(
|
||||
m_sumLayout,
|
||||
functionLayout,
|
||||
LayoutHelper::String(buffer, strlen(buffer), KDText::FontSize::Small));
|
||||
}
|
||||
m_sum.setLayout(m_sumLayout);
|
||||
if (step == Step::Result) {
|
||||
m_sum.setAlignment(0.5f, 0.5f);
|
||||
} else {
|
||||
m_sum.setAlignment(0.0f, 0.5f);
|
||||
}
|
||||
layoutSubviews(step);
|
||||
}
|
||||
|
||||
int StorageSumGraphController::LegendView::numberOfSubviews() const {
|
||||
return 3;
|
||||
}
|
||||
|
||||
View * StorageSumGraphController::LegendView::subviewAtIndex(int index) {
|
||||
assert(index >= 0 && index < 3);
|
||||
if (index == 0) {
|
||||
return &m_sum;
|
||||
}
|
||||
if (index == 1) {
|
||||
return &m_editableZone;
|
||||
}
|
||||
return &m_legend;
|
||||
}
|
||||
|
||||
void StorageSumGraphController::LegendView::layoutSubviews() {
|
||||
layoutSubviews(Step::FirstParameter);
|
||||
}
|
||||
|
||||
void StorageSumGraphController::LegendView::layoutSubviews(Step step) {
|
||||
KDCoordinate width = bounds().width();
|
||||
KDCoordinate heigth = bounds().height();
|
||||
KDSize legendSize = m_legend.minimalSizeForOptimalDisplay();
|
||||
|
||||
if (legendSize.width() > 0) {
|
||||
m_sum.setFrame(KDRect(0, k_symbolHeightMargin, width-legendSize.width(), m_sum.minimalSizeForOptimalDisplay().height()));
|
||||
m_legend.setFrame(KDRect(width-legendSize.width(), 0, legendSize.width(), heigth));
|
||||
} else {
|
||||
m_sum.setFrame(bounds());
|
||||
m_legend.setFrame(KDRectZero);
|
||||
}
|
||||
|
||||
KDSize largeCharSize = KDText::charSize();
|
||||
switch(step) {
|
||||
case Step::FirstParameter:
|
||||
m_editableZone.setFrame(KDRect(2*largeCharSize.width(), k_symbolHeightMargin+k_sigmaHeight/2, k_editableZoneWidth, k_editableZoneHeight));
|
||||
return;
|
||||
case Step::SecondParameter:
|
||||
m_editableZone.setFrame(KDRect(2*largeCharSize.width(), k_symbolHeightMargin+k_sigmaHeight/2-k_editableZoneHeight, k_editableZoneWidth, k_editableZoneHeight));
|
||||
return;
|
||||
default:
|
||||
m_editableZone.setFrame(KDRectZero);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
86
apps/shared/storage_sum_graph_controller.h
Normal file
86
apps/shared/storage_sum_graph_controller.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef SHARED_SUM_GRAPH_CONTROLLER_H
|
||||
#define SHARED_SUM_GRAPH_CONTROLLER_H
|
||||
|
||||
#include <escher.h>
|
||||
#include "storage_function_graph_view.h"
|
||||
#include "interactive_curve_view_range.h"
|
||||
#include "vertical_cursor_view.h"
|
||||
#include "curve_view_cursor.h"
|
||||
#include "simple_interactive_curve_view_controller.h"
|
||||
#include "storage_function.h"
|
||||
#include "text_field_delegate.h"
|
||||
|
||||
namespace Shared {
|
||||
|
||||
class StorageSumGraphController : public SimpleInteractiveCurveViewController, public TextFieldDelegate {
|
||||
public:
|
||||
StorageSumGraphController(Responder * parentResponder, StorageFunctionGraphView * curveView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, char sumSymbol);
|
||||
void viewWillAppear() override;
|
||||
void didEnterResponderChain(Responder * previousFirstResponder) override;
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void setFunction(StorageFunction * function);
|
||||
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
|
||||
bool textFieldDidAbortEditing(TextField * textField) override;
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
protected:
|
||||
virtual bool moveCursorHorizontallyToPosition(double position);
|
||||
enum class Step {
|
||||
FirstParameter = 0,
|
||||
SecondParameter = 1,
|
||||
Result = 2
|
||||
};
|
||||
Step m_step;
|
||||
double m_startSum;
|
||||
double m_endSum;
|
||||
StorageFunction * m_function;
|
||||
InteractiveCurveViewRange * m_graphRange;
|
||||
private:
|
||||
constexpr static float k_cursorTopMarginRatio = 0.06f; // (cursorHeight/2)/graphViewHeight
|
||||
constexpr static float k_cursorBottomMarginRatio = 0.28f; // (cursorHeight/2+bannerHeigh)/graphViewHeight
|
||||
virtual I18n::Message legendMessageAtStep(Step step) = 0;
|
||||
virtual double cursorNextStep(double position, int direction) = 0;
|
||||
virtual Poincare::Layout createFunctionLayout(StorageFunction * function) = 0;
|
||||
Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override { return m_graphRange; }
|
||||
Shared::CurveView * curveView() override { return m_graphView; }
|
||||
TextFieldDelegateApp * textFieldDelegateApp() override {
|
||||
return static_cast<TextFieldDelegateApp *>(app());
|
||||
}
|
||||
bool handleEnter() override;
|
||||
class LegendView : public View {
|
||||
public:
|
||||
LegendView(StorageSumGraphController * controller, char sumSymbol);
|
||||
LegendView(const LegendView& other) = delete;
|
||||
LegendView(LegendView&& other) = delete;
|
||||
LegendView& operator=(const LegendView& other) = delete;
|
||||
LegendView& operator=(LegendView&& other) = delete;
|
||||
TextField * textField() { return &m_editableZone; }
|
||||
KDSize minimalSizeForOptimalDisplay() const override;
|
||||
void drawRect(KDContext * ctx, KDRect rect) const override;
|
||||
void setLegendMessage(I18n::Message message, Step step);
|
||||
void setEditableZone(double d);
|
||||
void setSumSymbol(Step step, double start = NAN, double end = NAN, double result = NAN, Poincare::Layout sequenceName = Poincare::Layout());
|
||||
private:
|
||||
constexpr static KDCoordinate k_legendHeight = 35;
|
||||
constexpr static KDCoordinate k_editableZoneWidth = 12*KDText::charSize(KDText::FontSize::Small).width();
|
||||
constexpr static KDCoordinate k_editableZoneHeight = KDText::charSize(KDText::FontSize::Small).height();
|
||||
constexpr static KDCoordinate k_symbolHeightMargin = 8;
|
||||
constexpr static KDCoordinate k_sigmaHeight = 18;
|
||||
int numberOfSubviews() const override;
|
||||
View * subviewAtIndex(int index) override;
|
||||
void layoutSubviews() override;
|
||||
void layoutSubviews(Step step);
|
||||
ExpressionView m_sum;
|
||||
Poincare::Layout m_sumLayout;
|
||||
MessageTextView m_legend;
|
||||
TextField m_editableZone;
|
||||
char m_draftText[TextField::maxBufferSize()];
|
||||
char m_sumSymbol;
|
||||
};
|
||||
StorageFunctionGraphView * m_graphView;
|
||||
LegendView m_legendView;
|
||||
VerticalCursorView m_cursorView;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user