popup: place and bound the input panel

This commit is contained in:
2026-08-14 20:00:36 +09:00
parent 7619d2bd9d
commit 193ed009bd
6 changed files with 97 additions and 10 deletions

1
fn.h
View File

@@ -48,3 +48,4 @@ void* erealloc(void*, ulong);
int textinit(void); int textinit(void);
int textwidth(Str*); int textwidth(Str*);
void textdraw(u32int*, int, int, int, int, Str*); void textdraw(u32int*, int, int, int, int, Str*);
void textdrawfit(u32int*, int, int, int, int, int, Str*);

40
font.c
View File

@@ -6,6 +6,13 @@ static PangoFontMap *fontmap;
static PangoContext *context; static PangoContext *context;
static PangoLayout *layout; static PangoLayout *layout;
static void
resetlayout(void)
{
pango_layout_set_width(layout, -1);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
}
static void static void
textclear(void) textclear(void)
{ {
@@ -100,20 +107,32 @@ textwidth(Str *s)
{ {
PangoRectangle r; PangoRectangle r;
if(layout == nil)
return 0;
resetlayout();
if(!settext(s)) if(!settext(s))
return 0; return 0;
textextents(&r); textextents(&r);
return r.width; return r.width;
} }
void static void
textdraw(u32int *buf, int w, int h, int x, int y, Str *s) drawtext(u32int *buf, int w, int h, int x, int y, int fit, Str *s)
{ {
PangoRectangle r; PangoRectangle r;
cairo_surface_t *surface; cairo_surface_t *surface;
cairo_t *cr; cairo_t *cr;
int stride; int stride;
if(layout == nil)
return;
resetlayout();
if(fit != -1 && fit <= 0)
return;
if(fit > 0){
pango_layout_set_width(layout, fit * PANGO_SCALE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
}
if(buf == nil || w <= 0 || h <= 0 || !settext(s)) if(buf == nil || w <= 0 || h <= 0 || !settext(s))
return; return;
stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w); stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
@@ -131,7 +150,10 @@ textdraw(u32int *buf, int w, int h, int x, int y, Str *s)
pango_cairo_update_layout(cr, layout); pango_cairo_update_layout(cr, layout);
textextents(&r); textextents(&r);
/* A fallback face must not paint into another popup row. */ /* A fallback face must not paint into another popup row. */
cairo_rectangle(cr, 0, y, w, Fontsz); if(fit > 0)
cairo_rectangle(cr, x, y, fit, Fontsz);
else
cairo_rectangle(cr, 0, y, w, Fontsz);
cairo_clip(cr); cairo_clip(cr);
cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y); cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y);
cairo_set_source_rgb(cr, 0, 0, 0); cairo_set_source_rgb(cr, 0, 0, 0);
@@ -141,3 +163,15 @@ textdraw(u32int *buf, int w, int h, int x, int y, Str *s)
cairo_surface_flush(surface); cairo_surface_flush(surface);
cairo_surface_destroy(surface); cairo_surface_destroy(surface);
} }
void
textdraw(u32int *buf, int w, int h, int x, int y, Str *s)
{
drawtext(buf, w, h, x, y, -1, s);
}
void
textdrawfit(u32int *buf, int w, int h, int x, int y, int fit, Str *s)
{
drawtext(buf, w, h, x, y, fit, s);
}

View File

@@ -5,11 +5,15 @@ void
popupposition(Caret *caret, int pointerx, int pointery, int screenw, popupposition(Caret *caret, int pointerx, int pointery, int screenw,
int screenh, int w, int h, int *x, int *y) int screenh, int w, int h, int *x, int *y)
{ {
vlong px, py, xmax, ymax; vlong below, px, py, xmax, ymax;
if(caret->valid){ if(caret->valid){
px = caret->x; px = caret->x;
py = (vlong)caret->y + max(caret->h, 0); below = (vlong)caret->y + max(caret->h, 0);
if(below >= 0 && below + h <= screenh)
py = below;
else
py = (vlong)caret->y - h;
}else{ }else{
px = (vlong)pointerx + 10; px = (vlong)pointerx + 10;
py = (vlong)pointery + 10; py = (vlong)pointery + 10;

View File

@@ -178,8 +178,8 @@ font_render(struct ct *t)
{ {
static char *plain[] = { "A", "", "", "", "", "𠀋" }; static char *plain[] = { "A", "", "", "", "", "𠀋" };
u32int *buf, *mem; u32int *buf, *mem;
Str a, heart, missing; Str a, heart, longrow, missing;
int i, w; int i, natural, w, x, y;
mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]); mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]);
fillpixels(mem, Testn + 2 * Guard, guardcolor); fillpixels(mem, Testn + 2 * Guard, guardcolor);
@@ -216,6 +216,21 @@ font_render(struct ct *t)
checkclip(t, mem, Fontsz, -Fontsz / 2, &a); checkclip(t, mem, Fontsz, -Fontsz / 2, &a);
checkclip(t, mem, Fontsz, Testh - Fontsz / 2, &a); checkclip(t, mem, Fontsz, Testh - Fontsz / 2, &a);
longrow = mkstr("abcdefghijklmnopqrstuvwxyz");
natural = textwidth(&longrow);
CT_CHECK(t, natural > 2*Fontsz);
fillpixels(buf, Testn, Colbg);
textdrawfit(buf, Testw, Testh, 2*Fontsz, Fontsz,
2*Fontsz, &longrow);
CT_CHECK(t, hasink(buf, Testn, Colbg));
for(y = Fontsz; y < 2*Fontsz; y++){
for(x = 0; x < 2*Fontsz; x++)
CT_EQ_UINT(t, Colbg, buf[y * Testw + x] & Rgbmask);
for(x = 4*Fontsz; x < Testw; x++)
CT_EQ_UINT(t, Colbg, buf[y * Testw + x] & Rgbmask);
}
CT_EQ_INT(t, natural, textwidth(&longrow));
missing = mkstr("\xF4\x8F\xBF\xBF"); missing = mkstr("\xF4\x8F\xBF\xBF");
for(i = 0; i < 32; i++){ for(i = 0; i < 32; i++){
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart); textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart);

View File

@@ -22,11 +22,38 @@ popup_layout(struct ct *t)
caret.y = 590; caret.y = 590;
popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y); popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y);
CT_EQ_INT(t, 700, x); CT_EQ_INT(t, 700, x);
CT_EQ_INT(t, 540, y); CT_EQ_INT(t, 530, y);
/* Neither side fits: choose above, then clamp to the screen. */
caret.x = 20;
caret.y = 30;
caret.h = 20;
popupposition(&caret, 0, 0, 80, 80, 100, 100, &x, &y);
CT_EQ_INT(t, 0, x);
CT_EQ_INT(t, 0, y);
caret.x = 70;
caret.y = 80;
caret.h = 0;
popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y);
CT_EQ_INT(t, 70, x);
CT_EQ_INT(t, 80, y);
caret.h = -20;
popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y);
CT_EQ_INT(t, 70, x);
CT_EQ_INT(t, 80, y);
caret.x = INT_MIN; caret.x = INT_MIN;
caret.y = INT_MAX; caret.y = INT_MAX;
caret.h = INT_MAX; caret.h = INT_MAX;
popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y); popupposition(&caret, 0, 0, 800, 600, 100, 60, &x, &y);
CT_EQ_INT(t, 0, x); CT_EQ_INT(t, 0, x);
CT_EQ_INT(t, 540, y); CT_EQ_INT(t, 540, y);
caret.x = INT_MAX;
caret.y = INT_MIN;
caret.h = INT_MIN;
popupposition(&caret, 0, 0, INT_MAX, INT_MAX,
INT_MAX, INT_MAX, &x, &y);
CT_EQ_INT(t, 0, x);
CT_EQ_INT(t, 0, y);
} }

10
win.c
View File

@@ -161,6 +161,8 @@ drawkouho(Drawcmd *dc, int n, int w, int h)
int npre, sely, y, i; int npre, sely, y, i;
Str num, *s; Str num, *s;
if(w <= 0 || w > Imgw || h <= 0 || h > Imgh)
return;
fill(img, w * h, Colbg); fill(img, w * h, Colbg);
npre = dc->pre.n != 0; npre = dc->pre.n != 0;
if(npre) if(npre)
@@ -174,13 +176,15 @@ drawkouho(Drawcmd *dc, int n, int w, int h)
sclear(&num); sclear(&num);
sputr(&num, '1' + i + Asciitofull); sputr(&num, '1' + i + Asciitofull);
textdraw(img, w, h, 0, y, &num); textdraw(img, w, h, 0, y, &num);
textdraw(img, w, h, 2*Fontsz, y, s); textdrawfit(img, w, h, 2*Fontsz, y, w - 2*Fontsz, s);
} }
} }
static void static void
putimage(int w, int h) putimage(int w, int h)
{ {
if(w <= 0 || w > Imgw || h <= 0 || h > Imgh)
return;
xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc,
w, h, 0, 0, 0, depth, w * h * 4, (u8int*)img); w, h, 0, 0, 0, depth, w * h * 4, (u8int*)img);
/* The retained background pixmap lets the server repaint exposures. */ /* The retained background pixmap lets the server repaint exposures. */
@@ -213,7 +217,9 @@ winshow(Drawcmd *dc)
maxw = max(maxw, textwidth(&dc->kouho[i])); maxw = max(maxw, textwidth(&dc->kouho[i]));
vals[3] = h = (n + npre) * Fontsz; vals[3] = h = (n + npre) * Fontsz;
width = (vlong)maxw + 3*Fontsz; width = (vlong)maxw + 3*Fontsz;
vals[2] = w = min(max(width, 1), Imgw); vals[2] = w = min(min(max(width, 1), Imgw), scr->width_in_pixels);
if(w <= 0 || w > Imgw || h <= 0 || h > Imgh)
return 0;
px = py = 0; px = py = 0;
if(!dc->caret.valid){ if(!dc->caret.valid){
cookie = xcb_query_pointer(conn, scr->root); cookie = xcb_query_pointer(conn, scr->root);