Browse Source

feat: add support for tab stops (#1945)

* feat: add support for tab stops

* refactor: simplify `tab_offset == nil` case

Co-authored-by: Takase <20792268+takase1121@users.noreply.github.com>

* refactor: use `NAN` `offset` instead of bool to enable tab stops

---------

Co-authored-by: Takase <20792268+takase1121@users.noreply.github.com>
pull/1999/head
Guldoman 7 months ago
committed by GitHub
parent
commit
c73903f5be
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      data/core/docview.lua
  2. 20
      src/api/renderer.c
  3. 8
      src/rencache.c
  4. 2
      src/rencache.h
  5. 31
      src/renderer.c
  6. 5
      src/renderer.h

11
data/core/docview.lua

@ -178,7 +178,7 @@ function DocView:get_col_x_offset(line, col)
if font ~= default_font then font:set_tab_size(indent_size) end
local length = #text
if column + length <= col then
xoffset = xoffset + font:get_width(text)
xoffset = xoffset + font:get_width(text, {tab_offset = xoffset})
column = column + length
if column >= col then
return xoffset
@ -188,7 +188,7 @@ function DocView:get_col_x_offset(line, col)
if column >= col then
return xoffset
end
xoffset = xoffset + font:get_width(char)
xoffset = xoffset + font:get_width(char, {tab_offset = xoffset})
column = column + #char
end
end
@ -208,7 +208,7 @@ function DocView:get_x_offset_col(line, x)
for _, type, text in self.doc.highlighter:each_token(line) do
local font = style.syntax_fonts[type] or default_font
if font ~= default_font then font:set_tab_size(indent_size) end
local width = font:get_width(text)
local width = font:get_width(text, {tab_offset = xoffset})
-- Don't take the shortcut if the width matches x,
-- because we need last_i which should be calculated using utf-8.
if xoffset + width < x then
@ -216,7 +216,7 @@ function DocView:get_x_offset_col(line, x)
i = i + #text
else
for char in common.utf8_chars(text) do
local w = font:get_width(char)
local w = font:get_width(char, {tab_offset = xoffset})
if xoffset >= x then
return (xoffset - x > w / 2) and last_i or i
end
@ -450,12 +450,13 @@ function DocView:draw_line_text(line, x, y)
if string.sub(tokens[tokens_count], -1) == "\n" then
last_token = tokens_count - 1
end
local start_tx = tx
for tidx, type, text in self.doc.highlighter:each_token(line) do
local color = style.syntax[type]
local font = style.syntax_fonts[type] or default_font
-- do not render newline, fixes issue #1164
if tidx == last_token then text = text:sub(1, -2) end
tx = renderer.draw_text(font, text, tx, ty, color)
tx = renderer.draw_text(font, text, tx, ty, color, {tab_offset = tx - start_tx})
if tx > self.position.x + self.size.x then break end
end
return self:get_line_height()

20
src/api/renderer.c

@ -1,3 +1,4 @@
#include <math.h>
#include <string.h>
#include <assert.h>
#include "api.h"
@ -203,12 +204,26 @@ static int f_font_gc(lua_State *L) {
}
static RenTab checktab(lua_State *L, int idx) {
RenTab tab = {.offset = NAN};
if (lua_isnoneornil(L, idx)) {
return tab;
}
luaL_checktype(L, idx, LUA_TTABLE);
if (lua_getfield(L, idx, "tab_offset") == LUA_TNIL) {
return tab;
}
tab.offset = luaL_checknumber(L, -1);
return tab;
}
static int f_font_get_width(lua_State *L) {
RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1);
size_t len;
const char *text = luaL_checklstring(L, 2, &len);
RenTab tab = checktab(L, 3);
lua_pushnumber(L, ren_font_group_get_width(fonts, text, len, NULL));
lua_pushnumber(L, ren_font_group_get_width(fonts, text, len, tab, NULL));
return 1;
}
@ -373,7 +388,8 @@ static int f_draw_text(lua_State *L) {
double x = luaL_checknumber(L, 3);
int y = luaL_checknumber(L, 4);
RenColor color = checkcolor(L, 5, 255);
x = rencache_draw_text(ren_get_target_window(), fonts, text, len, x, y, color);
RenTab tab = checktab(L, 6);
x = rencache_draw_text(ren_get_target_window(), fonts, text, len, x, y, color, tab);
lua_pushnumber(L, x);
return 1;
}

8
src/rencache.c

@ -52,6 +52,7 @@ typedef struct {
float text_x;
size_t len;
int8_t tab_size;
RenTab tab;
char text[];
} DrawTextCommand;
@ -190,10 +191,10 @@ void rencache_draw_rect(RenWindow *window_renderer, RenRect rect, RenColor color
}
}
double rencache_draw_text(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len, double x, int y, RenColor color)
double rencache_draw_text(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len, double x, int y, RenColor color, RenTab tab)
{
int x_offset;
double width = ren_font_group_get_width(fonts, text, len, &x_offset);
double width = ren_font_group_get_width(fonts, text, len, tab, &x_offset);
RenRect rect = { x + x_offset, y, (int)(width - x_offset), ren_font_group_get_height(fonts) };
if (rects_overlap(last_clip_rect, rect)) {
int sz = len + 1;
@ -206,6 +207,7 @@ double rencache_draw_text(RenWindow *window_renderer, RenFont **fonts, const cha
cmd->text_x = x;
cmd->len = len;
cmd->tab_size = ren_font_group_get_tab_size(fonts);
cmd->tab = tab;
}
}
return x + width;
@ -320,7 +322,7 @@ void rencache_end_frame(RenWindow *window_renderer) {
break;
case DRAW_TEXT:
ren_font_group_set_tab_size(tcmd->fonts, tcmd->tab_size);
ren_draw_text(&rs, tcmd->fonts, tcmd->text, tcmd->len, tcmd->text_x, tcmd->rect.y, tcmd->color);
ren_draw_text(&rs, tcmd->fonts, tcmd->text, tcmd->len, tcmd->text_x, tcmd->rect.y, tcmd->color, tcmd->tab);
break;
}
}

2
src/rencache.h

@ -8,7 +8,7 @@
void rencache_show_debug(bool enable);
void rencache_set_clip_rect(RenWindow *window_renderer, RenRect rect);
void rencache_draw_rect(RenWindow *window_renderer, RenRect rect, RenColor color);
double rencache_draw_text(RenWindow *window_renderer, RenFont **font, const char *text, size_t len, double x, int y, RenColor color);
double rencache_draw_text(RenWindow *window_renderer, RenFont **font, const char *text, size_t len, double x, int y, RenColor color, RenTab tab);
void rencache_invalidate(void);
void rencache_begin_frame(RenWindow *window_renderer);
void rencache_end_frame(RenWindow *window_renderer);

31
src/renderer.c

@ -515,11 +515,27 @@ int ren_font_group_get_height(RenFont **fonts) {
}
// some fonts provide xadvance for whitespaces (e.g. Unifont), which we need to ignore
#define FONT_GET_XADVANCE(F, C, M) (is_whitespace((C)) || !(M) || !(M)->xadvance \
? (F)->space_advance * (float) ((C) == '\t' ? (F)->tab_size : 1) \
: (M)->xadvance)
float font_get_xadvance(RenFont *font, unsigned int codepoint, GlyphMetric *metric, double curr_x, RenTab tab) {
if (!is_whitespace(codepoint) && metric && metric->xadvance) {
return metric->xadvance;
}
if (codepoint != '\t') {
return font->space_advance;
}
float tab_size = font->space_advance * font->tab_size;
if (isnan(tab.offset)) {
return tab_size;
}
double offset = fmodl(curr_x + tab.offset, tab_size);
float adv = tab_size - offset;
// If there is not enough space until the next stop, skip it
if (adv < font->space_advance) {
adv += tab_size;
}
return adv;
}
double ren_font_group_get_width(RenFont **fonts, const char *text, size_t len, int *x_offset) {
double ren_font_group_get_width(RenFont **fonts, const char *text, size_t len, RenTab tab, int *x_offset) {
double width = 0;
const char* end = text + len;
@ -529,7 +545,7 @@ double ren_font_group_get_width(RenFont **fonts, const char *text, size_t len, i
text = utf8_to_codepoint(text, end, &codepoint);
GlyphMetric *metric = NULL;
font_group_get_glyph(fonts, codepoint, 0, NULL, &metric);
width += FONT_GET_XADVANCE(fonts[0], codepoint, metric);
width += font_get_xadvance(fonts[0], codepoint, metric, width, tab);
if (!set_x_offset && metric) {
set_x_offset = true;
*x_offset = metric->bitmap_left; // TODO: should this be scaled by the surface scale?
@ -562,13 +578,14 @@ void ren_font_dump(RenFont *font) {
}
#endif
double ren_draw_text(RenSurface *rs, RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color) {
double ren_draw_text(RenSurface *rs, RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color, RenTab tab) {
SDL_Surface *surface = rs->surface;
SDL_Rect clip;
SDL_GetClipRect(surface, &clip);
const int surface_scale = rs->scale;
double pen_x = x * surface_scale;
double original_pen_x = pen_x;
y *= surface_scale;
const char* end = text + len;
uint8_t* destination_pixels = surface->pixels;
@ -635,7 +652,7 @@ double ren_draw_text(RenSurface *rs, RenFont **fonts, const char *text, size_t l
}
}
float adv = FONT_GET_XADVANCE(fonts[0], codepoint, metric);
float adv = font_get_xadvance(fonts[0], codepoint, metric, pen_x - original_pen_x, tab);
if(!last) last = font;
else if(font != last || text == end) {

5
src/renderer.h

@ -19,6 +19,7 @@ typedef enum { FONT_ANTIALIASING_NONE, FONT_ANTIALIASING_GRAYSCALE, FONT_ANTIALI
typedef enum { FONT_STYLE_BOLD = 1, FONT_STYLE_ITALIC = 2, FONT_STYLE_UNDERLINE = 4, FONT_STYLE_SMOOTH = 8, FONT_STYLE_STRIKETHROUGH = 16 } ERenFontStyle;
typedef struct { uint8_t b, g, r, a; } RenColor;
typedef struct { int x, y, width, height; } RenRect;
typedef struct { double offset; } RenTab;
typedef struct { SDL_Surface *surface; int scale; } RenSurface;
struct RenWindow;
@ -36,8 +37,8 @@ void ren_font_group_set_size(RenFont **font, float size, int surface_scale);
void update_font_scale(RenWindow *window_renderer, RenFont **fonts);
#endif
void ren_font_group_set_tab_size(RenFont **font, int n);
double ren_font_group_get_width(RenFont **font, const char *text, size_t len, int *x_offset);
double ren_draw_text(RenSurface *rs, RenFont **font, const char *text, size_t len, float x, int y, RenColor color);
double ren_font_group_get_width(RenFont **font, const char *text, size_t len, RenTab tab, int *x_offset);
double ren_draw_text(RenSurface *rs, RenFont **font, const char *text, size_t len, float x, int y, RenColor color, RenTab tab);
void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color);

Loading…
Cancel
Save