// 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) && (selectedLineNumber >= 2) && (text->lines->getLine(selectedLineNumber - 2)->getEnd() == TextBlock::End::None) ? 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::OutOfText) { 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::OutOfText) { 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::OutOfText && (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