diff --git a/apps/sequence/Makefile b/apps/sequence/Makefile index fb2b09a4e..2c47f04dd 100644 --- a/apps/sequence/Makefile +++ b/apps/sequence/Makefile @@ -10,7 +10,6 @@ app_objs += $(addprefix apps/sequence/,\ graph/graph_controller.o\ graph/graph_view.o\ graph/term_sum_controller.o\ - graph/vertical_cursor_view.o\ list/list_controller.o\ list/list_parameter_controller.o\ list/sequence_toolbox.o\ diff --git a/apps/sequence/graph/graph_controller.cpp b/apps/sequence/graph/graph_controller.cpp index 78384fe3e..48d9aec2b 100644 --- a/apps/sequence/graph/graph_controller.cpp +++ b/apps/sequence/graph/graph_controller.cpp @@ -18,14 +18,6 @@ GraphController::GraphController(Responder * parentResponder, SequenceStore * se m_graphRange->setDelegate(this); } -void GraphController::viewWillAppear() { - m_view.setVerticalCursor(false); - m_view.setCursorView(&m_cursorView); - m_view.setBannerView(&m_bannerView); - m_view.setHighlight(-1.0f, -1.0f); - FunctionGraphController::viewWillAppear(); -} - I18n::Message GraphController::emptyMessage() { if (m_sequenceStore->numberOfDefinedFunctions() == 0) { return I18n::Message::NoSequence; @@ -42,7 +34,7 @@ BannerView * GraphController::bannerView() { } bool GraphController::handleEnter() { - m_termSumController.setSequence(m_sequenceStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor)); + m_termSumController.setFunction(m_sequenceStore->activeFunctionAtIndex(m_indexFunctionSelectedByCursor)); return FunctionGraphController::handleEnter(); } diff --git a/apps/sequence/graph/graph_controller.h b/apps/sequence/graph/graph_controller.h index 650c83f01..84af201d1 100644 --- a/apps/sequence/graph/graph_controller.h +++ b/apps/sequence/graph/graph_controller.h @@ -14,7 +14,6 @@ namespace Sequence { class GraphController : public Shared::FunctionGraphController { public: GraphController(Responder * parentResponder, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Expression::AngleUnit * angleUnitVersion, ButtonRowController * header); - void viewWillAppear() override; I18n::Message emptyMessage() override; TermSumController * termSumController(); private: diff --git a/apps/sequence/graph/graph_view.cpp b/apps/sequence/graph/graph_view.cpp index a000de9b1..4e9cecb84 100644 --- a/apps/sequence/graph/graph_view.cpp +++ b/apps/sequence/graph/graph_view.cpp @@ -8,11 +8,7 @@ namespace Sequence { GraphView::GraphView(SequenceStore * sequenceStore, InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, BannerView * bannerView, View * cursorView) : FunctionGraphView(graphRange, cursor, bannerView, cursorView), - m_sequenceStore(sequenceStore), - m_verticalCursor(false), - m_highlightedDotStart(-1), - m_highlightedDotEnd(-1), - m_shouldColorHighlighted(false) + m_sequenceStore(sequenceStore) { } @@ -33,7 +29,7 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { continue; } drawDot(ctx, rect, x, y, s->color()); - if (x >= m_highlightedDotStart && x <= m_highlightedDotEnd && s == m_selectedFunction) { + if (x >= m_highlightedStart && x <= m_highlightedEnd && s == m_selectedFunction) { KDColor color = m_shouldColorHighlighted ? s->color() : KDColorBlack; if (y >= 0.0f) { drawSegment(ctx, rect, Axis::Vertical, x, 0.0f, y, color, 1); @@ -45,49 +41,8 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { } } -void GraphView::setVerticalCursor(bool verticalCursor) { - m_verticalCursor = verticalCursor; -} - -void GraphView::reload() { - FunctionGraphView::reload(); - if (m_highlightedDotStart >= 0) { - float pixelLowerBound = floatToPixel(Axis::Horizontal, m_highlightedDotStart)-1; - float pixelUpperBound = floatToPixel(Axis::Horizontal, m_highlightedDotEnd)+2; - /* 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); - } -} - -void GraphView::setHighlight(int start, int end) { - if (m_highlightedDotStart != start || m_highlightedDotEnd != end) { - reload(); - m_highlightedDotStart = start; - m_highlightedDotEnd = end; - reload(); - } -} - -void GraphView::setHighlightColor(bool highlightColor) { - if (m_shouldColorHighlighted != highlightColor) { - reload(); - m_shouldColorHighlighted = highlightColor; - reload(); - } -} - float GraphView::samplingRatio() const { return 5.0f; } -KDSize GraphView::cursorSize() { - if (m_verticalCursor) { - return KDSize(1, 0); - } - return CurveView::cursorSize(); -} - } diff --git a/apps/sequence/graph/graph_view.h b/apps/sequence/graph/graph_view.h index 722466205..97d35ea99 100644 --- a/apps/sequence/graph/graph_view.h +++ b/apps/sequence/graph/graph_view.h @@ -11,18 +11,9 @@ public: GraphView(SequenceStore * sequenceStore, Shared::InteractiveCurveViewRange * graphRange, Shared::CurveViewCursor * cursor, Shared::BannerView * bannerView, View * cursorView); void drawRect(KDContext * ctx, KDRect rect) const override; - void setVerticalCursor(bool verticalCursor); - void reload() override; - void setHighlight(int start, int end); - void setHighlightColor(bool highlightColor); private: float samplingRatio() const override; - KDSize cursorSize() override; SequenceStore * m_sequenceStore; - bool m_verticalCursor; - int m_highlightedDotStart; - int m_highlightedDotEnd; - bool m_shouldColorHighlighted; }; } diff --git a/apps/sequence/graph/term_sum_controller.cpp b/apps/sequence/graph/term_sum_controller.cpp index 65fa6e669..e367a7db1 100644 --- a/apps/sequence/graph/term_sum_controller.cpp +++ b/apps/sequence/graph/term_sum_controller.cpp @@ -1,9 +1,6 @@ #include "term_sum_controller.h" #include "../../shared/text_field_delegate.h" -#include "../../../poincare/src/layout/baseline_relative_layout.h" -#include "../../../poincare/src/layout/condensed_sum_layout.h" -#include "../../../poincare/src/layout/string_layout.h" -#include "../../../poincare/src/layout/horizontal_layout.h" +#include "../app.h" #include #include @@ -15,16 +12,7 @@ using namespace Poincare; namespace Sequence { TermSumController::TermSumController(Responder * parentResponder, GraphView * graphView, CurveViewRange * graphRange, CurveViewCursor * cursor) : - SimpleInteractiveCurveViewController(parentResponder, graphRange, graphView, cursor), - m_graphView(graphView), - m_legendView(), - m_graphRange(graphRange), - m_sequence(nullptr), - m_cursorView(), - m_bufferCursorPosition(0), - m_step(0), - m_startSum(-1), - m_endSum(-1) + SumGraphController(parentResponder, graphView, graphRange, cursor, Ion::Charset::CapitalSigma) { } @@ -32,236 +20,32 @@ const char * TermSumController::title() { return I18n::translate(I18n::Message::TermSum); } -void TermSumController::viewWillAppear() { - m_graphRange->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); - m_graphView->setVerticalCursor(true); - m_graphView->setBannerView(&m_legendView); - m_graphView->setCursorView(&m_cursorView); - m_graphView->setOkView(nullptr); - m_graphView->selectMainView(true); - m_graphView->setHighlightColor(false); - m_graphView->setHighlight(-1.0f,-1.0f); - m_graphView->reload(); - - m_bufferCursorPosition = 0; - m_startSum = -1; - m_endSum = -1; - m_step = 0; - m_legendView.setLegendMessage(I18n::Message::SelectFirstTerm); - m_legendView.setSumSubscript(m_cursor->x()); -} - -bool TermSumController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::Plus || event == Ion::Events::Minus) { - return handleZoom(event); - } - if (m_step > 1 && event != Ion::Events::OK && event != Ion::Events::EXE && event != Ion::Events::Back) { +bool TermSumController::moveCursorHorizontallyToPosition(double position) { + if (position < 0.0) { return false; } - if (event == Ion::Events::Left) { - if (m_step > 0 && m_startSum >= m_cursor->x()) { - return false; - } - if (moveCursorHorizontallyToPosition(std::round(m_cursor->x()-1.0))) { - m_graphView->reload(); - return true; - } - return false; - } - if (event == Ion::Events::Right) { - if (moveCursorHorizontallyToPosition(std::round(m_cursor->x()+1.0))) { - m_graphView->reload(); - return true; - } - return false; - } - if (event.hasText() && event.text()[0] >= '0' && event.text()[0] <= '9') { - m_bufferCursorPosition = 10*m_bufferCursorPosition + event.text()[0]-'0'; - if (m_step > 0 && m_bufferCursorPosition < m_startSum) { - return false; - } - if (moveCursorHorizontallyToPosition(m_bufferCursorPosition)) { - m_graphView->reload(); - return true; - } - return false; - } - if (event == Ion::Events::OK || event == Ion::Events::EXE) { - if (m_step == 2) { - StackViewController * stack = (StackViewController *)parentResponder(); - stack->pop(); - return true; - } - if (m_step == 0) { - m_step++; - m_bufferCursorPosition = 0; - m_startSum = m_cursor->x(); - m_graphView->setHighlight(m_startSum,m_startSum); - m_legendView.setSumSuperscript(m_startSum, m_cursor->x()); - m_legendView.setLegendMessage(I18n::Message::SelectLastTerm); - return true; - } - m_step++; - m_endSum = m_cursor->x(); - TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app(); - double sum = m_sequence->sumOfTermsBetweenAbscissa(m_startSum, m_endSum, myApp->localContext()); - m_legendView.setSumResult(m_sequence->name(), sum); - m_legendView.setLegendMessage(I18n::Message::Default); - m_graphView->setHighlightColor(true); - m_graphView->setCursorView(nullptr); - m_graphView->reload(); - return true; - } - if (event == Ion::Events::Back && m_step > 0) { - m_step--; - m_bufferCursorPosition = 0; - if (m_step == 1) { - m_legendView.setLegendMessage(I18n::Message::SelectLastTerm); - m_graphView->setHighlightColor(false); - m_graphView->setCursorView(&m_cursorView); - m_graphView->reload(); - m_legendView.setSumSuperscript(m_startSum, m_cursor->x()); - } - if (m_step == 0) { - m_graphView->setHighlight(-1,-1); - moveCursorHorizontallyToPosition(m_startSum); - m_legendView.setLegendMessage(I18n::Message::SelectFirstTerm); - m_legendView.setSumSubscript(m_startSum); - m_graphView->reload(); - } - return true; - } - return false; + return SumGraphController::moveCursorHorizontallyToPosition(std::round(position)); } -bool TermSumController::moveCursorHorizontallyToPosition(int position) { - if (position < 0) { - return false; - } - double x = position; - TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app(); - if (m_sequence == nullptr) { - return false; - } - double y = m_sequence->evaluateAtAbscissa(x, myApp->localContext()); - m_cursor->moveTo(x, y); - if (m_step == 0) { - m_legendView.setSumSubscript(m_cursor->x()); - } - if (m_step == 1) { - m_graphView->setHighlight(m_startSum, m_cursor->x()); - m_legendView.setSumSuperscript(m_startSum, m_cursor->x()); - } - m_graphRange->panToMakePointVisible(x, y, k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); - return true; +double TermSumController::computeSum(double start, double end) { + App * myApp = static_cast(app()); + return static_cast(m_function)->sumOfTermsBetweenAbscissa(m_startSum, m_endSum, myApp->localContext()); } -void TermSumController::setSequence(Sequence * sequence) { - m_graphView->selectFunction(sequence); - m_sequence = sequence; -} - -CurveView * TermSumController::curveView() { - return m_graphView; -} - -InteractiveCurveViewRange * TermSumController::interactiveCurveViewRange() { - return m_graphRange; -} - -/* Legend View */ - -TermSumController::LegendView::LegendView() : - m_sum(0.0f, 0.5f, KDColorBlack, Palette::GreyBright), - m_sumLayout(nullptr), - m_legend(KDText::FontSize::Small, I18n::Message::Default, 0.0f, 0.5f, KDColorBlack, Palette::GreyBright) -{ -} - -TermSumController::LegendView::~LegendView() { - if (m_sumLayout != nullptr) { - delete m_sumLayout; - m_sumLayout = nullptr; +I18n::Message TermSumController::legendMessageAtStep(Step step) { + switch(step) { + case Step::FirstParameter: + return I18n::Message::SelectFirstTerm; + case Step::SecondParameter: + return I18n::Message::SelectLastTerm; + default: + return I18n::Message::Default; } } -void TermSumController::LegendView::drawRect(KDContext * ctx, KDRect rect) const { - ctx->fillRect(KDRect(0, bounds().height() - k_legendHeight, bounds().width(), k_legendHeight), Palette::GreyMiddle); -} - -KDSize TermSumController::LegendView::minimalSizeForOptimalDisplay() const { - return KDSize(0, k_legendHeight); -} - -void TermSumController::LegendView::setLegendMessage(I18n::Message message) { - m_legend.setMessage(message); - layoutSubviews(); -} - -void TermSumController::LegendView::setSumSubscript(float start) { - if (m_sumLayout) { - delete m_sumLayout; - m_sumLayout = nullptr; - } - const char sigma[] = {' ',Ion::Charset::CapitalSigma}; - char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; - Complex::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); - m_sumLayout = new CondensedSumLayout(new StringLayout(sigma, sizeof(sigma)), new StringLayout(buffer, strlen(buffer), KDText::FontSize::Small), nullptr); - m_sum.setExpression(m_sumLayout); - m_sum.setAlignment(0.0f, 0.5f); -} - -void TermSumController::LegendView::setSumSuperscript(float start, float end) { - if (m_sumLayout) { - delete m_sumLayout; - m_sumLayout = nullptr; - } - const char sigma[] = {' ', Ion::Charset::CapitalSigma}; - char bufferStart[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; - Complex::convertFloatToText(start, bufferStart, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); - char bufferEnd[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; - Complex::convertFloatToText(end, bufferEnd, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); - m_sumLayout = new CondensedSumLayout(new StringLayout(sigma, sizeof(sigma)), new StringLayout(bufferStart, strlen(bufferStart), KDText::FontSize::Small), new StringLayout(bufferEnd, strlen(bufferEnd), KDText::FontSize::Small)); - m_sum.setExpression(m_sumLayout); - m_sum.setAlignment(0.0f, 0.5f); -} - -void TermSumController::LegendView::setSumResult(const char * sequenceName, double result) { - ExpressionLayout * childrenLayouts[3]; - char buffer[2+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; - strlcpy(buffer, "= ", 3); - Complex::convertFloatToText(result, buffer+2, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - childrenLayouts[2] = new StringLayout(buffer, strlen(buffer), KDText::FontSize::Small); - childrenLayouts[1] = new BaselineRelativeLayout(new StringLayout(sequenceName, 1, KDText::FontSize::Small), new StringLayout("n", 1, KDText::FontSize::Small), BaselineRelativeLayout::Type::Subscript); - childrenLayouts[0] = m_sumLayout; - m_sumLayout = new HorizontalLayout(childrenLayouts, 3); - m_sum.setExpression(m_sumLayout); - m_sum.setAlignment(0.5f, 0.5f); -} - -int TermSumController::LegendView::numberOfSubviews() const { - return 2; -} - -View * TermSumController::LegendView::subviewAtIndex(int index) { - assert(index >= 0 && index < 2); - if (index == 0) { - return &m_sum; - } - return &m_legend; -} - -void TermSumController::LegendView::layoutSubviews() { - KDCoordinate width = bounds().width(); - KDCoordinate heigth = bounds().height(); - KDSize legendSize = m_legend.minimalSizeForOptimalDisplay(); - if (legendSize.width() > 0) { - m_sum.setFrame(KDRect(0, 0, width-legendSize.width(), heigth)); - m_legend.setFrame(KDRect(width-legendSize.width(), 0, legendSize.width(), heigth)); - return; - } - m_sum.setFrame(bounds()); - m_legend.setFrame(KDRectZero); +double TermSumController::cursorNextStep(double x, int direction) { + double delta = direction > 0 ? 1.0 : -1.0; + return std::round(m_cursor->x()+delta); } } diff --git a/apps/sequence/graph/term_sum_controller.h b/apps/sequence/graph/term_sum_controller.h index f55e067bf..aa6615783 100644 --- a/apps/sequence/graph/term_sum_controller.h +++ b/apps/sequence/graph/term_sum_controller.h @@ -4,63 +4,19 @@ #include #include "graph_view.h" #include "curve_view_range.h" -#include "vertical_cursor_view.h" -#include "../../shared/curve_view_cursor.h" -#include "../../shared/simple_interactive_curve_view_controller.h" +#include "../../shared/sum_graph_controller.h" namespace Sequence { -class TermSumController : public Shared::SimpleInteractiveCurveViewController { +class TermSumController : public Shared::SumGraphController { public: TermSumController(Responder * parentResponder, GraphView * graphView, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor); const char * title() override; - void viewWillAppear() override; - bool handleEvent(Ion::Events::Event event) override; - bool moveCursorHorizontallyToPosition(int position); - void setSequence(Sequence * sequence); private: - constexpr static float k_cursorTopMarginRatio = 0.07f; // (cursorHeight/2)/graphViewHeight - constexpr static float k_cursorRightMarginRatio = 0.04f; // (cursorWidth/2)/graphViewWidth - constexpr static float k_cursorBottomMarginRatio = 0.28f; // (cursorHeight/2+bannerHeigh)/graphViewHeight - constexpr static float k_cursorLeftMarginRatio = 0.04f; // (cursorWidth/2)/graphViewWidth - Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override; - Shared::CurveView * curveView() override; - class LegendView : public View { - public: - LegendView(); - ~LegendView(); - LegendView(const LegendView& other) = delete; - LegendView(LegendView&& other) = delete; - LegendView& operator=(const LegendView& other) = delete; - LegendView& operator=(LegendView&& other) = delete; - void drawRect(KDContext * ctx, KDRect rect) const override; - void setLegendMessage(I18n::Message message); - void setSumSubscript(float start); - void setSumSuperscript(float start, float end); - void setSumResult(const char * sequenceName, double result); - KDSize minimalSizeForOptimalDisplay() const override; - private: - constexpr static KDCoordinate k_legendHeight = 35; - void layoutSubviews() override; - int numberOfSubviews() const override; - View * subviewAtIndex(int index) override; - ExpressionView m_sum; - Poincare::ExpressionLayout * m_sumLayout; - MessageTextView m_legend; - }; - GraphView * m_graphView; - LegendView m_legendView; - CurveViewRange * m_graphRange; - Sequence * m_sequence; - VerticalCursorView m_cursorView; - /* The user can move the cursor to an abscissa n by typing the right digits. - * To be able to go to abscissa represented by more than one digit, we record - * the value typed by the used up to now (if he typed '1' and '4', - * m_bufferCursorPosition = 14). */ - int m_bufferCursorPosition; - int m_step; - int m_startSum; - int m_endSum; + bool moveCursorHorizontallyToPosition(double position) override; + double computeSum(double start, double end) override; + I18n::Message legendMessageAtStep(Step step) override; + double cursorNextStep(double position, int direction) override; }; } diff --git a/apps/shared/Makefile b/apps/shared/Makefile index 3fabe4a8f..defacd1e0 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -38,6 +38,7 @@ app_objs += $(addprefix apps/shared/,\ simple_interactive_curve_view_controller.o\ store_controller.o\ store_parameter_controller.o\ + sum_graph_controller.o\ tab_table_controller.o\ text_field_delegate.o\ text_field_delegate_app.o\ @@ -45,5 +46,6 @@ app_objs += $(addprefix apps/shared/,\ values_function_parameter_controller.o\ values_parameter_controller.o\ values_controller.o\ + vertical_cursor_view.o\ zoom_parameter_controller.o\ ) diff --git a/apps/shared/function_graph_controller.cpp b/apps/shared/function_graph_controller.cpp index 1f743c86f..1dd01db89 100644 --- a/apps/shared/function_graph_controller.cpp +++ b/apps/shared/function_graph_controller.cpp @@ -28,6 +28,11 @@ ViewController * FunctionGraphController::initialisationParameterController() { } void FunctionGraphController::viewWillAppear() { + functionGraphView()->setVerticalCursor(false); + functionGraphView()->setCursorView(&m_cursorView); + functionGraphView()->setBannerView(bannerView()); + functionGraphView()->setAreaHighlight(NAN,NAN); + if (functionGraphView()->context() == nullptr) { TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app(); functionGraphView()->setContext(myApp->localContext()); diff --git a/apps/shared/function_graph_view.cpp b/apps/shared/function_graph_view.cpp index 40e2d59b3..4d4f11911 100644 --- a/apps/shared/function_graph_view.cpp +++ b/apps/shared/function_graph_view.cpp @@ -10,12 +10,29 @@ FunctionGraphView::FunctionGraphView(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, BannerView * bannerView, View * cursorView) : CurveView(graphRange, cursor, bannerView, cursorView), m_selectedFunction(nullptr), + m_verticalCursor(false), + m_highlightedStart(NAN), + m_highlightedEnd(NAN), + m_shouldColorHighlighted(false), m_xLabels{}, m_yLabels{}, m_context(nullptr) { } +void FunctionGraphView::reload() { + CurveView::reload(); + if (!std::isnan(m_highlightedStart)) { + float pixelLowerBound = floatToPixel(Axis::Horizontal, m_highlightedStart)-2.0; + float pixelUpperBound = floatToPixel(Axis::Horizontal, m_highlightedEnd)+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); + } +} + void FunctionGraphView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); drawGrid(ctx, rect); @@ -41,6 +58,34 @@ void FunctionGraphView::selectFunction(Function * function) { } } +void FunctionGraphView::setVerticalCursor(bool verticalCursor) { + m_verticalCursor = verticalCursor; +} + +void FunctionGraphView::setAreaHighlight(float start, float end) { + if (m_highlightedStart != start || m_highlightedEnd != end) { + reload(); + m_highlightedStart = start; + m_highlightedEnd = end; + reload(); + } +} + +void FunctionGraphView::setAreaHighlightColor(bool highlightColor) { + if (m_shouldColorHighlighted != highlightColor) { + reload(); + m_shouldColorHighlighted = highlightColor; + reload(); + } +} + +KDSize FunctionGraphView::cursorSize() { + if (m_verticalCursor) { + return KDSize(1, 0); + } + return CurveView::cursorSize(); +} + char * FunctionGraphView::label(Axis axis, int index) const { return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]); } diff --git a/apps/shared/function_graph_view.h b/apps/shared/function_graph_view.h index 1a03e91d6..d7b9e3c44 100644 --- a/apps/shared/function_graph_view.h +++ b/apps/shared/function_graph_view.h @@ -13,13 +13,22 @@ class FunctionGraphView : public CurveView { public: FunctionGraphView(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, BannerView * bannerView, View * cursorView); + void reload() override; void drawRect(KDContext * ctx, KDRect rect) const override; void setContext(Poincare::Context * context); Poincare::Context * context() const; void selectFunction(Function * function); + void setVerticalCursor(bool verticalCursor); + void setAreaHighlight(float start, float end); + void setAreaHighlightColor(bool highlightColor); protected: Function * m_selectedFunction; + bool m_verticalCursor; + float m_highlightedStart; + float m_highlightedEnd; + bool m_shouldColorHighlighted; private: + KDSize cursorSize() override; 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)]; diff --git a/apps/shared/sum_graph_controller.cpp b/apps/shared/sum_graph_controller.cpp new file mode 100644 index 000000000..153128bcb --- /dev/null +++ b/apps/shared/sum_graph_controller.cpp @@ -0,0 +1,324 @@ +#include "sum_graph_controller.h" +#include "../apps_container.h" +#include "../../poincare/src/layout/baseline_relative_layout.h" +#include "../../poincare/src/layout/condensed_sum_layout.h" +#include "../../poincare/src/layout/string_layout.h" +#include "../../poincare/src/layout/horizontal_layout.h" + +#include +#include +#include + +using namespace Poincare; + +namespace Shared { + +SumGraphController::SumGraphController(Responder * parentResponder, FunctionGraphView * 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_graphView(graphView), + m_legendView(this, sumSymbol), + m_graphRange(range), + m_cursorView() +{ +} + +void SumGraphController::viewWillAppear() { + m_graphRange->panToMakePointVisible(m_cursor->x(), m_cursor->y(), k_cursorTopMarginRatio, k_cursorRightMarginRatio, k_cursorBottomMarginRatio, k_cursorLeftMarginRatio); + m_graphView->setVerticalCursor(true); + 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 SumGraphController::didEnterResponderChain(Responder * previousFirstResponder) { + app()->setFirstResponder(m_legendView.textField()); +} + +bool SumGraphController::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_graphView->reload(); + 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 SumGraphController::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 SumGraphController::setFunction(Function * function) { + m_graphView->selectFunction(function); + m_function = function; +} + +bool SumGraphController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { + AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); + Context * globalContext = appsContainer->globalContext(); + double floatBody = Expression::approximateToScalar(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 SumGraphController::textFieldDidAbortEditing(TextField * textField, const char * text) { + char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; + double parameter = NAN; + switch(m_step) { + case Step::FirstParameter: + parameter = m_startSum; + break; + case Step::SecondParameter: + parameter = m_endSum; + break; + default: + assert(false); + } + Complex::convertFloatToText(parameter, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); + textField->setText(buffer); + return true; +} + +bool SumGraphController::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 SumGraphController::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); + double sum = computeSum(m_startSum, m_endSum); + m_legendView.setSumSymbol(m_step, m_startSum, m_endSum, sum, m_function->name()); + m_legendView.setLegendMessage(I18n::Message::Default, m_step); + m_graphView->setAreaHighlightColor(true); + m_graphView->setCursorView(nullptr); + m_graphView->reload(); + myApp->setFirstResponder(this); + return true; +} + +/* Legend View */ + +SumGraphController::LegendView::LegendView(SumGraphController * controller, char sumSymbol) : + m_sum(0.0f, 0.5f, KDColorBlack, Palette::GreyBright), + m_sumLayout(nullptr), + m_legend(KDText::FontSize::Small, I18n::Message::Default, 0.0f, 0.5f, KDColorBlack, Palette::GreyBright), + m_editableZone(controller, m_draftText, m_draftText, TextField::maxBufferSize(), controller, false, KDText::FontSize::Small, 0.0f, 0.5f, KDColorBlack, Palette::GreyBright), + m_sumSymbol(sumSymbol) +{ +} + +SumGraphController::LegendView::~LegendView() { + if (m_sumLayout != nullptr) { + delete m_sumLayout; + m_sumLayout = nullptr; + } +} + +void SumGraphController::LegendView::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(bounds(), Palette::GreyBright); +} + +KDSize SumGraphController::LegendView::minimalSizeForOptimalDisplay() const { + return KDSize(0, k_legendHeight); +} + +void SumGraphController::LegendView::setLegendMessage(I18n::Message message, Step step) { + m_legend.setMessage(message); + layoutSubviews(step); +} + +void SumGraphController::LegendView::setEditableZone(double d) { + char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; + Complex::convertFloatToText(d, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); + m_editableZone.setText(buffer); +} + +void SumGraphController::LegendView::setSumSymbol(Step step, double start, double end, double result, const char * sequenceName) { + if (m_sumLayout) { + delete m_sumLayout; + m_sumLayout = nullptr; + } + const char sigma[] = {' ', m_sumSymbol}; + if (step == Step::FirstParameter) { + m_sumLayout = new StringLayout(sigma, sizeof(sigma)); + } else if (step == Step::SecondParameter) { + char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; + Complex::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); + m_sumLayout = new CondensedSumLayout(new StringLayout(sigma, sizeof(sigma)), new StringLayout(buffer, strlen(buffer), KDText::FontSize::Small), nullptr); + } else { + char buffer[2+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; + Complex::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); + ExpressionLayout * start = new StringLayout(buffer, strlen(buffer), KDText::FontSize::Small); + Complex::convertFloatToText(end, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Expression::FloatDisplayMode::Decimal); + ExpressionLayout * end = new StringLayout(buffer, strlen(buffer), KDText::FontSize::Small); + m_sumLayout = new CondensedSumLayout(new StringLayout(sigma, sizeof(sigma)), start, end); + + ExpressionLayout * childrenLayouts[3]; + strlcpy(buffer, "= ", 3); + Complex::convertFloatToText(result, buffer+2, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + childrenLayouts[2] = new StringLayout(buffer, strlen(buffer), KDText::FontSize::Small); + childrenLayouts[1] = new BaselineRelativeLayout(new StringLayout(sequenceName, 1, KDText::FontSize::Small), new StringLayout("n", 1, KDText::FontSize::Small), BaselineRelativeLayout::Type::Subscript); + childrenLayouts[0] = m_sumLayout; + m_sumLayout = new HorizontalLayout(childrenLayouts, 3); + } + m_sum.setExpression(m_sumLayout); + if (step == Step::Result) { + m_sum.setAlignment(0.5f, 0.5f); + } else { + m_sum.setAlignment(0.0f, 0.5f); + } + layoutSubviews(step); +} + +int SumGraphController::LegendView::numberOfSubviews() const { + return 3; +} + +View * SumGraphController::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 SumGraphController::LegendView::layoutSubviews() { + layoutSubviews(Step::FirstParameter); +} + +void SumGraphController::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); + } +} + +} diff --git a/apps/shared/sum_graph_controller.h b/apps/shared/sum_graph_controller.h new file mode 100644 index 000000000..53fa0a60e --- /dev/null +++ b/apps/shared/sum_graph_controller.h @@ -0,0 +1,89 @@ +#ifndef SHARED_SUM_GRAPH_CONTROLLER_H +#define SHARED_SUM_GRAPH_CONTROLLER_H + +#include +#include "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 "function.h" +#include "text_field_delegate.h" + +namespace Shared { + +class SumGraphController : public SimpleInteractiveCurveViewController, public TextFieldDelegate { +public: + SumGraphController(Responder * parentResponder, FunctionGraphView * curveView, InteractiveCurveViewRange * range, CurveViewCursor * cursor, char sumSymbol); + void viewWillAppear() override; + void didEnterResponderChain(Responder * previousFirstResponder) override; + bool handleEvent(Ion::Events::Event event) override; + void setFunction(Function * function); + bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; + bool textFieldDidAbortEditing(TextField * textField, const char * text) 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; + Function * m_function; +private: + constexpr static float k_cursorTopMarginRatio = 0.07f; // (cursorHeight/2)/graphViewHeight + constexpr static float k_cursorRightMarginRatio = 0.04f; // (cursorWidth/2)/graphViewWidth + constexpr static float k_cursorBottomMarginRatio = 0.28f; // (cursorHeight/2+bannerHeigh)/graphViewHeight + constexpr static float k_cursorLeftMarginRatio = 0.04f; // (cursorWidth/2)/graphViewWidth + virtual double computeSum(double start, double end) = 0; + virtual I18n::Message legendMessageAtStep(Step step) = 0; + virtual double cursorNextStep(double position, int direction) = 0; + Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override { return m_graphRange; } + Shared::CurveView * curveView() override { return m_graphView; } + TextFieldDelegateApp * textFieldDelegateApp() override { + return static_cast(app()); + } + bool handleEnter(); + class LegendView : public View { + public: + LegendView(SumGraphController * controller, char sumSymbol); + ~LegendView(); + 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, const char * sequenceName = nullptr); + private: + constexpr static KDCoordinate k_legendHeight = 35; + constexpr static KDCoordinate k_editableZoneWidth = 4*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::ExpressionLayout * m_sumLayout; + MessageTextView m_legend; + TextField m_editableZone; + char m_draftText[TextField::maxBufferSize()]; + char m_sumSymbol; + }; + FunctionGraphView * m_graphView; + LegendView m_legendView; + InteractiveCurveViewRange * m_graphRange; + VerticalCursorView m_cursorView; +}; + +} + +#endif diff --git a/apps/sequence/graph/vertical_cursor_view.cpp b/apps/shared/vertical_cursor_view.cpp similarity index 90% rename from apps/sequence/graph/vertical_cursor_view.cpp rename to apps/shared/vertical_cursor_view.cpp index f164f3477..dfbea82f3 100644 --- a/apps/sequence/graph/vertical_cursor_view.cpp +++ b/apps/shared/vertical_cursor_view.cpp @@ -1,6 +1,6 @@ #include "vertical_cursor_view.h" -namespace Sequence { +namespace Shared { void VerticalCursorView::drawRect(KDContext * ctx, KDRect rect) const { KDCoordinate height = bounds().height(); diff --git a/apps/sequence/graph/vertical_cursor_view.h b/apps/shared/vertical_cursor_view.h similarity index 62% rename from apps/sequence/graph/vertical_cursor_view.h rename to apps/shared/vertical_cursor_view.h index 7e028235a..218ea2259 100644 --- a/apps/sequence/graph/vertical_cursor_view.h +++ b/apps/shared/vertical_cursor_view.h @@ -1,9 +1,9 @@ -#ifndef SEQUENCE_VERTICAL_CURSOR_VIEW_H -#define SEQUENCE_VERTICAL_CURSOR_VIEW_H +#ifndef SHARED_VERTICAL_CURSOR_VIEW_H +#define SHARED_VERTICAL_CURSOR_VIEW_H #include -namespace Sequence { +namespace Shared { class VerticalCursorView : public View { public: