~aleteoryx/muditaos

5c4a5a4aea1f90a60e89a2e32b145a585830e2e1 — Przemyslaw Brudny 3 years ago 30487c6
[MOS-152] Text Abc input mode support added

Text Abc input mode support added with tests.
M module-apps/application-messages/widgets/SMSInputWidget.cpp => module-apps/application-messages/widgets/SMSInputWidget.cpp +1 -1
@@ 54,7 54,7 @@ namespace gui
        };

        inputText->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &Text) { application->getCurrentWindow()->navBarTemporaryMode(Text); },
            [=]() { application->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
            [=]() { application->getCurrentWindow()->selectSpecialCharacter(); }));

M module-apps/application-messages/windows/NewMessage.cpp => module-apps/application-messages/windows/NewMessage.cpp +1 -1
@@ 250,7 250,7 @@ namespace gui
        message->setTextLimitType(gui::TextLimitType::MaxSignsCount, msgConstants::maxConcatenatedLen);
        message->setEdges(gui::RectangleEdge::Bottom);
        message->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { navBarTemporaryMode(text); },
            [=]() { navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));

M module-apps/application-messages/windows/SearchStart.cpp => module-apps/application-messages/windows/SearchStart.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationMessages.hpp"


@@ 25,7 25,7 @@ namespace gui
        addWidget(body);
        auto text = inputBox(this, utils::translate("common_search_uc"), "search_32px_W_G");
        text->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &Text) { application->getCurrentWindow()->navBarTemporaryMode(Text); },
            [=]() { application->getCurrentWindow()->navBarRestoreFromTemporaryMode(); },
            [=]() { application->getCurrentWindow()->selectSpecialCharacter(); }));

M module-apps/application-notes/windows/NoteEditWindow.cpp => module-apps/application-notes/windows/NoteEditWindow.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NoteEditWindow.hpp"


@@ 74,7 74,7 @@ namespace app::notes
        edit->setEdges(gui::RectangleEdge::None);
        edit->setFont(::style::window::font::medium);
        edit->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { navBarTemporaryMode(text); },
            [=]() { navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));

M module-apps/application-phonebook/widgets/InputLinesWithLabelWidget.cpp => module-apps/application-phonebook/widgets/InputLinesWithLabelWidget.cpp +1 -1
@@ 52,7 52,7 @@ namespace gui
        inputText->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Bottom));
        inputText->setFont(style::window::font::medium);
        inputText->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { this->navBarTemporaryMode(text, true); },
            [=]() { this->navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));

M module-apps/application-settings/widgets/network/ApnInputWidget.cpp => module-apps/application-settings/widgets/network/ApnInputWidget.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApnInputWidget.hpp"


@@ 46,7 46,7 @@ namespace gui
        inputText->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        inputText->setFont(style::window::font::medium);
        inputText->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { navBarTemporaryMode(text); },
            [=]() { navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));

M module-apps/application-settings/windows/advanced/UITestWindow.cpp => module-apps/application-settings/windows/advanced/UITestWindow.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "UITestWindow.hpp"


@@ 54,7 54,7 @@ namespace gui
        text->addText(TextBlock("adds ", Font(46, Font::Weight::Light).raw(), TextBlock::End::None));
        text->addText(TextBlock("special chars too", Font(27).raw(), TextBlock::End::None));
        text->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { navBarTemporaryMode(text); },
            [=]() { navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));

M module-apps/application-settings/windows/display-keypad/QuotesAddWindow.cpp => module-apps/application-settings/windows/display-keypad/QuotesAddWindow.cpp +2 -2
@@ 75,7 75,7 @@ namespace gui
        authorText->setEdges(gui::RectangleEdge::Bottom);
        authorText->setFont(::style::window::font::mediumbold);
        authorText->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { navBarTemporaryMode(text); },
            [=]() { navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));


@@ 104,7 104,7 @@ namespace gui
        quoteText->setEdges(gui::RectangleEdge::Bottom);
        quoteText->setFont(::style::window::font::medium);
        quoteText->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            {InputMode::Abc, InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { navBarTemporaryMode(text); },
            [=]() { navBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));

M module-apps/apps-common/widgets/InputBox.cpp => module-apps/apps-common/widgets/InputBox.cpp +1 -1
@@ 54,7 54,7 @@ namespace gui
        inputField->setTextEllipsisType(TextEllipsis::Both);
        inputField->setEditMode(EditMode::Edit);
        inputField->setEdges(RectangleEdge::None);
        inputField->setInputMode(new InputMode({InputMode::ABC, InputMode::abc}));
        inputField->setInputMode(new InputMode({InputMode::Abc, InputMode::ABC, InputMode::abc}));
        inputField->setFont(style::window::font::mediumbold);

        if (!icon.empty()) {

M module-gui/gui/widgets/text/Text.cpp => module-gui/gui/widgets/text/Text.cpp +40 -11
@@ 62,8 62,8 @@ namespace gui

    Text::~Text()
    {
        if (mode != nullptr) {
            delete mode;
        if (inputMode != nullptr) {
            delete inputMode;
        }
    }



@@ 338,6 338,35 @@ namespace gui
        }
    }

    void Text::setInputMode(InputMode *&&mode)
    {
        if (this->inputMode != nullptr) {
            delete this->inputMode;
        }
        this->inputMode = mode;
    }

    std::string Text::getInputModeKeyMap()
    {
        if (inputMode != nullptr) {

            if (inputMode->is(InputMode::Abc)) {
                return inputMode->get(detectInputMode());
            }
            else {
                return inputMode->get();
            }
        }
        else {
            return "";
        }
    }

    InputMode::Mode Text::detectInputMode()
    {
        return cursor->checkSentenceBeginning() ? InputMode::ABC : InputMode::abc;
    }

    bool Text::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
    {
        Rect::onDimensionChanged(oldDim, newDim);


@@ 506,8 535,8 @@ namespace gui

    auto Text::handleRotateInputMode(const InputEvent &inputEvent) -> bool
    {
        if (mode != nullptr && inputEvent.isShortRelease(gui::KeyCode::KEY_AST)) {
            mode->next();
        if (inputMode != nullptr && inputEvent.isShortRelease(gui::KeyCode::KEY_AST)) {
            inputMode->next();
            return true;
        }
        return false;


@@ 515,16 544,16 @@ namespace gui

    auto Text::handleRestoreInputModeUI(const InputEvent &inputEvent) -> bool
    {
        if (mode != nullptr && inputEvent.isKeyRelease()) {
            mode->show_restore();
        if (inputMode != nullptr && inputEvent.isKeyRelease()) {
            inputMode->show_restore();
        }
        return false;
    }

    auto Text::handleSelectSpecialChar(const InputEvent &inputEvent) -> bool
    {
        if (mode != nullptr && inputEvent.isLongRelease() && inputEvent.is(gui::KeyCode::KEY_AST)) {
            mode->select_special_char();
        if (inputMode != nullptr && inputEvent.isLongRelease() && inputEvent.is(gui::KeyCode::KEY_AST)) {
            inputMode->select_special_char();
            return true;
        }
        return false;


@@ 613,7 642,7 @@ namespace gui
            return false;
        }

        auto code = translator.handle(inputEvent.getRawKey(), mode ? mode->get() : "");
        auto code = translator.handle(inputEvent.getRawKey(), getInputModeKeyMap());

        if (code != Profile::none_key && checkAdditionBounds(code) == AdditionBound::CanAddAll) {



@@ 622,7 651,7 @@ namespace gui
            debug_text("handleAddChar %d -> Begin", code);
            debug_text("%s times: %" PRIu32, inputEvent.str().c_str(), translator.getTimes());
            /// if we have multi press in non digit mode - we need to replace char and put next char from translator
            if (!(mode->is(InputMode::digit) || (mode->is(InputMode::phone))) && translator.getTimes() > 0) {
            if (!(inputMode->is(InputMode::digit) || (inputMode->is(InputMode::phone))) && translator.getTimes() > 0) {
                removeChar();
            }
            addChar(code);


@@ 650,7 679,7 @@ namespace gui
        auto code = text::npos;

        // phone mode
        if (mode->is(InputMode::phone) && inputEvent.is(KeyCode::KEY_0)) {
        if (inputMode->is(InputMode::phone) && inputEvent.is(KeyCode::KEY_0)) {
            code = '+';
        }
        // all other modes only handle digits

M module-gui/gui/widgets/text/Text.hpp => module-gui/gui/widgets/text/Text.hpp +9 -12
@@ 55,7 55,7 @@ namespace gui
        TextCursor *cursor                      = nullptr;
        CursorStartPosition cursorStartPosition = CursorStartPosition::DocumentEnd;
        std::unique_ptr<TextDocument> document  = std::make_unique<TextDocument>(std::list<TextBlock>());
        InputMode *mode                         = nullptr;
        InputMode *inputMode                    = nullptr;
        std::unique_ptr<Lines> lines            = nullptr;

        void buildDocument(const UTF8 &text);


@@ 68,8 68,8 @@ namespace gui
        unsigned int getCursorDrawSpace();

      public:
        ExpandMode expandMode    = ExpandMode::None;
        EditMode editMode        = EditMode::Edit;
        ExpandMode expandMode = ExpandMode::None;
        EditMode editMode     = EditMode::Edit;

        [[nodiscard]] bool isMode(EditMode edit) const
        {


@@ 77,7 77,7 @@ namespace gui
        }

      protected:
        TextType textType = TextType::MultiLine;
        TextType textType         = TextType::MultiLine;
        TextEllipsis ellipsisType = TextEllipsis::None;
        std::list<TextLimit> limitsList;



@@ 160,16 160,13 @@ namespace gui
        void setMinimumHeightToFitText(unsigned int linesCount = 1);
        void setColor(Color color);

        /// move ownership of mode ptr to Text
        void setInputMode(InputMode *&&mode);
        std::string getInputModeKeyMap();
        InputMode::Mode detectInputMode();

        // virtual methods from Item
        bool onInput(const InputEvent &inputEvent) override;
        /// move ownership of mode ptr to Text
        void setInputMode(InputMode *&&mode)
        {
            if (this->mode != nullptr) {
                delete this->mode;
            }
            this->mode = mode;
        };
        bool onFocus(bool state) override;
        bool onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) override;
        void setRadius(int value) override;

M module-gui/gui/widgets/text/core/cursors/TextBlockCursor.cpp => module-gui/gui/widgets/text/core/cursors/TextBlockCursor.cpp +25 -0
@@ 301,6 301,31 @@ namespace gui
        return nextBlock != blocksEnd() && nextBlock->isEmpty() && checkCurrentBlockNoNewLine();
    }

    auto BlockCursor::checkSentenceBeginning() const -> bool
    {
        // If position npos or 0 -> block sentence beginning.
        if (getPosition() == text::npos || getPosition() == 0) {
            return true;
        }
        else {

            unsigned int detectIndex = 1;
            // Iterate through white space
            while (currentBlock()->getText(getPosition() - detectIndex).substr(0, 1) == " ") {

                // If dot with space found return true
                if (currentBlock()->getText(getPosition() - detectIndex - 1).substr(0, 2) == ". ") {
                    return true;
                }
                detectIndex++;
            }

            // If detect bigger than Position - white spaces on block beginning.
            return detectIndex > getPosition();
        }
        return false;
    }

    const TextBlock &BlockCursor::operator*()
    {
        return *currentBlock();

M module-gui/gui/widgets/text/core/cursors/TextBlockCursor.hpp => module-gui/gui/widgets/text/core/cursors/TextBlockCursor.hpp +1 -0
@@ 145,6 145,7 @@ namespace gui

        auto checkIfBlockFirstAndEmpty(std::list<TextBlock>::iterator block) -> bool;
        auto checkIfNextBlockEmpty(std::list<TextBlock>::iterator nextBlock) -> bool;
        auto checkSentenceBeginning() const -> bool;

        auto operator+=(unsigned int) -> BlockCursor &;
        auto operator++() -> BlockCursor &;

M module-gui/gui/widgets/text/modes/InputMode.cpp => module-gui/gui/widgets/text/modes/InputMode.cpp +9 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "InputMode.hpp"


@@ 19,6 19,8 @@ static std::string getInputName(InputMode::Mode m)
    switch (m) {
    case InputMode::digit:
        return "123";
    case InputMode::Abc:
        return "Abc";
    case InputMode::ABC:
        return "ABC";
    case InputMode::abc:


@@ 69,6 71,12 @@ const std::string &InputMode::get()
    return utils::getInputLanguageFilename(actualInputMode);
}

const std::string &InputMode::get(InputMode::Mode mode)
{
    auto selectedInputMode = input_mode.at(mode);
    return utils::getInputLanguageFilename(selectedInputMode);
}

void InputMode::show_input_type()
{
    LOG_INFO("Mode: %d", modeNow());

M module-gui/gui/widgets/text/modes/InputMode.hpp => module-gui/gui/widgets/text/modes/InputMode.hpp +3 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 15,6 15,7 @@ class InputMode
    enum Mode
    {
        digit,
        Abc,
        abc,
        ABC,
        phone,


@@ 45,6 46,7 @@ class InputMode
    }
    void next();
    const std::string &get();
    const std::string &get(Mode mode);
    void select_special_char();
    [[nodiscard]] bool is(Mode mode) const
    {

M module-gui/test/test-catch-text/test-gui-Text.cpp => module-gui/test/test-catch-text/test-gui-Text.cpp +114 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "InitializedFontManager.hpp"


@@ 75,7 75,7 @@ namespace gui

        [[nodiscard]] auto *getInputMode()
        {
            return mode;
            return inputMode;
        }

        auto removeNCharacters(unsigned int n)


@@ 222,6 222,118 @@ TEST_CASE("handle input mode ABC/abc/1234")
    }
}

TEST_CASE("handle input mode Abc/ABC/abc")
{
    mockup::fontManager();
    using namespace gui;

    std::string testString_Abc         = "Adg";
    std::string testString_ABC         = "ADG";
    std::string testString_abc         = "adg";
    std::string testString_sentenceEnd = ". ";

    auto addTestStringByOnInput = [](gui::Text &text) {
        text.onInput(gui::InputEvent(
            {
                RawKey::State::Released,
                bsp::KeyCodes::NumericKey2,
            },
            gui::InputEvent::State::keyReleasedShort));
        text.onInput(gui::InputEvent(
            {
                RawKey::State::Released,
                bsp::KeyCodes::NumericKey3,
            },
            gui::InputEvent::State::keyReleasedShort));
        text.onInput(gui::InputEvent(
            {
                RawKey::State::Released,
                bsp::KeyCodes::NumericKey4,
            },
            gui::InputEvent::State::keyReleasedShort));
    };

    auto addSentenceEndByOnInput = [](gui::Text &text) {
        text.onInput(gui::InputEvent(
            {
                RawKey::State::Released,
                bsp::KeyCodes::NumericKey1,
            },
            gui::InputEvent::State::keyReleasedShort));
        text.onInput(gui::InputEvent(
            {
                RawKey::State::Released,
                bsp::KeyCodes::NumericKey0,
            },
            gui::InputEvent::State::keyReleasedShort));
    };

    auto modes     = {InputMode::Abc, InputMode::ABC, InputMode::abc};
    auto next_mode = gui::InputEvent({}, gui::InputEvent::State::keyReleasedShort, gui::KeyCode::KEY_AST);

    SECTION("Abc mode check")
    {
        auto text = gui::TestText();
        text.setInputMode(new InputMode(modes));

        REQUIRE(text.getInputMode()->is(InputMode::Abc));

        addTestStringByOnInput(text);

        REQUIRE(text.getText() == testString_Abc);
    }

    SECTION("ABC -> abc -> ABC")
    {
        auto text = gui::TestText();
        text.setInputMode(new InputMode(modes));

        text.onInput(next_mode);
        REQUIRE(text.getInputMode()->is(InputMode::ABC));
        addTestStringByOnInput(text);
        REQUIRE(text.getText() == testString_ABC);
        text.clear();

        text.onInput(next_mode);
        REQUIRE(text.getInputMode()->is(InputMode::abc));
        addTestStringByOnInput(text);
        REQUIRE(text.getText() == testString_abc);
        text.clear();

        text.onInput(next_mode);
        REQUIRE(text.getInputMode()->is(InputMode::Abc));
        addTestStringByOnInput(text);
        REQUIRE(text.getText() == testString_Abc);
        text.clear();
    }

    SECTION("Abc with sentence detection")
    {
        auto text = gui::TestText();
        text.setInputMode(new InputMode(modes));

        REQUIRE(text.getInputMode()->is(InputMode::Abc));
        addTestStringByOnInput(text);
        REQUIRE(text.getText() == testString_Abc);

        text.onInput(next_mode);
        REQUIRE(text.getInputMode()->is(InputMode::ABC));
        addTestStringByOnInput(text);
        REQUIRE(text.getText() == testString_Abc + testString_ABC);

        text.onInput(next_mode);
        text.onInput(next_mode);
        REQUIRE(text.getInputMode()->is(InputMode::Abc));
        addTestStringByOnInput(text);
        REQUIRE(text.getText() == testString_Abc + testString_ABC + testString_abc);

        addSentenceEndByOnInput(text);
        addTestStringByOnInput(text);
        REQUIRE(text.getText() ==
                testString_Abc + testString_ABC + testString_abc + testString_sentenceEnd + testString_Abc);
    }
}

TEST_CASE("handle longpress for digit in ABC mode")
{
    auto text  = gui::TestText();

M module-gui/test/test-catch-text/test-gui-TextBlockCursor.cpp => module-gui/test/test-catch-text/test-gui-TextBlockCursor.cpp +72 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <widgets/text/core/TextBlock.hpp>


@@ 477,3 477,74 @@ TEST_CASE("operator* for TextBlock")

    REQUIRE((*cursor).length() == doc.getText().length());
}

TEST_CASE("check sentence beginning")
{
    using namespace gui;
    auto [doc, font] = mockup::buildTestDocument();
    auto cursor      = BlockCursor(&doc, 0, 0, nullptr);

    auto addStringToBlock = [](const std::string &input, gui::BlockCursor &cursor) {
        for (unsigned int i = 0; i < input.length(); ++i) {
            cursor.addChar(input[i]);
            ++cursor;
        }
    };

    auto removeNSignsInBlock = [](unsigned int n, gui::BlockCursor &cursor) {
        for (unsigned int i = 0; i < n; ++i) {
            cursor.removeChar();
            --cursor;
        }
    };

    // Empty document sentence beginning.
    REQUIRE(cursor.checkSentenceBeginning());

    // Add empty spaces at document beginning - still sentence beginning
    addStringToBlock("  ", cursor);
    REQUIRE(cursor.checkSentenceBeginning());

    // Add one sign and check sentence beginning
    addStringToBlock("P", cursor);
    REQUIRE(!cursor.checkSentenceBeginning());

    // Remove one sign to check sentence beginning
    removeNSignsInBlock(1, cursor);
    REQUIRE(cursor.checkSentenceBeginning());

    // Add string ending with dot
    addStringToBlock("Test.", cursor);
    // No white space so not sentence beginning
    REQUIRE(!cursor.checkSentenceBeginning());

    // Add empty space. New sentence should be detected
    addStringToBlock(" ", cursor);
    REQUIRE(cursor.checkSentenceBeginning());

    // Remove space.
    removeNSignsInBlock(1, cursor);
    REQUIRE(!cursor.checkSentenceBeginning());

    // Add string ending with dot and white space
    addStringToBlock("Test. ", cursor);
    REQUIRE(cursor.checkSentenceBeginning());

    // Add extra sign
    addStringToBlock("P", cursor);
    REQUIRE(!cursor.checkSentenceBeginning());

    // Check number of blocks
    REQUIRE(cursor.getBlockNumber() == 0);

    // Add newline - new block and new sentence
    addStringToBlock("\n", cursor);
    // Check number of blocks
    REQUIRE(cursor.getBlockNumber() == 1);
    REQUIRE(cursor.checkSentenceBeginning());

    // Add string ending with dot
    addStringToBlock("Test.", cursor);
    // No white space so not sentence beginning
    REQUIRE(!cursor.checkSentenceBeginning());
}