Merge remote-tracking branch 'offical/master'

This commit is contained in:
ed
2024-05-26 10:50:16 -04:00
53 changed files with 3169 additions and 3259 deletions
+137 -23
View File
@@ -29,6 +29,7 @@ import "core:sort"
import "core:strings"
import "core:strconv"
import "core:math"
import textedit "core:text/edit"
COMMAND_LIST_SIZE :: #config(MICROUI_COMMAND_LIST_SIZE, 256 * 1024)
ROOT_LIST_SIZE :: #config(MICROUI_ROOT_LIST_SIZE, 32)
@@ -51,6 +52,7 @@ Clip :: enum u32 {
Color_Type :: enum u32 {
TEXT,
SELECTION_BG,
BORDER,
WINDOW_BG,
TITLE_BG,
@@ -111,7 +113,16 @@ Key :: enum u32 {
CTRL,
ALT,
BACKSPACE,
DELETE,
RETURN,
LEFT,
RIGHT,
HOME,
END,
A,
X,
C,
V,
}
Key_Set :: distinct bit_set[Key; u32]
@@ -235,6 +246,8 @@ Context :: struct {
key_down_bits, key_pressed_bits: Key_Set,
_text_store: [MAX_TEXT_STORE]u8,
text_input: strings.Builder, // uses `_text_store` as backing store with nil_allocator.
textbox_state: textedit.State,
textbox_offset: i32,
}
Stack :: struct($T: typeid, $N: int) {
@@ -260,6 +273,7 @@ default_style := Style{
scrollbar_size = 12, thumb_size = 8,
colors = {
.TEXT = {230, 230, 230, 255},
.SELECTION_BG = {90, 90, 90, 255},
.BORDER = {25, 25, 25, 255},
.WINDOW_BG = {50, 50, 50, 255},
.TITLE_BG = {25, 25, 25, 255},
@@ -305,12 +319,16 @@ default_draw_frame :: proc(ctx: ^Context, rect: Rect, colorid: Color_Type) {
}
}
init :: proc(ctx: ^Context) {
init :: proc(ctx: ^Context, set_clipboard: proc(user_data: rawptr, text: string) -> (ok: bool), get_clipboard: proc(user_data: rawptr) -> (text: string, ok: bool), clipboard_user_data: rawptr) {
ctx^ = {} // zero memory
ctx.draw_frame = default_draw_frame
ctx._style = default_style
ctx.style = &ctx._style
ctx.text_input = strings.builder_from_bytes(ctx._text_store[:])
ctx.textbox_state.set_clipboard = set_clipboard
ctx.textbox_state.get_clipboard = get_clipboard
ctx.textbox_state.clipboard_user_data = clipboard_user_data
}
begin :: proc(ctx: ^Context) {
@@ -967,23 +985,95 @@ checkbox :: proc(ctx: ^Context, label: string, state: ^bool) -> (res: Result_Set
textbox_raw :: proc(ctx: ^Context, textbuf: []u8, textlen: ^int, id: Id, r: Rect, opt := Options{}) -> (res: Result_Set) {
update_control(ctx, id, r, opt | {.HOLD_FOCUS})
font := ctx.style.font
if ctx.focus_id == id {
/* create a builder backed by the user's buffer */
builder := strings.builder_from_bytes(textbuf)
non_zero_resize(&builder.buf, textlen^)
ctx.textbox_state.builder = &builder
if ctx.textbox_state.id != u64(id) {
ctx.textbox_state.id = u64(id)
ctx.textbox_state.selection = {}
}
/* check selection bounds */
if ctx.textbox_state.selection[0] > textlen^ || ctx.textbox_state.selection[1] > textlen^ {
ctx.textbox_state.selection = {}
}
/* handle text input */
n := min(len(textbuf) - textlen^, strings.builder_len(ctx.text_input))
if n > 0 {
copy(textbuf[textlen^:], strings.to_string(ctx.text_input)[:n])
textlen^ += n
if strings.builder_len(ctx.text_input) > 0 {
if textedit.input_text(&ctx.textbox_state, strings.to_string(ctx.text_input)) > 0 {
textlen^ = strings.builder_len(builder)
res += {.CHANGE}
}
}
/* handle ctrl+a */
if .A in ctx.key_pressed_bits && .CTRL in ctx.key_down_bits && .ALT not_in ctx.key_down_bits {
ctx.textbox_state.selection = {textlen^, 0}
}
/* handle ctrl+x */
if .X in ctx.key_pressed_bits && .CTRL in ctx.key_down_bits && .ALT not_in ctx.key_down_bits {
if textedit.cut(&ctx.textbox_state) {
textlen^ = strings.builder_len(builder)
res += {.CHANGE}
}
}
/* handle ctrl+c */
if .C in ctx.key_pressed_bits && .CTRL in ctx.key_down_bits && .ALT not_in ctx.key_down_bits {
textedit.copy(&ctx.textbox_state)
}
/* handle ctrl+v */
if .V in ctx.key_pressed_bits && .CTRL in ctx.key_down_bits && .ALT not_in ctx.key_down_bits {
if textedit.paste(&ctx.textbox_state) {
textlen^ = strings.builder_len(builder)
res += {.CHANGE}
}
}
/* handle left/right */
if .LEFT in ctx.key_pressed_bits {
move: textedit.Translation = .Word_Left if .CTRL in ctx.key_down_bits else .Left
if .SHIFT in ctx.key_down_bits {
textedit.select_to(&ctx.textbox_state, move)
} else {
textedit.move_to(&ctx.textbox_state, move)
}
}
if .RIGHT in ctx.key_pressed_bits {
move: textedit.Translation = .Word_Right if .CTRL in ctx.key_down_bits else .Right
if .SHIFT in ctx.key_down_bits {
textedit.select_to(&ctx.textbox_state, move)
} else {
textedit.move_to(&ctx.textbox_state, move)
}
}
/* handle home/end */
if .HOME in ctx.key_pressed_bits {
if .SHIFT in ctx.key_down_bits {
textedit.select_to(&ctx.textbox_state, .Start)
} else {
textedit.move_to(&ctx.textbox_state, .Start)
}
}
if .END in ctx.key_pressed_bits {
if .SHIFT in ctx.key_down_bits {
textedit.select_to(&ctx.textbox_state, .End)
} else {
textedit.move_to(&ctx.textbox_state, .End)
}
}
/* handle backspace/delete */
if .BACKSPACE in ctx.key_pressed_bits && textlen^ > 0 {
move: textedit.Translation = .Word_Left if .CTRL in ctx.key_down_bits else .Left
textedit.delete_to(&ctx.textbox_state, move)
textlen^ = strings.builder_len(builder)
res += {.CHANGE}
}
/* handle backspace */
if .BACKSPACE in ctx.key_pressed_bits && textlen^ > 0 {
/* skip utf-8 continuation bytes */
for textlen^ > 0 {
textlen^ -= 1
if textbuf[textlen^] & 0xc0 != 0x80 {
break
}
}
if .DELETE in ctx.key_pressed_bits && textlen^ > 0 {
move: textedit.Translation = .Word_Right if .CTRL in ctx.key_down_bits else .Right
textedit.delete_to(&ctx.textbox_state, move)
textlen^ = strings.builder_len(builder)
res += {.CHANGE}
}
/* handle return */
@@ -991,6 +1081,25 @@ textbox_raw :: proc(ctx: ^Context, textbuf: []u8, textlen: ^int, id: Id, r: Rect
set_focus(ctx, 0)
res += {.SUBMIT}
}
/* handle click/drag */
if .LEFT in ctx.mouse_down_bits {
idx := textlen^
for i in 0..<textlen^ {
/* skip continuation bytes */
if textbuf[i] >= 0x80 && textbuf[i] < 0xc0 {
continue
}
if ctx.mouse_pos.x < r.x + ctx.textbox_offset + ctx.text_width(font, string(textbuf[:i])) {
idx = i
break
}
}
ctx.textbox_state.selection[0] = idx
if .LEFT in ctx.mouse_pressed_bits && .SHIFT not_in ctx.key_down_bits {
ctx.textbox_state.selection[1] = idx
}
}
}
textstr := string(textbuf[:textlen^])
@@ -998,16 +1107,21 @@ textbox_raw :: proc(ctx: ^Context, textbuf: []u8, textlen: ^int, id: Id, r: Rect
/* draw */
draw_control_frame(ctx, id, r, .BASE, opt)
if ctx.focus_id == id {
color := ctx.style.colors[.TEXT]
font := ctx.style.font
textw := ctx.text_width(font, textstr)
texth := ctx.text_height(font)
ofx := r.w - ctx.style.padding - textw - 1
textx := r.x + min(ofx, ctx.style.padding)
texty := r.y + (r.h - texth) / 2
text_color := ctx.style.colors[.TEXT]
sel_color := ctx.style.colors[.SELECTION_BG]
textw := ctx.text_width(font, textstr)
texth := ctx.text_height(font)
headx := ctx.text_width(font, textstr[:ctx.textbox_state.selection[0]])
tailx := ctx.text_width(font, textstr[:ctx.textbox_state.selection[1]])
ofmin := max(ctx.style.padding - headx, r.w - textw - ctx.style.padding)
ofmax := min(r.w - headx - ctx.style.padding, ctx.style.padding)
ctx.textbox_offset = clamp(ctx.textbox_offset, ofmin, ofmax)
textx := r.x + ctx.textbox_offset
texty := r.y + (r.h - texth) / 2
push_clip_rect(ctx, r)
draw_text(ctx, font, textstr, Vec2{textx, texty}, color)
draw_rect(ctx, Rect{textx + textw, texty, 1, texth}, color)
draw_rect(ctx, Rect{textx + min(headx, tailx), texty, abs(headx - tailx), texth}, sel_color)
draw_text(ctx, font, textstr, Vec2{textx, texty}, text_color)
draw_rect(ctx, Rect{textx + headx, texty, 1, texth}, text_color)
pop_clip_rect(ctx)
} else {
draw_control_text(ctx, textstr, r, .TEXT, opt)
+45 -13
View File
@@ -13,7 +13,8 @@ function stripNewline(str) {
return str.replace(/\n/, ' ')
}
const STRING_SIZE = 2*4;
const INT_SIZE = 4; // NOTE: set to `8` if the target has 64 bit ints (`wasm64p32` for example).
const STRING_SIZE = 2*INT_SIZE;
class WasmMemoryInterface {
constructor() {
@@ -69,19 +70,34 @@ class WasmMemoryInterface {
const hi = this.mem.getInt32 (addr + 4, true);
return lo + hi*4294967296;
};
loadF32(addr) { return this.mem.getFloat32(addr, true); }
loadF64(addr) { return this.mem.getFloat64(addr, true); }
loadInt(addr) { return this.mem.getInt32 (addr, true); }
loadUint(addr) { return this.mem.getUint32 (addr, true); }
loadPtr(addr) { return this.loadUint(addr); }
loadF32(addr) { return this.mem.getFloat32(addr, true); }
loadF64(addr) { return this.mem.getFloat64(addr, true); }
loadInt(addr) {
if (INT_SIZE == 8) {
return this.loadI64(addr);
} else if (INT_SIZE == 4) {
return this.loadI32(addr);
} else {
throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
}
};
loadUint(addr) {
if (INT_SIZE == 8) {
return this.loadU64(addr);
} else if (INT_SIZE == 4) {
return this.loadU32(addr);
} else {
throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
}
};
loadPtr(addr) { return this.loadU32(addr); }
loadBytes(ptr, len) {
return new Uint8Array(this.memory.buffer, ptr, len);
return new Uint8Array(this.memory.buffer, ptr, Number(len));
}
loadString(ptr, len) {
const bytes = this.loadBytes(ptr, len);
const bytes = this.loadBytes(ptr, Number(len));
return new TextDecoder().decode(bytes);
}
@@ -99,10 +115,26 @@ class WasmMemoryInterface {
this.mem.setUint32(addr + 0, value, true);
this.mem.setInt32 (addr + 4, Math.floor(value / 4294967296), true);
}
storeF32(addr, value) { this.mem.setFloat32(addr, value, true); }
storeF64(addr, value) { this.mem.setFloat64(addr, value, true); }
storeInt(addr, value) { this.mem.setInt32 (addr, value, true); }
storeUint(addr, value) { this.mem.setUint32 (addr, value, true); }
storeF32(addr, value) { this.mem.setFloat32(addr, value, true); }
storeF64(addr, value) { this.mem.setFloat64(addr, value, true); }
storeInt(addr, value) {
if (INT_SIZE == 8) {
this.storeI64(addr, value);
} else if (INT_SIZE == 4) {
this.storeI32(addr, value);
} else {
throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
}
}
storeUint(addr, value) {
if (INT_SIZE == 8) {
this.storeU64(addr, value);
} else if (INT_SIZE == 4) {
this.storeU32(addr, value);
} else {
throw new Error('Unhandled `INT_SIZE`, expected `4` or `8`');
}
}
// Returned length might not be the same as `value.length` if non-ascii strings are given.
storeString(addr, value) {