mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[escher/src/text_area] Add char limit in text_area line
Change-Id: I9284936f0202d788edc785aa3f7c82b45ab34cf5
This commit is contained in:
committed by
Émilie Feral
parent
51002066e9
commit
b92c819ea2
@@ -138,6 +138,7 @@ private:
|
||||
TextAreaDelegate * m_delegate;
|
||||
// Due to rect size limitation, the editor cannot display more than 1800 lines
|
||||
constexpr static int k_maxLines = 999;
|
||||
constexpr static int k_maxLineChars = 3000;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,24 +47,79 @@ bool TextArea::handleEventWithText(const char * text, bool indentation, bool for
|
||||
* indentation, stop here. */
|
||||
int spacesCount = 0;
|
||||
int totalIndentationSize = 0;
|
||||
int textLen = strlen(text);
|
||||
int addedTextLength = strlen(text);
|
||||
size_t previousTextLength = contentView()->getText()->textLength();
|
||||
char * insertionPosition = const_cast<char *>(cursorLocation());
|
||||
const char * textAreaBuffer = contentView()->text();
|
||||
if (indentation) {
|
||||
// Compute the indentation
|
||||
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
|
||||
totalIndentationSize = UTF8Helper::CountOccurrences(text, '\n') * spacesCount;
|
||||
if (contentView()->getText()->textLength() + textLen + totalIndentationSize >= contentView()->getText()->bufferSize()) {
|
||||
if (previousTextLength + addedTextLength + totalIndentationSize >= contentView()->getText()->bufferSize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the text will not overflow the max number of lines
|
||||
if (contentView()->getText()->textLineTotal() + UTF8Helper::CountOccurrences(text, '\n') >= k_maxLines) {
|
||||
/* KDCoordinate is a int16. We must limit the number of characters per line,
|
||||
* and lines per scripts, otherwise the line rects or content rect
|
||||
* height/width can overflow int16, which results in weird visual effects.*/
|
||||
// 1 - Number of Characters per line :
|
||||
if (previousTextLength + addedTextLength > k_maxLineChars) {
|
||||
/* Only check for long lines in long scripts. PreviousTextLength and
|
||||
* addedTextLength being greater than the actual number of glyphs is not an
|
||||
* issue here. After insertion, text buffer will have this structure :
|
||||
* ".../n"+"before"+"inserted1"+("/n.../n")?+"inserted2"+"after"+"\n..."
|
||||
* Lengths : b ib ia a
|
||||
* As maxBufferSize is lower than k_maxLineChars, there is no need to check
|
||||
* for inserted lines between "\n...\n" */
|
||||
static_assert(TextField::maxBufferSize() < k_maxLineChars, "Pasting text might cause content rect overflow.");
|
||||
|
||||
// Counting line text lengths before and after insertion.
|
||||
int b = 0;
|
||||
int a = 0;
|
||||
UTF8Helper::countGlyphsInLine(textAreaBuffer, &b, &a, insertionPosition);
|
||||
|
||||
if (a + b + addedTextLength > k_maxLineChars) {
|
||||
/* Overflow expected, depending on '/n' code point presence and position.
|
||||
* Counting : Glyphs inserted before first '/n' : ib
|
||||
* Glyphs inserted after last '/n' : ia
|
||||
* Number of '/n' : n */
|
||||
int glyphCount[3] = {0, 0, 0};
|
||||
UTF8Helper::PerformAtCodePoints(text, '\n',
|
||||
[](int, void * intArray, int, int) {
|
||||
// '\n' found, Increment n
|
||||
int * n = (int *)intArray + 2;
|
||||
*n = *n + 1;
|
||||
// Reset ia
|
||||
int * ia = (int *)intArray + 1;
|
||||
*ia = 0;
|
||||
},
|
||||
[](int, void * intArray, int, int) {
|
||||
if (((int *)intArray)[2] == 0) {
|
||||
// While no '\n' found, increment ib
|
||||
int * ib = (int *)intArray;
|
||||
*ib = *ib + 1;
|
||||
} else {
|
||||
// Increment ia
|
||||
int * ia = (int *)intArray + 1;
|
||||
*ia = *ia + 1;
|
||||
}
|
||||
},
|
||||
&glyphCount, 0, 0);
|
||||
// Insertion is not possible if one of the produced line is too long.
|
||||
if ((glyphCount[2] == 0 && a + glyphCount[0] + b > k_maxLineChars) || b + glyphCount[0] > k_maxLineChars || a + glyphCount[1] > k_maxLineChars) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2 - Total number of line :
|
||||
if (previousTextLength + addedTextLength > k_maxLines && contentView()->getText()->textLineTotal() + UTF8Helper::CountOccurrences(text, '\n') > k_maxLines) {
|
||||
// Only check for overflowed lines in long scripts to save computation
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -89,9 +144,9 @@ bool TextArea::handleEventWithText(const char * text, bool indentation, bool for
|
||||
UCodePointNull,
|
||||
true,
|
||||
nullptr,
|
||||
insertionPosition + textLen);
|
||||
insertionPosition + addedTextLength);
|
||||
}
|
||||
const char * endOfInsertedText = insertionPosition + textLen + totalIndentationSize;
|
||||
const char * endOfInsertedText = insertionPosition + addedTextLength + totalIndentationSize;
|
||||
const char * cursorPositionInCommand = TextInputHelpers::CursorPositionInCommand(insertionPosition, endOfInsertedText);
|
||||
|
||||
// Remove the Empty code points
|
||||
@@ -268,9 +323,16 @@ CodePoint TextArea::Text::removePreviousGlyph(char * * position) {
|
||||
assert(m_buffer <= *position && *position < m_buffer + m_bufferSize);
|
||||
|
||||
CodePoint removedCodePoint = 0;
|
||||
int removedSize = UTF8Helper::RemovePreviousGlyph(m_buffer, *position, &removedCodePoint);
|
||||
assert(removedSize > 0);
|
||||
|
||||
int removedSize = 0;
|
||||
if (UTF8Helper::PreviousCodePoint(m_buffer, *position) == '\n') {
|
||||
// See comments in handleEventWithText about max number of glyphs per line
|
||||
removedCodePoint = '\n';
|
||||
// removeText will handle max number of glyphs per line
|
||||
removedSize = removeText(*position-1, *position);
|
||||
} else {
|
||||
removedSize = UTF8Helper::RemovePreviousGlyph(m_buffer, *position, &removedCodePoint);
|
||||
assert(removedSize > 0);
|
||||
}
|
||||
// Set the new cursor position
|
||||
*position = *position - removedSize;
|
||||
return removedCodePoint;
|
||||
@@ -288,6 +350,24 @@ size_t TextArea::Text::removeText(const char * start, const char * end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Removing text can increase line length. See comments in handleEventWithText
|
||||
* about max number of glyphs per line. */
|
||||
if (textLength() - delta >= k_maxLineChars) {
|
||||
/* Only check for line length on long enough scripts. TextLength() and delta
|
||||
* being greater than the actual number of glyphs is not an issue here. */
|
||||
|
||||
// Counting text lengths between previous and last '/n' (non removed).
|
||||
int b = 0;
|
||||
int a = 0;
|
||||
UTF8Helper::countGlyphsInLine(text(), &b, &a, start, end);
|
||||
|
||||
if (a + b > k_maxLineChars) {
|
||||
// Resulting line would exceed limits, no text is removed
|
||||
// TODO error message: Add Message to explain failure to remove text
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t index = src - m_buffer; index < m_bufferSize; index++) {
|
||||
*dst = *src;
|
||||
if (*src == 0) {
|
||||
@@ -551,6 +631,9 @@ KDRect TextArea::ContentView::glyphFrameAtPosition(const char * text, const char
|
||||
assert(found);
|
||||
(void) found;
|
||||
|
||||
// Check for KDCoordinate overflow
|
||||
assert(x < KDCOORDINATE_MAX - glyphSize.width() && p.line() * glyphSize.height() < KDCOORDINATE_MAX - glyphSize.height());
|
||||
|
||||
return KDRect(
|
||||
x,
|
||||
p.line() * glyphSize.height(),
|
||||
|
||||
@@ -145,7 +145,9 @@ void TextInput::scrollToCursor() {
|
||||
* In order to avoid requiring two layouts, we allow overscrolling in
|
||||
* scrollToContentRect, and the last layout of the scroll view corrects the
|
||||
* size of the scroll view only once. */
|
||||
scrollToContentRect(contentView()->cursorRect(), true);
|
||||
KDRect cursorRect = contentView()->cursorRect();
|
||||
assert(cursorRect.top() >= 0 && cursorRect.right() >= 0 && cursorRect.bottom() >= 0 && cursorRect.left() >= 0);
|
||||
scrollToContentRect(cursorRect, true);
|
||||
}
|
||||
|
||||
void TextInput::deleteSelection() {
|
||||
@@ -201,7 +203,7 @@ bool TextInput::moveCursorLeft(int step) {
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// true is returned if there was at least one successful cursor mouvement
|
||||
// true is returned if there was at least one successful cursor movement
|
||||
return (i > 1 || canMove);
|
||||
}
|
||||
|
||||
@@ -218,7 +220,7 @@ bool TextInput::moveCursorRight(int step) {
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// true is returned if there was at least one successful cursor mouvement
|
||||
// true is returned if there was at least one successful cursor movement
|
||||
return (i > 1 || canMove);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,9 @@ const char * BeginningOfWord(const char * text, const char * word);
|
||||
// Returns the position of the first following char ' ', '\n' or 0
|
||||
const char * EndOfWord(const char * word);
|
||||
|
||||
// On a line, count number of glyphs before and after locations
|
||||
void countGlyphsInLine(const char * text, int * before, int * after, const char * beforeLocation, const char *afterLocation = nullptr);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -407,4 +407,18 @@ const char * EndOfWord(const char * word) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void countGlyphsInLine(const char * text, int * before, int * after, const char * beforeLocation, const char *afterLocation) {
|
||||
UTF8Helper::CodePointAction countGlyph = [](int, void * glyphCount, int, int) {
|
||||
int * castedCount = (int *) glyphCount;
|
||||
*castedCount = *castedCount + 1;
|
||||
};
|
||||
// Count glyphs before
|
||||
UTF8Helper::PerformAtCodePoints(text, UCodePointLineFeed, nullptr, countGlyph, before, 0, 0, UCodePointLineFeed, false, beforeLocation);
|
||||
if (afterLocation == nullptr) {
|
||||
afterLocation = beforeLocation;
|
||||
}
|
||||
// Count glyphs after
|
||||
UTF8Helper::PerformAtCodePoints(afterLocation, UCodePointLineFeed, nullptr, countGlyph, after, 0, 0, UCodePointLineFeed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ KDPoint KDContext::pushOrPullString(const char * text, KDPoint p, const KDFont *
|
||||
while (codePoint != UCodePointNull && (maxByteLength < 0 || codePointPointer < text + maxByteLength)) {
|
||||
codePointPointer = decoder.stringPosition();
|
||||
if (codePoint == UCodePointLineFeed) {
|
||||
assert(position.y() < KDCOORDINATE_MAX - glyphSize.height());
|
||||
position = KDPoint(0, position.y() + glyphSize.height());
|
||||
codePoint = decoder.nextCodePoint();
|
||||
} else if (codePoint == UCodePointTabulation) {
|
||||
|
||||
@@ -30,6 +30,7 @@ KDSize KDFont::stringSizeUntil(const char * text, const char * limit) const {
|
||||
currentStringPosition = decoder.stringPosition();
|
||||
codePoint = decoder.nextCodePoint();
|
||||
}
|
||||
assert(stringSize.width() >= 0 && stringSize.height() >= 0);
|
||||
return stringSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include <kandinsky/point.h>
|
||||
#include <assert.h>
|
||||
|
||||
KDPoint KDPoint::translatedBy(KDPoint other) const {
|
||||
assert((other.x() >= 0 && m_x <= KDCOORDINATE_MAX - other.x()) || (other.x() < 0 && m_x >= KDCOORDINATE_MIN - other.x()));
|
||||
assert((other.y() >= 0 && m_y <= KDCOORDINATE_MAX - other.y()) || (other.y() < 0 && m_y >= KDCOORDINATE_MIN - other.y()));
|
||||
|
||||
return KDPoint(m_x+other.x(), m_y+other.y());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user