From a180e477ec3efd9d35a4cf32b63ab7354a03371f Mon Sep 17 00:00:00 2001 From: Neil Date: Sun, 1 Jun 2025 08:38:14 +1000 Subject: Add SCI_SCROLLVERTICAL API. --- call/ScintillaCall.cxx | 4 ++++ cocoa/ScintillaCocoa.mm | 1 + doc/ScintillaDoc.html | 21 +++++++++++++++++++++ doc/ScintillaHistory.html | 3 +++ gtk/ScintillaGTK.cxx | 1 + include/Scintilla.h | 1 + include/Scintilla.iface | 3 +++ include/ScintillaCall.h | 1 + include/ScintillaMessages.h | 1 + qt/ScintillaEditBase/ScintillaQt.cpp | 1 + src/ContractionState.cxx | 7 +++++++ src/ContractionState.h | 1 + src/Editor.cxx | 34 +++++++++++++++++++++++++++++++--- src/Editor.h | 8 +++++++- win32/ScintillaWin.cxx | 1 + 15 files changed, 84 insertions(+), 4 deletions(-) diff --git a/call/ScintillaCall.cxx b/call/ScintillaCall.cxx index 11a1f1d52..d1d4012b2 100644 --- a/call/ScintillaCall.cxx +++ b/call/ScintillaCall.cxx @@ -1375,6 +1375,10 @@ void ScintillaCall::LineScroll(Position columns, Line lines) { Call(Message::LineScroll, columns, lines); } +void ScintillaCall::ScrollVertical(Line docLine, Line subLine) { + Call(Message::ScrollVertical, docLine, subLine); +} + void ScintillaCall::ScrollCaret() { Call(Message::ScrollCaret); } diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm index b1e242cc9..8a9af4058 100644 --- a/cocoa/ScintillaCocoa.mm +++ b/cocoa/ScintillaCocoa.mm @@ -1951,6 +1951,7 @@ void ScintillaCocoa::ScrollText(Sci::Line) { * Modifies the vertical scroll position to make the current top line show up as such. */ void ScintillaCocoa::SetVerticalScrollPos() { + Editor::SetVerticalScrollPos(); NSScrollView *scrollView = ScrollContainer(); if (scrollView) { NSClipView *clipView = scrollView.contentView; diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index 6a75fd63f..a2af4f64f 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -2397,6 +2397,7 @@ struct Sci_TextToFindFull { SCI_GETFIRSTVISIBLELINE → line
SCI_SETXOFFSET(int xOffset)
SCI_GETXOFFSET → int
+ SCI_SCROLLVERTICAL(line docLine, line subLine)
SCI_LINESCROLL(position columns, line lines)
SCI_SCROLLCARET
SCI_SCROLLRANGE(position secondary, position primary)
@@ -2430,6 +2431,26 @@ struct Sci_TextToFindFull { view. A value of 0 is the normal position with the first text column visible at the left of the view.

+

SCI_SCROLLVERTICAL(line docLine, line subLine)
+ Sets the vertical scroll position to be the display line for the + subLine of the docLine, similar to
+ SCI_SETFIRSTVISIBLELINE(SCI_VISIBLEFROMDOCLINE(docLine) + subLine).

+ +

The subLine is capped to the maximum number of sublines + for that document line which may change because of styling and wrapping. It is ignored when line + wrapping is off.

+ +

If line wrapping is on, then that + docLine/subLine + is remembered and reapplied as lines are wrapped. + This ensures that when wrapping is completed, which may take some time, the line at the top of the view is that + requested by the application and the view range is stable during the wrapping process. + It is forgotten once the document is fully wrapped or the user + performs scrolling manually such as by dragging the scroll bar.

+ +

This method is a good way for applications to restore the user's positional context when re-selecting a + document as it is robust to changes in window width and styles.

+

SCI_LINESCROLL(position columns, line lines)
This will attempt to scroll the display by the number of columns and lines that you specify. Positive line values increase the line number at the top of the screen (i.e. they move the text diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 5b636af5d..edf0b6344 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -601,6 +601,9 @@ Released 2 April 2025.

  • + Add SCI_SCROLLVERTICAL method to restore view position and maintain it while performing line wrapping. +
  • +
  • Add SC_UNDO_SELECTION_HISTORY_SCROLL flag to SCI_SETUNDOSELECTIONHISTORY which controls whether undo and redo restore vertical scroll position.
  • diff --git a/gtk/ScintillaGTK.cxx b/gtk/ScintillaGTK.cxx index 3f8ce2d9e..686a8c13f 100755 --- a/gtk/ScintillaGTK.cxx +++ b/gtk/ScintillaGTK.cxx @@ -1102,6 +1102,7 @@ void ScintillaGTK::ScrollText(Sci::Line linesToMove) { } void ScintillaGTK::SetVerticalScrollPos() { + Editor::SetVerticalScrollPos(); DwellEnd(true); gtk_adjustment_set_value(GTK_ADJUSTMENT(adjustmentv), static_cast(topLine)); } diff --git a/include/Scintilla.h b/include/Scintilla.h index e2555b862..f7afa4f50 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -561,6 +561,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCI_LINEFROMPOSITION 2166 #define SCI_POSITIONFROMLINE 2167 #define SCI_LINESCROLL 2168 +#define SCI_SCROLLVERTICAL 2817 #define SCI_SCROLLCARET 2169 #define SCI_SCROLLRANGE 2569 #define SCI_REPLACESEL 2170 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index af34b71db..9ba834aed 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -1422,6 +1422,9 @@ fun position PositionFromLine=2167(line line,) # Scroll horizontally and vertically. fun void LineScroll=2168(position columns, line lines) +# Scroll vertically with allowance for wrapping. +fun void ScrollVertical=2817(line docLine, line subLine) + # Ensure the caret is visible. fun void ScrollCaret=2169(,) diff --git a/include/ScintillaCall.h b/include/ScintillaCall.h index a2387f876..7a98c7e3d 100644 --- a/include/ScintillaCall.h +++ b/include/ScintillaCall.h @@ -389,6 +389,7 @@ public: Line LineFromPosition(Position pos); Position PositionFromLine(Line line); void LineScroll(Position columns, Line lines); + void ScrollVertical(Line docLine, Line subLine); void ScrollCaret(); void ScrollRange(Position secondary, Position primary); void ReplaceSel(const char *text); diff --git a/include/ScintillaMessages.h b/include/ScintillaMessages.h index 67b4f69c6..6a33f5c6d 100644 --- a/include/ScintillaMessages.h +++ b/include/ScintillaMessages.h @@ -311,6 +311,7 @@ enum class Message { LineFromPosition = 2166, PositionFromLine = 2167, LineScroll = 2168, + ScrollVertical = 2817, ScrollCaret = 2169, ScrollRange = 2569, ReplaceSel = 2170, diff --git a/qt/ScintillaEditBase/ScintillaQt.cpp b/qt/ScintillaEditBase/ScintillaQt.cpp index 88789979a..d5e6eb7b0 100644 --- a/qt/ScintillaEditBase/ScintillaQt.cpp +++ b/qt/ScintillaEditBase/ScintillaQt.cpp @@ -280,6 +280,7 @@ void ScintillaQt::ScrollText(Sci::Line linesToMove) void ScintillaQt::SetVerticalScrollPos() { + Editor::SetVerticalScrollPos(); scrollArea->verticalScrollBar()->setValue(topLine); emit verticalScrolled(topLine); } diff --git a/src/ContractionState.cxx b/src/ContractionState.cxx index b36e1ebd8..8f0f795e2 100644 --- a/src/ContractionState.cxx +++ b/src/ContractionState.cxx @@ -65,6 +65,7 @@ public: Sci::Line LinesInDoc() const noexcept override; Sci::Line LinesDisplayed() const noexcept override; Sci::Line DisplayFromDoc(Sci::Line lineDoc) const noexcept override; + Sci::Line DisplayFromDocSub(Sci::Line lineDoc, Sci::Line lineSub) const noexcept override; Sci::Line DisplayLastFromDoc(Sci::Line lineDoc) const noexcept override; Sci::Line DocFromDisplay(Sci::Line lineDisplay) const noexcept override; @@ -183,6 +184,12 @@ Sci::Line ContractionState::DisplayFromDoc(Sci::Line lineDoc) const noexce } } +template +Sci::Line ContractionState::DisplayFromDocSub(Sci::Line lineDoc, Sci::Line lineSub) const noexcept { + return DisplayFromDoc(lineDoc) + + std::min(lineSub, static_cast(GetHeight(lineDoc) - 1)); +} + template Sci::Line ContractionState::DisplayLastFromDoc(Sci::Line lineDoc) const noexcept { return DisplayFromDoc(lineDoc) + GetHeight(lineDoc) - 1; diff --git a/src/ContractionState.h b/src/ContractionState.h index ae753f8d1..a144364d0 100644 --- a/src/ContractionState.h +++ b/src/ContractionState.h @@ -21,6 +21,7 @@ public: virtual Sci::Line LinesInDoc() const noexcept=0; virtual Sci::Line LinesDisplayed() const noexcept=0; virtual Sci::Line DisplayFromDoc(Sci::Line lineDoc) const noexcept=0; + virtual Sci::Line DisplayFromDocSub(Sci::Line lineDoc, Sci::Line lineSub) const noexcept=0; virtual Sci::Line DisplayLastFromDoc(Sci::Line lineDoc) const noexcept=0; virtual Sci::Line DocFromDisplay(Sci::Line lineDisplay) const noexcept=0; diff --git a/src/Editor.cxx b/src/Editor.cxx index 4c35093db..8bc5d036a 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -202,6 +202,8 @@ Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) { recordingMacro = false; foldAutomatic = AutomaticFold::None; + insideWrapScroll = false; + convertPastes = true; SetRepresentations(); @@ -1679,8 +1681,15 @@ bool Editor::WrapLines(WrapScope ws) { // Decide where to start wrapping Sci::Line lineToWrap = wrapPending.start; Sci::Line lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal()); + const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine); - const Sci::Line subLineTop = topLine - pcs->DisplayFromDoc(lineDocTop); + LineDocSub lineScrollTo; + if (scrollToAfterWrap) { + lineScrollTo = scrollToAfterWrap.value(); + } else { + const Sci::Line subLineTop = topLine - pcs->DisplayFromDoc(lineDocTop); + lineScrollTo = { lineDocTop, subLineTop }; + } if (ws == WrapScope::wsVisible) { lineToWrap = std::clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal()); // Priority wrap to just after visible area. @@ -1731,21 +1740,23 @@ bool Editor::WrapLines(WrapScope ws) { wrapOccurred = WrapBlock(surface, lineToWrap, lineToWrapEnd); - goodTopLine = pcs->DisplayFromDoc(lineDocTop) + std::min( - subLineTop, static_cast(pcs->GetHeight(lineDocTop)-1)); + goodTopLine = pcs->DisplayFromDocSub(lineScrollTo.lineDoc, lineScrollTo.subLine); } } // If wrapping is done, bring it to resting position if (wrapPending.start >= lineEndNeedWrap) { wrapPending.Reset(); + scrollToAfterWrap.reset(); } } if (wrapOccurred) { + insideWrapScroll = true; SetScrollBars(); SetTopLine(std::clamp(goodTopLine, 0, MaxScrollPos())); SetVerticalScrollPos(); + insideWrapScroll = false; } return wrapOccurred; @@ -1985,6 +1996,12 @@ long Editor::TextWidth(uptr_t style, const char *text) { return 1; } +void Editor::SetVerticalScrollPos() { + if (!insideWrapScroll) { + scrollToAfterWrap.reset(); + } +} + // Empty method is overridden on GTK+ to show / hide scrollbars void Editor::ReconfigureScrollBars() {} @@ -5540,6 +5557,8 @@ void Editor::SetDocPointer(Document *document) { SetRepresentations(); + scrollToAfterWrap.reset(); + // Reset the contraction state to fully shown. pcs->Clear(); pcs->InsertLines(0, pdoc->LinesTotal() - 1); @@ -6549,6 +6568,15 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { HorizontalScrollTo(xOffset + static_cast(static_cast(wParam) * vs.spaceWidth)); return 1; + case Message::ScrollVertical: + if (Wrapping()) { + scrollToAfterWrap = { LineFromUPtr(wParam), lParam }; + } else { + scrollToAfterWrap.reset(); + } + ScrollTo(pcs->DisplayFromDocSub(LineFromUPtr(wParam), lParam)); + break; + case Message::SetXOffset: xOffset = static_cast(wParam); ContainerNeedsUpdate(Update::HScroll); diff --git a/src/Editor.h b/src/Editor.h index 46879cdc4..f8bc18970 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -283,6 +283,12 @@ protected: // ScintillaBase subclass needs access to much of Editor // Wrapping support WrapPending wrapPending; ActionDuration durationWrapOneByte; + bool insideWrapScroll; + struct LineDocSub { + Scintilla::Line lineDoc = 0; + Scintilla::Line subLine = 0; + }; + std::optional scrollToAfterWrap; bool convertPastes; @@ -417,7 +423,7 @@ protected: // ScintillaBase subclass needs access to much of Editor Sci::Position FormatRange(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam); long TextWidth(Scintilla::uptr_t style, const char *text); - virtual void SetVerticalScrollPos() = 0; + virtual void SetVerticalScrollPos(); virtual void SetHorizontalScrollPos() = 0; virtual bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) = 0; virtual void ReconfigureScrollBars(); diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index 571d1000c..b4c268562 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -2700,6 +2700,7 @@ void ScintillaWin::ChangeScrollPos(int barType, Sci::Position pos) { } void ScintillaWin::SetVerticalScrollPos() { + Editor::SetVerticalScrollPos(); ChangeScrollPos(SB_VERT, topLine); } -- cgit v1.2.3