pe: add scrolling in shell

This commit is contained in:
Lephenixnoir 2022-12-11 17:23:33 +01:00
parent 5051323860
commit e40f8cba15
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
4 changed files with 178 additions and 52 deletions

View File

@ -103,7 +103,7 @@ void console_line_update_render_lines(console_line_t *line, int width)
}
int console_line_render(int x, int y, console_line_t *line, int w, int dy,
int show_from, int cursor)
int show_from, int show_until, int cursor)
{
char const *p = line->data;
char const *endline = p + strlen(p);
@ -114,7 +114,7 @@ int console_line_render(int x, int y, console_line_t *line, int w, int dy,
char const *endscreen = drsize(p, NULL, w, NULL);
int len = endscreen - p;
if(line_number >= show_from) {
if(line_number >= show_from && line_number < show_until) {
dtext_opt(x, y, C_BLACK, C_NONE, DTEXT_LEFT, DTEXT_TOP, p, len);
if(cursor >= line_offset && cursor <= line_offset + len) {
int w, h;
@ -139,8 +139,16 @@ console_t *console_create(int backlog_size)
console_t *cons = malloc(sizeof *cons);
cons->lines = NULL;
cons->line_count = 0;
cons->absolute_lineno = 1;
cons->backlog_size = max(backlog_size, PE_CONSOLE_LINE_MAX_LENGTH);
cons->render_needed = true;
cons->render_font = NULL;
cons->render_width = 0;
cons->render_lines = 0;
cons->render_total_lines = 0;
if(!console_new_line(cons)) {
free(cons);
return NULL;
@ -169,6 +177,13 @@ bool console_new_line(console_t *cons)
return true;
}
/* static console_line_t *console_get_absolute_line(console_t const *cons,
int lineno)
{
int i = lineno - cons->absolute_lineno;
return (i >= 0 && i < cons->line_count) ? &cons->lines[i] : NULL;
} */
void console_clean_backlog(console_t *cons)
{
/* Find how many lines we can keep while using at most cons->backlog_size
@ -192,49 +207,9 @@ void console_clean_backlog(console_t *cons)
memmove(cons->lines, cons->lines + first_kept,
(cons->line_count - first_kept) * sizeof cons->lines[0]);
cons->line_count -= first_kept;
}
void console_render(int x, int y0, console_t const *cons, int w, int dy,
int visible_lines)
{
int total_lines = 0;
int watermark = visible_lines;
int y = y0;
#ifdef FX9860G
int text_w = w - 2, scroll_spacing = 1, scroll_w = 1;
#else
int text_w = w - 4, scroll_spacing = 2, scroll_w = 2;
#endif
for(int i = 0; i < cons->line_count; i++) {
console_line_update_render_lines(&cons->lines[i], w);
total_lines += cons->lines[i].render_lines;
watermark -= cons->lines[i].render_lines;
}
/* Show only visible lines */
for(int i = 0; i < cons->line_count; i++) {
console_line_t *line = &cons->lines[i];
bool show_cursor = (i == cons->line_count - 1);
if(watermark + line->render_lines > 0)
y = console_line_render(x, y, line, text_w, dy, -watermark,
show_cursor ? cons->cursor : -1);
watermark += line->render_lines;
}
/* Scrollbar */
if(total_lines > visible_lines) {
int first_shown = total_lines - visible_lines;
int h = dy * visible_lines;
int y1 = y0 + h * first_shown / total_lines;
int y2 = y0 + h * (first_shown + visible_lines) / total_lines;
drect(x + text_w + scroll_spacing, y1,
x + text_w + scroll_spacing + scroll_w - 1, y2,
C_BLACK);
}
/* Update the absolute number of the first line */
cons->absolute_lineno += first_kept;
}
void console_clear_render_flag(console_t *cons)
@ -250,6 +225,104 @@ void console_destroy(console_t *cons)
free(cons);
}
//=== Rendering functions ===//
/* Compute the shell's horizontal layout as:
1. Text width (region where the text is renderer)
2. Spacing left of the scrollbar
3. Width of the scrollbar */
static void view_params(int w,
int *text_w, int *scroll_spacing, int *scroll_width)
{
#ifdef FX9860G
if(text_w)
*text_w = w - 2;
if(scroll_spacing)
*scroll_spacing = 1;
if(scroll_width)
*scroll_width = 1;
#else
if(text_w)
*text_w = w - 4;
if(scroll_spacing)
*scroll_spacing = 2;
if(scroll_width)
*scroll_width = 2;
#endif
}
void console_compute_view(console_t *cons, font_t const *font,
int width, int lines)
{
cons->render_font = font;
cons->render_width = width;
cons->render_lines = lines;
cons->render_total_lines = 0;
int text_w;
view_params(width, &text_w, NULL, NULL);
for(int i = 0; i < cons->line_count; i++) {
console_line_update_render_lines(&cons->lines[i], text_w);
cons->render_total_lines += cons->lines[i].render_lines;
}
}
console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
console_scrollpos_t pos)
{
/* No scrolling case */
if(cons->render_total_lines < cons->render_lines)
return 0;
/* Normal clamp */
return max(0, min(pos, cons->render_total_lines - cons->render_lines));
}
void console_render(int x, int y0, console_t const *cons, int dy,
console_scrollpos_t pos)
{
int total_lines = cons->render_total_lines;
int visible_lines = cons->render_lines;
int w = cons->render_width;
int watermark = visible_lines - total_lines + pos;
int y = y0;
int text_w, scroll_spacing, scroll_w;
view_params(w, &text_w, &scroll_spacing, &scroll_w);
font_t const *old_font = dfont(cons->render_font);
/* Show only visible lines */
for(int i = 0; i < cons->line_count; i++) {
console_line_t *line = &cons->lines[i];
bool show_cursor = (i == cons->line_count - 1);
if(watermark + line->render_lines > 0)
y = console_line_render(x, y, line, text_w, dy, -watermark,
visible_lines - watermark, show_cursor ? cons->cursor : -1);
watermark += line->render_lines;
}
dfont(old_font);
/* Scrollbar */
if(total_lines > visible_lines) {
int first_shown = total_lines - visible_lines - pos;
int h = dy * visible_lines;
int y1 = y0 + h * first_shown / total_lines;
int y2 = y0 + h * (first_shown + visible_lines) / total_lines;
int color = C_BLACK;
#ifdef FXCG50
if(pos == 0) color = C_RGB(24, 24, 24);
#endif
drect(x + text_w + scroll_spacing, y1,
x + text_w + scroll_spacing + scroll_w - 1, y2,
color);
}
}
//=== Edition functions ===//
bool console_write_block_at_cursor(console_t *cons, char const *str, int n)

View File

@ -12,12 +12,18 @@
// * Dynamically-sized lines with reflow
// * Cap memory usage based on the total amount of text, not just line count
// * Basic ANSI-escape-based edition features (but only on the last line)
//
// The console tries fairly hard to focus on text manipulation and separate
// rendering. To render, one must first compute a "view" of the terminal, which
// essentially determines line wrapping and scrolling bounds, and then use that
// view and a valid scroll position within it to render.
//---
#ifndef __PYTHONEXTRA_CONSOLE_H
#define __PYTHONEXTRA_CONSOLE_H
#include <gint/keyboard.h>
#include <gint/display.h>
#include <stdbool.h>
/* Maximum line length, to ensure the console can threshold its memory usage
@ -62,6 +68,10 @@ void console_line_delete(console_line_t *line, int p, int n);
/* Update the number of render lines for the chosen width. */
void console_line_update_render_lines(console_line_t *line, int width);
/* Render a vertical slice of the wrapped line. */
int console_line_render(int x, int y, console_line_t *line, int w, int dy,
int show_from, int show_until, int cursor);
//=== Terminal emulator ===//
typedef struct
@ -75,14 +85,26 @@ typedef struct
than PE_CONSOLE_LINE_MAX_LENGTH. */
int backlog_size;
/* Absolute number of the first line in the dynamic line array. This is
the line number in the user-facing sense (it includes lines that were
cleaned up to free memory). */
int absolute_lineno;
/* Cursor position within the last line. */
int cursor;
/* Whether new data has been added and a frame should be rendered. */
bool render_needed;
/* View parameters from last console_compute_view() */
font_t const *render_font;
int render_width;
int render_lines;
int render_total_lines;
} console_t;
/* Scroll position measured as a number of lines up from the bottom. */
typedef int console_scrollpos_t;
/* Create a new console with the specified backlog size. */
console_t *console_create(int backlog_size);
@ -93,15 +115,33 @@ bool console_new_line(console_t *cons);
/* Clean up backlog if the total memory usage is exceeded. */
void console_clean_backlog(console_t *cons);
/* Render the console with the current font, at (x,y) in a rectangle of width
`w` and `lines` total lines separated by `dy` pixels. */
void console_render(int x, int y, console_t const *cons, int w, int dy,
int lines);
/* Clear the console's render flag, which is used to notify of changes. This
function is used when handing control of the display from the console to a
Python program so the console doesn't override the program's output. */
void console_clear_render_flag(console_t *cons);
/* Destroy the console and free all associated memory. */
void console_destroy(console_t *cons);
//=== Rendering interface ===//
/* Compute a view of the console for rendering and scrolling.
@font Font to render text with (use to compute line wrapping)
@width View width in pixels
@lines Number of text lines (spacing can be changed later) */
void console_compute_view(console_t *cons, font_t const *font,
int width, int lines);
/* Clamp a scrolling position to the range valid of the last computed view. */
console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
console_scrollpos_t pos);
/* Render the console at (x,y). The render `width`, the number of `lines` and
the text `font` are all as specified by the latest console_compute_view().
`dy` indicates line height. */
void console_render(int x, int y, console_t const *cons, int dy,
console_scrollpos_t pos);
//=== Edition functions ===//
/* Write string at the cursor's position within the last line. This writes a

View File

@ -94,6 +94,7 @@ widget_shell *widget_shell_create(console_t *console, void *parent)
s->font = dfont_default();
s->color = C_BLACK;
s->line_spacing = 0;
s->scroll = 0;
s->lines = 0;
s->shift = MOD_IDLE;
@ -167,11 +168,11 @@ static void widget_shell_poly_render(void *s0, int x, int y)
widget_shell *s = s0;
int line_height = s->font->line_height + s->line_spacing;
font_t const *old_font = dfont(s->font);
console_render(x, y, s->console, jwidget_content_width(s), line_height,
console_compute_view(s->console, s->font, jwidget_content_width(s),
s->lines);
s->scroll = console_clamp_scrollpos(s->console, s->scroll);
console_render(x, y, s->console, line_height, s->scroll);
console_clear_render_flag(s->console);
dfont(old_font);
}
static void widget_shell_update_mod(widget_shell *s, key_event_t ev)
@ -206,6 +207,16 @@ static bool widget_shell_poly_event(void *s0, jevent e)
widget_shell_update_mod(s, ev);
return true;
}
if(ev.type != KEYEV_UP && ev.key == KEY_UP) {
s->scroll++;
s->widget.update = true;
return true;
}
if(ev.type != KEYEV_UP && ev.key == KEY_DOWN) {
s->scroll--;
s->widget.update = true;
return true;
}
if(ev.type == KEYEV_UP)
return false;

View File

@ -56,6 +56,8 @@ typedef struct {
uint16_t color;
/* Extra line spacing */
int8_t line_spacing;
/* Current scroll position */
console_scrollpos_t scroll;
/* Internal information */
int timer_id;