Line Wrapping more basic logic.

This commit is contained in:
Martín Lucas Golini
2024-05-07 01:33:36 -03:00
parent beb6ff974c
commit d371ac3170
3 changed files with 173 additions and 11 deletions

View File

@@ -38,6 +38,8 @@ class FontStyleConfig {
OutlineColor == other.OutlineColor;
}
bool operator!=( const FontStyleConfig& other ) { return !( *this == other ); }
virtual void updateFontStyleConfig( const FontStyleConfig& fontStyleConfig ) {
Font = fontStyleConfig.Font;
Style = fontStyleConfig.Style;

View File

@@ -10,16 +10,27 @@ using namespace EE::UI::Doc;
namespace EE { namespace UI {
enum class LineWrapMode { Letter, Word };
enum class LineWrapMode { NoWrap, Letter, Word };
struct LineWrapInfo {
std::vector<size_t> wraps;
Float paddingStart;
bool hasWraps() const { return !wraps.empty(); }
std::vector<Int64> wraps;
Float paddingStart{ 0.f };
};
class LineWrapping {
class EE_API LineWrapping {
public:
struct Config {
LineWrapMode mode{ LineWrapMode::NoWrap };
bool keepIndentation{ true };
Uint32 tabWidth{ 4 };
std::optional<Uint32> maxCharactersWidth;
bool operator==( const Config& other ) {
return mode == other.mode && keepIndentation == other.keepIndentation &&
tabWidth == other.tabWidth && maxCharactersWidth == other.maxCharactersWidth;
}
bool operator!=( const Config& other ) { return !(*this == other); }
};
static LineWrapInfo computeLineBreaks( const String& string, const FontStyleConfig& fontStyle,
Float maxWidth, LineWrapMode mode, bool keepIndentation,
Uint32 tabWidth = 4 );
@@ -29,10 +40,32 @@ class LineWrapping {
LineWrapMode mode, bool keepIndentation,
Uint32 tabWidth = 4 );
LineWrapping( std::shared_ptr<TextDocument> doc, FontStyleConfig fontStyle, Config config );
size_t getTotalLines() const;
const Config& config() const { return mConfig; }
void reconstructBreaks();
void updateBreaks( Int64 fromLine, Int64 toLine, Int64 numLines );
void setConfig( Config config );
void setMaxWidth( Float maxWidth );
void setFontStyle( FontStyleConfig fontStyle );
protected:
std::shared_ptr<TextDocument> mDoc;
FontStyleConfig mFontStyle;
Config mConfig;
Float mMaxWidth{ 0 };
std::unordered_map<Int64, TextPosition> mWrappedLines;
std::unordered_map<Int64, Int64> mWrappedLineToIndex;
std::vector<TextPosition> mWrappedLines;
std::vector<Float> mWrappedLinesOffset;
std::vector<Int64> mWrappedLineToIndex;
Int64 toWrappedIndex( Int64 docIdx, bool retLast );
};
}} // namespace EE::UI

View File

@@ -10,7 +10,8 @@ LineWrapInfo LineWrapping::computeLineBreaks( const String& string,
LineWrapMode mode, bool keepIndentation,
Uint32 tabWidth ) {
LineWrapInfo info;
if ( string.empty() || nullptr == fontStyle.Font )
info.wraps.push_back( 0 );
if ( string.empty() || nullptr == fontStyle.Font || mode == LineWrapMode::NoWrap )
return info;
if ( keepIndentation ) {
@@ -49,7 +50,7 @@ LineWrapInfo LineWrapping::computeLineBreaks( const String& string,
else if ( ( curChar ) == '\r' )
w = 0;
if ( curChar != '\r' && !isMonospace ) {
if ( !isMonospace && curChar != '\r' ) {
w += fontStyle.Font->getKerning( prevChar, curChar, fontStyle.CharacterSize, bold,
italic, outlineThickness );
prevChar = curChar;
@@ -82,13 +83,15 @@ LineWrapInfo LineWrapping::computeLineBreaks( const TextDocument& doc, size_t li
const FontStyleConfig& fontStyle, Float maxWidth,
LineWrapMode mode, bool keepIndentation,
Uint32 tabWidth ) {
if ( nullptr == fontStyle.Font || fontStyle.Font->isMonospace() ||
nullptr == doc.getHighlighter() || doc.getSyntaxDefinition().getPatterns().empty() ) {
if ( nullptr == fontStyle.Font || mode == LineWrapMode::NoWrap ||
fontStyle.Font->isMonospace() || nullptr == doc.getHighlighter() ||
doc.getSyntaxDefinition().getPatterns().empty() ) {
return computeLineBreaks( doc.line( line ).getText(), fontStyle, maxWidth, mode,
keepIndentation, tabWidth );
}
LineWrapInfo info;
info.wraps.push_back( 0 );
const auto& string = doc.line( line ).getText();
const auto& tokens = doc.getHighlighter()->getLine( line );
@@ -167,4 +170,128 @@ LineWrapInfo LineWrapping::computeLineBreaks( const TextDocument& doc, size_t li
return info;
}
LineWrapping::LineWrapping( std::shared_ptr<TextDocument> doc, FontStyleConfig fontStyle,
Config config ) :
mDoc( std::move( doc ) ),
mFontStyle( std::move( fontStyle ) ),
mConfig( std::move( config ) ) {}
void LineWrapping::setMaxWidth( Float maxWidth ) {
if ( maxWidth != mMaxWidth ) {
mMaxWidth = maxWidth;
reconstructBreaks();
}
}
void LineWrapping::setFontStyle( FontStyleConfig fontStyle ) {
if ( fontStyle != mFontStyle ) {
mFontStyle = std::move( fontStyle );
reconstructBreaks();
}
}
void LineWrapping::setConfig( Config config ) {
if ( config != mConfig ){
mConfig = std::move( config );
reconstructBreaks();
}
}
void LineWrapping::reconstructBreaks() {
if ( 0 == mMaxWidth )
return;
mWrappedLines.clear();
mWrappedLineToIndex.clear();
mWrappedLinesOffset.clear();
size_t linesCount = mDoc->linesCount();
mWrappedLines.reserve( linesCount * 2 );
mWrappedLinesOffset.reserve( linesCount );
for ( auto i = 0; i < linesCount; i++ ) {
auto lb = computeLineBreaks( *mDoc, i, mFontStyle, mMaxWidth, mConfig.mode,
mConfig.keepIndentation, mConfig.tabWidth );
mWrappedLinesOffset.emplace_back( lb.paddingStart );
for ( const auto& col : lb.wraps )
mWrappedLines.emplace_back( i, col );
}
std::optional<Int64> lastWrap;
size_t i = 0;
mWrappedLineToIndex.reserve( linesCount );
for ( const auto& wl : mWrappedLines ) {
if ( !lastWrap || *lastWrap != wl.line() ) {
mWrappedLineToIndex.emplace_back( i );
lastWrap = wl.line();
}
i++;
}
}
Int64 LineWrapping::toWrappedIndex( Int64 docIdx, bool retLast ) {
auto idx = mWrappedLineToIndex[docIdx < 0 ? 0
: ( docIdx >= mWrappedLineToIndex.size()
? mWrappedLineToIndex.size() - 1
: docIdx )];
if ( retLast ) {
Int64 lastOfLine = mWrappedLines[idx].line();
for ( auto i = idx + 1; i < mWrappedLines.size(); i++ ) {
if ( mWrappedLines[i].line() == lastOfLine )
idx = i;
else
break;
}
}
return idx;
}
void LineWrapping::updateBreaks( Int64 fromLine, Int64 toLine, Int64 numLines ) {
// Get affected wrapped range
Int64 oldIdxFrom = toWrappedIndex( fromLine, false );
Int64 oldIdxTo = toWrappedIndex( toLine, true );
// Remove old wrapped lines
mWrappedLines.erase( mWrappedLines.begin() + oldIdxFrom, mWrappedLines.begin() + oldIdxTo );
// Remove old offsets
mWrappedLinesOffset.erase( mWrappedLinesOffset.begin() + fromLine,
mWrappedLinesOffset.begin() + toLine );
// Shift the line numbers
if ( numLines != 0 ) {
Int64 wrappedLines = mWrappedLines.size();
for ( Int64 i = oldIdxFrom; i < wrappedLines; i++ )
mWrappedLines[i].setLine( mWrappedLines[i].line() + numLines );
}
// Recompute line breaks
for ( auto i = fromLine; i <= toLine; i++ ) {
auto lb = computeLineBreaks( *mDoc, i, mFontStyle, mMaxWidth, mConfig.mode,
mConfig.keepIndentation, mConfig.tabWidth );
mWrappedLinesOffset.insert( mWrappedLinesOffset.begin() + i, lb.paddingStart );
Int64 loffset = 0;
for ( const auto& col : lb.wraps ) {
mWrappedLines.insert( mWrappedLines.begin() + i + loffset, { i, col } );
loffset++;
}
}
// Recompute wrapped line to index
Int64 line = fromLine;
Int64 wrappedLines = mWrappedLines.size();
for ( Int64 wrappedIdx = oldIdxFrom; wrappedIdx < wrappedLines; wrappedIdx++ ) {
if ( mWrappedLines[wrappedIdx].column() == 0 ) {
mWrappedLineToIndex[line] = wrappedIdx;
line++;
}
}
mWrappedLineToIndex.resize( mDoc->linesCount() );
}
size_t LineWrapping::getTotalLines() const {
return mConfig.mode == LineWrapMode::NoWrap ? mDoc->linesCount() : mWrappedLines.size();
}
}} // namespace EE::UI