emoji: add one-shot candidate search

This commit is contained in:
2026-08-11 22:24:59 +09:00
parent 82276bf37f
commit 0fff17c8b8
14 changed files with 891 additions and 346 deletions

279
strans.c
View File

@@ -3,8 +3,18 @@
static Im im;
static int popup = 0;
static int visible = 0;
static void dictqmap(Im*);
typedef struct Search Search;
struct Search
{
int on;
Str raw;
Str text;
};
static Search search;
static void
backmap(Im *im)
{
@@ -16,7 +26,7 @@ Lang langs[] = {
{LangJP, "hira", "kanji", transmap, backmap, dictqmap, nil, nil},
{LangJPK, "kata", "kanji", transmap, backmap, dictqmap, nil, nil},
{LangKO, "hangul", nil, transko, backko, dictqmap, nil, nil},
{LangEMOJI, "emoji", "emoji", transmap, backmap, dictqmap, nil, nil},
{LangEMOJI, nil, "emoji", nil, nil, nil, nil, nil},
{LangVI, "telex", nil, transvi, backvi, dictqmap, nil, nil},
};
int nlang = nelem(langs);
@@ -44,13 +54,15 @@ static void
show(void)
{
Drawcmd dc;
Str *pre;
int i, first, n;
if(!popup)
return;
sclear(&dc.pre);
if(!mapget(im.l->map, &im.pre, &dc.pre))
dc.pre = im.pre;
pre = search.on ? &search.text : &im.pre;
if(popup){
if(search.on || im.l->map == nil || !mapget(im.l->map, pre, &dc.pre))
dc.pre = *pre;
}
first = im.sel >= Maxdisp ? im.sel - Maxdisp + 1 : 0;
n = im.nkouho - first;
if(n > Maxdisp) n = Maxdisp;
@@ -58,6 +70,9 @@ show(void)
dc.sel = im.sel >= 0 ? im.sel - first : -1;
for(i = 0; i < n; i++)
dc.kouho[i] = im.kouho[first + i];
if(dc.pre.n == 0 && n == 0 && !visible)
return;
visible = dc.pre.n != 0 || n != 0;
chansend(drawc, &dc);
}
@@ -110,7 +125,7 @@ commit(Str *com)
{
Str val;
if(mapget(im.l->map, &im.pre, &val))
if(im.l->map != nil && mapget(im.l->map, &im.pre, &val))
sappend(com, &val);
else
sappend(com, &im.pre);
@@ -209,13 +224,240 @@ transmap(Im *im, Rune c)
return e;
}
static Rune
ctrlkey(Rune c)
{
if(c >= 'a' && c <= 'z')
return c - 'a' + 1;
return c;
}
static void
foldascii(Str *s)
{
int i;
for(i = 0; i < s->n; i++)
if(s->r[i] >= 'A' && s->r[i] <= 'Z')
s->r[i] += 'a' - 'A';
}
static void
transstr(Lang *l, Str *raw, Str *out)
{
static Im q;
Emit e;
Str mapped;
int i;
sclear(out);
if(l == nil || l->trans == nil || (l->trans == transmap && l->map == nil)){
*out = *raw;
return;
}
memset(&q, 0, sizeof q);
q.l = l;
for(i = 0; i < raw->n; i++){
e = l->trans(&q, raw->r[i]);
sappend(out, &e.s);
q.pre = e.next;
if(!e.eat && out->n < Maxrunes)
sputr(out, raw->r[i]);
}
if(q.pre.n == 0)
return;
if(l->map != nil && mapget(l->map, &q.pre, &mapped))
sappend(out, &mapped);
else
sappend(out, &q.pre);
}
static void
addkouho(Str *s)
{
int i;
for(i = 0; i < im.nkouho; i++)
if(scmp(&im.kouho[i], s) == 0)
return;
if(im.nkouho < Maxkouho)
im.kouho[im.nkouho++] = *s;
}
static void
emojilookup(Str *key, Dictres *res)
{
Dictreq req;
req.key = *key;
req.pre = req.key;
req.lang = LangEMOJI;
dictlookup(&req, res);
}
static void
emojiquery(void)
{
static Dictres res;
Str raw, local;
int i, rawhit, localhit;
raw = search.raw;
foldascii(&raw);
transstr(im.l, &search.raw, &local);
foldascii(&local);
clearkouho();
emojilookup(&raw, &res);
rawhit = res.nkouho != 0;
for(i = 0; i < res.nkouho; i++)
addkouho(&res.kouho[i]);
localhit = 0;
if(scmp(&raw, &local) != 0){
emojilookup(&local, &res);
localhit = res.nkouho != 0;
for(i = 0; i < res.nkouho; i++)
addkouho(&res.kouho[i]);
}
search.text = rawhit || !localhit ? raw : local;
show();
}
static void
endsearch(void)
{
search.on = 0;
sclear(&search.raw);
sclear(&search.text);
clearkouho();
show();
}
static void
startsearch(Str *com)
{
if(im.pre.n > 0)
commit(com);
sclear(&im.pre);
sclear(&im.raw);
clearkouho();
search.on = 1;
sclear(&search.raw);
sclear(&search.text);
show();
}
static void
commitsearch(Str *com)
{
sappend(com, &search.text);
endsearch();
}
static int
pickemoji(int n, Str *com)
{
if(n < 0 || n >= im.nkouho)
return 0;
sappend(com, &im.kouho[n]);
endsearch();
return 1;
}
static int
searchkey(u32int ks, u32int mod, Str *com)
{
int n, off;
Rune c;
if(ks == Ksuper || ks == Kshift)
return 1;
if(ks == Kdown || ks == Kup){
if(im.nkouho == 0)
return 1;
if(ks == Kdown && im.sel < im.nkouho - 1)
im.sel++;
if(ks == Kup && im.sel > 0)
im.sel--;
show();
return 1;
}
n = ks - '1';
off = im.sel >= Maxdisp ? im.sel - Maxdisp + 1 : 0;
if((mod & Malt) && n >= 0 && n < Maxdisp){
pickemoji(off + n, com);
return 1;
}
if(ks == Ktab){
if(im.nkouho == 0)
return 1;
if(mod & Mshift){
if(im.sel <= 0)
im.sel = im.nkouho - 1;
else
im.sel--;
}else if(im.sel >= im.nkouho - 1)
im.sel = 0;
else
im.sel++;
show();
return 1;
}
if(ks == Kret){
if(im.nkouho > 0)
return pickemoji(im.sel >= 0 ? im.sel : 0, com);
commitsearch(com);
return 1;
}
if(ks == Kback){
if(search.raw.n == 0)
return 1;
spopr(&search.raw);
emojiquery();
return 1;
}
if(ks == Kesc){
endsearch();
return 1;
}
if(mod & Mctrl){
c = ctrlkey(ks);
if(c == LangEMOJI){
endsearch();
return 1;
}
if(c == 0x10){
popup = !popup;
show();
return 1;
}
endsearch();
if(setlang(c))
return 1;
return 0;
}
if(ks >= Kspec || ks < ' ' || (mod & (Malt|Msuper))){
commitsearch(com);
return 0;
}
if(search.raw.n >= Maxrunes){
commitsearch(com);
return 0;
}
sputr(&search.raw, ks);
emojiquery();
return 1;
}
static int
keystroke(u32int ks, u32int mod, Str *com)
{
int n, off;
Rune c;
if(ks == Ksuper || ks == Kshift)
return 1;
if(search.on)
return searchkey(ks, mod, com);
if(ks == Kdown || ks == Kup){
if(im.nkouho == 0)
return 0;
@@ -271,14 +513,18 @@ keystroke(u32int ks, u32int mod, Str *com)
return 0;
}
if(mod & Mctrl){
reset();
if(ks >= 'a' && ks <= 'z')
ks -= 'a' - 1;
if(ks == 0x10){
popup = !popup;
c = ctrlkey(ks);
if(c == LangEMOJI){
startsearch(com);
return 1;
}
if(setlang(ks))
if(c == 0x10){
popup = !popup;
show();
return 1;
}
reset();
if(setlang(c))
return 1;
return 0;
}
@@ -305,6 +551,8 @@ init(void)
{
memset(&im, 0, sizeof(im));
im.l = getlang(LangEN);
memset(&search, 0, sizeof search);
visible = 0;
}
static void
@@ -316,15 +564,15 @@ imhandlekey(Keyreq *kr)
sclear(&res.preedit);
res.eaten = keystroke(kr->ks, kr->mod, &res.commit);
if(kr->want)
res.preedit = im.pre;
res.preedit = search.on ? search.text : im.pre;
chansend(kr->reply, &res);
}
void
imthread(void*)
{
static Dictres res;
Keyreq kr;
Dictres res;
Alt alts[] = {
{keyc, &kr, CHANRCV, nil},
{dictresc, &res, CHANRCV, nil},
@@ -339,7 +587,8 @@ imthread(void*)
imhandlekey(&kr);
break;
case 1:
if(scmp(&res.key, &im.pre) == 0){
if(!search.on && res.lang == im.l->lang &&
scmp(&res.key, &im.pre) == 0){
setkouho(&res);
show();
}