diff options
author | Neil <nyamatongwe@gmail.com> | 2015-11-11 19:14:30 +1100 |
---|---|---|
committer | Neil <nyamatongwe@gmail.com> | 2015-11-11 19:14:30 +1100 |
commit | 23460f666bf34479850000eed40ba0ea38ca6665 (patch) | |
tree | 1c199ed2a3adb52f3cb6790b39532235e66e38f2 | |
parent | 3684fe443af3fcc0e5ad8ab674203a58c4ccaac3 (diff) | |
download | scintilla-mirror-23460f666bf34479850000eed40ba0ea38ca6665.tar.gz |
Implemented idle styling. This allows painting without first styling all visible
text then styling in the background using idle-time.
-rw-r--r-- | cocoa/ScintillaCocoa.mm | 10 | ||||
-rw-r--r-- | doc/ScintillaDoc.html | 21 | ||||
-rw-r--r-- | doc/ScintillaHistory.html | 6 | ||||
-rw-r--r-- | include/Scintilla.h | 6 | ||||
-rw-r--r-- | include/Scintilla.iface | 12 | ||||
-rw-r--r-- | src/Document.cxx | 28 | ||||
-rw-r--r-- | src/Document.h | 2 | ||||
-rw-r--r-- | src/Editor.cxx | 80 | ||||
-rw-r--r-- | src/Editor.h | 6 |
9 files changed, 161 insertions, 10 deletions
diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm index 8b95aae0e..982ca0423 100644 --- a/cocoa/ScintillaCocoa.mm +++ b/cocoa/ScintillaCocoa.mm @@ -1801,13 +1801,15 @@ void ScintillaCocoa::WillDraw(NSRect rect) { RefreshStyleData(); PRectangle rcWillDraw = NSRectToPRectangle(rect); - int positionAfterRect = PositionAfterArea(rcWillDraw); - pdoc->EnsureStyledTo(positionAfterRect); + const int posAfterArea = PositionAfterArea(rcWillDraw); + const int posAfterMax = PositionAfterMaxStyling(posAfterArea, true); + pdoc->StyleToAdjustingLineDuration(posAfterMax); + StartIdleStyling(posAfterMax < posAfterArea); NotifyUpdateUI(); if (WrapLines(wsVisible)) { // Wrap may have reduced number of lines so more lines may need to be styled - positionAfterRect = PositionAfterArea(rcWillDraw); - pdoc->EnsureStyledTo(positionAfterRect); + const int posAfterAreaWrapped = PositionAfterArea(rcWillDraw); + pdoc->EnsureStyledTo(posAfterAreaWrapped); // The wrapping process has changed the height of some lines so redraw all. Redraw(); } diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index e77c064c3..34722f259 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -82,7 +82,7 @@ <h1>Scintilla Documentation</h1> - <p>Last edited 31 July 2015 NH</p> + <p>Last edited 11 November 2015 NH</p> <p>There is <a class="jump" href="Design.html">an overview of the internal design of Scintilla</a>.<br /> @@ -2391,6 +2391,8 @@ struct Sci_TextToFind { <a class="message" href="#SCI_SETSTYLING">SCI_SETSTYLING(int length, int style)</a><br /> <a class="message" href="#SCI_SETSTYLINGEX">SCI_SETSTYLINGEX(int length, const char *styles)</a><br /> + <a class="message" href="#SCI_SETIDLESTYLING">SCI_SETIDLESTYLING(int idleStyling)</a><br /> + <a class="message" href="#SCI_GETIDLESTYLING">SCI_GETIDLESTYLING</a><br /> <a class="message" href="#SCI_SETLINESTATE">SCI_SETLINESTATE(int line, int value)</a><br /> <a class="message" href="#SCI_GETLINESTATE">SCI_GETLINESTATE(int line)</a><br /> <a class="message" href="#SCI_GETMAXLINESTATE">SCI_GETMAXLINESTATE</a><br /> @@ -2426,6 +2428,23 @@ struct Sci_TextToFind { <code>SCI_STARTSTYLING</code> should be called before the first call to this. </p> + <p><b id="SCI_SETIDLESTYLING">SCI_SETIDLESTYLING(int idleStyling)</b><br /> + <b id="SCI_GETIDLESTYLING">SCI_GETIDLESTYLING</b><br /> + By default, <code>SC_IDLESTYLING_NONE</code> (0), + syntax styling is performed for all the currently visible text before displaying it. + On very large files, this may make scrolling down slow. + With <code>SC_IDLESTYLING_TOVISIBLE</code> (1), + a small amount of styling is performed before display and then + further styling is performed incrementally in the background as an idle-time task. + This may result in the text initially appearing uncoloured and then, some time later, it is coloured. + Text after the currently visible portion may be styled in the background with <code>SC_IDLESTYLING_AFTERVISIBLE</code> (2). + To style both before and after the visible text in the background use <code>SC_IDLESTYLING_ALL</code> (3). + </p> + <p> + Since wrapping also needs to perform styling and also uses idle time, this setting has no effect when + the document is displayed wrapped. + </p> + <p><b id="SCI_SETLINESTATE">SCI_SETLINESTATE(int line, int value)</b><br /> <b id="SCI_GETLINESTATE">SCI_GETLINESTATE(int line)</b><br /> As well as the 8 bits of lexical state stored for each character there is also an integer diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 9aea9b6e3..0639d28fb 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -497,6 +497,12 @@ Released 6 November 2015. </li> <li> + Allow painting without first styling all visible text then styling in the background + using idle-time. This helps performance when scrolling down in very large documents. + Can also incrementally style after the visible area to the end of the document so that + the document is already styled when the user scrolls to it. + </li> + <li> On GTK+ on OS X, fix warning during destruction. <a href="http://sourceforge.net/p/scintilla/bugs/1777/">Bug #1777</a>. </li> diff --git a/include/Scintilla.h b/include/Scintilla.h index e3443409b..c985cea77 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -506,6 +506,12 @@ typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, #define SCI_WORDSTARTPOSITION 2266 #define SCI_WORDENDPOSITION 2267 #define SCI_ISRANGEWORD 2691 +#define SC_IDLESTYLING_NONE 0 +#define SC_IDLESTYLING_TOVISIBLE 1 +#define SC_IDLESTYLING_AFTERVISIBLE 2 +#define SC_IDLESTYLING_ALL 3 +#define SCI_SETIDLESTYLING 2692 +#define SCI_GETIDLESTYLING 2693 #define SC_WRAP_NONE 0 #define SC_WRAP_WORD 1 #define SC_WRAP_CHAR 2 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index ad50a2aaf..04fd767cd 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -1260,6 +1260,18 @@ fun int WordEndPosition=2267(position pos, bool onlyWordCharacters) # Is the range start..end considered a word? fun bool IsRangeWord=2691(position start, position end) +enu IdleStyling=SC_IDLESTYLING_ +val SC_IDLESTYLING_NONE=0 +val SC_IDLESTYLING_TOVISIBLE=1 +val SC_IDLESTYLING_AFTERVISIBLE=2 +val SC_IDLESTYLING_ALL=3 + +# Sets limits to idle styling. +set void SetIdleStyling=2692(int idleStyling,) + +# Retrieve the limits to idle styling. +get int GetIdleStyling=2693(,) + enu Wrap=SC_WRAP_ val SC_WRAP_NONE=0 val SC_WRAP_WORD=1 diff --git a/src/Document.cxx b/src/Document.cxx index 9201e162f..65c63c138 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -109,6 +109,7 @@ Document::Document() { useTabs = true; tabIndents = true; backspaceUnindents = false; + durationStyleOneLine = 0.00001; matchesValid = false; regex = 0; @@ -1892,6 +1893,33 @@ void Document::EnsureStyledTo(int pos) { } } +void Document::StyleToAdjustingLineDuration(int pos) { + // Place bounds on the duration used to avoid glitches spiking it + // and so causing slow styling or non-responsive scrolling + const double minDurationOneLine = 0.000001; + const double maxDurationOneLine = 0.0001; + + // Alpha value for exponential smoothing. + // Most recent value contributes 25% to smoothed value. + const double alpha = 0.25; + + const Sci_Position lineFirst = LineFromPosition(GetEndStyled()); + ElapsedTime etStyling; + EnsureStyledTo(pos); + const double durationStyling = etStyling.Duration(); + const Sci_Position lineLast = LineFromPosition(GetEndStyled()); + if (lineLast >= lineFirst + 8) { + // Only adjust for styling multiple lines to avoid instability + const double durationOneLine = durationStyling / (lineLast - lineFirst); + durationStyleOneLine = alpha * durationOneLine + (1.0 - alpha) * durationStyleOneLine; + if (durationStyleOneLine < minDurationOneLine) { + durationStyleOneLine = minDurationOneLine; + } else if (durationStyleOneLine > maxDurationOneLine) { + durationStyleOneLine = maxDurationOneLine; + } + } +} + void Document::LexerChanged() { // Tell the watchers the lexer has changed. for (std::vector<WatcherWithUserData>::iterator it = watchers.begin(); it != watchers.end(); ++it) { diff --git a/src/Document.h b/src/Document.h index ea8ddfbed..71a45879e 100644 --- a/src/Document.h +++ b/src/Document.h @@ -246,6 +246,7 @@ public: bool useTabs; bool tabIndents; bool backspaceUnindents; + double durationStyleOneLine; DecorationList decorations; @@ -400,6 +401,7 @@ public: bool SCI_METHOD SetStyles(Sci_Position length, const char *styles); int GetEndStyled() const { return endStyled; } void EnsureStyledTo(int pos); + void StyleToAdjustingLineDuration(int pos); void LexerChanged(); int GetStyleClock() const { return styleClock; } void IncrementStyleClock(); diff --git a/src/Editor.cxx b/src/Editor.cxx index 6141b0628..a86f8c96e 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -173,6 +173,8 @@ Editor::Editor() { paintAbandonedByStyling = false; paintingAllText = false; willRedrawAll = false; + idleStyling = SC_IDLESTYLING_NONE; + needIdleStyling = false; modEventMask = SC_MODEVENTMASKALL; @@ -919,7 +921,7 @@ void Editor::ScrollTo(int line, bool moveThumb) { SetTopLine(topLineNew); // Optimize by styling the view as this will invalidate any needed area // which could abort the initial paint if discovered later. - StyleToPositionInView(PositionAfterArea(GetClientRectangle())); + StyleAreaBounded(GetClientRectangle(), true); #ifndef UNDER_CE // Perform redraw rather than scroll if many lines would be redrawn anyway. if (performBlit) { @@ -1692,7 +1694,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { paintAbandonedByStyling = false; - StyleToPositionInView(PositionAfterArea(rcArea)); + StyleAreaBounded(rcArea, false); PRectangle rcClient = GetClientRectangle(); //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n", @@ -4893,6 +4895,8 @@ bool Editor::Idle() { // No more wrapping if (!wrapPending.NeedsWrap()) wrappingDone = true; + } else if (needIdleStyling) { + IdleStyling(); } // Add more idle things to do here, but make sure idleDone is @@ -4900,7 +4904,7 @@ bool Editor::Idle() { // false will stop calling this idle function until SetIdle() is // called again. - idleDone = wrappingDone; // && thatDone && theOtherThingDone... + idleDone = wrappingDone && !needIdleStyling; // && thatDone && theOtherThingDone... return !idleDone; } @@ -5003,12 +5007,71 @@ void Editor::StyleToPositionInView(Position pos) { } } +int Editor::PositionAfterMaxStyling(int posMax, bool scrolling) const { + if ((idleStyling == SC_IDLESTYLING_NONE) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) { + // Both states do not limit styling + return posMax; + } + + // Try to keep time taken by styling reasonable so interaction remains smooth. + // When scrolling, allow less time to ensure responsive + const double secondsAllowed = scrolling ? 0.005 : 0.02; + + const int linesToStyle = Platform::Clamp(static_cast<int>(secondsAllowed / pdoc->durationStyleOneLine), + 10, 0x10000); + const int stylingMaxLine = std::min( + static_cast<int>(pdoc->LineFromPosition(pdoc->GetEndStyled()) + linesToStyle), + pdoc->LinesTotal()); + return std::min(static_cast<int>(pdoc->LineStart(stylingMaxLine)), posMax); +} + +void Editor::StartIdleStyling(bool truncatedLastStyling) { + if ((idleStyling == SC_IDLESTYLING_ALL) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) { + if (pdoc->GetEndStyled() < pdoc->Length()) { + // Style remainder of document in idle time + needIdleStyling = true; + } + } else if (truncatedLastStyling) { + needIdleStyling = true; + } + + if (needIdleStyling) { + SetIdle(true); + } +} + +// Style for an area but bound the amount of styling to remain responsive +void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) { + const int posAfterArea = PositionAfterArea(rcArea); + const int posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling); + if (posAfterMax < posAfterArea) { + // Idle styling may be performed before current visible area + // Style a bit now then style further in idle time + pdoc->StyleToAdjustingLineDuration(posAfterMax); + } else { + // Can style all wanted now. + StyleToPositionInView(posAfterArea); + } + StartIdleStyling(posAfterMax < posAfterArea); +} + +void Editor::IdleStyling() { + const int posAfterArea = PositionAfterArea(GetClientRectangle()); + const int endGoal = (idleStyling >= SC_IDLESTYLING_AFTERVISIBLE) ? + pdoc->Length() : posAfterArea; + const int posAfterMax = PositionAfterMaxStyling(endGoal, false); + pdoc->StyleToAdjustingLineDuration(posAfterMax); + if (pdoc->GetEndStyled() >= endGoal) { + needIdleStyling = false; + } +} + void Editor::IdleWork() { // Style the line after the modification as this allows modifications that change just the // line of the modification to heal instead of propagating to the rest of the window. - if (workNeeded.items & WorkNeeded::workStyle) + if (workNeeded.items & WorkNeeded::workStyle) { StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2)); - + } NotifyUpdateUI(); workNeeded.Reset(); } @@ -6400,6 +6463,13 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_ISRANGEWORD: return pdoc->IsWordAt(static_cast<int>(wParam), static_cast<int>(lParam)); + + case SCI_SETIDLESTYLING: + idleStyling = static_cast<int>(wParam); + break; + + case SCI_GETIDLESTYLING: + return idleStyling; case SCI_SETWRAPMODE: if (vs.SetWrapState(static_cast<int>(wParam))) { diff --git a/src/Editor.h b/src/Editor.h index 7b88cbab0..0d788b5a8 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -234,6 +234,8 @@ protected: // ScintillaBase subclass needs access to much of Editor bool paintingAllText; bool willRedrawAll; WorkNeeded workNeeded; + int idleStyling; + bool needIdleStyling; int modEventMask; @@ -525,6 +527,10 @@ protected: // ScintillaBase subclass needs access to much of Editor int PositionAfterArea(PRectangle rcArea) const; void StyleToPositionInView(Position pos); + int PositionAfterMaxStyling(int posMax, bool scrolling) const; + void StartIdleStyling(bool truncatedLastStyling); + void StyleAreaBounded(PRectangle rcArea, bool scrolling); + void IdleStyling(); virtual void IdleWork(); virtual void QueueIdleWork(WorkNeeded::workItems items, int upTo=0); |