add layout

This commit is contained in:
2025-06-23 22:58:25 -05:00
parent dca3ede464
commit 8ac78e0be6
8 changed files with 1449 additions and 19 deletions

View File

@@ -271,7 +271,7 @@ src += [
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c', 'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c', 'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c', 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'qjs_num.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c' 'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'qjs_num.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c', 'qjs_layout.c'
] ]
# quirc src # quirc src
src += [ src += [

View File

@@ -8,6 +8,22 @@ var graphics = use('graphics')
var util = use('util') var util = use('util')
var input = use('input') var input = use('input')
function normalizeSpacing(spacing) {
if (typeof spacing == 'number') {
return {l: spacing, r: spacing, t: spacing, b: spacing}
} else if (Array.isArray(spacing)) {
if (spacing.length == 2) {
return {l: spacing[0], r: spacing[0], t: spacing[1], b: spacing[1]}
} else if (spacing.length == 4) {
return {l: spacing[0], r: spacing[1], t: spacing[2], b: spacing[3]}
}
} else if (typeof spacing == 'object') {
return {l: spacing.l || 0, r: spacing.r || 0, t: spacing.t || 0, b: spacing.b || 0}
} else {
return {l:0, r:0, t:0, b:0}
}
}
var lay_ctx = layout.make_context(); var lay_ctx = layout.make_context();
var clay_base = { var clay_base = {
@@ -55,7 +71,7 @@ clay.draw = function draw(size, fn, config = {})
box.content = lay_ctx.get_rect(box.id); box.content = lay_ctx.get_rect(box.id);
box.boundingbox = Object.assign({}, box.content); box.boundingbox = Object.assign({}, box.content);
var padding = util.normalizeSpacing(box.config.padding || 0); var padding = normalizeSpacing(box.config.padding || 0);
if (padding.l || padding.r || padding.t || padding.b) { if (padding.l || padding.r || padding.t || padding.b) {
// Adjust the boundingbox to include the padding // Adjust the boundingbox to include the padding
box.boundingbox.x -= padding.l; box.boundingbox.x -= padding.l;
@@ -64,7 +80,7 @@ clay.draw = function draw(size, fn, config = {})
box.boundingbox.height += padding.t + padding.b; box.boundingbox.height += padding.t + padding.b;
} }
box.marginbox = Object.assign({}, box.content); box.marginbox = Object.assign({}, box.content);
var margin = util.normalizeSpacing(box.config.margin || 0); var margin = normalizeSpacing(box.config.margin || 0);
box.marginbox.x -= margin.l; box.marginbox.x -= margin.l;
box.marginbox.y -= margin.t; box.marginbox.y -= margin.t;
box.marginbox.width += margin.l+margin.r; box.marginbox.width += margin.l+margin.r;
@@ -118,11 +134,12 @@ function image_size(img)
function add_item(config) function add_item(config)
{ {
// Normalize the child's margin // Normalize the child's margin
var margin = util.normalizeSpacing(config.margin || 0); var margin = normalizeSpacing(config.margin || 0);
var padding = util.normalizeSpacing(config.padding || 0); var padding = normalizeSpacing(config.padding || 0);
var childGap = root_config.child_gap || 0; var childGap = root_config.child_gap || 0;
// Adjust for child_gap // Adjust for child_gap
root_config._childIndex ??= 0
if (root_config._childIndex > 0) { if (root_config._childIndex > 0) {
var parentContain = root_config.contain || 0; var parentContain = root_config.contain || 0;
var isVStack = (parentContain & layout.contain.column) != 0; var isVStack = (parentContain & layout.contain.column) != 0;
@@ -187,8 +204,8 @@ clay.text = function text(str, ...configs)
{ {
var config = rectify_configs(configs); var config = rectify_configs(configs);
config.size ??= [0,0]; config.size ??= [0,0];
config.font = graphics.get_font(config.font) // config.font = graphics.get_font(config.font)
var tsize = config.font.text_size(str, 0, 0, config.size.x); var tsize = 12; //config.font.text_size(str, 0, 0, config.size.x);
config.size = config.size.map((x,i) => Math.max(x, tsize[i])); config.size = config.size.map((x,i) => Math.max(x, tsize[i]));
config.text = str; config.text = str;
add_item(config); add_item(config);
@@ -209,8 +226,8 @@ var button_base = Object.assign(Object.create(clay_base), {
clay.button = function button(str, action, config = {}) clay.button = function button(str, action, config = {})
{ {
config.__proto__ = button_base; config.__proto__ = button_base;
config.font = graphics.get_font(config.font) // config.font = graphics.get_font(config.font)
config.size = config.font.text_size(str) config.size = 12;//config.font.text_size(str)
add_item(config); add_item(config);
config.text = str; config.text = str;
config.action = action; config.action = action;

View File

@@ -16,7 +16,7 @@ var game = args[0]
var video var video
var cnf = use('accio/config') //var cnf = use('accio/config')
$_.start(e => { $_.start(e => {
if (e.type != 'greet') return if (e.type != 'greet') return
@@ -30,7 +30,7 @@ $_.start(e => {
start_pipeline() start_pipeline()
}, args[0], $_) }, args[0], $_)
}) })
}, 'prosperon/sdl_video', cnf) }, 'prosperon/sdl_video', {})
var geometry = use('geometry') var geometry = use('geometry')

View File

@@ -1546,6 +1546,7 @@ JSC_CCALL(os_value_id,
#include "qjs_wota.h" #include "qjs_wota.h"
#include "qjs_socket.h" #include "qjs_socket.h"
#include "qjs_nota.h" #include "qjs_nota.h"
#include "qjs_layout.h"
//JSValue js_imgui_use(JSContext *js); //JSValue js_imgui_use(JSContext *js);
#define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use} #define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use}
@@ -1581,7 +1582,7 @@ void ffi_load(JSContext *js)
arrput(rt->module_registry, MISTLINE(text)); arrput(rt->module_registry, MISTLINE(text));
arrput(rt->module_registry, MISTLINE(wota)); arrput(rt->module_registry, MISTLINE(wota));
arrput(rt->module_registry, MISTLINE(nota)); arrput(rt->module_registry, MISTLINE(nota));
// power user // power user
arrput(rt->module_registry, MISTLINE(js)); arrput(rt->module_registry, MISTLINE(js));
arrput(rt->module_registry, MISTLINE(debug)); arrput(rt->module_registry, MISTLINE(debug));
@@ -1598,6 +1599,7 @@ void ffi_load(JSContext *js)
arrput(rt->module_registry, MISTLINE(graphics)); arrput(rt->module_registry, MISTLINE(graphics));
arrput(rt->module_registry, MISTLINE(video)); arrput(rt->module_registry, MISTLINE(video));
arrput(rt->module_registry, MISTLINE(soloud)); arrput(rt->module_registry, MISTLINE(soloud));
arrput(rt->module_registry, MISTLINE(layout));
// arrput(rt->module_registry, MISTLINE(imgui)); // arrput(rt->module_registry, MISTLINE(imgui));
arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use})); arrput(rt->module_registry, ((ModuleEntry){"camera", js_camera_use}));

1196
source/layout.h Normal file

File diff suppressed because it is too large Load Diff

213
source/qjs_layout.c Normal file
View File

@@ -0,0 +1,213 @@
#include "quickjs.h"
#include <stdint.h>
#include <math.h>
#define LAY_FLOAT 1
#define LAY_IMPLEMENTATION
#include "layout.h"
static JSClassID js_layout_class_id;
#define GETLAY \
lay_context *lay = JS_GetOpaque2(js, self, js_layout_class_id); \
if (!lay) return JS_EXCEPTION;
#define GETITEM(VAR, ARG) \
lay_id VAR; \
if (JS_ToUint32(js, &VAR, ARG)) return JS_EXCEPTION; \
static JSValue js_layout_context_new(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
lay_context *lay = js_malloc(js, sizeof(*lay));
lay_init_context(lay);
JSValue obj = JS_NewObjectClass(js, js_layout_class_id);
JS_SetOpaque(obj, lay);
return obj;
}
static JSValue js_layout_item(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
GETLAY
lay_id item_id = lay_item(lay);
return JS_NewUint32(js,item_id);
}
static JSValue js_layout_set_size(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
{
if (argc < 2) return JS_EXCEPTION;
GETLAY
GETITEM(id, argv[0])
double size[2];
JSValue width_val = JS_GetPropertyUint32(js, argv[1], 0);
JSValue height_val = JS_GetPropertyUint32(js, argv[1], 1);
JS_ToFloat64(js, &size[0], width_val);
JS_ToFloat64(js, &size[1], height_val);
JS_FreeValue(js, width_val);
JS_FreeValue(js, height_val);
if (isnan(size[0])) size[0] = 0;
if (isnan(size[1])) size[1] = 0;
lay_set_size_xy(lay, id, size[0], size[1]);
return JS_NULL;
}
static JSValue js_layout_set_behave(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
{
GETLAY
GETITEM(id, argv[0])
uint32_t behave_flags;
JS_ToUint32(js, &behave_flags, argv[1]);
lay_set_behave(lay, id, behave_flags);
return JS_NULL;
}
static JSValue js_layout_set_contain(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
{
GETLAY
GETITEM(id, argv[0])
uint32_t contain_flags;
JS_ToUint32(js, &contain_flags, argv[1]);
lay_set_contain(lay, id, contain_flags);
return JS_NULL;
}
#define PULLMARGIN(DIR,MARGIN) \
JSValue js_##DIR = JS_GetPropertyStr(js, argv[1], #DIR); \
if (!JS_IsNull(js_##DIR)) { \
JS_ToFloat64(js, &margins[MARGIN], js_##DIR); \
if (isnan(margins[MARGIN])) margins[MARGIN] = 0; \
JS_FreeValue(js, js_##DIR); \
}\
static JSValue js_layout_set_margins(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
{
GETLAY
GETITEM(id, argv[0])
double margins[4] = {0};
PULLMARGIN(l,0)
PULLMARGIN(r,2)
PULLMARGIN(t,1)
PULLMARGIN(b,3)
lay_set_margins_ltrb(lay, id, margins[0], margins[1], margins[2], margins[3]);
return JS_NULL;
}
static JSValue js_layout_insert(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
GETLAY
GETITEM(parent, argv[0])
GETITEM(child, argv[1])
lay_insert(lay, parent, child);
return JS_NULL;
}
static JSValue js_layout_append(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
GETLAY
GETITEM(earlier, argv[0])
GETITEM(later, argv[1])
lay_append(lay, earlier, later);
return JS_NULL;
}
static JSValue js_layout_push(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
GETLAY
GETITEM(parent, argv[0])
GETITEM(child, argv[1])
lay_push(lay, parent, child);
return JS_NULL;
}
static JSValue js_layout_run(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
GETLAY
lay_run_context(lay);
return JS_NULL;
}
static JSValue js_layout_get_rect(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
GETLAY
GETITEM(id, argv[0])
lay_vec4 rect = lay_get_rect(lay, id);
JSValue ret = JS_NewObject(js);
JS_SetPropertyStr(js, ret, "x", JS_NewFloat64(js, rect[0]));
JS_SetPropertyStr(js, ret, "y", JS_NewFloat64(js, rect[1]));
JS_SetPropertyStr(js, ret, "width", JS_NewFloat64(js, rect[2]));
JS_SetPropertyStr(js, ret, "height", JS_NewFloat64(js, rect[3]));
return ret;
}
static JSValue js_layout_reset(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
GETLAY
lay_reset_context(lay);
return JS_NULL;
}
static void js_layout_finalizer(JSRuntime *rt, JSValue val) {
lay_context *ctx = JS_GetOpaque(val, js_layout_class_id);
lay_destroy_context(ctx);
js_free_rt(rt, ctx);
}
static JSClassDef js_layout_class = {
"layout_context",
.finalizer = js_layout_finalizer,
};
static const JSCFunctionListEntry js_layout_proto_funcs[] = {
JS_CFUNC_DEF("item", 1, js_layout_item),
JS_CFUNC_DEF("insert", 2, js_layout_insert),
JS_CFUNC_DEF("append", 2, js_layout_append),
JS_CFUNC_DEF("push", 2, js_layout_push),
JS_CFUNC_DEF("run", 0, js_layout_run),
JS_CFUNC_DEF("get_rect", 1, js_layout_get_rect),
JS_CFUNC_DEF("reset", 0, js_layout_reset),
JS_CFUNC_DEF("set_margins", 2, js_layout_set_margins),
JS_CFUNC_DEF("set_size", 2, js_layout_set_size),
JS_CFUNC_DEF("set_contain", 2, js_layout_set_contain),
JS_CFUNC_DEF("set_behave", 2, js_layout_set_behave),
};
static const JSCFunctionListEntry js_layout_funcs[] = {
JS_CFUNC_DEF("make_context", 0, js_layout_context_new)
};
JSValue js_layout_use(JSContext *js)
{
JS_NewClassID(&js_layout_class_id);
JS_NewClass(JS_GetRuntime(js), js_layout_class_id, &js_layout_class);
JSValue proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, proto, js_layout_proto_funcs, sizeof(js_layout_proto_funcs) / sizeof(JSCFunctionListEntry));
JS_SetClassProto(js, js_layout_class_id, proto);
JSValue contain = JS_NewObject(js);
JSValue behave = JS_NewObject(js);
JS_SetPropertyStr(js, contain, "row", JS_NewUint32(js, LAY_ROW));
JS_SetPropertyStr(js, contain, "column", JS_NewUint32(js, LAY_COLUMN));
JS_SetPropertyStr(js, contain, "layout", JS_NewUint32(js, LAY_LAYOUT));
JS_SetPropertyStr(js, contain, "flex", JS_NewUint32(js, LAY_FLEX));
JS_SetPropertyStr(js, contain, "wrap", JS_NewUint32(js, LAY_WRAP));
JS_SetPropertyStr(js, contain, "nowrap", JS_NewUint32(js, LAY_NOWRAP));
JS_SetPropertyStr(js, contain, "start", JS_NewUint32(js, LAY_START));
JS_SetPropertyStr(js, contain, "middle", JS_NewUint32(js, LAY_MIDDLE));
JS_SetPropertyStr(js, contain, "end", JS_NewUint32(js, LAY_END));
JS_SetPropertyStr(js, contain, "justify", JS_NewUint32(js, LAY_JUSTIFY));
JS_SetPropertyStr(js, behave, "left", JS_NewUint32(js, LAY_LEFT));
JS_SetPropertyStr(js, behave, "top", JS_NewUint32(js, LAY_TOP));
JS_SetPropertyStr(js, behave, "right", JS_NewUint32(js, LAY_RIGHT));
JS_SetPropertyStr(js, behave, "bottom", JS_NewUint32(js, LAY_BOTTOM));
JS_SetPropertyStr(js, behave, "hfill", JS_NewUint32(js, LAY_HFILL));
JS_SetPropertyStr(js, behave, "vfill", JS_NewUint32(js, LAY_VFILL));
JS_SetPropertyStr(js, behave, "hcenter", JS_NewUint32(js, LAY_HCENTER));
JS_SetPropertyStr(js, behave, "vcenter", JS_NewUint32(js, LAY_VCENTER));
JS_SetPropertyStr(js, behave, "center", JS_NewUint32(js, LAY_CENTER));
JS_SetPropertyStr(js, behave, "fill", JS_NewUint32(js, LAY_FILL));
JS_SetPropertyStr(js, behave, "break", JS_NewUint32(js, LAY_BREAK));
JSValue export = JS_NewObject(js);
JS_SetPropertyFunctionList(js, export, js_layout_funcs, sizeof(js_layout_funcs)/sizeof(JSCFunctionListEntry));
JS_SetPropertyStr(js, export, "behave", behave);
JS_SetPropertyStr(js, export, "contain", contain);
return export;
}

8
source/qjs_layout.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef QJS_LAYOUT_H
#define QJS_LAYOUT_H
#include "cell.h"
JSValue js_layout_use(JSContext *js);
#endif

View File

@@ -90,7 +90,7 @@
32: dump line number table 32: dump line number table
64: dump compute_stack_size 64: dump compute_stack_size
*/ */
#define DUMP_BYTECODE (1) //#define DUMP_BYTECODE (1)
/* dump the occurence of the automatic GC */ /* dump the occurence of the automatic GC */
//#define DUMP_GC //#define DUMP_GC
/* dump objects freed by the garbage collector */ /* dump objects freed by the garbage collector */
@@ -9702,12 +9702,6 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
static BOOL is_safe_integer(double d)
{
return isfinite(d) && floor(d) == d &&
fabs(d) <= (double)MAX_SAFE_INTEGER;
}
int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
{ {
int64_t v; int64_t v;