xF: make sure the caret is visible in X11 editor
Alpine 3.24 Success
Arch Linux AUR Success
OpenBSD 7.8 Success

This commit is contained in:
2026-08-07 10:26:33 +02:00
parent 1704f90934
commit 4f5d4cb64b
+48 -21
View File
@@ -837,8 +837,8 @@ app_editor_position (void)
size_t position = 0;
for (int i = 0; i < g.editor.point; i++)
{
uint8_t encoded[6];
int len = u8_uctomb (encoded, g.editor.line[i], sizeof encoded);
uint8_t utf8[6];
int len = u8_uctomb (utf8, g.editor.line[i], sizeof utf8);
if (len > 0)
position += len;
}
@@ -1954,44 +1954,67 @@ tui_make_editor (chtype attrs)
#ifdef WITH_X11
static void
x11_render_editor (struct widget *self)
x11_process_editor (struct widget *self, int *x, int *caret, bool draw)
{
x11_render_padding (self);
struct x11_font *font = x11_widget_font (self);
XftColor color = { .color = *x11_fg (self) };
XftColor fg = { .color = *x11_fg (self) }, bg = { .color = *x11_bg (self) };
// A simplistic adaptation of tui_render_editor() follows.
// TODO: Make this scroll around the caret, and fade like labels.
int x = self->x;
int caret = x;
for (size_t i = 0; i < g.editor.len; i++)
{
if ((int) i == g.editor.point)
caret = x;
*caret = *x;
ucs4_t codepoint = g.editor.line[i];
if (!iscntrl_ascii (codepoint))
{
uint8_t encoded[7] = {};
if (u8_uctomb (encoded, codepoint, sizeof encoded - 1) > 0)
x += x11_font_draw (font, &color, x, self->y, (char *) encoded);
uint8_t utf8[7] = {};
if (u8_uctomb (utf8, codepoint, sizeof utf8 - 1) > 0)
*x += draw
? x11_font_draw (font, &fg, *x, self->y, (char *) utf8)
: x11_font_hadvance (font, (char *) utf8);
continue;
}
char escaped[] = { '^', codepoint == 127 ? '?' : codepoint + 64, 0 };
int width = x11_font_hadvance (font, escaped);
XRenderFillRectangle (g_xui.dpy, PictOpSrc, g_xui.x11_pixmap_picture,
x11_fg (self), x, self->y, width, self->height);
XftColor reverse = { .color = *x11_bg (self) };
x += x11_font_draw (font, &reverse, x, self->y, escaped);
if (draw)
{
XftDrawRect (g_xui.xft_draw, &fg, *x, self->y, width, self->height);
*x += x11_font_draw (font, &bg, *x, self->y, escaped);
}
else
*x += width;
}
if (g.editor.point == (int) g.editor.len)
caret = x;
*caret = *x;
}
static void
x11_render_editor (struct widget *self)
{
enum { CARET_WIDTH = 2 };
int caret = 0, width = 0;
x11_process_editor (self, &width, &caret, false);
// Automatically scroll around the caret by changing where we start drawing:
int x = self->x, width_limit = self->width - CARET_WIDTH;
if (width > width_limit)
{
// Push it to the left until the caret is in the middle...
int offset = MAX (0, caret - width_limit / 2);
// ...but then push it to the right until text fills the whole width.
offset = MIN (offset, width - width_limit);
x -= offset;
}
x11_render_padding (self);
x11_process_editor (self, &x, &caret, true);
XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff };
XRenderFillRectangle (g_xui.dpy, PictOpDifference, g_xui.x11_pixmap_picture,
&white, caret, self->y, 2, self->height);
&white, caret, self->y, CARET_WIDTH, self->height);
}
static struct widget *
@@ -2003,7 +2026,11 @@ x11_make_editor (chtype attrs)
widget->width = -1;
widget->height = g_xui.vunit;
widget->text = "";
return widget;
// Enforce clipping by putting this inside a container.
struct widget *wrapper = xui_hbox (widget);
wrapper->width = -1;
return wrapper;
}
static struct xui_scrollbar_part