Further wxRichTextCtrl performance improvements

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@53382 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Julian Smart
2008-04-27 13:29:53 +00:00
parent 06e6496e0f
commit 7051fa4161
4 changed files with 283 additions and 159 deletions

View File

@@ -589,7 +589,7 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range,
// Skip
}
else
child->Draw(dc, range, selectionRange, childRect, descent, style);
child->Draw(dc, range, selectionRange, rect, descent, style);
}
node = node->GetNext();
@@ -857,25 +857,29 @@ wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool c
wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
// child is a paragraph
wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
wxASSERT (child != NULL);
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
while (node2)
wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
if (obj->GetRange().Contains(pos))
{
wxRichTextLine* line = node2->GetData();
// child is a paragraph
wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
wxASSERT (child != NULL);
wxRichTextRange range = line->GetAbsoluteRange();
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
while (node2)
{
wxRichTextLine* line = node2->GetData();
if (range.Contains(pos) ||
wxRichTextRange range = line->GetAbsoluteRange();
// If the position is end-of-paragraph, then return the last line of
// of the paragraph.
(range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
return line;
if (range.Contains(pos) ||
node2 = node2->GetNext();
// If the position is end-of-paragraph, then return the last line of
// of the paragraph.
(range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
return line;
node2 = node2->GetNext();
}
}
node = node->GetNext();
@@ -3105,7 +3109,7 @@ wxRichTextParagraph::~wxRichTextParagraph()
}
/// Draw the item
bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int WXUNUSED(descent), int style)
bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int WXUNUSED(descent), int style)
{
wxTextAttr attr = GetCombinedAttributes();
@@ -3184,52 +3188,56 @@ bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxR
wxRichTextLine* line = node->GetData();
wxRichTextRange lineRange = line->GetAbsoluteRange();
int maxDescent = line->GetDescent();
// Lines are specified relative to the paragraph
wxPoint linePosition = line->GetPosition() + GetPosition();
wxPoint objectPosition = linePosition;
// Loop through objects until we get to the one within range
wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
int i = 0;
while (node2)
// Don't draw if off the screen
if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
{
wxRichTextObject* child = node2->GetData();
wxPoint objectPosition = linePosition;
int maxDescent = line->GetDescent();
if (!child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
// Loop through objects until we get to the one within range
wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
int i = 0;
while (node2)
{
// Draw this part of the line at the correct position
wxRichTextRange objectRange(child->GetRange());
objectRange.LimitTo(lineRange);
wxRichTextObject* child = node2->GetData();
wxSize objectSize;
if (!child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
{
// Draw this part of the line at the correct position
wxRichTextRange objectRange(child->GetRange());
objectRange.LimitTo(lineRange);
wxSize objectSize;
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
if (i < (int) line->GetObjectSizes().GetCount())
{
objectSize.x = line->GetObjectSizes()[(size_t) i];
}
else
if (i < (int) line->GetObjectSizes().GetCount())
{
objectSize.x = line->GetObjectSizes()[(size_t) i];
}
else
#endif
{
int descent = 0;
child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
{
int descent = 0;
child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
}
// Use the child object's width, but the whole line's height
wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
objectPosition.x += objectSize.x;
i ++;
}
else if (child->GetRange().GetStart() > lineRange.GetEnd())
// Can break out of inner loop now since we've passed this line's range
break;
// Use the child object's width, but the whole line's height
wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
objectPosition.x += objectSize.x;
i ++;
node2 = node2->GetNext();
}
else if (child->GetRange().GetStart() > lineRange.GetEnd())
// Can break out of inner loop now since we've passed this line's range
break;
node2 = node2->GetNext();
}
node = node->GetNext();
@@ -5548,8 +5556,6 @@ bool wxRichTextBuffer::BeginStyle(const wxTextAttr& style)
SetDefaultStyle(newStyle);
// wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize());
return true;
}
@@ -6518,6 +6524,58 @@ wxRichTextAction::~wxRichTextAction()
{
}
void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
{
// Store a list of line start character and y positions so we can figure out which area
// we need to refresh
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
// NOTE: we're assuming that the buffer is laid out correctly at this point.
// If we had several actions, which only invalidate and leave layout until the
// paint handler is called, then this might not be true. So we may need to switch
// optimisation on only when we're simply adding text and not simultaneously
// deleting a selection, for example. Or, we make sure the buffer is laid out correctly
// first, but of course this means we'll be doing it twice.
if (!m_buffer->GetDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
{
wxSize clientSize = m_ctrl->GetClientSize();
wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
int lastY = firstVisiblePt.y + clientSize.y;
wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetRange().GetStart());
wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
while (node)
{
wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
while (node2)
{
wxRichTextLine* line = node2->GetData();
wxPoint pt = line->GetAbsolutePosition();
wxRichTextRange range = line->GetAbsoluteRange();
if (pt.y > lastY)
{
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
}
else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
{
optimizationLineCharPositions.Add(range.GetStart());
optimizationLineYPositions.Add(pt.y);
}
if (node2)
node2 = node2->GetNext();
}
if (node)
node = node->GetNext();
}
}
#endif
}
bool wxRichTextAction::Do()
{
m_buffer->Modify(true);
@@ -6532,49 +6590,7 @@ bool wxRichTextAction::Do()
wxArrayInt optimizationLineYPositions;
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
// NOTE: we're assuming that the buffer is laid out correctly at this point.
// If we had several actions, which only invalidate and leave layout until the
// paint handler is called, then this might not be true. So we may need to switch
// optimisation on only when we're simply adding text and not simultaneously
// deleting a selection, for example. Or, we make sure the buffer is laid out correctly
// first, but of course this means we'll be doing it twice.
if (!m_buffer->GetDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
{
wxSize clientSize = m_ctrl->GetClientSize();
wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
int lastY = firstVisiblePt.y + clientSize.y;
wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetRange().GetStart());
wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
while (node)
{
wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
while (node2)
{
wxRichTextLine* line = node2->GetData();
wxPoint pt = line->GetAbsolutePosition();
wxRichTextRange range = line->GetAbsoluteRange();
if (pt.y > lastY)
{
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
}
else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
{
optimizationLineCharPositions.Add(range.GetStart());
optimizationLineYPositions.Add(pt.y);
}
if (node2)
node2 = node2->GetNext();
}
if (node)
node = node->GetNext();
}
}
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
#endif
m_buffer->InsertFragment(GetRange().GetStart(), m_newParagraphs);
@@ -6599,10 +6615,7 @@ bool wxRichTextAction::Do()
newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1));
if (optimizationLineCharPositions.GetCount() > 0)
UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
else
UpdateAppearance(newCaretPosition, true /* send update event */);
UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
@@ -6617,6 +6630,13 @@ bool wxRichTextAction::Do()
}
case wxRICHTEXT_DELETE:
{
wxArrayInt optimizationLineCharPositions;
wxArrayInt optimizationLineYPositions;
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
#endif
m_buffer->DeleteRange(GetRange());
m_buffer->UpdateRanges();
m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
@@ -6625,7 +6645,7 @@ bool wxRichTextAction::Do()
if (caretPos >= m_buffer->GetRange().GetEnd())
caretPos --;
UpdateAppearance(caretPos, true /* send update event */);
UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
@@ -6671,13 +6691,20 @@ bool wxRichTextAction::Undo()
{
case wxRICHTEXT_INSERT:
{
wxArrayInt optimizationLineCharPositions;
wxArrayInt optimizationLineYPositions;
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
#endif
m_buffer->DeleteRange(GetRange());
m_buffer->UpdateRanges();
m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
long newCaretPosition = GetPosition() - 1;
UpdateAppearance(newCaretPosition, true /* send update event */);
UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
@@ -6692,11 +6719,18 @@ bool wxRichTextAction::Undo()
}
case wxRICHTEXT_DELETE:
{
wxArrayInt optimizationLineCharPositions;
wxArrayInt optimizationLineYPositions;
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
#endif
m_buffer->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
m_buffer->UpdateRanges();
m_buffer->Invalidate(GetRange());
UpdateAppearance(GetPosition(), true /* send update event */);
UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
wxRichTextEvent cmdEvent(
wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
@@ -6735,7 +6769,7 @@ bool wxRichTextAction::Undo()
}
/// Update the control appearance
void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions)
void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
{
if (m_ctrl)
{
@@ -6746,7 +6780,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
// Find refresh rectangle if we are in a position to optimise refresh
if (m_cmdId == wxRICHTEXT_INSERT && optimizationLineCharPositions && optimizationLineCharPositions->GetCount() > 0)
if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
{
size_t i;
@@ -6757,17 +6791,57 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
int firstY = 0;
int lastY = firstVisiblePt.y + clientSize.y;
bool foundStart = false;
bool foundEnd = false;
// position offset - how many characters were inserted
int positionOffset = GetRange().GetLength();
// Determine whether this is Do or Undo, and adjust positionOffset accordingly
if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
positionOffset = - positionOffset;
// find the first line which is being drawn at the same position as it was
// before. Since we're talking about a simple insertion, we can assume
// that the rest of the window does not need to be redrawn.
wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetPosition());
if (para)
{
// Find line containing GetPosition().
wxRichTextLine* line = NULL;
wxRichTextLineList::compatibility_iterator node2 = para->GetLines().GetFirst();
while (node2)
{
wxRichTextLine* l = node2->GetData();
wxRichTextRange range = l->GetAbsoluteRange();
if (range.Contains(GetRange().GetStart()-1))
{
line = l;
break;
}
node2 = node2->GetNext();
}
if (line)
{
// Step back a couple of lines to where we can be sure of reformatting correctly
wxRichTextLineList::compatibility_iterator lineNode = para->GetLines().Find(line);
if (lineNode)
{
lineNode = lineNode->GetPrevious();
if (lineNode)
{
line = (wxRichTextLine*) lineNode->GetData();
lineNode = lineNode->GetPrevious();
if (lineNode)
line = (wxRichTextLine*) lineNode->GetData();
}
}
firstY = line->GetAbsolutePosition().y;
}
}
wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
while (node)
{
@@ -6787,14 +6861,19 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
}
// Detect last line in the buffer
else if (!node2->GetNext() && para->GetRange().Contains(m_buffer->GetRange().GetEnd()))
{
foundEnd = true;
lastY = pt.y + line->GetSize().y;
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
break;
}
else
{
if (!foundStart)
{
firstY = pt.y - firstVisiblePt.y;
foundStart = true;
}
// search for this line being at the same position as before
for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
{
@@ -6803,7 +6882,8 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
{
// Stop, we're now the same as we were
foundEnd = true;
lastY = pt.y - firstVisiblePt.y;
lastY = pt.y;
node2 = wxRichTextLineList::compatibility_iterator();
node = wxRichTextObjectList::compatibility_iterator();
@@ -6821,17 +6901,13 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent
node = node->GetNext();
}
if (!foundStart)
firstY = firstVisiblePt.y;
firstY = wxMax(firstVisiblePt.y, firstY);
if (!foundEnd)
lastY = firstVisiblePt.y + clientSize.y;
wxRect rect(firstVisiblePt.x, firstY, firstVisiblePt.x + clientSize.x, lastY - firstY);
// Convert to device coordinates
wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
m_ctrl->RefreshRect(rect);
// TODO: we need to make sure that lines are only drawn if in the update region. The rect
// passed to Draw is currently used in different ways (to pass the position the content should
// be drawn at as well as the relevant region).
}
else
#endif