Merge branch 'wxrichtextctrl_word_wrapping_opt' of https://github.com/mehmet-soyturk/wxWidgets

Optimize wrapping long lines in wxRichTextCtrl.

See https://github.com/wxWidgets/wxWidgets/pull/2523
This commit is contained in:
Vadim Zeitlin
2021-10-26 19:43:03 +02:00
2 changed files with 79 additions and 27 deletions

View File

@@ -4654,6 +4654,7 @@ protected:
// The lines that make up the wrapped paragraph // The lines that make up the wrapped paragraph
wxRichTextLineList m_cachedLines; wxRichTextLineList m_cachedLines;
wxVector<wxRichTextLine*> m_cachedLinesVect;
// Whether the paragraph is impacted by floating objects from above // Whether the paragraph is impacted by floating objects from above
int m_impactedByFloatingObjects; int m_impactedByFloatingObjects;

View File

@@ -5697,6 +5697,7 @@ void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
void wxRichTextParagraph::ClearLines() void wxRichTextParagraph::ClearLines()
{ {
WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines); WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
m_cachedLinesVect.clear();
} }
/// Get/set the object size for the given range. Returns false if the range /// Get/set the object size for the given range. Returns false if the range
@@ -6529,7 +6530,8 @@ wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
{ {
if (pos < (int) m_cachedLines.GetCount()) if (pos < (int) m_cachedLines.GetCount())
{ {
wxRichTextLine* line = m_cachedLines.Item(pos)->GetData(); wxASSERT(m_cachedLinesVect.size() == m_cachedLines.GetCount());
wxRichTextLine* line = m_cachedLinesVect[pos];
line->Init(this); line->Init(this);
return line; return line;
} }
@@ -6537,6 +6539,7 @@ wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
{ {
wxRichTextLine* line = new wxRichTextLine(this); wxRichTextLine* line = new wxRichTextLine(this);
m_cachedLines.Append(line); m_cachedLines.Append(line);
m_cachedLinesVect.push_back(line);
return line; return line;
} }
} }
@@ -6555,6 +6558,7 @@ bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
delete line; delete line;
} }
} }
m_cachedLinesVect.resize(lineCount);
return true; return true;
} }
@@ -6815,21 +6819,57 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons
int offset = GetRange().GetStart(); int offset = GetRange().GetStart();
wxString str = m_text; const bool allSelected = selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd();
if (context.HasVirtualText(this)) const bool noneSelected = selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd();
wxString stringChunk;
wxString stringWhole;
// If nothing or everything is selected, our algorithm does not need stringWhole. It
// is enough to preprocess stringChunk only.
// In case of partial selection we need to preprocess stringWhole too.
if (allSelected || noneSelected)
{ {
if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length()) const wxString* pWholeString = &m_text;
str = m_text; if (context.HasVirtualText(this))
{
if (context.GetVirtualText(this, stringWhole) && stringWhole.Length() == m_text.Length())
pWholeString = &stringWhole;
}
long len = range.GetLength();
stringChunk = pWholeString->Mid(range.GetStart() - offset, (size_t) len);
// Replace line break characters with spaces
wxString toRemove = wxRichTextLineBreakChar;
stringChunk.Replace(toRemove, wxT(" "));
if (textAttr.HasTextEffects() &&
(textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
{
stringChunk.MakeUpper();
}
} }
else
{
stringWhole = m_text;
if (context.HasVirtualText(this))
{
if (!context.GetVirtualText(this, stringWhole) || stringWhole.Length() != m_text.Length())
stringWhole = m_text;
}
// Replace line break characters with spaces // Replace line break characters with spaces
wxString toRemove = wxRichTextLineBreakChar; wxString toRemove = wxRichTextLineBreakChar;
str.Replace(toRemove, wxT(" ")); stringWhole.Replace(toRemove, wxT(" "));
if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))) if (textAttr.HasTextEffects() &&
str.MakeUpper(); (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
{
stringWhole.MakeUpper();
}
long len = range.GetLength(); long len = range.GetLength();
wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len); stringChunk = stringWhole.Mid(range.GetStart() - offset, (size_t) len);
}
// Test for the optimized situations where all is selected, or none // Test for the optimized situations where all is selected, or none
// is selected. // is selected.
@@ -6899,12 +6939,12 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons
// TODO: new selection code // TODO: new selection code
// (a) All selected. // (a) All selected.
if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd()) if (allSelected)
{ {
DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true); DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
} }
// (b) None selected. // (b) None selected.
else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd()) else if (noneSelected)
{ {
// Draw all unselected // Draw all unselected
DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false); DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
@@ -6914,6 +6954,8 @@ bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, cons
// (c) Part selected, part not // (c) Part selected, part not
// Let's draw unselected chunk, selected chunk, then unselected chunk. // Let's draw unselected chunk, selected chunk, then unselected chunk.
const wxString& str = stringWhole;
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT); dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
// 1. Initial unselected chunk, if any, up until start of selection. // 1. Initial unselected chunk, if any, up until start of selection.
@@ -7215,23 +7257,32 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz
bool haveDescent = false; bool haveDescent = false;
int startPos = range.GetStart() - GetRange().GetStart(); int startPos = range.GetStart() - GetRange().GetStart();
long len = range.GetLength();
wxString str(m_text); wxString stringChunk;
if (context.HasVirtualText(this))
{ {
if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length()) // We don't need stringWhole. Only prepare stringChunk.
str = m_text; wxString stringWhole;
const wxString* pWholeString = &m_text;
if (context.HasVirtualText(this))
{
if (context.GetVirtualText(this, stringWhole) && stringWhole.Length() == m_text.Length())
pWholeString = &stringWhole;
}
long len = range.GetLength();
stringChunk = pWholeString->Mid(startPos, (size_t) len);
// Replace line break characters with spaces
wxString toRemove = wxRichTextLineBreakChar;
stringChunk.Replace(toRemove, wxT(" "));
if (textAttr.HasTextEffects() &&
(textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
{
stringChunk.MakeUpper();
}
} }
wxString toReplace = wxRichTextLineBreakChar;
str.Replace(toReplace, wxT(" "));
wxString stringChunk = str.Mid(startPos, (size_t) len);
if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
stringChunk.MakeUpper();
wxCoord w, h; wxCoord w, h;
int width = 0; int width = 0;
if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND) if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)