mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-24 22:08:33 -05:00
Add Text scrolling, Add text starting position, Added text scrolling, updated TextLine line endings, TextBlocks newline handling, updated SMS Bubble and Notes text items. Added tests for scrolling.
262 lines
9.4 KiB
C++
262 lines
9.4 KiB
C++
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include "TextLineCursor.hpp"
|
|
#include "Text.hpp"
|
|
#include "log/log.hpp"
|
|
|
|
#define debug_text_cursor(...)
|
|
// #define debug_text_cursor(...) LOG_DEBUG(__VA_ARGS__)
|
|
|
|
namespace gui
|
|
{
|
|
TextLineCursor::TextLineCursor(gui::Text *parent, unsigned int pos, unsigned int block)
|
|
: TextCursor(parent, pos, block)
|
|
{}
|
|
|
|
auto TextLineCursor::checkNextLineDocumentEnd(unsigned int selectedLineNumber) -> bool
|
|
{
|
|
auto nextLine = text->lines->getLine(selectedLineNumber + 1);
|
|
auto selectedLine = text->lines->getLine(selectedLineNumber);
|
|
|
|
return nextLine->length() == 0 && selectedLine->getEnd() != TextBlock::End::Newline;
|
|
}
|
|
|
|
void TextLineCursor::handleDownNavigation(unsigned int selectedLineNumber, unsigned int selectedLineCursorPosition)
|
|
{
|
|
auto selectedLine = text->lines->getLine(selectedLineNumber);
|
|
auto nextLine = text->lines->getLine(selectedLineNumber + 1);
|
|
auto nextLineEndAddition = nextLine->getEnd() == TextBlock::End::Newline ? 1 : 0;
|
|
|
|
auto noNewAtBeginningAddition =
|
|
selectedLine->getEnd() == TextBlock::End::None && selectedLineCursorPosition == 0 ? 1 : 0;
|
|
|
|
auto nextLineMoveCount = nextLine->length() > selectedLineCursorPosition
|
|
? selectedLineCursorPosition
|
|
: nextLine->length() - nextLineEndAddition;
|
|
|
|
// Next line will be displayed as part of Right move action on right lines border
|
|
auto moveCount =
|
|
(selectedLine->length() - selectedLineCursorPosition) + nextLineMoveCount + noNewAtBeginningAddition;
|
|
|
|
moveCursor(NavigationDirection::RIGHT, moveCount);
|
|
}
|
|
|
|
void TextLineCursor::handleUpNavigation(unsigned int selectedLineNumber, unsigned int selectedLineCursorPosition)
|
|
{
|
|
auto previousLine = text->lines->getLine(selectedLineNumber - 1);
|
|
auto previousLineEndAddition = previousLine->getEnd() == TextBlock::End::Newline ? 1 : 0;
|
|
|
|
auto previousLineMoveCount = previousLine->length() > selectedLineCursorPosition
|
|
? previousLine->length() - previousLineEndAddition - selectedLineCursorPosition
|
|
: 0;
|
|
|
|
auto newLineAtBeginningSubtraction =
|
|
previousLine->getEnd() == TextBlock::End::Newline && selectedLineCursorPosition == 0 ? 1 : 0;
|
|
|
|
auto moveCount = selectedLineCursorPosition + previousLineEndAddition + previousLineMoveCount -
|
|
newLineAtBeginningSubtraction;
|
|
|
|
moveCursor(NavigationDirection::LEFT, moveCount);
|
|
}
|
|
|
|
auto TextLineCursor::displayPreviousLine() -> bool
|
|
{
|
|
if (!text->lines->previousLinesStart.empty()) {
|
|
|
|
auto [lineStartBlockNumber, lineStartBlockPosition] = text->lines->previousLinesStart.back();
|
|
text->lines->previousLinesStart.pop_back();
|
|
text->lines->drawStartConditions = {lineStartBlockNumber, lineStartBlockPosition};
|
|
|
|
text->drawLines();
|
|
|
|
auto moveCount = text->lines->first().length();
|
|
|
|
// update cursor position on screen so it points in same place after scroll
|
|
onScreenPosition += moveCount;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
auto TextLineCursor::displayNextLine() -> bool
|
|
{
|
|
if (text->lines->stopCondition != LinesDrawStop::OUT_OF_TEXT) {
|
|
|
|
text->lines->addToPreviousLinesStartList(text->lines->first().getLineStartBlockNumber(),
|
|
text->lines->first().getLineStartBlockPosition());
|
|
|
|
auto moveCount = text->lines->first().length();
|
|
|
|
text->lines->drawStartConditions = {text->lines->getLine(1)->getLineStartBlockNumber(),
|
|
text->lines->getLine(1)->getLineStartBlockPosition()};
|
|
|
|
text->drawLines();
|
|
|
|
// update cursor position on screen so it points in same place after scroll
|
|
onScreenPosition -= moveCount;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
auto TextLineCursor::moveCursor(gui::NavigationDirection direction) -> gui::TextCursor::Move
|
|
{
|
|
debug_text_cursor("Before move cursor: screen pos: %d block: %d pos: %d %s",
|
|
onScreenPosition,
|
|
getBlockNumber(),
|
|
BlockCursor::getPosition(),
|
|
atBegin() ? "at begin" : "middle");
|
|
|
|
if (!checkDocument()) {
|
|
return Move::Error;
|
|
}
|
|
|
|
auto [selectedLine, selectedLineCursorPosition, selectedLineNumber] = getSelectedLine();
|
|
|
|
// last line is sometimes not visible (sometimes empty) and added only for scrolling purposes.
|
|
unsigned int lastVisibleLineNumber = text->lines->countVisible() - 1;
|
|
|
|
debug_text_cursor(
|
|
"Selected line cursor pos: %d, selected line nr: %d", selectedLineCursorPosition, selectedLineNumber);
|
|
|
|
// No lines draw yet to navigate top bottom -> check left/right text navigation
|
|
if (selectedLine == nullptr) {
|
|
return TextCursor::moveCursor(direction);
|
|
}
|
|
|
|
/// up - corner case
|
|
if ((checkNpos() || (direction == NavigationDirection::UP && selectedLineNumber == 0))) {
|
|
|
|
if (!text->lines->previousLinesStart.empty()) {
|
|
|
|
displayPreviousLine();
|
|
|
|
return moveCursor(NavigationDirection::UP);
|
|
}
|
|
else {
|
|
return TextCursor::Move::Start;
|
|
}
|
|
}
|
|
|
|
/// down - corner case
|
|
if ((checkNpos() || (direction == NavigationDirection::DOWN && selectedLineNumber == lastVisibleLineNumber))) {
|
|
|
|
if (text->lines->stopCondition != LinesDrawStop::OUT_OF_TEXT) {
|
|
|
|
if (checkNextLineDocumentEnd(selectedLineNumber)) {
|
|
return TextCursor::Move::End;
|
|
}
|
|
|
|
handleDownNavigation(selectedLineNumber, selectedLineCursorPosition);
|
|
|
|
return TextCursor::Move::Down;
|
|
}
|
|
else {
|
|
return TextCursor::Move::End;
|
|
}
|
|
}
|
|
|
|
/// left - corner case
|
|
if ((checkNpos() || (direction == NavigationDirection::LEFT && selectedLineNumber == 0))) {
|
|
|
|
if (!text->lines->previousLinesStart.empty() &&
|
|
(text->lines->first().getLineStartBlockNumber() == getBlockNumber() &&
|
|
text->lines->first().getLineStartBlockPosition() == BlockCursor::getPosition())) {
|
|
|
|
displayPreviousLine();
|
|
|
|
return TextCursor::moveCursor(NavigationDirection::LEFT);
|
|
}
|
|
else {
|
|
return TextCursor::moveCursor(direction);
|
|
}
|
|
}
|
|
|
|
/// right - corner case
|
|
if ((checkNpos() || (direction == NavigationDirection::RIGHT && selectedLineNumber == lastVisibleLineNumber))) {
|
|
|
|
if (text->lines->stopCondition != LinesDrawStop::OUT_OF_TEXT &&
|
|
(selectedLineCursorPosition ==
|
|
(text->lines->getLine(lastVisibleLineNumber)->length() -
|
|
(text->lines->getLine(lastVisibleLineNumber)->getEnd() == TextBlock::End::Newline ? 1 : 0)))) {
|
|
|
|
if (checkNextLineDocumentEnd(selectedLineNumber)) {
|
|
return TextCursor::Move::End;
|
|
}
|
|
|
|
auto ret = TextCursor::moveCursor(NavigationDirection::RIGHT);
|
|
|
|
displayNextLine();
|
|
|
|
return ret;
|
|
}
|
|
else {
|
|
|
|
return TextCursor::moveCursor(direction);
|
|
}
|
|
}
|
|
|
|
if (direction == NavigationDirection::UP) {
|
|
|
|
handleUpNavigation(selectedLineNumber, selectedLineCursorPosition);
|
|
|
|
debug_text_cursor("After move cursor: screen pos: %d block: %d pos: %d %s",
|
|
onScreenPosition,
|
|
getBlockNumber(),
|
|
BlockCursor::getPosition(),
|
|
atBegin() ? "at begin" : "middle");
|
|
|
|
return Move::Up;
|
|
}
|
|
|
|
if (direction == NavigationDirection::DOWN) {
|
|
|
|
handleDownNavigation(selectedLineNumber, selectedLineCursorPosition);
|
|
|
|
debug_text_cursor("After move cursor: screen pos: %d block: %d pos: %d %s",
|
|
onScreenPosition,
|
|
getBlockNumber(),
|
|
BlockCursor::getPosition(),
|
|
atBegin() ? "at begin" : "middle");
|
|
|
|
return Move::Down;
|
|
}
|
|
|
|
return TextCursor::moveCursor(direction);
|
|
}
|
|
|
|
TextCursor::Move TextLineCursor::moveCursor(NavigationDirection direction, unsigned int n)
|
|
{
|
|
auto ret = TextCursor::Move::Start;
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
ret = TextLineCursor::moveCursor(direction);
|
|
|
|
if (ret == Move::Start || ret == Move::End || ret == Move::Error) {
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool TextLineCursor::removeChar()
|
|
{
|
|
auto linesSize = text->lines->size();
|
|
|
|
auto ret = TextCursor::removeChar();
|
|
|
|
// After sign removal check if lines count decreased - if so add previous line,
|
|
if (ret && linesSize > text->lines->size()) {
|
|
displayPreviousLine();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
} // namespace gui
|