aboutsummaryrefslogtreecommitdiff
path: root/src/ui.moon
diff options
context:
space:
mode:
authorAlex Pickering <alex@cogarr.net>2026-02-01 13:14:32 -0600
committerAlexander M Pickering <alex@cogarr.net>2026-02-01 13:14:32 -0600
commit3a975db66a3711f34e8b64bb27a8eaca79fdeca9 (patch)
treefcc12f8f9d638ff575c1963796de76b7628854b4 /src/ui.moon
downloadggj26-3a975db66a3711f34e8b64bb27a8eaca79fdeca9.tar.gz
ggj26-3a975db66a3711f34e8b64bb27a8eaca79fdeca9.tar.bz2
ggj26-3a975db66a3711f34e8b64bb27a8eaca79fdeca9.zip
Initial commitHEADmaster
Diffstat (limited to 'src/ui.moon')
-rw-r--r--src/ui.moon269
1 files changed, 269 insertions, 0 deletions
diff --git a/src/ui.moon b/src/ui.moon
new file mode 100644
index 0000000..25847bd
--- /dev/null
+++ b/src/ui.moon
@@ -0,0 +1,269 @@
+hc = require("party.hc.init")
+win = require("window")
+log = require("log")
+util = require("util")
+Button = require("ui.button")
+Joystick = require("ui.joystick")
+Textbox = require("ui.textbox")
+sprites = require("sprites")
+color = require("color")
+
+ui_world = hc.new(64)
+
+am.eval_js(require("controller_bridge"))
+
+ui = {}
+ui.events = {
+ touch: {}
+ mouse: {}
+ controller: {}
+ keyboard: {}
+}
+ui.button = (x,y,width,height,text,controller_binds,font) ->
+ font = font or sprites.yataghan64
+ log.info(string.format("Creating button at (%d, %d) with size (%d, %d) and text %q",x,y,width,height,text),{"ui"})
+ controller_binds = controller_binds or {}
+ assert(x and type(x) == "number", "x must be anumber")
+ assert(y and type(y) == "number", "y must be anumber")
+ assert(width and type(width) == "number", "width must be anumber")
+ assert(height and type(height) == "number", "height must be anumber")
+ button = Button(x,y,width,height,text, font)
+ ui.node\append(button.node)
+ bounds = ui_world\rectangle(x,y,width,height)
+ ui.events.touch[bounds] = button
+ ui.events.mouse[bounds] = button
+ for bind in *controller_binds
+ ui.events.controller[bind] = button
+ button
+
+ui.click = (x,y) ->
+ ui_world\shapesAt(x,y)
+
+ui.joystick = (x,y,r,controller_binds) ->
+ controller_binds = controller_binds or {}
+ joystick = Joystick(x,y,r)
+ ui.node\append(joystick.node)
+ bounds = ui_world\circle(x,y,r)
+ ui.events.touch[bounds] = joystick
+ for bind in *controller_binds
+ ui.events.controller[bind] = joystick
+ joystick
+
+ui.text = (x,y,width,height,text) ->
+ line_height = 16
+ text = text or ""
+ rope = {
+ raw: text
+ width: 0
+ height: 0
+ lines: {} --list of line, a line is:
+ -- {
+ -- raw: string
+ -- tokens: token[]
+ -- width: number (pixels)
+ -- height: number (pixels)
+ -- }
+ -- A token is:
+ -- {
+ -- raw: string
+ -- width: number (pixels)
+ -- height: number (pixels)
+ -- }
+ }
+ current_line = {
+ raw: ""
+ tokens: {}
+ width: 0
+ height: 0
+ }
+ for word in text\gmatch("(%S+)")
+ t = am.text(sprites.yataghan32, " " .. word)
+ tps = t.width
+ if tps + current_line.width > width -- create a new line
+ rope.width = math.max(rope.width, current_line.width)
+ rope.height += line_height + current_line.height
+ if #rope.lines == 0 -- no line height for the first line
+ rope.height -= line_height
+ table.insert(rope.lines, current_line)
+ t = am.text(sprites.yataghan32, word)
+ token = {
+ raw: word
+ width: t.width
+ height: t.height
+ }
+ current_line = {
+ raw: word
+ tokens: {
+ token
+ }
+ width: t.width
+ height: t.height
+ }
+ else -- append token to this line
+ t = am.text(sprites.yataghan32, word)
+ token = {
+ raw: word
+ width: t.width
+ height: t.height
+ }
+ current_line.raw = current_line.raw .. " " .. word
+ current_line.width += tps
+ current_line.height = math.max(current_line.height, t.height)
+ table.insert(current_line.tokens, token)
+ if #current_line.tokens > 0
+ table.insert(rope.lines, current_line)
+ group = am.group!
+ y_cursor = -line_height -- no line height for first line
+ for i = 1, #rope.lines
+ y_cursor -= line_height
+ line_pos = am.translate(x, y + y_cursor)
+ line_text = am.text(sprites.yataghan32, rope.lines[i].raw,color.am_color.foreground)
+ line_pos\append(line_text)
+ group\append(line_pos)
+ y_cursor -= rope.lines[i].height
+ rope.height = -y_cursor
+ if rope.height == 0
+ rope.height = 1
+ if rope.width == 0
+ rope.width = 1
+ bounds = ui_world\rectangle(x,y,rope.width, rope.height)
+ ui.node\append(group)
+ element = {
+ node: group
+ rope: rope
+ }
+ -- No events?
+ element
+
+ui.textbox = (x,y,width,height,value,placeholder) ->
+ value = value or ""
+ placeholder = placeholder or ""
+ textbox = Textbox(x,y,width,height,value,placeholder)
+ ui.node\append(textbox.node)
+ bounds = ui_world\rectangle(x,y,width,height)
+ ui.events.mouse[bounds] = textbox
+ ui.events.keyboard[textbox] = true
+ textbox
+
+ui.delete = (element) ->
+ ui.node\remove(element.node)
+ for b,e in pairs(ui.events.mouse)
+ if e == element
+ ui.events.mouse[b] = nil
+ if ui.events.keyboard[element]
+ ui.events.keyboard[element] = nil
+ for b,e in pairs(ui.events.touch)
+ if e == element
+ ui.events.touch[b] = nil
+ for b,e in pairs(ui.events.controller)
+ if e == element
+ ui.events.controller[b] = nil
+
+ui.node = am.group!
+
+has_fire = (obj) ->
+ assert(obj.fire, obj.__class.__name .. " doesn't have a .fire method")
+
+--ui.dbg = am.translate(0,0)\append(am.circle(vec2(0,0),5,vec4(0,0,0,1)))\append(am.text("Hello, world!"))
+--ui.node\append(ui.dbg)
+
+ui.node\action(() ->
+ pos = win\mouse_position()
+ down = win\mouse_pressed("left")
+ up = win\mouse_released("left")
+ wheel = win\mouse_wheel_delta()
+ keys = win\keys_pressed()
+ am.eval_js("CONT.loop()")
+ cont_state = am.eval_js("CONT.last_state")
+ -- Debugging for mouse position:
+ --ui.dbg.position2d = pos
+ mo_tbl =
+ event: "mouse_over"
+ data: pos
+ md_tbl =
+ event: "mouse_down"
+ data: pos
+ mu_tbl =
+ event: "mouse_up"
+ data: pos
+ for collider,_ in pairs(ui_world\shapesAt(pos.x, pos.y))
+ match = ui.events.mouse[collider]
+ if match
+ has_fire(match)
+ --log.info("Found button under mouse:" .. tostring(match), {"ui","mouseover"})
+ match\fire(mo_tbl)
+ if down
+ log.info("Found button under mouse:" .. tostring(match), {"ui","mousedown"})
+ match\fire(md_tbl)
+ if up
+ log.info("Found button under mouse:" .. tostring(match), {"ui","mouseup"})
+ match\fire(mu_tbl)
+ if math.length(wheel) > 0
+ etbl =
+ event: "mouse_scroll"
+ data: wheel
+ for collider, uiobj in pairs(ui.events.mouse)
+ has_fire(uiobj)
+ uiobj\fire(etbl)
+ if #keys > 0
+ --print("Got keys:" .. tostring(keys))
+ etbl =
+ event: "keys_pressed"
+ data: keys
+ shift: win\key_down("lshift") or win\key_down("rshift")
+ for uiobj, _ in pairs(ui.events.keyboard)
+ has_fire(uiobj)
+ if uiobj\fire(etbl)
+ break -- allow any keyboard listener to "trap" the signal by returning true
+ if cont_state.on
+ for axis,value in pairs(cont_state.axes)
+ name = "axis" .. axis
+ uiobj = ui.events.controller[name]
+ if uiobj and has_fire(uiobj)
+ etbl =
+ event: "controller_axis"
+ data: value
+ uiobj\fire(etbl)
+ for button,value in pairs(cont_state.buttons)
+ name = "button" .. button
+ uiobj = ui.events.controller[name]
+ if uiobj and has_fire(uiobj)
+ etbl =
+ event: "controller_pressed"
+ data: value
+ uiobj\fire(etbl)
+
+-- in_touch_events = {
+-- "active_touch"
+-- "touches_began"
+-- }
+-- for touch in *win\active_touches!
+-- tpos = win\touch_position(touch)
+-- etbl =
+-- event: "active_touch"
+-- data: tpos
+-- for collider,_ in pairs(ui_world\shapesAt(tpos.x, tpos.y))
+-- print("Touched collider:", collider)
+-- match = ui.events.touch[collider]
+-- if match
+-- has_fire(match)
+-- match\fire(etbl)
+-- delta = win\touch_delta(touch)
+-- if math.length(delta) > 0
+-- dtbl =
+-- event: "touch_delta"
+-- data: delta
+-- for _, uiobj in pairs(ui.events.touch)
+-- has_fire(uiobj)
+-- uiobj\fire(dtbl)
+-- for touch in *win\touches_ended!
+-- etbl =
+-- event: "touches_ended"
+-- data: win\touch_position(touch)
+-- for _,uiobj in pairs(ui.events.touch)
+-- has_fire(uiobj)
+-- uiobj\fire(etbl)
+ -- todo: expand this with controller support.
+)
+
+ui