mdview2: статус-бар в отдельный модуль, упростить alloc_set_storage

- mdview2_status.c: вынести статус-бар/меню/спиннер из ядра
- mdview2.c: убрать retry-цикл в alloc_set_storage (fail-fast вместо
  ложной устойчивости — при нехватке EMM под индекс контент тоже не влезет)
- mdview2.h: дополнить экспортами статус-модуля
- mdview2_md.c / mdview2_raw.c: зачистка после расщепления
- mdview/mdview.c: переименовать scroll_* → md_scroll_* (симметрия)
- docs/fast_ram.md, docs/turboc.txt: добавить справочные доки
- examples/mdview2/README.MD, READMEBG.MD: обновить описание

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 23:19:00 +03:00
parent 05916a3cc6
commit 1b78dda125
12 changed files with 1802 additions and 443 deletions
+48 -114
View File
@@ -1,6 +1,7 @@
/*
* mdview2_md.c — представление MD-документа (peer к mdview2_raw.c): отрисовка
* области из рендер-кэша, статус-бар, нижнее меню и прокрутка (верт./гориз.).
* области из рендер-кэша + прокрутка (верт./гориз.) + навигация md_key().
* Статус-бар, меню и спиннер — в mdview2_status.c (общие для MD/RAW).
*
* Работает поверх готового рендер-кэша (cache_dir_get → cache_rec_t) и
* состояния навигации (top_line/viewport_x/n_lines, см. mdview2.h). Никакого
@@ -11,8 +12,7 @@
*/
#include <stdint.h>
#include <stdio.h> /* dec8/dec16 */
#include <conio.h> /* wrchar/gotoxy/cputs/textattr/scroll/COLOR */
#include <conio.h> /* wrchar/scroll/COLOR */
#include <bios/text.h> /* bios_set_place/bios_fillcharattr */
#include "mdview2.h"
@@ -61,101 +61,6 @@ void draw_viewport_from_cache(void)
}
}
/* Число строк, готовых к показу. Во время загрузки последняя эмитированная
* строка ещё в g_cells (не зафлашена), поэтому доступны [0..n_lines-2]. */
uint16_t drawable_lines(void)
{
if (g_loading) return (n_lines >= 1) ? (uint16_t)(n_lines - 1) : 0;
return n_lines;
}
/* Возвращает процент прокрутки (0..100) относительно доступного диапазона. */
static uint8_t calc_pct(void)
{
uint16_t total = drawable_lines();
uint8_t pct;
if (total <= VIEW_H)
pct = 100;
else
pct = (uint8_t)((uint32_t)top_line * 100UL / (uint32_t)(total - VIEW_H));
return pct;
}
/* Рисует динамическую часть статус-бара без printf.
* Полностью перезаписывает колонки 45..79, чтобы не оставлять «хвосты». */
void render_status_numbers(void)
{
uint16_t total = drawable_lines();
uint16_t last = top_line + VIEW_H;
if (last > total) last = total;
uint8_t pct = calc_pct();
textattr(ATTR_BAR);
bios_set_place(0, 45);
bios_fillcharattr(' ', ATTR_BAR, (uint8_t)(SCREEN_W - 45));
wrchar(45, 0, 0xB3, ATTR_BAR); /* вертикальный разделитель */
gotoxy(47, 0);
cputs("L ");
dec16(top_line + 1);
cputs("-");
dec16(last);
cputs(" / ");
dec16(total);
if (g_loading) cputs("..."); /* ещё грузится */
wrchar(70, 0, 0xB3, ATTR_BAR); /* вертикальный разделитель */
gotoxy( 73, 0);
dec8(pct);
cputs("%");
}
/* Полностью перерисовывает статус-бар (заголовок + числа). */
void render_full_status(void)
{
fill_row(0, ATTR_BAR);
/* Размещение в шапке: "MDVIEW", затем слот спиннера и имя файла. */
put_str_attr(1, 0, "MDVIEW", ATTR_BAR);
put_str_attr(10, 0, filename, ATTR_BAR);
put_str_attr(37, 0, enc_name(g_encoding), ATTR_BAR); /* текущая кодировка */
render_status_numbers();
}
/* Обновляет только изменяемую числовую часть статус-бара. */
void render_updated_status(void)
{
render_status_numbers();
}
/* Отрисовывает нижнюю строку меню. */
void render_menu(void)
{
/* Блок i = колонка i*8 (8 шириной): 2 поз. НОМЕР клавиши без 'F' (стиль
* ATTR_MENU_K, ведущий «чёрный» пробел кроме F10) + 6 поз. ТЕКСТ-функция
* (ATTR_MENU_T) сразу за номером. Номера рисуем для ВСЕХ 10 клавиш; текст —
* только у задействованных и доступных сейчас. */
fill_row(MENU_ROW, ATTR_MENU_T);
char num[3]; num[2] = 0;
for (uint8_t i = 0; i < 9; i++) { /* F1..F9: ' 1'..' 9' */
num[0] = ' '; num[1] = (char)('1' + i);
put_str_attr((uint8_t)(i * 8), MENU_ROW, num, ATTR_MENU_K);
}
put_str_attr(9 * 8, MENU_ROW, "10", ATTR_MENU_K); /* F10 */
put_str_attr(0 * 8 + 2, MENU_ROW, "Help", ATTR_MENU_T);
#if WITH_RAW
if (g_ready) /* RAW доступен после постройки документа */
put_str_attr(1 * 8 + 2, MENU_ROW, (g_view == VIEW_MD) ? "RAW" : "MD", ATTR_MENU_T);
if (g_view != VIEW_MD) /* Wrap/Unwrap — только в RAW */
put_str_attr(2 * 8 + 2, MENU_ROW,
(g_view == VIEW_RAW_WRAP) ? "UnWrap" : "Wrap", ATTR_MENU_T);
#endif
if (g_f8_enabled) /* смена кодировки сейчас возможна */
put_str_attr(7 * 8 + 2, MENU_ROW, "CodePg", ATTR_MENU_T);
put_str_attr(9 * 8 + 2, MENU_ROW, "Exit", ATTR_MENU_T);
}
/* ==================================================================
* Прокрутка
@@ -172,7 +77,7 @@ void clamp_top(void)
}
/* Прокрутка вверх на n строк с частичной перерисовкой при n == 1. */
void scroll_up(uint16_t n)
void md_scroll_up(uint16_t n)
{
uint16_t new_top_line = (top_line >= n) ? (uint16_t)(top_line - n) : 0;
if( new_top_line != top_line) {
@@ -187,7 +92,7 @@ void scroll_up(uint16_t n)
}
/* Прокрутка вниз на n строк с частичной перерисовкой при n == 1. */
void scroll_down(uint16_t n)
void md_scroll_down(uint16_t n)
{
uint16_t new_top_line = (top_line + n < n_lines - VIEW_H) ?
(uint16_t)(top_line + n) : n_lines - VIEW_H;
@@ -208,7 +113,7 @@ void scroll_down(uint16_t n)
* Фаза 5: ширина берётся прямо из директории кэша (rec.len — уже
* посчитанная реальная длина в ячейках после рендера), без единого
* обращения к fb()/seg_off()/исходному файлу. */
void scroll_h(int8_t delta)
void md_scroll_horizon(int8_t delta)
{
uint16_t maxw = 0;
for (uint8_t i = 0; i < VIEW_H; i++) {
@@ -247,6 +152,14 @@ void scroll_h(int8_t delta)
draw_viewport_from_cache();
}
/* Процент прокрутки (0..100) относительно доступного диапазона. */
static uint8_t calc_md_pct(void)
{
uint16_t total = drawable_lines();
if (total <= VIEW_H) return 100;
return (uint8_t)((uint32_t)top_line * 100UL / (uint32_t)(total - VIEW_H));
}
/* ---- навигация MD после загрузки (peer к raw_key) ---------------- */
/* Обрабатывает навигационную клавишу в MD-режиме (вызывается из главного
@@ -255,19 +168,40 @@ void scroll_h(int8_t delta)
uint8_t md_key(uint8_t scan)
{
switch (scan) {
case KEY_UP: scroll_up(1); break;
case KEY_DOWN: scroll_down(1); break;
case KEY_LEFT: scroll_h(-(int8_t)HPAN_STEP); break;
case KEY_RIGHT: scroll_h(+(int8_t)HPAN_STEP); break;
case KEY_PGUP: scroll_up(VIEW_H); break;
case KEY_PGDN: scroll_down(VIEW_H); break;
case KEY_HOME: top_line = 0; viewport_x = 0;
draw_viewport_from_cache(); break;
case KEY_END: top_line = (n_lines > VIEW_H) ? (uint16_t)(n_lines - VIEW_H) : 0;
viewport_x = 0;
draw_viewport_from_cache(); break;
default: return 0; /* не наша клавиша */
case KEY_UP:
md_scroll_up(1);
break;
case KEY_DOWN:
md_scroll_down(1);
break;
case KEY_LEFT:
md_scroll_horizon(-(int8_t)HPAN_STEP);
break;
case KEY_RIGHT:
md_scroll_horizon(+(int8_t)HPAN_STEP);
break;
case KEY_PGUP:
md_scroll_up(VIEW_H);
break;
case KEY_PGDN:
md_scroll_down(VIEW_H);
break;
case KEY_HOME:
top_line = 0;
viewport_x = 0;
draw_viewport_from_cache();
break;
case KEY_END:
top_line = (n_lines > VIEW_H) ? (uint16_t)(n_lines - VIEW_H) : 0;
viewport_x = 0;
draw_viewport_from_cache();
break;
default:
return 0; /* не наша клавиша */
}
render_updated_status();
render_percent_progress(calc_md_pct());
render_md_status_numbers();
return 1;
}