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

Xylia Allegretta <[email protected]> Tue, 6 Jan 2026 23:17:55 +0000
Newsgroups gmane.editors.nano.devel
Message-ID <CAE7GgQ9-M038Xf==upk6hL1jgn98HwoiApJxDbeM1Kb4aLpC4g@mail.gmail.com>
On Tue, Jan 6, 2026 at 10:02 PM Chris Allegretta <[email protected]> wrote:
> How about the following:
> - By default, scroll horizontally by 1
> - If JUMPY_SCROLLING is set, scroll by some other value, 8, COLS/2, or
> what have you.... Seems to me like someone who's enabled jumpy scrolling
> is ok with large movements of the screen, and doesn't mind (or prefers)
> recalibrating what they're looking at after a lot of the text has moved.
> This would be logically consistent with that.

That's a pretty easy change. I've added a check for if JUMPY_SCROLLNG is
set in the scrolling update function, and eliminated a variable in winio that
I realized was unnecessary.

-Xylia Allegretta.
fullscrolling-4.patch (text/x-patch, 11 KB)
From 0e0103c96699ce647d3e34923eddbce012c64974 Mon Sep 17 00:00:00 2001
From: Xylia Allegretta <[email protected]>
Date: Tue, 6 Jan 2026 18:10:10 -0500
Subject: [PATCH] new feature: horizontal scrolling of full screen (patch v4)

That's a pretty easy change. I've added a check for if JUMPY_SCROLLNG is
set in the scrolling update function, and eliminated a variable in winio that
I realized was unnecessary.

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       | 84 ++++++++++++++++++++++++++++++++++++++++++++---
 8 files changed, 118 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..4beecdd4 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -81,6 +81,8 @@ 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_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 +1761,37 @@ 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(), scroll_dist = 1;
+	bool lscrolled = FALSE, rscrolled = FALSE;
+	if (ISSET(JUMPY_SCROLLING))
+		scroll_dist = 8;
+	/* Scroll until the cursor is on-screen. */
+	while (TRUE) {
+		if (xtemp >= fullsc_pos + editwincols - 2) {
+			rscrolled = TRUE;
+			fullsc_pos += scroll_dist;
+		}
+		else if (xtemp < fullsc_pos + 2 && fullsc_pos >= scroll_dist) {
+			lscrolled = TRUE;
+			fullsc_pos -= scroll_dist;
+		}
+		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,22 @@ 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;
+			return converted;
+		}
+	}
+
 	text += start_x;
 
 #ifndef NANO_TINY
@@ -2518,7 +2567,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 +2890,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_pos)) {
 		wattron(midwin, hilite_attribute);
 		mvwaddch(midwin, row, margin, '<');
 		wattroff(midwin, hilite_attribute);
@@ -3086,6 +3141,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 +3405,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 +3425,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 +3465,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