Re: [PATCH] new feature: horizontal scrolling of full screen

Xylia Allegretta <[email protected]> Tue, 6 Jan 2026 02:03:01 +0000
Newsgroups gmane.editors.nano.devel
Message-ID <CAE7GgQ8Q9fJ+01GYkEqKu=izd7o-PtKhuabCSdU9T8TV29y2=A@mail.gmail.com>
On Mon, Jan 5, 2026 at 4:22 PM Benno Schulenberg <[email protected]> wrote:
> To me it's not just a bit jarring -- it is majorly jarring.

Yeah, that's fair. It probably doesn't bother me as much because I expect it
from having written the patch, but I could see how anyone who doesn't could
easily be confused by it. I still like it, though.

> In summary: a patch that horizontally "scrolls" the viewport in
> steps of nearly a full screen width will not be accepted.

Well, okay. I've modified the patch to scroll in steps of 8 characters, but it
could easily be changed later to be entirely smooth, I guess. It might be
worth additionally changing the distance from the side of the screen that
causes scrolling. I've also added some more checks and made a screen
refresh call the function to update the scroll position if it hadn't
been already.
It seems like undo/redo, return, cut/uncut, the help text, and the mouse
are working better now.

> I normally press <End> on it. With the
> smooth-horizontal-scrolling patch this would move the viewport
> as far to the right as needed.

With <End> moving to the end of the line, it would only move the viewpoint to
show the rest of the text if the line were under two pages, no? You'd have to
do a lot of backwards scrolling if it were longer.

> And with
> a judicious use of Ctrl+Left/Right, one can bring the central
> parts of a very wide table into view, to see precisely the
> columns that one is interested in -- with your patch the table
> is always "split" at a multiple of nearly the screen width.

I feel like for a table or large block of text, using <Ctrl> and the cursor keys
is pretty inconvenient, as you'd have to press them for as many entries or
words as there are between you and your target, but I will admit that splitting
the table into segments is also frustrating and probably even more
inconvenient, as it can't be resolved by the user in any way. And I suppose
you could always keep Ctrl+Left/Right held down, although that might also
be hard to follow if the screen is scrolling quickly. Regardless, I see why
you prefer the smooth or shorter increment scrolling. The new version of
the patch is attached.

-Xylia Allegretta.
fullscrolling-3.patch (text/x-patch, 11.2 KB)
From 8b4ea6406fef9f77e88d35e42d5519faa9c71d20 Mon Sep 17 00:00:00 2001
From: Xylia Allegretta <[email protected]>
Date: Mon, 5 Jan 2026 19:58:18 -0500
Subject: [PATCH] new feature: horizontal scrolling of full screen (patch v3)

I've modified the patch to scroll in steps of 8 characters. I
also added some more checks and made a screen refresh call the function to
update the scroll position if it hadn't been already. It seems like undo/redo,
return, cut/uncut, the help text, and the mouse are working better now.

Signed-off-by: Xylia Allegretta <[email protected]>
---
 src/cut.c         |  6 ++--
 src/definitions.h |  3 +-
 src/global.c      |  3 ++
 src/move.c        |  8 +++--
 src/nano.c        | 16 +++++++--
 src/prototypes.h  |  3 ++
 src/text.c        |  8 +++++
 src/winio.c       | 86 ++++++++++++++++++++++++++++++++++++++++++++---
 8 files changed, 120 insertions(+), 13 deletions(-)

diff --git a/src/cut.c b/src/cut.c
index a2d4aecf..dc7e86e4 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -50,8 +50,10 @@ void expunge(undo_type action)
 					&openfile->current->data[openfile->current_x + charlen],
 					line_len - charlen + 1);
 #ifndef NANO_TINY
-		/* When softwrapping, a changed number of chunks requires a refresh. */
-		if (ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount)
+		/* When softwrapping, a changed number of chunks requires a refresh.
+		 * Alternatively, when fullscrolling, a change in scrolling requires a refresh. */
+		if ((ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount) ||
+					(ISSET(FULL_SCROLLING) && fullsc_update()))
 			refresh_needed = TRUE;
 
 		/* Adjust the mark if it is after the cursor on the current line. */
diff --git a/src/definitions.h b/src/definitions.h
index dfe2106f..3262821f 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -376,7 +376,8 @@ enum {
 	USE_MAGIC,
 	MINIBAR,
 	ZERO,
-	MODERN_BINDINGS
+	MODERN_BINDINGS,
+	FULL_SCROLLING
 };
 
 /* Structure types. */
diff --git a/src/global.c b/src/global.c
index 84912f17..271f6d49 100644
--- a/src/global.c
+++ b/src/global.c
@@ -296,6 +296,9 @@ size_t light_from_col = 0;
 size_t light_to_col = 0;
 	/* Where the spotlighted text ends. */
 
+size_t fullsc_pos = 0;
+	/* The amount the screen has scrolled right when fullscrolling. */
+
 /* To make the functions and shortcuts lists clearer. */
 #define VIEW  TRUE    /* Is allowed in view mode. */
 #define NOVIEW  FALSE
diff --git a/src/move.c b/src/move.c
index f79107ab..23486ca9 100644
--- a/src/move.c
+++ b/src/move.c
@@ -511,7 +511,9 @@ void do_home(void)
 
 	/* If we changed chunk, we might be offscreen.  Otherwise,
 	 * update current if the mark is on or we changed "page". */
-	if (ISSET(SOFTWRAP) && moved_off_chunk)
+	if (ISSET(FULL_SCROLLING) && fullsc_update())
+		edit_refresh();
+	else if (ISSET(SOFTWRAP) && moved_off_chunk)
 		edit_redraw(was_current, FLOWING);
 	else if (line_needs_update(was_column, openfile->placewewant))
 		update_line(openfile->current, openfile->current_x);
@@ -563,7 +565,9 @@ void do_end(void)
 
 	/* If we changed chunk, we might be offscreen.  Otherwise,
 	 * update current if the mark is on or we changed "page". */
-	if (ISSET(SOFTWRAP) && moved_off_chunk)
+	if (ISSET(FULL_SCROLLING) && fullsc_update())
+		edit_refresh();
+	else if (ISSET(SOFTWRAP) && moved_off_chunk)
 		edit_redraw(was_current, FLOWING);
 	else if (line_needs_update(was_column, openfile->placewewant))
 		update_line(openfile->current, openfile->current_x);
diff --git a/src/nano.c b/src/nano.c
index 2b45ca76..ee3081ab 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -1356,6 +1356,8 @@ int process_click(void)
 
 		if (ISSET(SOFTWRAP))
 			leftedge = leftedge_for(xplustabs(), openfile->current);
+		else if (ISSET(FULL_SCROLLING))
+			leftedge = fullsc_pos;
 		else
 #endif
 			leftedge = get_page_start(xplustabs());
@@ -1554,10 +1556,12 @@ void inject(char *burst, size_t count)
 #ifndef NANO_TINY
 	/* When softwrapping and the number of chunks in the current line changed,
 	 * or we were on the last row of the edit window and moved to a new chunk,
-	 * we need a full refresh. */
-	if (ISSET(SOFTWRAP) && (extra_chunks_in(openfile->current) != old_amount ||
+	 * we need a full refresh. A refresh is also necessary when fullscrolling
+	 * is set and the scrolling changes. */
+	if ((ISSET(SOFTWRAP) && (extra_chunks_in(openfile->current) != old_amount ||
 					(openfile->cursor_row == editwinrows - 1 &&
-					chunk_for(openfile->placewewant, openfile->current) > original_row))) {
+					chunk_for(openfile->placewewant, openfile->current) > original_row))) ||
+					(ISSET(FULL_SCROLLING) && fullsc_update())) {
 		refresh_needed = TRUE;
 		focusing = FALSE;
 	}
@@ -2642,6 +2646,12 @@ int main(int argc, char **argv)
 		die(_("Can open just one file\n"));
 #endif
 
+	/* Enabled by default for testing. */
+	SET(FULL_SCROLLING);
+	/* These options should be mutually exclusive. */
+	if (ISSET(SOFTWRAP))
+		UNSET(SOFTWRAP);
+
 	prepare_for_display();
 
 #ifdef ENABLE_NANORC
diff --git a/src/prototypes.h b/src/prototypes.h
index c83380df..84bd2d78 100644
--- a/src/prototypes.h
+++ b/src/prototypes.h
@@ -197,6 +197,8 @@ extern bool spotlighted;
 extern size_t light_from_col;
 extern size_t light_to_col;
 
+extern size_t fullsc_pos;
+
 typedef void (*functionptrtype)(void);
 
 /* The two needed functions from browser.c. */
@@ -687,3 +689,4 @@ void flip_newbuffer(void);
 #endif
 void discard_buffer(void);
 void do_cancel(void);
+bool fullsc_update(void);
diff --git a/src/text.c b/src/text.c
index 3d553447..54f8cd3c 100644
--- a/src/text.c
+++ b/src/text.c
@@ -665,6 +665,10 @@ void do_undo(void)
 
 	openfile->totsize = u->wassize;
 
+	/* If scrolling has changed, refresh. */
+	if (ISSET(FULL_SCROLLING) && fullsc_update())
+		refresh_needed = TRUE;
+
 #ifdef ENABLE_COLOR
 	if (u->type <= REPLACE)
 		check_the_multis(openfile->current);
@@ -834,6 +838,10 @@ void do_redo(void)
 
 	openfile->totsize = u->newsize;
 
+	/* If scrolling has changed, refresh. */
+	if (ISSET(FULL_SCROLLING) && fullsc_update())
+		refresh_needed = TRUE;
+
 #ifdef ENABLE_COLOR
 	if (u->type <= REPLACE)
 		check_the_multis(openfile->current);
diff --git a/src/winio.c b/src/winio.c
index 277851d9..564c38f4 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -81,6 +81,10 @@ static size_t macro_length = 0;
 		/* The current length of the macro. */
 static size_t milestone = 0;
 		/* Where the last burst of recorded keystrokes started. */
+static bool fullsc_culled = FALSE;
+		/* If a line has been culled due to being offscreen. */
+static bool fullsc_refresh_check = TRUE;
+		/* Whether or not to update scrolling when a refresh occurs. */
 
 /* Add the given code to the macro buffer. */
 void add_to_macrobuffer(int code)
@@ -1759,6 +1763,35 @@ void set_blankdelay_to_one(void)
 	countdown = 1;
 }
 
+/* Fullscrolling helper function. Returns TRUE if a refresh is needed due to scrolling. */
+bool fullsc_update(void)
+{
+	size_t xtemp = xplustabs();
+	bool lscrolled = FALSE, rscrolled = FALSE;
+	/* Scroll until the cursor is on-screen. */
+	while (TRUE) {
+		if (xtemp >= fullsc_pos + editwincols - 2) {
+			rscrolled = TRUE;
+			fullsc_pos += 8;
+		}
+		else if (xtemp < fullsc_pos + 2 && fullsc_pos >= 8) {
+			lscrolled = TRUE;
+			fullsc_pos -= 8;
+		}
+		else
+			break;
+		/* Prevent infinite loop when screen is squished. */
+		if (lscrolled && rscrolled)
+			break;
+	}
+
+	/* Don't needlessly call this function again during the next refresh. */
+	if (lscrolled || rscrolled)
+		fullsc_refresh_check = FALSE;
+
+	return lscrolled || rscrolled;
+}
+
 /* Convert text into a string that can be displayed on screen.  The caller
  * wants to display text starting with the given column, and extending for
  * at most span columns.  column is zero-based, and span is one-based, so
@@ -1787,6 +1820,24 @@ char *display_string(const char *text, size_t column, size_t span,
 	size_t beyond = column + span;
 		/* The column number just beyond the last shown character. */
 
+	/* If fullscrolling is enabled, cull offscreen text. */
+	if (isdata && ISSET(FULL_SCROLLING)) {
+		/* Is text offscreen? */
+		if (fullsc_pos!=0 && fullsc_pos >= breadth(text)) {
+			/* Create blank string. */
+			/* This has the side effect of the mark highlighting the
+			 * entire line, which could be a good or bad thing. */
+			size_t i;
+			for (i=0; i<allocsize-1; i++)
+				converted[i]=' ';
+			converted[i]='\0';
+			has_more = FALSE;
+			fullsc_culled = TRUE;
+			return converted;
+		} else
+			fullsc_culled = FALSE;
+	}
+
 	text += start_x;
 
 #ifndef NANO_TINY
@@ -2518,7 +2569,10 @@ void place_the_cursor(void)
 #endif
 	{
 		row = openfile->current->lineno - openfile->edittop->lineno;
-		column -= get_page_start(column);
+		if (ISSET(FULL_SCROLLING))
+			column -= fullsc_pos;
+		else
+			column -= get_page_start(column);
 	}
 
 	if (row < editwinrows)
@@ -2838,14 +2892,17 @@ int update_line(linestruct *line, size_t index)
 #endif
 
 	row = line->lineno - openfile->edittop->lineno;
-	from_col = get_page_start(wideness(line->data, index));
+	if (ISSET(FULL_SCROLLING))
+		from_col = fullsc_pos;
+	else
+		from_col = get_page_start(wideness(line->data, index));
 
 	/* Expand the piece to be drawn to its representable form, and draw it. */
 	converted = display_string(line->data, from_col, editwincols, TRUE, FALSE);
 	draw_row(row, converted, line, from_col);
 	free(converted);
 
-	if (from_col > 0) {
+	if (from_col > 0 || (ISSET(FULL_SCROLLING) && fullsc_culled)) {
 		wattron(midwin, hilite_attribute);
 		mvwaddch(midwin, row, margin, '<');
 		wattroff(midwin, hilite_attribute);
@@ -3086,6 +3143,12 @@ void edit_scroll(bool direction)
 	wscrl(midwin, (direction == BACKWARD) ? -1 : 1);
 	scrollok(midwin, FALSE);
 
+	/* If fullscrolling, and the page changed, refresh. */
+	if (ISSET(FULL_SCROLLING) && fullsc_update()) {
+		refresh_needed = TRUE;
+		return;
+	}
+
 	/* If we're not on the first "page" (when not softwrapping), or the mark
 	 * is on, the row next to the scrolled region needs to be redrawn too. */
 	if (line_needs_update(openfile->placewewant, 0) && nrows < editwinrows)
@@ -3344,6 +3407,12 @@ void edit_redraw(linestruct *old_current, update_type manner)
 		return;
 	}
 
+	/* If fullscrolling, and the page changed, refresh. */
+	if (ISSET(FULL_SCROLLING) && fullsc_update()) {
+		refresh_needed = TRUE;
+		return;
+	}
+
 #ifndef NANO_TINY
 	/* If the mark is on, update all lines between old_current and current. */
 	if (openfile->mark) {
@@ -3358,8 +3427,10 @@ void edit_redraw(linestruct *old_current, update_type manner)
 	} else
 #endif
 		/* Otherwise, update old_current only if it differs from current
-		 * and was horizontally scrolled. */
-		if (old_current != openfile->current && get_page_start(was_pww) > 0)
+		 * and was horizontally scrolled. When fullscrolling, there's no
+		 * reason to do this. */
+		if (old_current != openfile->current && (get_page_start(was_pww) > 0
+						&& !ISSET(FULL_SCROLLING)))
 			update_line(old_current, 0);
 
 	/* Update current if the mark is on or it has changed "page", or if it
@@ -3396,6 +3467,11 @@ void edit_refresh(void)
 	}
 #endif
 
+	/* If refresh was not caused by a manual fullsc_update() call, call now. */
+	if (ISSET(FULL_SCROLLING) && fullsc_refresh_check)
+		fullsc_update();
+	fullsc_refresh_check = TRUE;
+
 #ifndef NANO_TINY
 	if (sidebar)
 		draw_scrollbar();
-- 
2.52.0