Skip to content

Commit

Permalink
ui: Add proper unicode handling in TextBox
Browse files Browse the repository at this point in the history
Signed-off-by: Marek Maškarinec <[email protected]>
  • Loading branch information
marekmaskarinec committed Jan 30, 2024
1 parent d4b82a4 commit 28f38bd
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ root = true
charset = utf-8
end_of_line = lf
indent_style = tab
indent_size = 8
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
71 changes: 48 additions & 23 deletions src/staembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -2941,6 +2941,7 @@ const char *th_em_modulesrc[] = {
"\t\"placeholders.um\"\n"
"\t\"rect.um\"\n"
"\t\"th.um\"\n"
"\t\"utf8.um\"\n"
")\n"
"\n"
"//~~struct BoxStyle\n"
Expand Down Expand Up @@ -3572,21 +3573,36 @@ const char *th_em_modulesrc[] = {
"\n"
"//~~struct TextBox\n"
"type TextBox* = struct {\n"
"\t// the content of the textbox\n"
"\tbuffer: str\n"
"\t// index of the cursor\n"
"\tcursor: int\n"
"}\n"
"\t// contains other unexported rules...\n"
"//~~\n"
"\tbuffer: []utf8.Rune\n"
"}\n"
"\n"
"//~~fn TextBox.clear\n"
"// Clears the textbox\n"
"fn (this: ^TextBox) clear*() {\n"
"//~~\n"
"\tthis.buffer = \"\"\n"
"\tthis.buffer = {}\n"
"\tthis.cursor = 0\n"
"}\n"
"\n"
"//~~fn TextBox.getBuf()\n"
"// Get the content of the textbox.\n"
"fn (this: ^TextBox) getBuf*(): str {\n"
"//~~\n"
"\treturn utf8.encode(this.buffer)\n"
"}\n"
"\n"
"//~~fn TextBox.setBuf()\n"
"// Get the content of the textbox.\n"
"fn (this: ^TextBox) setBuf*(s: str) {\n"
"//~~\n"
"\tthis.buffer = utf8.decode(s)\n"
"\tthis.cursor = len(this.buffer)\n"
"}\n"
"\n"
"//~~fn Gui.textBox\n"
"// Adds a single line textbox to the gui.\n"
"// TODO:\n"
Expand Down Expand Up @@ -3635,8 +3651,7 @@ const char *th_em_modulesrc[] = {
"\t\t\tinput.isPressedRepeat(input.key_backspace) {\n"
"\n"
"\t\t\tif tb.cursor > 0 {\n"
"\t\t\t\ttb.buffer = slice(tb.buffer, 0, tb.cursor - 1) +\n"
"\t\t\t\t\tslice(tb.buffer, tb.cursor)\n"
"\t\t\t\ttb.buffer = append(slice(tb.buffer, 0, tb.cursor - 1), slice(tb.buffer, tb.cursor))\n"
"\t\t\t\ttb.cursor--\n"
"\t\t\t}\n"
"\t\t}\n"
Expand All @@ -3648,34 +3663,28 @@ const char *th_em_modulesrc[] = {
"\t\t\tv = false\n"
"\t\t}\n"
"\n"
"\t\ts := input.getStr()\n"
"\t\tfor i in s {\n"
"\t\t\tif int(s[i]) < int(\' \') || int(s[i]) == int(\'z\') + 1 {\n"
"\t\t\t\tv = false\n"
"\t\t\t}\n"
"\t\t}\n"
"\t\tif len(s) > 0 && v {\n"
"\t\t\ttb.buffer = slice(tb.buffer, 0, tb.cursor) + s +\n"
"\t\t\t\tslice(tb.buffer, tb.cursor)\n"
"\t\t\ttb.cursor += len(s)\n"
"\t\trunes := utf8.decode(input.getStr())\n"
"\t\tif len(runes) > 0 && v {\n"
"\t\t\ttb.buffer = append(slice(tb.buffer, 0, tb.cursor), append(slice(tb.buffer, tb.cursor), runes))\n"
"\t\t\ttb.cursor += len(runes)\n"
"\t\t}\n"
"\n"
"\t\treturn\n"
"\t}\n"
"\n"
"\n"
"\tstyle := gui.getStyle()\n"
"\n"
"\tstyle.negBox.draw(r)\n"
"\tcanvas.beginScissorRect(r)\n"
"\n"
"\tdm := style.ft.measure(tb.buffer).mulf(style.ftScale)\n"
"\tbuf := utf8.encode(tb.buffer)\n"
"\tdm := style.ft.measure(buf).mulf(style.ftScale)\n"
"\n"
"\tp := th.Vf2{}\n"
"\tp.y = r.y + r.h/2 - dm.y/2\n"
"\tc := th.Vf2{r.x, p.y}\n"
"\n"
"\tcdmx := style.ft.measure(slice(tb.buffer, 0, tb.cursor)).x * style.ftScale\n"
"\tcdmx := style.ft.measure(utf8.encode(slice(tb.buffer, 0, tb.cursor))).x * style.ftScale\n"
"\tif cdmx < r.w - 2 {\n"
"\t\tp.x = r.x + 1\n"
"\t\tc.x = p.x + cdmx\n"
Expand All @@ -3686,7 +3695,7 @@ const char *th_em_modulesrc[] = {
"\n"
"\taW := style.ft.measure(\"A\").x * style.ftScale\n"
"\n"
"\tstyle.ft.draw(tb.buffer, p, style.ftColor, style.ftScale)\n"
"\tstyle.ft.draw(buf, p, style.ftColor, style.ftScale)\n"
"\tif gui.selection == gui.idx {\n"
"\t\tcanvas.drawRect(style.ftColor, rect.mk(c.x, c.y, aW / 4, dm.y))\n"
"\t}\n"
Expand Down Expand Up @@ -6792,11 +6801,9 @@ const char *th_em_moduledocs[] = {
"\n"
"```\n"
"type TextBox* = struct {\n"
"\t// the content of the textbox\n"
"\tbuffer: str\n"
"\t// index of the cursor\n"
"\tcursor: int\n"
"}\n"
"\t// contains other unexported rules...\n"
"```\n"
"\n"
"\n"
Expand All @@ -6810,6 +6817,24 @@ const char *th_em_moduledocs[] = {
"Clears the textbox\n"
"\n"
"\n"
"## fn TextBox.getBuf()\n"
"\n"
"```\n"
"fn (this: ^TextBox) getBuf*(): str {\n"
"```\n"
"\n"
"Get the content of the textbox.\n"
"\n"
"\n"
"## fn TextBox.setBuf()\n"
"\n"
"```\n"
"fn (this: ^TextBox) setBuf*(s: str) {\n"
"```\n"
"\n"
"Get the content of the textbox.\n"
"\n"
"\n"
"## fn Gui.textBox\n"
"\n"
"```\n"
Expand Down
4 changes: 3 additions & 1 deletion tests/uit.um
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ fn init*() {
window.setup("gui test", 600, 600)
window.setViewport(th.Vf2{ 200, 200 })

ft := font.load("etc/roboto.ttf", 64)
ft := font.load("etc/roboto.ttf", 96, font.filterLinear)

gui = ui.mk(rect.mk(0, 0, 200, 200), ui.getDefaultStyle())
gui.getStyle().ft = ft
gui.getStyle().ftScale = 0.1

win = ui.mk(rect.mk(100, 100, 80, 50), ui.getDefaultStyle())
win.getStyle().containerBox.color = 0x88aa88ff
Expand Down
49 changes: 29 additions & 20 deletions umka/ui.um
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"placeholders.um"
"rect.um"
"th.um"
"utf8.um"
)

//~~struct BoxStyle
Expand Down Expand Up @@ -643,21 +644,36 @@ type TextBoxConfig* = struct {

//~~struct TextBox
type TextBox* = struct {
// the content of the textbox
buffer: str
// index of the cursor
cursor: int
}
// contains other unexported rules...
//~~
buffer: []utf8.Rune
}

//~~fn TextBox.clear
// Clears the textbox
fn (this: ^TextBox) clear*() {
//~~
this.buffer = ""
this.buffer = {}
this.cursor = 0
}

//~~fn TextBox.getBuf()
// Get the content of the textbox.
fn (this: ^TextBox) getBuf*(): str {
//~~
return utf8.encode(this.buffer)
}

//~~fn TextBox.setBuf()
// Get the content of the textbox.
fn (this: ^TextBox) setBuf*(s: str) {
//~~
this.buffer = utf8.decode(s)
this.cursor = len(this.buffer)
}

//~~fn Gui.textBox
// Adds a single line textbox to the gui.
// TODO:
Expand Down Expand Up @@ -706,8 +722,7 @@ fn (gui: ^Gui) textBox*(tb: ^TextBox, cfg: TextBoxConfig = {}) {
input.isPressedRepeat(input.key_backspace) {

if tb.cursor > 0 {
tb.buffer = slice(tb.buffer, 0, tb.cursor - 1) +
slice(tb.buffer, tb.cursor)
tb.buffer = append(slice(tb.buffer, 0, tb.cursor - 1), slice(tb.buffer, tb.cursor))
tb.cursor--
}
}
Expand All @@ -719,34 +734,28 @@ fn (gui: ^Gui) textBox*(tb: ^TextBox, cfg: TextBoxConfig = {}) {
v = false
}

s := input.getStr()
for i in s {
if int(s[i]) < int(' ') || int(s[i]) == int('z') + 1 {
v = false
}
}
if len(s) > 0 && v {
tb.buffer = slice(tb.buffer, 0, tb.cursor) + s +
slice(tb.buffer, tb.cursor)
tb.cursor += len(s)
runes := utf8.decode(input.getStr())
if len(runes) > 0 && v {
tb.buffer = append(slice(tb.buffer, 0, tb.cursor), append(slice(tb.buffer, tb.cursor), runes))
tb.cursor += len(runes)
}

return
}


style := gui.getStyle()

style.negBox.draw(r)
canvas.beginScissorRect(r)

dm := style.ft.measure(tb.buffer).mulf(style.ftScale)
buf := utf8.encode(tb.buffer)
dm := style.ft.measure(buf).mulf(style.ftScale)

p := th.Vf2{}
p.y = r.y + r.h/2 - dm.y/2
c := th.Vf2{r.x, p.y}

cdmx := style.ft.measure(slice(tb.buffer, 0, tb.cursor)).x * style.ftScale
cdmx := style.ft.measure(utf8.encode(slice(tb.buffer, 0, tb.cursor))).x * style.ftScale
if cdmx < r.w - 2 {
p.x = r.x + 1
c.x = p.x + cdmx
Expand All @@ -757,7 +766,7 @@ fn (gui: ^Gui) textBox*(tb: ^TextBox, cfg: TextBoxConfig = {}) {

aW := style.ft.measure("A").x * style.ftScale

style.ft.draw(tb.buffer, p, style.ftColor, style.ftScale)
style.ft.draw(buf, p, style.ftColor, style.ftScale)
if gui.selection == gui.idx {
canvas.drawRect(style.ftColor, rect.mk(c.x, c.y, aW / 4, dm.y))
}
Expand Down

0 comments on commit 28f38bd

Please sign in to comment.