mdview2 RAW: чинить дубликат строки при скролле у конца файла

В RAW-режиме offset ряда под экраном (raw_bot) велся инкрементально в
предположении, что экран всегда полон контентом. После Wrap → конец файла
→ Unwrap контент кончается в середине экрана, raw_bot рассинхронизировался
(raw_scroll_up1 делал raw_bot = raw_prev(raw_bot)), и guard raw_bot >=
file_size в scroll-down ложно проходил → дубликат последней строки внизу.

Фикс: убран хрупкий raw_bot. raw_scroll_down1 проходит VIEW_H рядов от
raw_top и скроллит вниз только если контент реально уходит за нижний край
(иначе внизу была бы пустая строка). Кнопка «вниз» теперь работает лишь
когда под экраном есть контент; иначе доступна только «вверх».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 11:07:07 +03:00
parent c0dd1621e6
commit 858f748e7a
+16 -7
View File
@@ -20,7 +20,6 @@
#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */ #if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */
static uint32_t raw_top = 0; /* байт-offset верхней строки экрана */ static uint32_t raw_top = 0; /* байт-offset верхней строки экрана */
static uint32_t raw_bot = 0; /* offset ряда сразу ПОД экраном (для прокрутки) */
static uint16_t raw_hpan = 0; /* гориз. сдвиг (только HSCROLL) */ static uint16_t raw_hpan = 0; /* гориз. сдвиг (только HSCROLL) */
#define HPAN_STEP 8u #define HPAN_STEP 8u
@@ -190,12 +189,13 @@ void raw_draw(void)
uint32_t off = raw_top; uint32_t off = raw_top;
for (uint8_t r = 0; r < VIEW_H; r++) for (uint8_t r = 0; r < VIEW_H; r++)
off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + r)); off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + r));
raw_bot = off; /* offset первого ряда НИЖЕ экрана */
} }
/* Прокрутка на одну строку: аппаратный scroll окна документа + отрисовка /* Прокрутка на одну строку: аппаратный scroll окна документа + отрисовка
* ОДНОЙ новой строки (как в MD-режиме). raw_bot — offset ряда сразу под * ОДНОЙ новой строки (как в MD-режиме). Смещение нижнего ряда вычисляется
* экраном, ведётся инкрементально. */ * проходом VIEW_H рядов от raw_top — без отдельного инкрементального счётчика,
* который рассинхронизировался, когда контент кончается в середине экрана
* (короче окна после wrap→unwrap у конца файла). */
static uint32_t raw_next(uint32_t off) static uint32_t raw_next(uint32_t off)
{ {
return (g_view == VIEW_RAW_WRAP) ? raw_next_wrap(off) : raw_next_line(off); return (g_view == VIEW_RAW_WRAP) ? raw_next_wrap(off) : raw_next_line(off);
@@ -207,9 +207,19 @@ static uint32_t raw_prev(uint32_t off)
static void raw_scroll_down1(void) static void raw_scroll_down1(void)
{ {
if (raw_bot >= file_size) return; /* уже внизу */ /* Скроллим вниз ТОЛЬКО если контент уходит за нижний край. Проходим VIEW_H
* рядов от raw_top: если контент кончился раньше (экран не полон) — внизу
* была бы пустая строка, не скроллим; если ровно по нижний ряд (off==EOF) —
* ниже пусто, тоже не скроллим. Иначе off = ряд сразу под экраном. */
uint32_t off = raw_top;
for (uint8_t r = 0; r < VIEW_H; r++) {
if (off >= file_size) return; /* экран не заполнен контентом */
off = raw_next(off);
}
if (off >= file_size) return; /* контент ровно по экран, ниже пусто */
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */ scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */
raw_bot = raw_draw_one(raw_bot, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); /* новый нижний ряд */
raw_top = raw_next(raw_top); raw_top = raw_next(raw_top);
} }
static void raw_scroll_up1(void) static void raw_scroll_up1(void)
@@ -218,7 +228,6 @@ static void raw_scroll_up1(void)
uint32_t nt = raw_prev(raw_top); uint32_t nt = raw_prev(raw_top);
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */ scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */
(void)raw_draw_one(nt, VIEW_TOP_ROW); (void)raw_draw_one(nt, VIEW_TOP_ROW);
raw_bot = raw_prev(raw_bot);
raw_top = nt; raw_top = nt;
} }