From a18b674c5aa64d361b23d8e9a029b8eb2e10ad47 Mon Sep 17 00:00:00 2001 From: daffyb Date: Wed, 12 Jun 2019 01:13:51 +0200 Subject: [PATCH 01/22] Update base.fr.i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Numéro serie" >>> "Numéro série" --- apps/settings/base.fr.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index c76912acc..9a6cc3547 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -20,6 +20,6 @@ Cartesian = "Algébrique " Polar = "Exponentielle " Brightness = "Luminosité" SoftwareVersion = "Version du logiciel" -SerialNumber = "Numéro serie" +SerialNumber = "Numéro série" UpdatePopUp = "Rappel mise à jour" BetaPopUp = "Rappel version bêta" From 426deda68def4b6282194b2c2bfd34bab9f30c61 Mon Sep 17 00:00:00 2001 From: daffyb Date: Wed, 12 Jun 2019 01:40:59 +0200 Subject: [PATCH 02/22] Update base.fr.i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit some e become é --- apps/graph/base.fr.i18n | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/graph/base.fr.i18n b/apps/graph/base.fr.i18n index 5014969aa..2d4af4a84 100644 --- a/apps/graph/base.fr.i18n +++ b/apps/graph/base.fr.i18n @@ -12,14 +12,14 @@ Zeros = "Zéros" Tangent = "Tangente" Intersection = "Intersection" Preimage = "Antécédent" -SelectLowerBound = "Selectionner la borne inférieure" -SelectUpperBound = "Selectionner la borne supérieure" +SelectLowerBound = "Sélectionner la borne inférieure" +SelectUpperBound = "Sélectionner la borne supérieure" NoMaximumFound = "Aucun maximum trouvé" NoMinimumFound = "Aucun minimum trouvé" NoZeroFound = "Aucun zéro trouvé" NoIntersectionFound = "Aucune intersection trouvée" NoPreimageFound = "Aucun antécédent trouvé" -DerivativeFunctionColumn = "Colonne de la fonction derivée" -HideDerivativeColumn = "Masquer la fonction derivée" +DerivativeFunctionColumn = "Colonne de la fonction dérivée" +HideDerivativeColumn = "Masquer la fonction dérivée" AllowedCharactersAZaz09 = "Caractères autorisés : A-Z, a-z, 0-9, _" ReservedName = "Nom réservé" From 53eb5ddd952a55bbb3203fa1057d4e0d333abf70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 13 Jun 2019 11:03:23 +0200 Subject: [PATCH 03/22] [apps/solver] Fix string length computation The layout for delta=b^2*4ac did not appear properly --- apps/solver/solutions_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/solver/solutions_controller.cpp b/apps/solver/solutions_controller.cpp index 77e2138e4..dcb422691 100644 --- a/apps/solver/solutions_controller.cpp +++ b/apps/solver/solutions_controller.cpp @@ -79,7 +79,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt { m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); const char * deltaB = "Δ=b"; - static_cast(m_delta2Layout).addOrMergeChildAtIndex(LayoutHelper::String(deltaB, 3, KDFont::SmallFont), 0, false); + static_cast(m_delta2Layout).addOrMergeChildAtIndex(LayoutHelper::String(deltaB, strlen(deltaB), KDFont::SmallFont), 0, false); for (int i = 0; i < EquationStore::k_maxNumberOfExactSolutions; i++) { m_exactValueCells[i].setParentResponder(m_contentView.selectableTableView()); } From 1328277132fd78a15469097558b7c85c19dd9bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 12 Jun 2019 17:53:45 +0200 Subject: [PATCH 04/22] [ion/unicode] StringGlyphLength method --- ion/Makefile | 3 ++- ion/include/ion/unicode/utf8_helper.h | 4 ++++ ion/src/shared/unicode/utf8_helper.cpp | 17 ++++++++++++++++- ion/test/utf8_helper.cpp | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 ion/test/utf8_helper.cpp diff --git a/ion/Makefile b/ion/Makefile index b2ea8e4ba..1c3e616a4 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -34,8 +34,9 @@ tests += $(addprefix ion/test/,\ keyboard.cpp\ storage.cpp\ utf8_decoder.cpp\ + utf8_helper.cpp\ ) ifdef ION_STORAGE_LOG SFLAGS += -DION_STORAGE_LOG=1 -endif \ No newline at end of file +endif diff --git a/ion/include/ion/unicode/utf8_helper.h b/ion/include/ion/unicode/utf8_helper.h index 2a665f25c..cfb15c128 100644 --- a/ion/include/ion/unicode/utf8_helper.h +++ b/ion/include/ion/unicode/utf8_helper.h @@ -68,6 +68,10 @@ int RemovePreviousCodePoint(const char * text, char * location, CodePoint * c); const char * CodePointAtGlyphOffset(const char * buffer, int position); size_t GlyphOffsetAtCodePoint(const char * buffer, const char * position); +/* Return the number of glyphs in a string. + * For instance, strlen("∑") = 3 but StringGlyphLength("∑") = 1 */ +size_t StringGlyphLength(const char * s, int maxSize = -1); + }; #endif diff --git a/ion/src/shared/unicode/utf8_helper.cpp b/ion/src/shared/unicode/utf8_helper.cpp index 51a8bf3ac..0eeb12c9b 100644 --- a/ion/src/shared/unicode/utf8_helper.cpp +++ b/ion/src/shared/unicode/utf8_helper.cpp @@ -326,5 +326,20 @@ size_t GlyphOffsetAtCodePoint(const char * buffer, const char * position) { return glyphIndex; } +size_t StringGlyphLength(const char * s, int maxSize) { + if (maxSize == 0) { + return 0; + } + UTF8Decoder decoder(s); + CodePoint codePoint = decoder.nextCodePoint(); + size_t glyphIndex = 0; + while (codePoint != UCodePointNull && (maxSize < 0 || ((decoder.stringPosition() - s) <= maxSize))) { + if (!codePoint.isCombining()) { + glyphIndex++; + } + codePoint = decoder.nextCodePoint(); + } + return glyphIndex; +} -}; +} diff --git a/ion/test/utf8_helper.cpp b/ion/test/utf8_helper.cpp new file mode 100644 index 000000000..975b5cff1 --- /dev/null +++ b/ion/test/utf8_helper.cpp @@ -0,0 +1,14 @@ +#include +#include + +void assert_string_glyph_length_is(const char * string, int maxSize, size_t result) { + quiz_assert(UTF8Helper::StringGlyphLength(string, maxSize) == result); +} + +QUIZ_CASE(ion_utf8_helper_glyph_length) { + assert_string_glyph_length_is("123", -1, 3); + assert_string_glyph_length_is("1ᴇ3", -1, 3); + assert_string_glyph_length_is("∑∫𝐢", -1, 3); + assert_string_glyph_length_is("123", 2, 2); + assert_string_glyph_length_is("1ᴇ3", 2, 1); +} From 15b4803ba06f21d7dc3183126025a09e111be671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 13 Jun 2019 10:44:04 +0200 Subject: [PATCH 05/22] [apps/statistics] Fix banner view Scientific mode, Data: 10 - 1 20 - 5 30 - 4 40 - 3 50 - 1 Display the histogram, there are problems in the banner view --- apps/statistics/histogram_controller.cpp | 37 ++++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 70d02ac1d..fcd94ed73 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -90,6 +90,20 @@ Responder * HistogramController::tabController() const { return (parentResponder()->parentResponder()->parentResponder()->parentResponder()); } +void pad(char * buffer, int bufferSize, int * currentNumberOfChar, int maxGlyphLengthWithPadding) { + assert(*currentNumberOfChar <= bufferSize); + size_t currentGlyphLength = UTF8Helper::StringGlyphLength(buffer, *currentNumberOfChar); + bool addedPadding = false; + while (currentGlyphLength < maxGlyphLengthWithPadding && *currentNumberOfChar < bufferSize) { + *currentNumberOfChar = *currentNumberOfChar + UTF8Decoder::CodePointToChars(' ', buffer + *currentNumberOfChar, bufferSize - *currentNumberOfChar); + addedPadding = true; + currentGlyphLength++; + } + if (addedPadding) { + buffer[*currentNumberOfChar-1] = 0; + } +} + void HistogramController::reloadBannerView() { if (selectedSeriesIndex() < 0) { return; @@ -107,7 +121,7 @@ void HistogramController::reloadBannerView() { // Add lower bound if (selectedSeriesIndex() >= 0) { double lowerBound = m_store->startOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex); - numberOfChar += PoincareHelpers::ConvertFloatToText(lowerBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + numberOfChar += PoincareHelpers::ConvertFloatToText(lowerBound, buffer+numberOfChar, bufferSize-numberOfChar, Constant::LargeNumberOfSignificantDigits); } numberOfChar+= UTF8Decoder::CodePointToChars(';', buffer + numberOfChar, bufferSize - numberOfChar); @@ -115,15 +129,12 @@ void HistogramController::reloadBannerView() { // Add upper bound if (selectedSeriesIndex() >= 0) { double upperBound = m_store->endOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex); - numberOfChar += PoincareHelpers::ConvertFloatToText(upperBound, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + numberOfChar += PoincareHelpers::ConvertFloatToText(upperBound, buffer+numberOfChar, bufferSize-numberOfChar, Constant::LargeNumberOfSignificantDigits); } numberOfChar+= UTF8Decoder::CodePointToChars('[', buffer + numberOfChar, bufferSize - numberOfChar); // Padding - for (int i = numberOfChar; i < k_maxIntervalLegendLength; i++) { - numberOfChar+= UTF8Decoder::CodePointToChars(' ', buffer + numberOfChar, bufferSize - numberOfChar); - } - buffer[k_maxIntervalLegendLength] = 0; + pad(buffer, bufferSize, &numberOfChar, k_maxIntervalLegendLength); m_view.bannerView()->intervalView()->setText(buffer); // Add Size Data @@ -135,13 +146,10 @@ void HistogramController::reloadBannerView() { double size = 0; if (selectedSeriesIndex() >= 0) { size = m_store->heightOfBarAtIndex(selectedSeriesIndex(), *m_selectedBarIndex); - numberOfChar += PoincareHelpers::ConvertFloatToText(size, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + numberOfChar += PoincareHelpers::ConvertFloatToText(size, buffer+numberOfChar, bufferSize-numberOfChar, Constant::LargeNumberOfSignificantDigits); } // Padding - for (int i = numberOfChar; i < k_maxLegendLength; i++) { - numberOfChar+= UTF8Decoder::CodePointToChars(' ', buffer + numberOfChar, bufferSize - numberOfChar); - } - buffer[k_maxLegendLength] = 0; + pad(buffer, bufferSize, &numberOfChar, k_maxLegendLength); m_view.bannerView()->sizeView()->setText(buffer); // Add Frequency Data @@ -152,13 +160,10 @@ void HistogramController::reloadBannerView() { numberOfChar += legendLength; if (selectedSeriesIndex() >= 0) { double frequency = size/m_store->sumOfOccurrences(selectedSeriesIndex()); - numberOfChar += PoincareHelpers::ConvertFloatToText(frequency, buffer+numberOfChar, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); + numberOfChar += PoincareHelpers::ConvertFloatToText(frequency, buffer+numberOfChar, bufferSize - numberOfChar, Constant::LargeNumberOfSignificantDigits); } // Padding - for (int i = numberOfChar; i < k_maxLegendLength; i++) { - numberOfChar+= UTF8Decoder::CodePointToChars(' ', buffer + numberOfChar, bufferSize - numberOfChar); - } - buffer[k_maxLegendLength] = 0; + pad(buffer, bufferSize, &numberOfChar, k_maxLegendLength); m_view.bannerView()->frequencyView()->setText(buffer); m_view.bannerView()->reload(); From e5190f38abc3b05c19865f486203a17eecc30572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 13 Jun 2019 15:31:55 +0200 Subject: [PATCH 06/22] [escher/text_area] Fix text indentation handling We used a small buffer to preprocess a text to insert in text area (add indentation, remove empty code points, compute the next cursor location), but the size of this buffer was sometimes too small and caused a crash. Now we do all the text odification in place in the text area buffer. --- escher/include/escher/text_input_helpers.h | 2 +- escher/src/text_area.cpp | 85 +++++++++++++--------- escher/src/text_input_helpers.cpp | 5 +- ion/include/ion/unicode/utf8_helper.h | 17 ++++- ion/src/shared/unicode/utf8_helper.cpp | 41 +++++++---- 5 files changed, 93 insertions(+), 57 deletions(-) diff --git a/escher/include/escher/text_input_helpers.h b/escher/include/escher/text_input_helpers.h index 04718b4b5..2fa8e2671 100644 --- a/escher/include/escher/text_input_helpers.h +++ b/escher/include/escher/text_input_helpers.h @@ -6,7 +6,7 @@ namespace TextInputHelpers { -const char * CursorPositionInCommand(const char * text); +const char * CursorPositionInCommand(const char * text, const char * stoppingPosition = nullptr); /* Returns the pointer to the char that should be right of the cursor, which is * the first char between : * - The first EmptyChar (which is the position of the first argument) diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index 86bb1d560..e1c73ea97 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -34,47 +34,62 @@ static inline void InsertSpacesAtLocation(int spacesCount, char * buffer, int bu } bool TextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) { - constexpr int bufferSize = TextField::maxBufferSize(); - char buffer[bufferSize]; - - size_t textLength = strlcpy(buffer, text, bufferSize); - - // Add indentation spaces + if (*text == 0) { + return false; + } + /* Compute the indentation. If the text cannot be inserted with the + * indentation, stop here. */ + int spacesCount = 0; + int totalIndentationSize = 0; + int textLen = strlen(text); + char * insertionPosition = const_cast(cursorLocation()); if (indentation) { // Compute the indentation - int spacesCount = indentationBeforeCursor(); - const char * teaxtAreaBuffer = contentView()->text(); - if (cursorLocation() > teaxtAreaBuffer && UTF8Helper::PreviousCodePointIs(teaxtAreaBuffer, cursorLocation(), ':')) { + spacesCount = indentationBeforeCursor(); + const char * textAreaBuffer = contentView()->text(); + if (insertionPosition > textAreaBuffer && UTF8Helper::PreviousCodePointIs(textAreaBuffer, insertionPosition, ':')) { spacesCount += k_indentationSpaces; } - // Check the text will not overflow the buffer - int totalIndentationSize = UTF8Helper::CountOccurrences(text, '\n') * spacesCount; - if (contentView()->getText()->textLength() + textLength + totalIndentationSize >= contentView()->getText()->bufferSize() || textLength == 0) { + totalIndentationSize = UTF8Helper::CountOccurrences(text, '\n') * spacesCount; + if (contentView()->getText()->textLength() + textLen + totalIndentationSize >= contentView()->getText()->bufferSize()) { return false; } - - UTF8Helper::PerformAtCodePoints( - buffer, '\n', - [](int codePointOffset, void * text, int indentation) { - int offset = codePointOffset + UTF8Decoder::CharSizeOfCodePoint('\n'); - InsertSpacesAtLocation(indentation, (char *)text + offset, TextField::maxBufferSize() - offset); //TODO - }, - [](int c1, void * c2, int c3) {}, - (void *)buffer, - spacesCount); } - const char * cursorPositionInCommand = TextInputHelpers::CursorPositionInCommand(buffer); - - // Remove the Empty code points - UTF8Helper::RemoveCodePoint(buffer, UCodePointEmpty, &cursorPositionInCommand); // Insert the text - if (insertTextAtLocation(buffer, cursorLocation())) { - // Set the cursor location - const char * nextCursorLocation = cursorLocation() + (forceCursorRightOfText ? strlen(buffer) : cursorPositionInCommand - buffer); - setCursorLocation(nextCursorLocation); + if (!insertTextAtLocation(text, insertionPosition)) { + return true; } + + // Insert the indentation + if (indentation) { + UTF8Helper::PerformAtCodePoints( + insertionPosition, + '\n', + [](int codePointOffset, void * text, int indentation, int bufferLength) { + int offset = codePointOffset + UTF8Decoder::CharSizeOfCodePoint('\n'); + InsertSpacesAtLocation(indentation, (char *)text + offset, bufferLength); + }, + [](int c1, void * c2, int c3, int c4) {}, + (void *)insertionPosition, + spacesCount, + contentView()->getText()->bufferSize() - (insertionPosition - contentView()->getText()->text()), + UCodePointNull, + true, + nullptr, + insertionPosition + textLen); + } + const char * endOfInsertedText = insertionPosition + textLen + totalIndentationSize; + const char * cursorPositionInCommand = TextInputHelpers::CursorPositionInCommand(insertionPosition, endOfInsertedText); + + // Remove the Empty code points + UTF8Helper::RemoveCodePoint(insertionPosition, UCodePointEmpty, &cursorPositionInCommand, endOfInsertedText); + + // Set the cursor location + const char * nextCursorLocation = forceCursorRightOfText ? endOfInsertedText : cursorPositionInCommand; + setCursorLocation(nextCursorLocation); + return true; } @@ -136,14 +151,14 @@ int TextArea::indentationBeforeCursor() const { * indentation size when encountering spaces, reset it to 0 when encountering * another code point, until reaching the beginning of the line. */ UTF8Helper::PerformAtCodePoints(const_cast