fix clay
This commit is contained in:
139
clay.cm
139
clay.cm
@@ -1,17 +1,21 @@
|
|||||||
// clay2.cm - Revised UI layout engine emitting flat drawables
|
// clay.cm - UI layout engine emitting flat drawables with annotated tree
|
||||||
//
|
//
|
||||||
// Changes from clay.cm:
|
// - Uses meme/merge for config chains
|
||||||
// - No __proto__, uses meme/merge
|
|
||||||
// - Emits flat list of drawables for film2d
|
// - Emits flat list of drawables for film2d
|
||||||
// - Supports scissor clipping
|
// - Supports scissor clipping
|
||||||
//
|
// - Returns annotated tree root with .drawables for clay_input compatibility
|
||||||
// Now returns [drawable, drawable, ...] instead of {type:'group', ...}
|
|
||||||
|
|
||||||
var layout = use('layout')
|
var layout = use('layout')
|
||||||
var graphics = use('graphics')
|
var graphics = use('graphics')
|
||||||
|
|
||||||
var clay = {}
|
var clay = {}
|
||||||
|
|
||||||
|
// Unique key objects for tree traversal (used by clay_input)
|
||||||
|
var CHILDREN = {}
|
||||||
|
var PARENT = {}
|
||||||
|
clay.CHILDREN = CHILDREN
|
||||||
|
clay.PARENT = PARENT
|
||||||
|
|
||||||
// Layout context
|
// Layout context
|
||||||
var lay_ctx = layout.make_context()
|
var lay_ctx = layout.make_context()
|
||||||
|
|
||||||
@@ -65,9 +69,26 @@ var config_stack = []
|
|||||||
|
|
||||||
// Rewriting state management for cleaner recursion
|
// Rewriting state management for cleaner recursion
|
||||||
var tree_stack = []
|
var tree_stack = []
|
||||||
|
var _next_id = 0
|
||||||
|
|
||||||
|
function annotate_tree(node, root_height, parent_node) {
|
||||||
|
var rect = lay_ctx.get_rect(node.id)
|
||||||
|
node.boundingbox = {
|
||||||
|
x: rect.x,
|
||||||
|
y: root_height - (rect.y + rect.height),
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height
|
||||||
|
}
|
||||||
|
node[CHILDREN] = node.children
|
||||||
|
node[PARENT] = parent_node
|
||||||
|
arrfor(node.children, function(child) {
|
||||||
|
annotate_tree(child, root_height, node)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
clay.layout = function(fn, size) {
|
clay.layout = function(fn, size) {
|
||||||
lay_ctx.reset()
|
lay_ctx.reset()
|
||||||
|
_next_id = 0
|
||||||
var root_id = lay_ctx.item()
|
var root_id = lay_ctx.item()
|
||||||
|
|
||||||
lay_ctx.set_size(root_id, size)
|
lay_ctx.set_size(root_id, size)
|
||||||
@@ -85,8 +106,12 @@ clay.layout = function(fn, size) {
|
|||||||
|
|
||||||
lay_ctx.run()
|
lay_ctx.run()
|
||||||
|
|
||||||
// Post-layout: build flat drawable list
|
// Annotate tree for clay_input (boundingbox, CHILDREN, PARENT)
|
||||||
return build_drawables(root_node, size.height)
|
annotate_tree(root_node, size.height, null)
|
||||||
|
|
||||||
|
// Build flat drawable list and attach to tree root
|
||||||
|
root_node.drawables = build_drawables(root_node, size.height)
|
||||||
|
return root_node
|
||||||
}
|
}
|
||||||
|
|
||||||
function build_drawables(node, root_height, parent, parent_scissor) {
|
function build_drawables(node, root_height, parent, parent_scissor) {
|
||||||
@@ -130,8 +155,10 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
|||||||
|
|
||||||
// Background
|
// Background
|
||||||
if (node.config.background_image) {
|
if (node.config.background_image) {
|
||||||
|
_next_id = _next_id + 1
|
||||||
if (node.config.slice) {
|
if (node.config.slice) {
|
||||||
push(drawables, {
|
push(drawables, {
|
||||||
|
_id: _next_id,
|
||||||
type: 'sprite',
|
type: 'sprite',
|
||||||
image: node.config.background_image,
|
image: node.config.background_image,
|
||||||
pos: {x: vis_x, y: vis_y},
|
pos: {x: vis_x, y: vis_y},
|
||||||
@@ -139,11 +166,12 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
|||||||
height: rect.height,
|
height: rect.height,
|
||||||
slice: node.config.slice,
|
slice: node.config.slice,
|
||||||
color: node.config.background_color || {r:1, g:1, b:1, a:1},
|
color: node.config.background_color || {r:1, g:1, b:1, a:1},
|
||||||
layer: p_layer - 0.1, // slightly behind content
|
layer: p_layer - 0.1,
|
||||||
scissor: current_scissor
|
scissor: current_scissor
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
push(drawables, {
|
push(drawables, {
|
||||||
|
_id: _next_id,
|
||||||
type: 'sprite',
|
type: 'sprite',
|
||||||
image: node.config.background_image,
|
image: node.config.background_image,
|
||||||
pos: {x: vis_x, y: vis_y},
|
pos: {x: vis_x, y: vis_y},
|
||||||
@@ -155,7 +183,9 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (node.config.background_color) {
|
} else if (node.config.background_color) {
|
||||||
|
_next_id = _next_id + 1
|
||||||
push(drawables, {
|
push(drawables, {
|
||||||
|
_id: _next_id,
|
||||||
type: 'rect',
|
type: 'rect',
|
||||||
pos: {x: vis_x, y: vis_y},
|
pos: {x: vis_x, y: vis_y},
|
||||||
width: rect.width,
|
width: rect.width,
|
||||||
@@ -168,7 +198,9 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
|||||||
|
|
||||||
// Content (Image/Text)
|
// Content (Image/Text)
|
||||||
if (node.config.image) {
|
if (node.config.image) {
|
||||||
|
_next_id = _next_id + 1
|
||||||
push(drawables, {
|
push(drawables, {
|
||||||
|
_id: _next_id,
|
||||||
type: 'sprite',
|
type: 'sprite',
|
||||||
image: node.config.image,
|
image: node.config.image,
|
||||||
pos: {x: vis_x, y: vis_y},
|
pos: {x: vis_x, y: vis_y},
|
||||||
@@ -181,22 +213,16 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.config.text) {
|
if (node.config.text) {
|
||||||
|
_next_id = _next_id + 1
|
||||||
push(drawables, {
|
push(drawables, {
|
||||||
|
_id: _next_id,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
text: node.config.text,
|
text: node.config.text,
|
||||||
font: node.config.font_path,
|
font: node.config.font_path,
|
||||||
size: node.config.font_size,
|
size: node.config.font_size,
|
||||||
color: node.config.color,
|
color: node.config.color,
|
||||||
pos: {x: vis_x, y: vis_y + rect.height}, // Baseline adjustment
|
pos: {x: vis_x, y: vis_y + rect.height},
|
||||||
anchor_y: 1.0, // Text usually draws from baseline up or top down?
|
anchor_y: 1.0,
|
||||||
// film2d text uses top-left by default unless anchor set.
|
|
||||||
// Original clay put it at `y + rect.height`.
|
|
||||||
// Let's assume origin top-left, so we might need anchor adjustment or just position.
|
|
||||||
// If frame is top-down (0 at top), `abs_y` is top.
|
|
||||||
// `rect.y` in layout is bottom-up? "rect.y is from bottom" says original comment.
|
|
||||||
// `abs_y = root_height - (rect.y + rect.height)` -> Top edge of element.
|
|
||||||
// Text usually wants baseline.
|
|
||||||
// If we put it at `vis_y + rect.height`, that's bottom of element.
|
|
||||||
layer: p_layer,
|
layer: p_layer,
|
||||||
scissor: current_scissor
|
scissor: current_scissor
|
||||||
})
|
})
|
||||||
@@ -216,6 +242,20 @@ function build_drawables(node, root_height, parent, parent_scissor) {
|
|||||||
function process_configs(configs) {
|
function process_configs(configs) {
|
||||||
var cfg = meme(base_config, configs)
|
var cfg = meme(base_config, configs)
|
||||||
|
|
||||||
|
// Parse shorthand font string (e.g. 'blackcastle.64') into font_path and font_size
|
||||||
|
var font_parts = null
|
||||||
|
var parsed_size = null
|
||||||
|
if (cfg.font && is_text(cfg.font)) {
|
||||||
|
font_parts = array(cfg.font, '.')
|
||||||
|
if (length(font_parts) >= 2) {
|
||||||
|
parsed_size = number(font_parts[length(font_parts) - 1])
|
||||||
|
if (parsed_size) {
|
||||||
|
cfg.font_size = parsed_size
|
||||||
|
cfg.font_path = font_parts[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cfg.color = normalize_color(cfg.color, base_config.color)
|
cfg.color = normalize_color(cfg.color, base_config.color)
|
||||||
if (cfg.background_color) cfg.background_color = normalize_color(cfg.background_color, {r:1,g:1,b:1,a:1})
|
if (cfg.background_color) cfg.background_color = normalize_color(cfg.background_color, {r:1,g:1,b:1,a:1})
|
||||||
|
|
||||||
@@ -305,34 +345,34 @@ clay.zstack = function(configs, fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Leaf nodes
|
// Leaf nodes
|
||||||
clay.image = function(path, configs) {
|
clay.image = function(path, configs, extra) {
|
||||||
var img = graphics.texture(path)
|
var img = graphics.texture(path)
|
||||||
var c = [{image: path}]
|
var c = [{image: path}]
|
||||||
var final_config = process_configs(configs)
|
var _configs = configs ? (is_array(configs) ? configs : [configs]) : []
|
||||||
|
if (extra) _configs = array(_configs, is_array(extra) ? extra : [extra])
|
||||||
|
var final_config = process_configs(_configs)
|
||||||
if (!final_config.size && !final_config.behave)
|
if (!final_config.size && !final_config.behave)
|
||||||
c.size = {width: img.width, height: img.height}
|
push(c, {size: {width: img.width, height: img.height}})
|
||||||
|
|
||||||
var _configs = is_array(configs) ? configs : [configs]
|
|
||||||
|
|
||||||
push_node(array(c, _configs), null)
|
push_node(array(c, _configs), null)
|
||||||
pop_node()
|
pop_node()
|
||||||
}
|
}
|
||||||
|
|
||||||
clay.text = function(str, configs) {
|
clay.text = function(str, configs, extra) {
|
||||||
var c = [{text: str}]
|
var c = [{text: str}]
|
||||||
var final_config = process_configs(configs)
|
var _configs = configs ? (is_array(configs) ? configs : [configs]) : []
|
||||||
|
if (extra) _configs = array(_configs, is_array(extra) ? extra : [extra])
|
||||||
|
var final_config = process_configs(_configs)
|
||||||
if (!final_config.size && !final_config.behave) {
|
if (!final_config.size && !final_config.behave) {
|
||||||
c.size = {width: 100, height: 20}
|
push(c, {size: {width: 100, height: 20}})
|
||||||
}
|
}
|
||||||
|
|
||||||
var _configs = is_array(configs) ? configs : [configs]
|
|
||||||
|
|
||||||
push_node(array(c, _configs), null)
|
push_node(array(c, _configs), null)
|
||||||
pop_node()
|
pop_node()
|
||||||
}
|
}
|
||||||
|
|
||||||
clay.rectangle = function(configs) {
|
clay.rectangle = function(configs) {
|
||||||
var _configs = is_array(configs) ? configs : [configs]
|
var _configs = configs ? (is_array(configs) ? configs : [configs]) : [{}]
|
||||||
push_node(_configs, null)
|
push_node(_configs, null)
|
||||||
pop_node()
|
pop_node()
|
||||||
}
|
}
|
||||||
@@ -350,6 +390,47 @@ clay.button = function(str, action, configs) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spacer — fills available space
|
||||||
|
clay.spacer = function(config) {
|
||||||
|
var cfg = config || {}
|
||||||
|
if (!cfg.behave) cfg.behave = layout.behave.hfill | layout.behave.vfill
|
||||||
|
push_node([cfg], null)
|
||||||
|
pop_node()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience draw wrapper — auto-detects call patterns:
|
||||||
|
// clay.draw(fn) — default 640x360
|
||||||
|
// clay.draw(fn, size_obj) — fn first, size object second
|
||||||
|
// clay.draw(size_array, fn) — size array first, fn second
|
||||||
|
clay.draw = function(arg1, arg2) {
|
||||||
|
var fn = null
|
||||||
|
var size = {width: 640, height: 360}
|
||||||
|
if (is_function(arg1)) {
|
||||||
|
fn = arg1
|
||||||
|
if (arg2) {
|
||||||
|
if (is_array(arg2)) size = {width: arg2[0], height: arg2[1]}
|
||||||
|
else if (is_object(arg2)) size = arg2
|
||||||
|
}
|
||||||
|
} else if (is_array(arg1)) {
|
||||||
|
size = {width: arg1[0], height: arg1[1]}
|
||||||
|
fn = arg2
|
||||||
|
}
|
||||||
|
return clay.layout(fn, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset all drawables in an array by a position
|
||||||
|
clay.offset_drawables = function(drawables, offset) {
|
||||||
|
var result = []
|
||||||
|
var i = 0
|
||||||
|
var d = null
|
||||||
|
for (i = 0; i < length(drawables); i = i + 1) {
|
||||||
|
d = meme(drawables[i])
|
||||||
|
d.pos = {x: d.pos.x + offset.x, y: d.pos.y + offset.y}
|
||||||
|
push(result, d)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
clay.behave = layout.behave
|
clay.behave = layout.behave
|
||||||
clay.contain = layout.contain
|
clay.contain = layout.contain
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ function find_path(node, path, pos) {
|
|||||||
if (!pointer_enabled(node)) return null
|
if (!pointer_enabled(node)) return null
|
||||||
if (!rect_contains(node, pos)) return null
|
if (!rect_contains(node, pos)) return null
|
||||||
|
|
||||||
var next_path = array(path, node)
|
var next_path = array(path)
|
||||||
|
next_path[] = node
|
||||||
var i = 0
|
var i = 0
|
||||||
var child = null
|
var child = null
|
||||||
var child_path = null
|
var child_path = null
|
||||||
|
|||||||
3
core.cm
3
core.cm
@@ -78,6 +78,9 @@ core.start = function(config) {
|
|||||||
// Start main loop
|
// Start main loop
|
||||||
_main_loop()
|
_main_loop()
|
||||||
|
|
||||||
|
// Call start callback after the first frame and main loop are established
|
||||||
|
if (config.start) config.start()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1824,8 +1824,8 @@ function _render_text(ctx, drawable) {
|
|||||||
if (ax != 0 || ay != 0) {
|
if (ax != 0 || ay != 0) {
|
||||||
dim = font.text_size(drawable.text)
|
dim = font.text_size(drawable.text)
|
||||||
if (dim) {
|
if (dim) {
|
||||||
text_pos.x -= dim.x * ax
|
text_pos.x -= dim[0] * ax
|
||||||
text_pos.y -= dim.y * ay
|
text_pos.y -= dim[1] * ay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user