Speeded up wrapping (again), this time using partial text extents.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@53285 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart
2008-04-21 15:26:27 +00:00
parent a159366a2f
commit 31778480a9
2 changed files with 153 additions and 52 deletions

View File

@@ -338,7 +338,7 @@ public:
/// Get the object size for the given range. Returns false if the range /// Get the object size for the given range. Returns false if the range
/// is invalid for this object. /// is invalid for this object.
virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const = 0; virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const = 0;
/// Do a split, returning an object containing the second part, and setting /// Do a split, returning an object containing the second part, and setting
/// the first part in 'this'. /// the first part in 'this'.
@@ -565,7 +565,7 @@ public:
/// 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
/// is invalid for this object. /// is invalid for this object.
virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const;
// Accessors // Accessors
@@ -604,7 +604,7 @@ public:
/// 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
/// is invalid for this object. /// is invalid for this object.
virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const;
/// Delete range /// Delete range
virtual bool DeleteRange(const wxRichTextRange& range); virtual bool DeleteRange(const wxRichTextRange& range);
@@ -920,7 +920,7 @@ public:
/// 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
/// is invalid for this object. /// is invalid for this object.
virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const;
/// Finds the absolute position and row height for the given character position /// Finds the absolute position and row height for the given character position
virtual bool FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart); virtual bool FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart);
@@ -972,7 +972,7 @@ public:
/// Find a suitable wrap position. wrapPosition is the last position in the line to the left /// Find a suitable wrap position. wrapPosition is the last position in the line to the left
/// of the split. /// of the split.
bool FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition); bool FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents);
/// Find the object at the given position /// Find the object at the given position
wxRichTextObject* FindObjectAtPosition(long position); wxRichTextObject* FindObjectAtPosition(long position);
@@ -1037,7 +1037,7 @@ public:
/// 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
/// is invalid for this object. /// is invalid for this object.
virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position/* = wxPoint(0,0)*/) const; virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const;
/// Get any text in this object for the given range /// Get any text in this object for the given range
virtual wxString GetTextForRange(const wxRichTextRange& range) const; virtual wxString GetTextForRange(const wxRichTextRange& range) const;
@@ -1199,7 +1199,7 @@ public:
/// Get the object size for the given range. Returns false if the range /// Get the object size for the given range. Returns false if the range
/// is invalid for this object. /// is invalid for this object.
virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const;
/// Returns true if the object is empty /// Returns true if the object is empty
virtual bool IsEmpty() const { return !m_image.Ok(); } virtual bool IsEmpty() const { return !m_image.Ok(); }

View File

@@ -48,6 +48,9 @@ WX_DEFINE_LIST(wxRichTextLineList)
// Switch off if the platform doesn't like it for some reason // Switch off if the platform doesn't like it for some reason
#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
// Use GetPartialTextExtents for platforms that support it natively
#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
const wxChar wxRichTextLineBreakChar = (wxChar) 29; const wxChar wxRichTextLineBreakChar = (wxChar) 29;
// Helpers for efficiency // Helpers for efficiency
@@ -510,13 +513,13 @@ bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
} }
/// Get/set the size for the given range. Assume only has one child. /// Get/set the size for the given range. Assume only has one child.
bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
{ {
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
if (node) if (node)
{ {
wxRichTextObject* child = node->GetData(); wxRichTextObject* child = node->GetData();
return child->GetRangeSize(range, size, descent, dc, flags, position); return child->GetRangeSize(range, size, descent, dc, flags, position, partialExtents);
} }
else else
return false; return false;
@@ -741,7 +744,7 @@ void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
} }
/// Get/set the size for the given range. /// Get/set the size for the given range.
bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
{ {
wxSize sz; wxSize sz;
@@ -3284,6 +3287,16 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
node = node->GetNext(); node = node->GetNext();
} }
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
wxArrayInt partialExtents;
wxSize paraSize;
int paraDescent;
// This calculates the partial text extents
GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), & partialExtents);
#endif
// Split up lines // Split up lines
// We may need to go back to a previous child, in which case create the new line, // We may need to go back to a previous child, in which case create the new line,
@@ -3341,7 +3354,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
// Find a place to wrap. This may walk back to previous children, // Find a place to wrap. This may walk back to previous children,
// for example if a word spans several objects. // for example if a word spans several objects.
if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition)) if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition, & partialExtents))
{ {
// If the function failed, just cut it off at the end of this child. // If the function failed, just cut it off at the end of this child.
wrapPosition = child->GetRange().GetEnd(); wrapPosition = child->GetRange().GetEnd();
@@ -3568,7 +3581,7 @@ void wxRichTextParagraph::ClearLines()
/// 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
/// is invalid for this object. /// is invalid for this object.
bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
{ {
if (!range.IsWithin(GetRange())) if (!range.IsWithin(GetRange()))
return false; return false;
@@ -3580,9 +3593,17 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz
wxSize sz; wxSize sz;
wxArrayInt childExtents;
wxArrayInt* p;
if (partialExtents)
p = & childExtents;
else
p = NULL;
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node) while (node)
{ {
wxRichTextObject* child = node->GetData(); wxRichTextObject* child = node->GetData();
if (!child->GetRange().IsOutside(range)) if (!child->GetRange().IsOutside(range))
{ {
@@ -3592,12 +3613,30 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz
rangeToUse.LimitTo(child->GetRange()); rangeToUse.LimitTo(child->GetRange());
int childDescent = 0; int childDescent = 0;
if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y))) if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p))
{ {
sz.y = wxMax(sz.y, childSize.y); sz.y = wxMax(sz.y, childSize.y);
sz.x += childSize.x; sz.x += childSize.x;
descent = wxMax(descent, childDescent); descent = wxMax(descent, childDescent);
if (partialExtents)
{
int lastSize;
if (partialExtents->GetCount() > 0)
lastSize = (*partialExtents)[partialExtents->GetCount()-1];
else
lastSize = 0;
size_t i;
for (i = 0; i < childExtents.GetCount(); i++)
{
partialExtents->Add(childExtents[i] + lastSize);
}
}
} }
if (p)
p->Clear();
} }
node = node->GetNext(); node = node->GetNext();
@@ -3981,55 +4020,81 @@ bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTex
} }
/// Find a suitable wrap position. /// Find a suitable wrap position.
bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition) bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
{ {
// Find the first position where the line exceeds the available space. // Find the first position where the line exceeds the available space.
wxSize sz; wxSize sz;
long breakPosition = range.GetEnd(); long breakPosition = range.GetEnd();
// Binary chop for speed #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
long minPos = range.GetStart(); if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
long maxPos = range.GetEnd();
while (true)
{ {
if (minPos == maxPos) int widthBefore;
{
int descent = 0;
GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace) if (range.GetStart() > GetRange().GetStart())
breakPosition = minPos - 1; widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
break;
}
else if ((maxPos - minPos) == 1)
{
int descent = 0;
GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace)
breakPosition = minPos - 1;
else
{
GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace)
breakPosition = maxPos-1;
}
break;
}
else else
widthBefore = 0;
size_t i;
for (i = (size_t) range.GetStart(); i < (size_t) range.GetEnd(); i++)
{ {
long nextPos = minPos + ((maxPos - minPos) / 2); int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
int descent = 0; if (widthFromStartOfThisRange >= availableSpace)
GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace)
{ {
maxPos = nextPos; breakPosition = i-1;
break;
}
}
}
else
#endif
{
// Binary chop for speed
long minPos = range.GetStart();
long maxPos = range.GetEnd();
while (true)
{
if (minPos == maxPos)
{
int descent = 0;
GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace)
breakPosition = minPos - 1;
break;
}
else if ((maxPos - minPos) == 1)
{
int descent = 0;
GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace)
breakPosition = minPos - 1;
else
{
GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace)
breakPosition = maxPos-1;
}
break;
} }
else else
{ {
minPos = nextPos; long nextPos = minPos + ((maxPos - minPos) / 2);
int descent = 0;
GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
if (sz.x > availableSpace)
{
maxPos = nextPos;
}
else
{
minPos = nextPos;
}
} }
} }
} }
@@ -4595,7 +4660,7 @@ void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
/// 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
/// is invalid for this object. /// is invalid for this object.
bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position) const bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
{ {
if (!range.IsWithin(GetRange())) if (!range.IsWithin(GetRange()))
return false; return false;
@@ -4668,10 +4733,21 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz
// break up the string at the Tab // break up the string at the Tab
wxString stringFragment = stringChunk.BeforeFirst(wxT('\t')); wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
stringChunk = stringChunk.AfterFirst(wxT('\t')); stringChunk = stringChunk.AfterFirst(wxT('\t'));
int oldWidth = width;
dc.GetTextExtent(stringFragment, & w, & h); dc.GetTextExtent(stringFragment, & w, & h);
width += w; width += w;
int absoluteWidth = width + position.x; int absoluteWidth = width + position.x;
if (partialExtents)
{
// Add these partial extents
wxArrayInt p;
dc.GetPartialTextExtents(stringFragment, p);
size_t j;
for (j = 0; j < p.GetCount(); j++)
partialExtents->Add(oldWidth + p[j]);
}
bool notFound = true; bool notFound = true;
for (int i = 0; i < tabCount && notFound; ++i) for (int i = 0; i < tabCount && notFound; ++i)
{ {
@@ -4690,13 +4766,30 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz
notFound = false; notFound = false;
width = nextTabPos - position.x; width = nextTabPos - position.x;
if (partialExtents)
partialExtents->Add(width);
} }
} }
} }
} }
dc.GetTextExtent(stringChunk, & w, & h, & descent); if (!stringChunk.IsEmpty())
width += w; {
dc.GetTextExtent(stringChunk, & w, & h, & descent);
int oldWidth = width;
width += w;
if (partialExtents)
{
// Add these partial extents
wxArrayInt p;
dc.GetPartialTextExtents(stringChunk, p);
size_t j;
for (j = 0; j < p.GetCount(); j++)
partialExtents->Add(oldWidth + p[j]);
}
}
if ( bScript ) if ( bScript )
dc.SetFont(font); dc.SetFont(font);
@@ -6694,11 +6787,19 @@ bool wxRichTextImage::Layout(wxDC& WXUNUSED(dc), const wxRect& rect, int WXUNUSE
/// 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
/// is invalid for this object. /// is invalid for this object.
bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags), wxPoint WXUNUSED(position)) const bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
{ {
if (!range.IsWithin(GetRange())) if (!range.IsWithin(GetRange()))
return false; return false;
if (partialExtents)
{
if (m_image.Ok())
partialExtents->Add(m_image.GetWidth());
else
partialExtents->Add(0);
}
if (!m_image.Ok()) if (!m_image.Ok())
return false; return false;