Start work on horizontal align for multicells

This commit is contained in:
Kovid Goyal 2025-02-19 07:50:40 +05:30
parent 714910382b
commit 15f711d6b5
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
9 changed files with 47 additions and 24 deletions

View file

@ -79,6 +79,8 @@ There are only a handful of metadata keys, defined in the table below:
"v", "Integer from 0 to 2", "0", "The vertical alignment to use for fractionally scaled text. ``0`` - top, ``1`` - bottom, ``2`` - centered"
"h", "Integer from 0 to 2", "0", "The horizontal alignment to use for fractionally scaled text. ``0`` - left, ``1`` - right, ``2`` - centered"
How it works
------------------

View file

@ -322,6 +322,7 @@ def parsers() -> None:
'n': ('subscale_n', 'uint'),
'd': ('subscale_d', 'uint'),
'v': ('vertical_align', 'uint'),
'h': ('horizontal_align', 'uint'),
}
text = generate(
'parse_multicell_code', 'screen_handle_multicell_command', 'multicell_command', keymap, 'MultiCellCommand',

View file

@ -34,7 +34,11 @@ typedef struct {
} GPUSpriteTracker;
typedef struct RunFont {
unsigned scale, subscale_n, subscale_d, vertical_align, multicell_y;
unsigned scale, subscale_n, subscale_d, multicell_y;
union {
struct { uint8_t vertical: 4; uint8_t horizontal: 4; };
uint8_t val;
} align;
ssize_t font_idx;
} RunFont;
@ -92,7 +96,7 @@ typedef struct ScaledFontData {
#include "kitty-verstable.h"
typedef union DecorationsKey {
struct { uint8_t scale : 8, subscale_n : 8, subscale_d : 8, vertical_align : 8, multicell_y : 8, u1 : 8, u2 : 8, u3 : 8; };
struct { uint8_t scale : 8, subscale_n : 8, subscale_d : 8, align : 8, multicell_y : 8, u1 : 8, u2 : 8, u3 : 8; };
uint64_t val;
} DecorationsKey;
static_assert(sizeof(DecorationsKey) == sizeof(uint64_t), "Fix the ordering of DecorationsKey");
@ -322,7 +326,7 @@ sprite_position_for(FontGroup *fg, RunFont rf, glyph_index *glyphs, unsigned gly
uint8_t subscale = ((rf.subscale_n & 0xf) << 4) | (rf.subscale_d & 0xf);
SpritePosition *s = find_or_create_sprite_position(
font->sprite_position_hash_table, glyphs, glyph_count, ligature_index, cell_count,
rf.scale, subscale, rf.multicell_y, rf.vertical_align, &created);
rf.scale, subscale, rf.multicell_y, rf.align.val, &created);
if (!s) { PyErr_NoMemory(); return NULL; }
return s;
}
@ -905,7 +909,7 @@ calculate_regions_for_line(RunFont rf, unsigned cell_height, Region *src, Region
unsigned src_height = src->bottom;
Region src_in_full_coords = *src; unsigned full_dest_height = cell_height * rf.scale;
if (rf.subscale_n && rf.subscale_d) {
switch(rf.vertical_align) {
switch(rf.align.vertical) {
case 0: break; // top aligned no change
case 1: // bottom aligned
src_in_full_coords.top = full_dest_height - src_height;
@ -1012,7 +1016,7 @@ render_decorations(FontGroup *fg, Region src, Region dest, FontCellMetrics scale
static DecorationMetadata
index_for_decorations(FontGroup *fg, RunFont rf, Region src, Region dest, FontCellMetrics scaled_metrics) {
const DecorationsKey key = {.scale=rf.scale, .subscale_n = rf.subscale_n, .subscale_d = rf.subscale_d, .vertical_align = rf.vertical_align, .multicell_y = rf.multicell_y, .u1 = 0, .u2 = 0, .u3 = 0 };
const DecorationsKey key = {.scale=rf.scale, .subscale_n = rf.subscale_n, .subscale_d = rf.subscale_d, .align = rf.align.val, .multicell_y = rf.multicell_y, .u1 = 0, .u2 = 0, .u3 = 0 };
decorations_index_map_t_itr i = vt_get(&fg->decorations_index_map, key);
if (!vt_is_end(i)) return i.data->val;
DecorationMetadata val;
@ -1799,7 +1803,7 @@ cell_cap_for_codepoint(const char_type cp) {
static bool
run_fonts_are_equal(const RunFont *a, const RunFont *b) {
return a->font_idx == b->font_idx && a->scale == b->scale && a->subscale_n == b->subscale_n && a->subscale_d == b->subscale_d && a->vertical_align == b->vertical_align && a->multicell_y == b->multicell_y;
return a->font_idx == b->font_idx && a->scale == b->scale && a->subscale_n == b->subscale_n && a->subscale_d == b->subscale_d && a->align.val == b->align.val && a->multicell_y == b->multicell_y;
}
static bool
@ -1833,7 +1837,8 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line, index_type lnum, Cursor *cursor,
i += mcd_x_limit(cpu_cell) - cpu_cell->x - 1;
continue;
}
cell_font.scale = cpu_cell->scale; cell_font.subscale_n = cpu_cell->subscale_n; cell_font.subscale_d = cpu_cell->subscale_d; cell_font.vertical_align = cpu_cell->vertical_align;
cell_font.scale = cpu_cell->scale; cell_font.subscale_n = cpu_cell->subscale_n; cell_font.subscale_d = cpu_cell->subscale_d;
cell_font.align.vertical = cpu_cell->valign; cell_font.align.horizontal = cpu_cell->halign;
cell_font.multicell_y = cpu_cell->y;
}
text_in_cell(cpu_cell, line->text_cache, lc);

View file

@ -78,8 +78,11 @@ write_multicell_ansi_prefix(ANSILineState *s, const CPUCell *mcd) {
if (mcd->subscale_d) {
w('d'); w('='); nonnegative_integer_as_utf32(mcd->subscale_d, s->output_buf); w(':');
}
if (mcd->vertical_align) {
w('v'); w('='); nonnegative_integer_as_utf32(mcd->vertical_align, s->output_buf); w(':');
if (mcd->valign) {
w('v'); w('='); nonnegative_integer_as_utf32(mcd->valign, s->output_buf); w(':');
}
if (mcd->halign) {
w('h'); w('='); nonnegative_integer_as_utf32(mcd->halign, s->output_buf); w(':');
}
if (s->output_buf->buf[s->output_buf->len - 1] == ':') s->output_buf->len--;
w(';');
@ -98,12 +101,12 @@ close_multicell(ANSILineState *s) {
static void
start_multicell_if_needed(ANSILineState *s, const CPUCell *c) {
if (!c->natural_width || c->scale > 1 || c->subscale_n || c->subscale_d || c->vertical_align) write_multicell_ansi_prefix(s, c);
if (!c->natural_width || c->scale > 1 || c->subscale_n || c->subscale_d || c->valign || c->halign) write_multicell_ansi_prefix(s, c);
}
static bool
multicell_is_continuation_of_previous(const CPUCell *prev, const CPUCell *curr) {
if (prev->scale != curr->scale || prev->subscale_n != curr->subscale_n || prev->subscale_d != curr->subscale_d || prev->vertical_align != curr->vertical_align) return false;
if (prev->scale != curr->scale || prev->subscale_n != curr->subscale_n || prev->subscale_d != curr->subscale_d || prev->valign != curr->valign || prev->halign != curr->halign) return false;
if (prev->natural_width) return curr->natural_width;
return prev->width == curr->width && !curr->natural_width;
}

View file

@ -44,6 +44,7 @@ static_assert(sizeof(GPUCell) == 20, "Fix the ordering of GPUCell");
#define WIDTH_BITS 3
#define SUBSCALE_BITS 4
#define VALIGN_BITS 2
#define HALIGN_BITS 2
typedef union CPUCell {
struct {
@ -59,9 +60,10 @@ typedef union CPUCell {
char_type x : WIDTH_BITS + SCALE_BITS;
char_type y : SCALE_BITS;
char_type width: WIDTH_BITS;
char_type vertical_align: VALIGN_BITS;
char_type valign: VALIGN_BITS;
char_type halign: HALIGN_BITS;
char_type temp_flag: 1;
char_type : 17;
char_type : 15;
};
struct {
char_type ch_and_idx: sizeof(char_type) * 8;
@ -95,7 +97,7 @@ typedef struct {
} Line;
typedef struct MultiCellCommand {
unsigned int width, scale, subscale_n, subscale_d, vertical_align;
unsigned int width, scale, subscale_n, subscale_d, vertical_align, horizontal_align;
size_t payload_sz;
} MultiCellCommand;

View file

@ -23,7 +23,8 @@ static inline void parse_multicell_code(PS *self, uint8_t *parser_buf,
scale = 's',
subscale_n = 'n',
subscale_d = 'd',
vertical_align = 'v'
vertical_align = 'v',
horizontal_align = 'h'
};
enum KEYS key = 'a';
@ -51,6 +52,9 @@ static inline void parse_multicell_code(PS *self, uint8_t *parser_buf,
case vertical_align:
value_state = UINT;
break;
case horizontal_align:
value_state = UINT;
break;
default:
REPORT_ERROR("Malformed MultiCellCommand control block, invalid key "
"character: 0x%x",
@ -131,6 +135,7 @@ static inline void parse_multicell_code(PS *self, uint8_t *parser_buf,
U(subscale_n);
U(subscale_d);
U(vertical_align);
U(horizontal_align);
default:
break;
}
@ -183,12 +188,13 @@ static inline void parse_multicell_code(PS *self, uint8_t *parser_buf,
}
REPORT_VA_COMMAND(
"K s { sI sI sI sI sI ss#}", self->window_id, "multicell_command",
"K s { sI sI sI sI sI sI ss#}", self->window_id, "multicell_command",
"width", (unsigned int)g.width, "scale", (unsigned int)g.scale,
"subscale_n", (unsigned int)g.subscale_n, "subscale_d",
(unsigned int)g.subscale_d, "vertical_align",
(unsigned int)g.vertical_align,
(unsigned int)g.vertical_align, "horizontal_align",
(unsigned int)g.horizontal_align,
"", (char *)parser_buf + payload_start, g.payload_sz);

View file

@ -1258,7 +1258,8 @@ screen_handle_multicell_command(Screen *self, const MultiCellCommand *cmd, const
CPUCell mcd = {
.width=MIN(cmd->width, M(WIDTH_BITS)), .scale=MAX(1u, MIN(cmd->scale, M(SCALE_BITS))),
.subscale_n=MIN(cmd->subscale_n, M(SUBSCALE_BITS)), .subscale_d=MIN(cmd->subscale_d, M(SUBSCALE_BITS)),
.vertical_align=MIN(cmd->vertical_align, M(VALIGN_BITS)), .is_multicell=true
.valign=MIN(cmd->vertical_align, M(VALIGN_BITS)), .halign=MIN(cmd->horizontal_align, M(HALIGN_BITS)),
.is_multicell=true
};
#undef M
if (mcd.width) handle_fixed_width_multicell_command(self, mcd, self->lc);
@ -5389,10 +5390,10 @@ test_parse_written_data(Screen *screen, PyObject *args) {
static PyObject*
multicell_data_as_dict(CPUCell mcd) {
return Py_BuildValue("{sI sI sI sI sO sI}",
return Py_BuildValue("{sI sI sI sI sO sI sI}",
"scale", (unsigned int)mcd.scale, "width", (unsigned int)mcd.width,
"subscale_n", (unsigned int)mcd.subscale_n, "subscale_d", (unsigned int)mcd.subscale_d,
"natural_width", mcd.natural_width ? Py_True : Py_False, "vertical_align", mcd.vertical_align);
"natural_width", mcd.natural_width ? Py_True : Py_False, "vertical_align", mcd.valign, "horizontal_align", mcd.halign);
}
static PyObject*

View file

@ -36,8 +36,10 @@ def parse_bytes(screen, data, dump_callback=None):
screen.test_parse_written_data(dump_callback)
def draw_multicell(screen: Screen, text: str, width: int = 0, scale: int = 1, subscale_n: int = 0, subscale_d: int = 0, vertical_align: int = 0) -> None:
cmd = f'\x1b]{TEXT_SIZE_CODE};w={width}:s={scale}:n={subscale_n}:d={subscale_d}:v={vertical_align};{text}\a'
def draw_multicell(
screen: Screen, text: str, width: int = 0, scale: int = 1, subscale_n: int = 0, subscale_d: int = 0, vertical_align: int = 0, horizontal_align: int = 0
) -> None:
cmd = f'\x1b]{TEXT_SIZE_CODE};w={width}:s={scale}:n={subscale_n}:d={subscale_d}:v={vertical_align}:h={horizontal_align};{text}\a'
parse_bytes(screen, cmd.encode())

View file

@ -53,6 +53,7 @@ def ae(key):
ae('subscale_n')
ae('subscale_d')
ae('vertical_align')
ae('horizontal_align')
ae('text')
ae('natural_width')
ae('next_char_was_wrapped')
@ -448,8 +449,8 @@ def ta(expected):
s.reset()
s.reset()
multicell(s, 'a', width=2, scale=3, subscale_n=1, subscale_d=2, vertical_align=1)
ta(f'\x1b]{TEXT_SIZE_CODE};w=2:s=3:n=1:d=2:v=1;a\x07')
multicell(s, 'a', width=2, scale=3, subscale_n=1, subscale_d=2, vertical_align=3, horizontal_align=3)
ta(f'\x1b]{TEXT_SIZE_CODE};w=2:s=3:n=1:d=2:v=3:h=3;a\x07')
s.draw('a')
multicell(s, 'b', width=2)
s.draw('c')