#define wxRICHTEXT_VARIABLE_WIDTH 0x04
#define wxRICHTEXT_VARIABLE_HEIGHT 0x08
+// Only lay out the part of the buffer that lies within
+// the rect passed to Layout.
+#define wxRICHTEXT_LAYOUT_SPECIFIED_RECT 0x10
+
/*!
* Flags returned from hit-testing
*/
#define wxRICHTEXT_DEFAULT_TYPE_COLOUR wxColour(0, 0, 200)
#define wxRICHTEXT_DEFAULT_FOCUS_RECT_COLOUR wxColour(100, 80, 80)
#define wxRICHTEXT_DEFAULT_CARET_WIDTH 2
+// Minimum buffer size before delayed layout kicks in
+#define wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD 20000
+// Milliseconds before layout occurs after resize
+#define wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL 50
/*!
* Forward declarations
/// Set filename
void SetFilename(const wxString& filename) { m_filename = filename; }
+ /// Set the threshold in character positions for doing layout optimization during sizing
+ void SetDelayedLayoutThreshold(long threshold) { m_delayedLayoutThreshold = threshold; }
+
+ /// Get the threshold in character positions for doing layout optimization during sizing
+ long GetDelayedLayoutThreshold() const { return m_delayedLayoutThreshold; }
+
// Operations
// editing
/// Layout the buffer: which we must do before certain operations, such as
/// setting the caret position.
- virtual bool Layout();
+ virtual bool Layout(bool onlyVisibleRect = false);
/// Move the caret to the given character position
virtual bool MoveCaret(long pos, bool showAtLineStart = false);
void OnSetFocus(wxFocusEvent& event);
void OnKillFocus(wxFocusEvent& event);
+ /// Idle-time processing
+ void OnIdle(wxIdleEvent& event);
+
// Implementation
/// Set font, and also default attributes
bool DeleteSelectedContent(long* newPos= NULL);
/// Transform logical to physical
- wxPoint GetPhysicalPoint(const wxPoint& ptLogical);
+ wxPoint GetPhysicalPoint(const wxPoint& ptLogical) const;
/// Transform physical to logical
- wxPoint GetLogicalPoint(const wxPoint& ptPhysical);
+ wxPoint GetLogicalPoint(const wxPoint& ptPhysical) const;
/// Finds the caret position for the next word. Direction
/// is 1 (forward) or -1 (backwards).
/// Is the given position visible on the screen?
bool IsPositionVisible(long pos) const;
+ /// Returns the first visible position in the current view
+ long GetFirstVisiblePosition() const;
+
// Overrides
virtual wxSize DoGetBestSize() const ;
/// Start position for drag
wxPoint m_dragStart;
+
+ /// Do we need full layout in idle?
+ bool m_fullLayoutRequired;
+ wxLongLong m_fullLayoutTime;
+ long m_fullLayoutSavedPosition;
+
+ /// Threshold for doing delayed layout
+ long m_delayedLayoutThreshold;
};
/*!
lengths. See wxRichTextCtrl::ExtendSelection.
- Word forward can miss first word on line.
- Doesn't correctly undo deletion of bulleted paragraphs.
+- Sizing doesn't restore the scroll position correctly (related
+ to the use of buffer layout optimization and OnIdle full layout.)
IMPROVEMENTS:
/// Lay the item out
bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int style)
{
- wxRect availableSpace(rect.x + m_leftMargin,
+ wxRect availableSpace;
+ bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
+
+ // If only laying out a specific area, the passed rect has a different meaning:
+ // the visible part of the buffer.
+ if (formatRect)
+ {
+ availableSpace = wxRect(0 + m_leftMargin,
+ 0 + m_topMargin,
+ rect.width - m_leftMargin - m_rightMargin,
+ rect.height);
+
+ // Invalidate the part of the buffer from the first visible line
+ // to the end. If other parts of the buffer are currently invalid,
+ // then they too will be taken into account if they are above
+ // the visible point.
+ long startPos = 0;
+ wxRichTextLine* line = GetLineAtYPosition(rect.y);
+ if (line)
+ startPos = line->GetAbsoluteRange().GetStart();
+
+ Invalidate(wxRichTextRange(startPos, GetRange().GetEnd()));
+ }
+ else
+ availableSpace = wxRect(rect.x + m_leftMargin,
rect.y + m_topMargin,
rect.width - m_leftMargin - m_rightMargin,
rect.height - m_topMargin - m_bottomMargin);
// Get invalid range, rounding to paragraph start/end.
wxRichTextRange invalidRange = GetInvalidRange(true);
- if (invalidRange == wxRICHTEXT_NONE)
+ if (invalidRange == wxRICHTEXT_NONE && !formatRect)
return true;
if (invalidRange == wxRICHTEXT_ALL)
}
}
+ // A way to force speedy rest-of-buffer layout (the 'else' below)
+ bool forceQuickLayout = false;
+
while (node)
{
// Assume this box only contains paragraphs
wxASSERT (child != NULL);
// TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
- if (child && (layoutAll || child->GetLines().GetCount() == 0 || !child->GetRange().IsOutside(invalidRange)))
+ if (child && !forceQuickLayout && (layoutAll || child->GetLines().GetCount() == 0 || !child->GetRange().IsOutside(invalidRange)))
{
child->Layout(dc, availableSpace, style);
// Layout must set the cached size
availableSpace.y += child->GetCachedSize().y;
maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
+
+ // If we're just formatting the visible part of the buffer,
+ // and we're now past the bottom of the window, start quick
+ // layout.
+ if (formatRect && child->GetPosition().y > rect.GetBottom())
+ forceQuickLayout = true;
}
else
{
if (pos == child->GetRange().GetStart())
{
if (previousObject)
- *previousObject = child;
+ {
+ if (node->GetPrevious())
+ *previousObject = node->GetPrevious()->GetData();
+ else
+ *previousObject = NULL;
+ }
return child;
}
(attr1.GetRightIndent() != attr2.GetRightIndent()))
return false;
- if ((flags && wxTEXT_ATTR_PARA_SPACING_AFTER) &&
+ if ((flags & wxTEXT_ATTR_PARA_SPACING_AFTER) &&
(attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
return false;
- if ((flags && wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
+ if ((flags & wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
(attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
return false;
- if ((flags && wxTEXT_ATTR_LINE_SPACING) &&
+ if ((flags & wxTEXT_ATTR_LINE_SPACING) &&
(attr1.GetLineSpacing() != attr2.GetLineSpacing()))
return false;
- if ((flags && wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
+ if ((flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
(attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
return false;
- if ((flags && wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
+ if ((flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
(attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
return false;
(attr1.GetRightIndent() != attr2.GetRightIndent()))
return false;
- if ((flags && wxTEXT_ATTR_PARA_SPACING_AFTER) &&
+ if ((flags & wxTEXT_ATTR_PARA_SPACING_AFTER) &&
(attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
return false;
- if ((flags && wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
+ if ((flags & wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
(attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
return false;
- if ((flags && wxTEXT_ATTR_LINE_SPACING) &&
+ if ((flags & wxTEXT_ATTR_LINE_SPACING) &&
(attr1.GetLineSpacing() != attr2.GetLineSpacing()))
return false;
- if ((flags && wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
+ if ((flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
(attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
return false;
- if ((flags && wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
+ if ((flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
(attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
return false;
#endif
EVT_PAINT(wxRichTextCtrl::OnPaint)
EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground)
+ EVT_IDLE(wxRichTextCtrl::OnIdle)
EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick)
EVT_MOTION(wxRichTextCtrl::OnMoveMouse)
EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp)
m_editable = true;
m_caretAtLineStart = false;
m_dragging = false;
+ m_fullLayoutRequired = false;
+ m_fullLayoutTime = 0;
+ m_fullLayoutSavedPosition = 0;
+ m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD;
}
/// Call Freeze to prevent refresh
/// Sizing
void wxRichTextCtrl::OnSize(wxSizeEvent& event)
{
- GetBuffer().Invalidate(wxRICHTEXT_ALL);
+ // Only do sizing optimization for large buffers
+ if (GetBuffer().GetRange().GetEnd() > m_delayedLayoutThreshold)
+ {
+ m_fullLayoutRequired = true;
+ m_fullLayoutTime = wxGetLocalTimeMillis();
+ m_fullLayoutSavedPosition = GetFirstVisiblePosition();
+ Layout(true /* onlyVisibleRect */);
+ }
+ else
+ GetBuffer().Invalidate(wxRICHTEXT_ALL);
RecreateBuffer();
event.Skip();
}
+
+/// Idle-time processing
+void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
+{
+ const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL;
+
+ if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval)))
+ {
+ m_fullLayoutRequired = false;
+ m_fullLayoutTime = 0;
+ GetBuffer().Invalidate(wxRICHTEXT_ALL);
+ ShowPosition(m_fullLayoutSavedPosition);
+ Refresh();
+ }
+ event.Skip();
+}
+
/// Set up scrollbars, e.g. after a resize
void wxRichTextCtrl::SetupScrollbars(bool atTop)
{
return true;
}
-/// Transform logical to physical (unscrolling)
-wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical)
+/// Transform logical to physical
+wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const
{
wxPoint pt;
CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y);
}
/// Transform physical to logical
-wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical)
+wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const
{
wxPoint pt;
CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y);
/// Layout the buffer: which we must do before certain operations, such as
/// setting the caret position.
-bool wxRichTextCtrl::Layout()
+bool wxRichTextCtrl::Layout(bool onlyVisibleRect)
{
- if (GetBuffer().GetDirty())
+ if (GetBuffer().GetDirty() || onlyVisibleRect)
{
wxRect availableSpace(GetClientSize());
if (availableSpace.width == 0)
availableSpace.width = 10;
if (availableSpace.height == 0)
availableSpace.height = 10;
+
+ int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT;
+ if (onlyVisibleRect)
+ {
+ flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
+ availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0)));
+ }
wxClientDC dc(this);
dc.SetFont(GetFont());
GetBuffer().Defragment();
GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
- GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
+ GetBuffer().Layout(dc, availableSpace, flags);
GetBuffer().SetDirty(false);
if (!IsFrozen())
{
wxRichTextAttr attr;
attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
- attr.SetFontWeight(!IsSelectionUnderlined());
+ attr.SetFontUnderlined(!IsSelectionUnderlined());
if (HasSelection())
return SetStyle(GetSelectionRange(), attr);
return false;
}
+/// Returns the first visible position in the current view
+long wxRichTextCtrl::GetFirstVisiblePosition() const
+{
+ wxRichTextLine* line = GetBuffer().GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y);
+ if (line)
+ return line->GetAbsoluteRange().GetStart();
+ else
+ return 0;
+}
+
#endif
// wxUSE_RICHTEXT