From 4d551ad5e5d4f9643d69e70638788fb9a78f209a Mon Sep 17 00:00:00 2001 From: Julian Smart Date: Fri, 21 Oct 2005 13:02:15 +0000 Subject: [PATCH 1/1] Fixed some styling bugs, optimized resize for large files git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@35962 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/richtext/richtextbuffer.h | 4 ++ include/wx/richtext/richtextctrl.h | 30 +++++++++++-- samples/richtext/todo.txt | 2 + src/richtext/richtextbuffer.cpp | 66 ++++++++++++++++++++++------ src/richtext/richtextctrl.cpp | 64 +++++++++++++++++++++++---- 5 files changed, 141 insertions(+), 25 deletions(-) diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index 97469190c8..68e1fefd45 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -134,6 +134,10 @@ class WXDLLIMPEXP_ADV wxTextAttrEx; #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 */ diff --git a/include/wx/richtext/richtextctrl.h b/include/wx/richtext/richtextctrl.h index fcd77bd77e..407fc1cb95 100644 --- a/include/wx/richtext/richtextctrl.h +++ b/include/wx/richtext/richtextctrl.h @@ -57,6 +57,10 @@ #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 @@ -136,6 +140,12 @@ public: /// 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 @@ -373,7 +383,7 @@ public: /// 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); @@ -555,6 +565,9 @@ public: void OnSetFocus(wxFocusEvent& event); void OnKillFocus(wxFocusEvent& event); + /// Idle-time processing + void OnIdle(wxIdleEvent& event); + // Implementation /// Set font, and also default attributes @@ -633,10 +646,10 @@ public: 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). @@ -645,6 +658,9 @@ public: /// 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 ; @@ -689,6 +705,14 @@ private: /// 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; }; /*! diff --git a/samples/richtext/todo.txt b/samples/richtext/todo.txt index 6c3b40396f..8cc4886ceb 100644 --- a/samples/richtext/todo.txt +++ b/samples/richtext/todo.txt @@ -10,6 +10,8 @@ BUGS: 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: diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 168cac92aa..9f180d4421 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -516,7 +516,31 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, /// 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); @@ -530,7 +554,7 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl // 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) @@ -556,6 +580,9 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl } } + // A way to force speedy rest-of-buffer layout (the 'else' below) + bool forceQuickLayout = false; + while (node) { // Assume this box only contains paragraphs @@ -564,13 +591,19 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl 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 { @@ -2540,7 +2573,12 @@ wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** prev if (pos == child->GetRange().GetStart()) { if (previousObject) - *previousObject = child; + { + if (node->GetPrevious()) + *previousObject = node->GetPrevious()->GetData(); + else + *previousObject = NULL; + } return child; } @@ -4321,23 +4359,23 @@ bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2, i (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; @@ -4403,23 +4441,23 @@ bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2, (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; diff --git a/src/richtext/richtextctrl.cpp b/src/richtext/richtextctrl.cpp index 272fa94fb3..8e8ec62272 100644 --- a/src/richtext/richtextctrl.cpp +++ b/src/richtext/richtextctrl.cpp @@ -56,6 +56,7 @@ BEGIN_EVENT_TABLE( wxRichTextCtrl, wxScrolledWindow ) #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) @@ -177,6 +178,10 @@ void wxRichTextCtrl::Init() 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 @@ -1323,13 +1328,39 @@ bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags) /// 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) { @@ -2119,8 +2150,8 @@ bool wxRichTextCtrl::SetFont(const wxFont& font) 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); @@ -2129,7 +2160,7 @@ wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) } /// 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); @@ -2213,15 +2244,22 @@ bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart) /// 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()); @@ -2230,7 +2268,7 @@ bool wxRichTextCtrl::Layout() 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()) @@ -2354,7 +2392,7 @@ bool wxRichTextCtrl::ApplyUnderlineToSelection() { wxRichTextAttr attr; attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE); - attr.SetFontWeight(!IsSelectionUnderlined()); + attr.SetFontUnderlined(!IsSelectionUnderlined()); if (HasSelection()) return SetStyle(GetSelectionRange(), attr); @@ -2415,6 +2453,16 @@ bool wxRichTextCtrl::SetDefaultStyleToCursorStyle() 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 -- 2.45.2