Rewrite linebreaking algorithm.
Previous version was overly simplistic to deal correctly with multiple spaces or textruns not separated by spaces. Fixes https://gitlab.com/kicad/code/kicad/issues/14683
This commit is contained in:
parent
2ae2d7ee1a
commit
e3adc76a72
|
@ -35,6 +35,7 @@
|
||||||
// The "official" name of the building Kicad stroke font (always existing)
|
// The "official" name of the building Kicad stroke font (always existing)
|
||||||
#include <font/kicad_font_name.h>
|
#include <font/kicad_font_name.h>
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
#include "wx/tokenzr.h"
|
||||||
|
|
||||||
|
|
||||||
// markup_parser.h includes pegtl.hpp which includes windows.h... which leaks #define DrawText
|
// markup_parser.h includes pegtl.hpp which includes windows.h... which leaks #define DrawText
|
||||||
|
@ -426,20 +427,22 @@ void wordbreakMarkup( std::vector<std::pair<wxString, int>>* aWords,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wxString textRun = aNode->asWxString();
|
wxString textRun = aNode->asWxString();
|
||||||
wxArrayString words;
|
wxStringTokenizer tokenizer( textRun, " ", wxTOKEN_RET_DELIMS );
|
||||||
|
std::vector<wxString> words;
|
||||||
|
|
||||||
wxStringSplit( textRun, words, ' ' );
|
while( tokenizer.HasMoreTokens() )
|
||||||
|
words.emplace_back( tokenizer.GetNextToken() );
|
||||||
|
|
||||||
if( textRun.EndsWith( wxS( " " ) ) )
|
for( const wxString& word : words )
|
||||||
words.Add( wxS( " " ) );
|
|
||||||
|
|
||||||
for( size_t ii = 0; ii < words.size(); ++ii )
|
|
||||||
{
|
{
|
||||||
int w = aFont->GetTextAsGlyphs( nullptr, nullptr, words[ii], aSize, { 0, 0 },
|
wxString chars = word;
|
||||||
|
chars.Trim();
|
||||||
|
|
||||||
|
int w = aFont->GetTextAsGlyphs( nullptr, nullptr, chars, aSize, { 0, 0 },
|
||||||
ANGLE_0, false, { 0, 0 }, textStyle ).x;
|
ANGLE_0, false, { 0, 0 }, textStyle ).x;
|
||||||
|
|
||||||
aWords->emplace_back( std::make_pair( words[ii], w ) );
|
aWords->emplace_back( std::make_pair( word, w ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,35 +494,70 @@ void FONT::LinebreakText( wxString& aText, int aColumnWidth, const VECTOR2I& aSi
|
||||||
|
|
||||||
for( size_t ii = 0; ii < textLines.Count(); ++ii )
|
for( size_t ii = 0; ii < textLines.Count(); ++ii )
|
||||||
{
|
{
|
||||||
bool virginLine = true;
|
std::vector<std::pair<wxString, int>> markup;
|
||||||
int lineWidth = 0;
|
|
||||||
std::vector<std::pair<wxString, int>> words;
|
std::vector<std::pair<wxString, int>> words;
|
||||||
|
|
||||||
wordbreakMarkup( &words, textLines[ii], aSize, textStyle );
|
wordbreakMarkup( &markup, textLines[ii], aSize, textStyle );
|
||||||
|
|
||||||
for( size_t jj = 0; jj < words.size(); /* advance in loop */ )
|
for( const auto& [ run, runWidth ] : markup )
|
||||||
{
|
{
|
||||||
if( virginLine )
|
if( !words.empty() && !words.back().first.EndsWith( ' ' ) )
|
||||||
{
|
{
|
||||||
// First word is always placed, even when wider than columnWidth.
|
words.back().first += run;
|
||||||
aText += words[jj].first;
|
words.back().second += runWidth;
|
||||||
lineWidth += words[jj].second;
|
|
||||||
jj++;
|
|
||||||
|
|
||||||
virginLine = false;
|
|
||||||
}
|
|
||||||
else if( lineWidth + spaceWidth + words[jj].second < aColumnWidth - aThickness )
|
|
||||||
{
|
|
||||||
aText += " " + words[jj].first;
|
|
||||||
lineWidth += spaceWidth + words[jj].second;
|
|
||||||
jj++;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
aText += '\n';
|
words.emplace_back( std::make_pair( run, runWidth ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool buryMode = false;
|
||||||
|
int lineWidth = 0;
|
||||||
|
wxString pendingSpaces;
|
||||||
|
|
||||||
|
for( const auto& [ word, wordWidth ] : words )
|
||||||
|
{
|
||||||
|
int pendingSpaceWidth = (int) pendingSpaces.Length() * spaceWidth;
|
||||||
|
bool overflow = lineWidth + pendingSpaceWidth + wordWidth > aColumnWidth - aThickness;
|
||||||
|
|
||||||
|
if( overflow && pendingSpaces.Length() > 0 )
|
||||||
|
{
|
||||||
|
aText += '\n';
|
||||||
lineWidth = 0;
|
lineWidth = 0;
|
||||||
virginLine = true;
|
pendingSpaces = wxEmptyString;
|
||||||
|
pendingSpaceWidth = 0;
|
||||||
|
buryMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( word == wxS( " " ) )
|
||||||
|
{
|
||||||
|
pendingSpaces += word;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( buryMode )
|
||||||
|
{
|
||||||
|
buryMode = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aText += pendingSpaces;
|
||||||
|
lineWidth += pendingSpaceWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( word.EndsWith( ' ' ) )
|
||||||
|
{
|
||||||
|
aText += word.Left( word.Length() - 1 );
|
||||||
|
pendingSpaces = wxS( " " );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
aText += word;
|
||||||
|
pendingSpaces = wxEmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
lineWidth += wordWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue