text_editor: add viewport scrolling

This commit is contained in:
Zack Buhman 2025-03-04 02:14:40 -06:00
parent 487289f3c9
commit cf4b078d0a
16 changed files with 597 additions and 36 deletions

View File

@ -91,6 +91,7 @@ namespace ft6 {
}
namespace ft6 {
namespace scan_code {
constexpr uint32_t first_printable = 0x4;
constexpr uint32_t last_printable = 0x38;
const uint8_t code_point[last_printable + 1][2] = {
@ -154,4 +155,3 @@ namespace ft6 {
};
}
}

View File

@ -127,7 +127,7 @@ void gap_cursor_pos_abs(struct gap_buffer& gb, int32_t pos)
gap_cursor_pos(gb, pos - gb.gap_start);
}
int32_t gap_column_number(struct gap_buffer& gb)
int32_t gap_column_number(const struct gap_buffer& gb)
{
int32_t line_start = 0;
if (gb.line.gap > 0)

View File

@ -32,5 +32,5 @@ void gap_resize(struct gap_buffer& gb);
void gap_append(struct gap_buffer& gb, char_type c);
void gap_pop(struct gap_buffer& gb);
void gap_cursor_pos(struct gap_buffer& gb, int32_t delta);
int32_t gap_column_number(struct gap_buffer& gb);
int32_t gap_column_number(const struct gap_buffer& gb);
void gap_cursor_pos_line(struct gap_buffer& gb, int32_t delta);

View File

@ -119,14 +119,36 @@ void keyboard_debug(ft6::data_transfer::data_format * keyboards, uint32_t frame_
}
}
static inline bool is_shifted(const uint8_t modifier_key)
constexpr inline bool is_modified(uint32_t modifier, uint32_t bit)
{
return
(ft6::data_transfer::modifier_key::right_shift() & modifier_key) ||
(ft6::data_transfer::modifier_key::left_shift() & modifier_key);
uint32_t mask = ~(bit);
return ((modifier & bit) != 0) && ((modifier & mask) == 0);
}
void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb)
constexpr inline bool is_unmodified(uint32_t modifier)
{
return modifier == 0;
}
constexpr inline bool is_shifted(uint32_t modifier)
{
uint32_t bit = ft6::data_transfer::modifier_key::right_shift() | ft6::data_transfer::modifier_key::left_shift();
return is_modified(modifier, bit);
}
constexpr inline bool is_left_alted(uint32_t modifier)
{
uint32_t bit = (ft6::data_transfer::modifier_key::left_alt());
return is_modified(modifier, bit);
}
constexpr inline bool is_ctrled(uint32_t modifier)
{
uint32_t bit = (ft6::data_transfer::modifier_key::left_control() | ft6::data_transfer::modifier_key::right_control());
return is_modified(modifier, bit);
}
void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb, viewport_window& vw)
{
uint32_t this_frame = (frame_ix + 0) & 1;
uint32_t next_frame = (frame_ix + 1) & 1;
@ -144,15 +166,21 @@ void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame
}
if (make) {
// make
int modifier_key = keyboards[this_frame].modifier_key;
uint8_t scan_code = keyboards[this_frame].scan_code_array[i];
if (scan_code <= ft6::scan_code::last_printable) {
bool shifted = is_shifted(keyboards[this_frame].modifier_key);
if (is_shifted(modifier_key) || is_unmodified(modifier_key)) {
if (scan_code >= ft6::scan_code::first_printable && scan_code <= ft6::scan_code::last_printable) {
bool shifted = is_shifted(modifier_key);
char_type code_point = ft6::scan_code::code_point[scan_code][shifted];
if (code_point != 0) {
gap_append(gb, code_point);
continue;
}
}
}
if (is_unmodified(modifier_key)) {
switch (scan_code) {
case ft6::scan_code::_return: gap_append(gb, '\n'); break;
case ft6::scan_code::backspace: gap_pop(gb); break;
@ -162,8 +190,32 @@ void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame
case ft6::scan_code::right_arrow: gap_cursor_pos(gb, 1); break;
case ft6::scan_code::up_arrow: gap_cursor_pos_line(gb, -1); break;
case ft6::scan_code::down_arrow: gap_cursor_pos_line(gb, 1); break;
default:
default: break;
}
}
if (is_left_alted(modifier_key)) {
switch (scan_code) {
case ft6::scan_code::v_V:
vw.first_line -= 1;
if (vw.first_line < 0)
vw.first_line = 0;
break;
default: break;
}
}
if (is_ctrled(modifier_key)) {
switch (scan_code) {
case ft6::scan_code::v_V:
vw.first_line += 1;
if ((vw.first_line - 1) >= gb.line.length) {
vw.first_line = gb.line.length;
}
break;
default: break;
}
}
}
}

View File

@ -4,9 +4,10 @@
#include "maple/maple_bus_ft6.hpp"
#include "gap_buffer.hpp"
#include "viewport_window.hpp"
void keyboard_do_get_condition(ft6::data_transfer::data_format& data);
void keyboard_debug(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix);
void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb);
void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb, viewport_window& vw);

70
text_editor/parse.cpp Normal file
View File

@ -0,0 +1,70 @@
#include <stddef.h>
#include "parse.hpp"
int parse_base10_digit(char c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
default: return -1;
}
}
const char * parse_base10(const char * s, int * n)
{
*n = 0;
int sign = 1;
if (*s == '-') {
sign = -1;
s++;
}
while (true) {
int digit = parse_base10_digit(*s);
if (digit == -1)
break;
*n *= 10;
*n += digit;
s++;
}
*n *= sign;
return s;
}
const char * parse_base10_64(const char * s, int64_t * n)
{
*n = 0;
int sign = 1;
if (*s == '-') {
sign = -1;
s++;
}
while (true) {
int digit = parse_base10_digit(*s);
if (digit == -1)
break;
*n *= 10;
*n += digit;
s++;
}
*n *= sign;
return s;
}

5
text_editor/parse.hpp Normal file
View File

@ -0,0 +1,5 @@
#include <stddef.h>
int parse_base10_digit(char c);
const char * parse_base10(const char * s, int * n);
const char * parse_base10_64(const char * s, int64_t * n);

173
text_editor/printf.cpp Normal file
View File

@ -0,0 +1,173 @@
#include <stdint.h>
#include <stdarg.h>
#include "parse.hpp"
#include "unparse.hpp"
#include "printf.hpp"
enum format_type {
FORMAT_BASE10_UNSIGNED,
FORMAT_BASE10,
FORMAT_BASE10_64,
FORMAT_POINTER,
FORMAT_BASE16,
FORMAT_STRING,
FORMAT_CHAR,
FORMAT_PERCENT,
};
struct format {
enum format_type type;
int pad_length;
char fill_char;
};
static const char * parse_escape(const char * format, struct format * ft);
static const char * parse_fill_pad(const char * format, struct format * ft)
{
if (*format == 0)
return format;
if (*format >= '1' || *format <= '9')
ft->fill_char = ' ';
else
ft->fill_char = *format++;
format = parse_base10(format, &ft->pad_length);
return parse_escape(format, ft);
}
static const char * parse_escape(const char * format, struct format * ft)
{
switch (*format) {
case 0:
return format;
case 'u':
ft->type = FORMAT_BASE10_UNSIGNED;
return format + 1;
case 'd':
ft->type = FORMAT_BASE10;
return format + 1;
case 'l':
ft->type = FORMAT_BASE10_64;
return format + 1;
case 'p':
ft->type = FORMAT_POINTER;
return format + 1;
case 'x':
ft->type = FORMAT_BASE16;
return format + 1;
case 's':
ft->type = FORMAT_STRING;
return format + 1;
case 'c':
ft->type = FORMAT_CHAR;
return format + 1;
case '%':
ft->type = FORMAT_PERCENT;
return format + 1;
default:
return parse_fill_pad(format, ft);
}
}
static inline int copy_string(const char * src, int src_len, char * dst, int dst_len, int dst_ix)
{
int i;
for (i = 0; i < src_len; i++) {
if (dst_ix + i >= dst_len)
break;
dst[dst_ix + i] = src[i];
}
return i;
}
int snprintf(char * buf, int buf_len, const char * format, ...)
{
va_list args;
va_start(args, format);
int buf_ix = 0;
while (buf_ix < buf_len) {
if (*format == 0)
break;
switch (*format) {
case '%':
{
struct format ft;
ft.pad_length = 0;
ft.fill_char = 0;
format = parse_escape(format + 1, &ft);
switch (ft.type) {
case FORMAT_BASE10_UNSIGNED:
{
uint32_t num = va_arg(args, uint32_t);
char s[10];
int offset = unparse_base10_unsigned(s, num, ft.pad_length, ft.fill_char);
buf_ix += copy_string(s, offset, buf, buf_len, buf_ix);
}
break;
case FORMAT_BASE10:
{
int32_t num = va_arg(args, int32_t);
char s[10];
int offset = unparse_base10(s, num, ft.pad_length, ft.fill_char);
buf_ix += copy_string(s, offset, buf, buf_len, buf_ix);
}
break;
case FORMAT_BASE10_64:
{
int64_t num = va_arg(args, int64_t);
char s[20];
int offset = unparse_base10_64(s, num, ft.pad_length, ft.fill_char);
buf_ix += copy_string(s, offset, buf, buf_len, buf_ix);
}
break;
case FORMAT_POINTER:
{
const char s[2] = {'0', 'x'};
buf_ix += copy_string(s, 2, buf, buf_len, buf_ix);
}
/* fall through */;
case FORMAT_BASE16:
{
uint32_t num = va_arg(args, uint32_t);
char s[8];
int offset = unparse_base16(s, num, ft.pad_length, ft.fill_char);
buf_ix += copy_string(s, offset, buf, buf_len, buf_ix);
}
break;
case FORMAT_STRING:
{
const char * s = va_arg(args, const char *);
while (*s != 0 && buf_ix < buf_len) {
buf[buf_ix++] = *s++;
}
}
break;
case FORMAT_CHAR:
{
const int c = va_arg(args, const int);
buf[buf_ix++] = c;
}
break;
case FORMAT_PERCENT:
buf[buf_ix++] = '%';
break;
}
}
break;
default:
{
char c = *format++;
buf[buf_ix++] = c;
}
break;
}
}
va_end(args);
return buf_ix;
}

3
text_editor/printf.hpp Normal file
View File

@ -0,0 +1,3 @@
#pragma once
int snprintf(char * buf, int len, const char * format, ...);

View File

@ -1,6 +1,7 @@
#include "render.hpp"
#include "minmax.hpp"
#include "transform.hpp"
#include "unparse.hpp"
#include "holly/ta_global_parameter.hpp"
#include "holly/ta_fifo_polygon_converter.hpp"
@ -36,9 +37,11 @@ cursor_advance render_primary_buffer(ta_parameter_writer& writer,
const gap_buffer& gb,
const viewport_window& window)
{
int32_t first_line = min(window.first_line, gb.line.length - 1);
int32_t first_line = window.first_line;
cursor_advance cursor = { 0 };
cursor.x = -1;
cursor.y = -1;
int32_t h_advance = 0;
int32_t v_advance = 0;
@ -47,7 +50,16 @@ cursor_advance render_primary_buffer(ta_parameter_writer& writer,
int row = first_line;
int32_t init_i = first_line >= 0 ? gb.line.offsets[first_line] + 1 : 0;
// line 0 is prior to the beginning of gb.line.offsets
// gb.line.offsets is the index of the newline character
int32_t init_i;
if (first_line == 0) {
init_i = 0;
} else if ((first_line - 1) >= 0 && (first_line - 1) < gb.line.length) {
init_i = gb.line.offsets[first_line - 1] + 1;
} else {
init_i = gb.size;
}
for (int32_t i = init_i; i <= gb.size; i++) {
if (i == gb.gap_start) {
uint32_t ix = get_font_ix(font, ' ');
@ -97,7 +109,41 @@ cursor_advance render_primary_buffer(ta_parameter_writer& writer,
return cursor;
}
void render_cursor(ta_parameter_writer& parameter,
void render_text(ta_parameter_writer& writer,
const font * font,
const glyph * glyphs,
const char * buf,
int length,
int32_t x0,
int32_t y0)
{
int32_t h_advance = 0;
int32_t v_advance = 0;
const float r_texture_width = 1.0f / font->texture_width;
const float r_texture_height = 1.0f / font->texture_height;
for (int i = 0; i < length; i++) {
int32_t x = x0 + int_26_6(h_advance);
int32_t y = y0 + int_26_6(v_advance);
char_type c = buf[i];
uint32_t ix = get_font_ix(font, c);
auto& glyph = glyphs[ix];
transform_glyph(writer,
r_texture_width,
r_texture_height,
glyph,
x,
y
);
h_advance += glyph.metrics.horiAdvance;
}
}
void render_cursor(ta_parameter_writer& writer,
const cursor_advance& cursor,
const viewport_window& window)
{
@ -106,7 +152,7 @@ void render_cursor(ta_parameter_writer& parameter,
float width = int_26_6(cursor.width);
float height = int_26_6(cursor.height);
transform_cursor(parameter,
transform_cursor(writer,
x, // x
y, // y
width,
@ -114,6 +160,39 @@ void render_cursor(ta_parameter_writer& parameter,
);
}
static inline int copy_str(const char * s, char * d, int d_ix)
{
while (*s != 0) {
d[d_ix++] = *s++;
}
return d_ix;
}
void render_status(ta_parameter_writer& writer,
const font * font,
const glyph * glyphs,
const gap_buffer& gb,
const viewport_window& window)
{
char buf[256];
int ix = 0;
ix = copy_str("(", buf, ix);
ix += unparse_base10(&buf[ix], gb.line.gap, 0, 0);
ix = copy_str(",", buf, ix);
ix += unparse_base10(&buf[ix], gap_column_number(gb), 0, 0);
ix = copy_str(") ", buf, ix);
ix += unparse_base10(&buf[ix], window.first_line, 0, 0);
render_text(writer,
font,
glyphs,
buf,
ix,
window.box.x0,
480 - int_26_6(font->face_metrics.height) * 2);
}
void render(ta_parameter_writer& writer,
const font * font,
const glyph * glyphs,
@ -124,6 +203,8 @@ void render(ta_parameter_writer& writer,
font->texture_width,
font->texture_height);
render_status(writer, font, glyphs, gb, window);
cursor_advance cursor = render_primary_buffer(writer, font, glyphs, gb, window);
render_cursor(writer, cursor, window);

View File

@ -256,7 +256,7 @@ void main()
struct editor_state state = { 0 };
gap_init_from_buf(state.gb, buf, buf_size, 0);
line_init_from_buf(state.gb, offsets, offsets_size);
viewport_init_fullscreen(state.window);
viewport_init_fullscreen(state.window, font);
ft6::data_transfer::data_format keyboards[2] = { 0 };
@ -269,7 +269,7 @@ void main()
while (true) {
keyboard_do_get_condition(keyboards[frame_ix & 1]);
keyboard_debug(keyboards, frame_ix);
keyboard_update(keyboards, frame_ix, state.gb);
keyboard_update(keyboards, frame_ix, state.gb, state.window);
ta_polygon_converter_init2(texture_memory_alloc.isp_tsp_parameters[0].start,
texture_memory_alloc.isp_tsp_parameters[0].end,

View File

@ -4,6 +4,7 @@ TEXT_EDITOR_OBJ = \
text_editor/render.o \
text_editor/keyboard.o \
text_editor/transform.o \
text_editor/unparse.o \
holly/video_output.o \
holly/core.o \
holly/region_array.o \

View File

@ -204,6 +204,6 @@ void transform_glyph(ta_parameter_writer& writer,
}
const float z = 0.1f;
uint32_t base_color = 0xff0080ff;
uint32_t base_color = 0xffffff;
transfer_quad_type_3(writer, out[0], out[1], out[2], out[3], z, base_color);
}

165
text_editor/unparse.cpp Normal file
View File

@ -0,0 +1,165 @@
#include "minmax.hpp"
#include "unparse.hpp"
int digits_base10(uint32_t n)
{
if (n >= 1000000000ul) return 10;
if (n >= 100000000ul) return 9;
if (n >= 10000000ul) return 8;
if (n >= 1000000ul) return 7;
if (n >= 100000ul) return 6;
if (n >= 10000ul) return 5;
if (n >= 1000ul) return 4;
if (n >= 100ul) return 3;
if (n >= 10ul) return 2;
return 1;
}
int unparse_base10_unsigned(char * s, uint32_t n, int len, char fill)
{
int digits = 0;
digits += digits_base10(n);
len = max(digits, len);
int ret = len;
while (len > digits) {
*s++ = fill;
--len;
}
while (len > 0) {
const uint32_t digit = n % 10;
n = n / 10;
s[--len] = digit + 48;
}
return ret;
}
int unparse_base10(char * s, int32_t n, int len, char fill)
{
bool negative = false;
int digits = 0;
if (n < 0) {
digits += 1;
n = -n;
negative = true;
}
digits += digits_base10(n);
len = max(digits, len);
int ret = len;
while (len > digits) {
*s++ = fill;
--len;
}
if (negative) {
*s++ = '-';
len--;
}
while (len > 0) {
const uint32_t digit = n % 10;
n = n / 10;
s[--len] = digit + 48;
}
return ret;
}
int digits_base10_64(uint64_t n)
{
if (n >= 10000000000000000000ull) return 20;
if (n >= 1000000000000000000ull) return 19;
if (n >= 100000000000000000ull) return 18;
if (n >= 10000000000000000ull) return 17;
if (n >= 1000000000000000ull) return 16;
if (n >= 100000000000000ull) return 15;
if (n >= 10000000000000ull) return 14;
if (n >= 1000000000000ull) return 13;
if (n >= 100000000000ull) return 12;
if (n >= 10000000000ull) return 11;
if (n >= 1000000000ull) return 10;
if (n >= 100000000ull) return 9;
if (n >= 10000000ull) return 8;
if (n >= 1000000ull) return 7;
if (n >= 100000ull) return 6;
if (n >= 10000ull) return 5;
if (n >= 1000ull) return 4;
if (n >= 100ull) return 3;
if (n >= 10ull) return 2;
return 1;
}
int unparse_base10_64(char * s, int64_t n, int len, char fill)
{
bool negative = false;
int digits = 0;
if (n < 0) {
digits += 1;
n = -n;
negative = true;
}
digits += digits_base10_64(n);
len = max(digits, len);
int ret = len;
while (len > digits) {
*s++ = fill;
--len;
}
if (negative) {
*s++ = '-';
len--;
}
while (len > 0) {
const uint32_t digit = n % 10;
n = n / 10;
s[--len] = digit + 48;
}
return ret;
}
static int digits_base16(uint32_t n)
{
if (n <= 0xf) return 1;
if (n <= 0xff) return 2;
if (n <= 0xfff) return 3;
if (n <= 0xffff) return 4;
if (n <= 0xfffff) return 5;
if (n <= 0xffffff) return 6;
if (n <= 0xfffffff) return 7;
return 8;
}
int unparse_base16(char * s, uint32_t n, int len, char fill)
{
int digits = digits_base16(n);
len = max(digits, len);
int ret = len;
while (len > digits) {
*s++ = fill;
--len;
}
while (len > 0) {
uint32_t nib = n & 0xf;
n = n >> 4;
if (nib > 9) {
nib += (97 - 10);
} else {
nib += (48 - 0);
}
s[--len] = nib;
}
return ret;
}

8
text_editor/unparse.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
int unparse_base10_unsigned(char * s, uint32_t n, int len, char fill);
int unparse_base10(char * s, int32_t n, int len, char fill);
int unparse_base10_64(char * s, int64_t n, int len, char fill);
int unparse_base16(char * s, uint32_t n, int len, char fill);

View File

@ -2,6 +2,8 @@
#include <cstdint>
#include "font/font.h"
struct viewport_window {
int32_t first_line;
struct {
@ -12,11 +14,11 @@ struct viewport_window {
} box;
};
inline void viewport_init_fullscreen(viewport_window& window)
static inline void viewport_init_fullscreen(viewport_window& window, const font * font)
{
window.first_line = 0;
window.box.x0 = 10;
window.box.y0 = 20;
window.box.x1 = 640 - 10;
window.box.y1 = 480 - 20;
window.box.y1 = 480 - (font->face_metrics.height >> 6) * 2;
}