gridPaging

When a library grid asks for its next page, and what it does when a page after its first fails. The first page has its own failure state instead (BaseGridView.onGridLoadFailed).

WHEN TO ASK: keep a runway. A grid loads the rows near where the user is, not the whole library: every loaded item costs memory (512 MB devices), a server query, and render-thread time as it lands. How far ahead is enough depends on two things measured as the grid is used rather than fixed in advance: how long a page takes to arrive (server, network and device together) and how fast the user is moving down the rows. The next page is asked for when the rows left below the focused row would run out before a page could arrive; at rest, one screen of rows is kept ahead. Only one page is ever in flight.

In source/ rather than in the view so a Rooibos suite can reach it: a component's functions are scoped to it. Same move as gridPage.bs.

Members

(static, constant) IDLE_MS

Default Value
  • 1000

(static, constant) MIN_SPAN_MS

Default Value
  • 250

(static, constant) RUNWAY_MARGIN

Starting values, not measurements. Tune them from the per-grid log line (BaseGridView.logGridSession), which records the stalls and the rows loaded but never seen.

RUNWAY_MARGIN: how much sooner than the bare estimate to ask: page times vary, and a stall costs the user more than an early page costs the device. IDLE_MS: a pause longer than this between focus moves ends a burst of scrolling: the speed of the last burst says nothing about the next. SPEED_WINDOW_MS: the scroll speed is the rate over this much of the latest movement, not between two single reports: rows can be reported milliseconds apart when the render thread catches up after a page lands (measured 2026-09-25 on a 512 MB Stick: one pair read as 158 rows/s), and a rate over a window counts such a row at its true share. MIN_SPAN_MS: the shortest span a rate is taken over, for the same reason. SMOOTHING: the weight of the newest page in the running average of page time.

Default Value
  • 1.5

(static, constant) SMOOTHING

Default Value
  • 0.5

(static, constant) SPEED_WINDOW_MS

Default Value
  • 1000

Methods

(static) hasMore(loadedItems, total) → {boolean}

Whether the query has items the grid has not loaded yet. Never before its first page has landed: that page has its own failure state and Try again, and a new query's total is 0 until then.

Parameters:
NameTypeDescription
loadedItemsinteger

items in the grid

totalinteger

the query's total, counted once, on its first page

Returns:
Type: 
boolean

(static) isWaitingAtEnd(focusedRow, lastLoadedRow, hasMore, isLoading) → {boolean}

Whether the user has reached the last loaded row while the next page is on its way: the one moment a later page's loading is theirs to see.

Parameters:
NameTypeDescription
focusedRowinteger
lastLoadedRowinteger
hasMoreboolean
isLoadingboolean
Returns:
Type: 
boolean

(static) lastRow(loadedItems, columns) → {integer}

The last row holding a loaded item; -1 when nothing is loaded.

Parameters:
NameTypeDescription
loadedItemsinteger
columnsinteger
Returns:
Type: 
integer

(static) newFailureNotice() → {object}

The failure notice for a new query: nothing told yet.

Returns:
Type: 
object

(static) newPageTimer() → {object}

How long a page takes to arrive, for a grid that has loaded none: unknown.

Returns:
Type: 
object

(static) newScrollTracker() → {object}

How fast the user is moving down the rows, for a new query: not moving.

Returns:
Type: 
object

(static) pageLoaded(notice) → {void}

A page loaded, so the next failure is news again.

Parameters:
NameTypeDescription
noticeobject

from newFailureNotice(); modified

Returns:
Type: 
void

(static) recordPageTime(timer, ms) → {void}

A page arrived ms after it was asked for.

Parameters:
NameTypeDescription
timerobject

from newPageTimer(); modified

msinteger
Returns:
Type: 
void

(static) runwayRows(screenRows, rowsPerSecond, pageMs) → {integer}

How many loaded rows the grid wants below the focused row: the rows the user would pass while a page is on its way, with a margin, and never less than one screen.

Parameters:
NameTypeDescription
screenRowsinteger

rows the grid shows at once

rowsPerSecondfloat

from scrollSpeed()

pageMsfloat

from a page timer; negative when no page has been timed

Returns:
Type: 
integer

(static) scrollSpeed(tracker, nowMs) → {float}

Rows per second down the grid now, over the latest movement; 0 once the user has paused. Only moving down counts: moving up returns to rows already loaded.

Parameters:
NameTypeDescription
trackerobject

from newScrollTracker()

nowMsinteger

the clock trackFocus was given

Returns:
Type: 
float

(static) shouldLoadMore(focusedRow, lastLoadedRow, hasMore, isLoading, wantedRows) → {boolean}

Whether to ask for the next page now.

Parameters:
NameTypeDescription
focusedRowinteger

the focused row

lastLoadedRowinteger

the last row holding a loaded item

hasMoreboolean

whether the query has items the grid has not loaded

isLoadingboolean

whether a page is in flight

wantedRowsinteger

from runwayRows()

Returns:
Type: 
boolean

(static) shouldTellPageFailure(notice, isShown) → {boolean}

A later page failed. Whether to tell the user now, counted as told when so.

Once, until a page loads again: moving near the end of the grid asks again, and each failed ask must not raise another toast. Never while the grid is not on screen: that is another screen, and the grid asks again once it is back (shouldLoadMore), where a failure is told.

Parameters:
NameTypeDescription
noticeobject

from newFailureNotice(); modified

isShownboolean

whether the grid is the view on screen

Returns:
Type: 
boolean

(static) trackFocus(tracker, row, nowMs) → {void}

The user is now at row.

A report of the row already tracked is not a move and changes nothing: the grid reports its focused item again when focus comes back to it, and where a scroll comes to rest after its rows were already reported one by one (measured 2026-09-25).

Parameters:
NameTypeDescription
trackerobject

from newScrollTracker(); modified

rowinteger

the row the user is at

nowMsinteger

a millisecond clock, the same one on every call

Returns:
Type: 
void