Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Created October 8, 2012 23:55
Show Gist options
  • Save fowlmouth/3855702 to your computer and use it in GitHub Desktop.
Save fowlmouth/3855702 to your computer and use it in GitHub Desktop.
lua repl thinger
import lua, lualib, lauxlib, sfml, sfml_colors, strutils, sf_gui, json
const
ScreenW = 800
ScreenH = 600
var
window = new_render_window(video_mode(ScreenW, ScreenH, 32), "Lame Lua", sfDefaultStyle)
event: TEvent
text = newText("herro", guifont, 18)
gui = newContainer("luathinger_settings.json")
txtarea = PTextArea(gui["input"])
clock = newClock()
cursor_sprite = txtarea.cursor_sprite.copy()
luaOutput = PTextArea(gui["lua-output"])
proc last*[A](s: var seq[A]): A {.inline.} =
return s[high(s)]
cursor_sprite.set_outline_color Green
gui.set_pack_padding vec2i(4, 4)
gui.set_position(vec2f(0, 16 * txtarea.height))
gui.newButton("test 1", onClick = proc(b: PButton) =
b.setString("CLICKED!"))
gui.newButton("test2", onclick = proc(b: PButton) =
b.setString("zomg"))
var
dbg1 = gui.newButton("##")
dbg2 = gui.newButton("##")
luaError = gui.newButtonStyled(color = Red)
mousePos: TVector2i
block:
var b1 = gui.newButton("##")
b1.updateProc = proc(b: PButton; dt: float) =
b.text.setString("@ "& $txtarea.cursor)
dbg1.updateProc = proc(b: PButton; dt: float) =
b.set_string($mousePos)
dbg2.updateProc = proc(b: PButton; dt: float) =
b.set_string($txtarea.basepos(mouse_pos))
var
notif = gui.newNotifyArea(vec2f(0, ScreenH - 16))
proc postinc(i: var int, by = 1): int =
result = i
inc(i, by)
proc stackdump(L: PState) =
var top = L.get_top
echo "top is ", top
var res = ""
for i in 1..top:
case luatype(L, i)
of TSTRING:
res.add($tostring(L, i))
of TBOOLEAN:
res.add($toboolean(L, i))
of lua.TNUMBER:
res.add($tonumber(L, i))
else:
res.add($typename(L, i))
res.add(' ')
echo res
{.pragma: lua, cdecl.}
proc lua_print(L: PState): int {.lua.} =
let num_args = L.get_top
var res = ""
L.get_global "tostring"
for i in 1..num_args:
L.push_value(-1)
L.push_value(i)
L.call 1, 1
var s = L.tostring(-1)
if s == nil:
stackdump(L)
break
res.add($s)
if i < num_args:
res.add " "
L.pop 1
luaOutput.add_output(res)
stackdump(L)
return 0
discard """
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
const char *s;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tostring(L, -1); /* get result */
if (s == NULL)
return luaL_error(L, LUA_QL("tostring") " must return a string to "
LUA_QL("print"));
if (i>1) fputs("\t", stdout);
fputs(s, stdout);
lua_pop(L, 1); /* pop result */
}
fputs("\n", stdout);
return 0;
}
"""
proc lua_listvars(L: PState): int {.cdecl.}=
#var ar: PDebug = cast[PDebug](alloc0(sizeof(TDebug)))
var ar: TDebug
var i = 0
if L.getStack(1, addr ar) == 0:
return 0
i = 1
var name: cstring
while true:
name = getLocal(L, addr ar, postinc(i))
if name.isNil: break
luaOutput.add_output("local "& $( i - 1 ) &' '& $name)
L.pop(1)
return 0
var L = newstate()
openlibs(L)
for lib in {"print": lua_print, "listvars": lua_listvars}:
register(L, cstring(lib[0]), lib[1])
gui.newButton("stackdump", onClick = proc(b: PButton) =
stackdump(L))
echo($gui.getposition)
window.set_framerate_limit 60
while window.isOpen:
let dt = clock.restart.asMilliseconds / 1000
while window.pollEvent(event):
case event.kind
of evt_closed:
window.close()
of evt_mousebuttonpressed:
if event.mouseButton.button == MouseLeft:
gui.click(mousePos)
elif event.mouseButton.button == MouseMiddle:
var s = ""
for line in txtarea.lines:
if line != "":
s.add(line)
s.add "\n"
var error = loadbuffer(L, s, s.len(), "line") != 0 or
pcall(L, 0, 0, 0) != 0
if error:
luaError.setString($toString(L, -1))
stackdump(L)
pop(L, 1)
notif.add("Button pressed: "& $event.mouseButton.button)
of evt_mouse_moved:
mouse_pos.x = event.mouseMove.x
mouse_pos.y = event.mouseMove.y
var
base_pos = txtarea.basepos(mouse_pos)
cursor_sprite.set_position vec2f(
base_pos.x * txtarea.glyphsize.width,
base_pos.y * txtarea.glyphsize.height)
of evt_key_pressed:
txtarea.handle event.key
if event.key.code == keyEscape:
window.close
of evt_text_entered:
txtarea.handle event.text
else: discard
gui.update(dt)
window.clear Black
window.draw gui
window.draw cursor_sprite
window.display()
{
"widgets":{
"handle":{
"type": "handle",
"text": "foo",
"bg-color": "dpurp",
"color": "white",
"fontsize": 12},
"input":{
"type": "textgrid",
"handle": "handle",
"bg-color": "black",
"color": "white",
"cursor-color": "notus",
"tiled-pos": [1,1],
"size": [30, 25]},
"lua-output":{
"type": "textgrid",
"tiled-pos": [30,1],
"bg-color": "dgray",
"color": "bizzeige",
"handle": "handle"}
},
"layout":{
"cursor":{
}
},
"colors":{
"black":[0,0,0],
"white":[255, 255, 255],
"gray":[150,150,150],
"dgray":[40, 40, 40],
"dgray-a25":[40, 40, 40, 60],
"dgreen":[31,112,10],
"lgreen":[121,204,60],
"bizzeige":[230,213,195],
"martha":[172,135,93],
"dpurp":[39,10,51],
"lpurp":[69,26,62],
"notus":[61,123,128],
"ganymede":[177,230,209]
}
}
import sfml, sfml_colors, json, tables, strutils
type
PWidget* = ref TWidget
TWidget* = object of TObject
PClickable* = ref TClickable
TClickable* = object of TWidget
bounds: TFloatRect
hidden: bool
PTextLAbel* = ref TTextLabel
TTextLabel* = object of TClickable
bg*: PRectangleShape
text*: PText
PButton* = ref TButton
TButton* = object of TTextLabel
enabled: bool
onClick: proc(b: PButton)
updateProc*: proc(b: PButton; dt: float)
PHandle* = ref THandle
THandle* = object of TTextLabel
widget: PWidget
c: PContainer
PackDir = enum
PackDown, PackUp, PackLeft, PackRight
TMessage* = tuple[
life: float; text: PText]
PNotifyArea* = ref TNotifyArea
TNotifyArea* = object of TWidget
pos: TVector2f
layout: PackDir
texts: seq[TMessage]
lifetime: float
PContainer* = ref TContainer
TContainer* = object of TClickable
widgets*: seq[PWidget]
namedWidgets: TTable[string, PWidget]
position: TVector2f
layout: PackDir
pack_pos: TVector2f
pack_padding: TVector2i
PTextArea* = ref TTextArea
TTextArea* = object of TClickable
lines*: seq[string]
texts*: seq[PText]
textProto: PText
cursor*: TVector2i
cursorSprite*: PRectangleShape
glyphsize*: TIntRect
bg: PRectangleShape
height*, width*: int
var
guiFont* = newFont("LiberationMono-Regular.ttf")
method click*(w: PWidget; pos: TVector2i) = nil
method setPosition*(w: PWidget; val: TVector2f) = nil
method getPosition*(w: PWidget): TVector2f = nil
template unless(cond: expr; body: stmt): stmt =
if not(cond):
body
proc basePos*(t: PTextArea; vec: TVector2i): TVector2i =
result.x = ((vec.x - (vec.x mod t.glyphsize.width)) / t.glyphsize.width).int32
result.y = ((vec.y - (vec.y mod t.glyphsize.height))/ t.glyphsize.height).int32
method set_pack_padding*(c: PContainer; val: TVector2i) =
c.pack_padding = val
method click*(b: PButton; pos: TVector2i) =
if b.bounds.contains(pos.x.float, pos.y.float) and not b.onClick.isNil:
b.onClick(b)
method click*(t: PTextArea; pos: TVector2i) =
discard """echo repr(evt)
echo repr(t.basepos(evt.mouseButton.x, evt.mouseButton.y))"""
if t.bounds.contains(pos.x, pos.y):
let bp = t.basepos(pos - vec2i(t.text_proto.get_position))
if bp.y < t.height: t.cursor.y = bp.y
if bp.x < t.width:
t.cursor.x = bp.x
if t.cursor.x > t.lines[t.cursor.y].len:
t.cursor.x = t.lines[t.cursor.y].len.cint
echo($t.cursor)
method click*(h: PHandle; pos: TVector2i) =
if not h.widget.isNil:
h.widget.click pos
method click*(c: PContainer; pos: TVector2i) =
for w in c.widgets:
w.click(pos)
proc pack*[A: PText|PRectangleShape|PSprite|PCircleShape](c: PContainer; obj: A) =
setPosition(obj, c.position + c.pack_pos)
case c.layout
of pack_down:
c.pack_pos.y += (obj.getLocalBounds.height.cfloat +
c.pack_padding.y.cfloat)
of pack_up:
c.pack_pos.y -= (obj.getLocalBounds.height.cfloat +
c.pack_padding.y.cfloat)
of pack_left:
c.pack_pos.x -= (obj.getLocalBounds.width.cfloat +
c.pack_padding.x.cfloat)
of pack_right:
c.pack_pos.x += (obj.getLocalBounds.width.cfloat +
c.pack_padding.x.cfloat)
proc free*(obj: PNotifyArea) =
for i in obj.texts:
i.text.destroy()
method get_height*(b: PButton): int32 =
return b.text.getCharacterSize()
method pack_move*(vec: var TVector2f; layout: PackDir; b: PButton) =
case layout
of pack_down:
vec.y += b.getHeight.float
of pack_up:
vec.y -= b.getHeight.float
else: nil
proc pack_move*(vec: var TVector2f; layout: PackDir; val: float) =
case layout
of pack_down:
vec.y += val
of pack_up:
vec.y -= val
else: nil
method add*(c: PContainer, w: PWidget) =
c.widgets.add w
method add*(c: PContainer; b: PButton) =
##add(c, PWidget(b))
c.widgets.add b
proc newNotifyArea*(): PNotifyArea =
new(result, free)
result.texts = @[]
result.layout = pack_up
result.lifetime = 1.2
proc newNotifyArea*(c: PContainer; pos: TVector2f): PNotifyArea{.discardable.}=
result = newNotifyArea()
result.pos = pos
c.add result
method setPosition*(c: PContainer, val: TVector2f) =
c.position = val
method getPosition*(c: PContainer): TVector2f =
result = c.position
method setString*(w: PWidget; str: string) =
quit "setString not implemented!"
method setString*(L: PTextLabel; str: string) =
L.text.setString str
let b = L.text.get_local_bounds
L.bg.set_size vec2f(b.width, b.height)
method add*(n: PNotifyArea; msg: string) =
var m: TMessage
m.life = n.lifetime
m.text = newText(msg, guiFont, 16)
n.texts.add(m)
method setFillColor*(t: PTextArea; color: TColor) =
t.bg.set_fill_color color
method setFillColor*(t: PTextLabel; color: TColor) =
t.bg.set_fill_color color
method setColor*(t: PTextArea; color: TColor) =
t.text_proto.set_color color
for i in 0..high(t.texts):
t.texts[i].set_color color
method setColor*(t: PTextLabel; color: TColor) =
t.text.set_color color
method `[]`*(a: PContainer; b: int): PWidget =
return a.widgets[b]
method `[]`*(a: PContainer, b: string): PWidget =
return a.namedWidgets[b]
proc setPosition*(h: PTextLabel; pos: TVector2f) =
h.text.setPosition pos
h.bg.setPosition pos
proc newHandle*(c: PContainer; widget: PWidget; text: string; size: cint): PHandle {.discardable.} =
new(result)# free)
#init(PButton(result), c, text, nil)
result.widget = widget
result.text = newText(text, guiFont, size)
result.bg = newRectangleShape()
result.c = c
c.add result
proc copy*(h: PHandle; c: PContainer): Phandle {.discardable.} =
new(result)
result.text = h.text.copy()
result.bg = h.bg.copy()
result.c = c
c.add result
proc setup*(handle: PHandle; name: string; pos: TVector2f; widget: PWidget) =
handle.widget = widget
handle.text.set_string name
handle.set_position pos
handle.widget.set_position pos + vec2f(0.0, handle.text.getLocalBounds.height)
var res = handle.c.widgets.find(widget)
if res > -1:
handle.c.widgets.del(handle.c.widgets.find(widget))
proc newContainer*(pos = vec2f(0, 0)): PContainer =
new(result)
result.widgets = @[]
result.position = pos
proc newTextGrid*(c: PContainer; pos = vec2f(0, 0); width = 80; height = 25): PTextArea
type
PColorPalette* = ref object
colors: seq[tuple[name: string; color: TColor]]
idx: int
sprite_rect: PRectangleShape
var
colors: TTable[string, TColor]
colorPalette: PColorPalette
proc setIndex*(cp: PColorPalette; idx: int)
proc newColorPalette(colors: var TTable[string, TColor]): PColorPalette =
new(result)
result.sprite_rect = newRectangleShape(vec2f(100,100))
result.colors = @[]
for name, c in pairs(colors):
result.colors.add((name, c))
result.set_index 0
proc setIndex*(cp: PColorPalette; idx: int) =
cp.idx = idx
cp.sprite_rect.set_fill_color cp.colors[idx].color
proc `$`*(a: TColor): string =
return "($1, $2, $3, $4)".format(a.r, a.g, a.b, a.a)
proc color_f(n: PJsonNode): TColor =
result.r = (n[0].fnum * uint8.high.float).uint8
result.g = (n[1].fnum * uint8.high.float).uint8
result.b = (n[2].fnum * uint8.high.float).uint8
if n.len > 3:
result.a = (n[3].fnum * uint8.high.float).uint8
else:
result.a = uint8.high
proc color(n: PJsonNode): TColor =
if n.kind == JString and colors.hasKey(n.str):
return colors[n.str]
assert n.kind == JArray and n.len >= 3
if n[0].kind == JFloat:
return color_f(n)
result.r = n[0].num.uint8
result.g = n[1].num.uint8
result.b = n[2].num.uint8
if n.len > 3:
result.a = n[3].num.uint8
else:
result.a = uint8.high
proc newContainer*(schema_file: string): PContainer =
result = newContainer(vec2f(0,0))
result.namedWidgets = initTable[string, PWidget]()
colors = initTable[string, tcolor]()
var settings = json.parseFile(schema_file)
for name, col in settings["colors"].pairs:
colors[name] = color(col)
colorPalette = newColorPalette(colors)
for name, obj in settings["widgets"].pairs:
case obj["type"].str.tolower
of "handle":
var w = newHandle(result, nil, obj["text"].str, obj["fontsize"].num.cint)
if obj.exists_key"bg-color":
w.set_fill_color color(obj["bg-color"])
if obj.exists_key"color":
w.set_color color(obj["color"])
result.namedWidgets[name] = w
of "textgrid":
var
pos: TVector2f
size = vec2i(80, 25)
if obj.exists_key"tiled-pos":
pos.x = obj["tiled-pos"][0].num.cfloat * 10.cfloat
pos.y = obj["tiled-pos"][1].num.cfloat * 16.cfloat
if obj.exists_key"size":
size.x = obj["size"][0].num.cint
size.y = obj["size"][1].num.cint
var t = newTextGrid(result, pos, size.x, size.y)
if obj.exists_key("handle"):
var h = PHandle(result[obj["handle"].str])
if h.widget.isNil:
h.setup name, pos, t
else:
var hh = h.copy(result)
hh.setup name, pos, t
if obj.exists_key"bg-color":
t.set_fill_color(color(obj["bg-color"]))
if obj.exists_key"color":
t.set_color color(obj["color"])
if obj.exists_key"cursor-color":
t.cursor_sprite.set_outline_color color(obj["cursor-color"])
result.namedWidgets[name] = t
echo "new textgrid"
else:
discard
proc updateline*(t: PTextArea; idx: int) =
t.texts[idx].setPosition(
t.text_proto.get_position + vec2f(
0.0, t.glyphsize.height.cfloat * idx.cfloat))
method set_string*(b: PButton; s: string) =
b.text.set_string s
proc free(b: PButton) =
b.text.destroy()
proc free(h: PHandle) =
h.text.destroy()
proc init(obj: PButton; c: PContainer; text: string; onClick: proc(b: PButton)) =
obj.text = newText(text, guiFont, 16)
c.pack(obj.text)
obj.bounds = obj.text.get_global_bounds
obj.onClick = onClick
proc newButton*(c: PContainer; text: string;
onClick: proc(b: PButton) = nil): PButton {.discardable.} =
new(result, free)
init(result, c, text, onclick)
c.add result
proc newButtonStyled*(c: PContainer; color: TColor = Green;
onClick: proc(b: PButton) = nil): PButton {.discardable.} =
result = newButton(c, "##", onClick)
result.text.setColor color
method hide*(c: PClickable) = c.hidden = true
method show*(c: PClickable) = c.hidden = false
method isHidden*(c: PClickable): bool= return c.hidden
method draw*(window: PRenderWindow; w: PWidget) = nil
method draw*(window: PRenderWindow; w: PWidget; dt: float) = nil
method update*(w: PWidget; dt: float) = nil
method update*(b: PButton; dt: float) =
unless b.updateProc.isNil:
b.updateProc(b, dt)
method update*(t: PTextArea; dt: float) =
discard """for i in 0.. <t.height:
if t.texts[i].isNil:
echo "texts was nil?"
else:
t.texts[i].set_string t.lines[i]"""
method update*(n: PNotifyArea; dt: float) =
var i = 0
var pos = n.pos
while i < len(n.texts):
n.texts[i].life -= dt
if n.texts[i].life <= 0.0:
n.texts[i].text.destroy()
n.texts.delete i
else:
n.texts[i].text.set_position pos
pack_move(pos, n.layout, n.texts[i].text.get_local_bounds.height)
inc i
method update*(n: PContainer; dt: float) =
for w in n.widgets:
update(w, dt)
method draw*(window: PRenderWindow; n: PNotifyArea) =
for t in n.texts:
window.draw t.text
method draw*(window: PRenderWindow; t: PTextArea) =
window.draw t.bg
t.cursor_sprite.set_position(
(t.cursor * vec2f(t.glyphsize.width, t.glyphsize.height)) +
t.text_proto.get_position)
window.draw t.cursor_sprite
for i in 0..high(t.texts):
window.draw t.texts[i]
method draw*(window: PrenderWindow; b: PButton) =
window.draw b.text
method draw*(window: PRenderWindow; h: PHandle) =
window.draw h.widget
window.draw h.bg
window.draw h.text
method draw*(window: PRenderWindow; c: PContainer) =
if c.hidden: return
for w in c.widgets:
draw(window, w)
proc updateLine*(t: PTextArea){.inline.} =
t.texts[t.cursor.y].setString(t.lines[t.cursor.y])
echo repr(t.lines[t.cursor.y])
proc setCursor*(t: PTextArea; x, y: int) =
t.cursor.x = x.max(0).min(t.width-1).cint
t.cursor.y = y.max(0).min(t.height-1).cint
proc moveCursor*(t: PTextArea; x, y: int) =
t.updateLine()
t.setCursor t.cursor.x+x, t.cursor.y+y
if y != 0: t.updateLine()
method add*(t: PTextArea, line: string) =
t.lines.add line
var txt = t.textProto.copy()
txt.setstring line
t.texts.add txt
t.updateline(high(t.texts))
proc add_output*(t: PTextArea; line: string) =
t.lines[t.cursor.y] = line
t.move_cursor 0, 1
proc addlines*(t: PTextArea; num: int) =
for i in 0..num-1:
t.add("")
proc newTextGrid*(c: PContainer; pos = vec2f(0, 0); width = 80; height = 25): PTextArea =
new(result)
result.lines = @[]
result.texts = @[]
result.height = height
result.width = width
var glyp = guiFont.getGlyph('#'.cuint, 16, false)
result.textProto = newText("", guiFont, 16)
result.textProto.set_color Green
result.textProto.set_position pos + c.get_position()
result.glyphsize = glyp.bounds
result.glyphsize.height = 16
result.glyphsize.width = 10
result.bg = sfml.newRectangleShape(
vec2f(result.glyphsize.width * width,
result.glyphsize.height* height))
result.cursor_sprite = new_rectangle_shape(vec2f(
result.glyphsize.width.float, result.glyphsize.height.float))
result.cursor_sprite.set_fill_color Transparent
result.cursor_sprite.set_outline_color Blue
result.cursor_sprite.set_outline_thickness 1.5
addLines(result, height)
set_position(result, pos + c.get_position)
discard """for i in 0.. <height:
var txt = text.copy()
txt.setstring s
result.texts.add txt
result.lines.add s
pos.y += result.glyphsize.height.float
result.texts.last.setPosition pos"""
c.add result
method handle*(w: PWidget; e: var TTextEvent) = nil
method handle*(t: PTextArea; evt: var TTextEvent) =
let c = evt.unicode
if c in 31..126:
if t.lines.len-1 < t.cursor.y:
t.addlines(t.cursor.y - t.lines.len + 1)
if high(t.lines[t.cursor.y]) < t.cursor.x:
echo "setting len to ", t.cursor.x + 1, " ", (t.cursor.x + 1) - high(t.lines[t.cursor.y])
var h = len(t.lines[t.cursor.y])
t.lines[t.cursor.y].set_len(t.cursor.x + 1)
while h < len(t.lines[t.cursor.y]):
echo h
t.lines[t.cursor.y][h] = ' '
inc h
#t.cursor.x = len(t.lines[t.cursor.y]).cint
echo("Cursor is ", $t.cursor, " ", char(c))
t.lines[t.cursor.y][t.cursor.x] = char(c)
t.moveCursor(1, 0)
#echo "recorded"
elif c == 8: ### should have named it D
let rem = substr(t.lines[t.cursor.y], t.cursor.x+1)
t.lines[t.cursor.y].setLen(t.cursor.x)
echo(t.lines[t.cursor.y], " + ", rem)
t.lines[t.cursor.y].add rem
t.moveCursor(-1, 0)
elif c == 127: ## delete
## str[0..cursor] + str[cursor+1 .. -1]
let rem = substr(t.lines[t.cursor.y], t.cursor.x+1)
t.lines[t.cursor.y].setLen(t.cursor.x)
echo(t.lines[t.cursor.y], " + ", rem)
t.lines[t.cursor.y].add rem
t.updateline()
elif c == 13: ##\n
t.moveCursor(0, 1)
else:
echo c
#t.cursor.x = max(0, min(t.width, t.cursor.x + x)).cint
#t.cursor.y = max(0, min(t.height, t.cursor.y + y)).cint
method handle*(t: PTextArea; evt: var TKeyEvent) =
case evt.code
of KeyLeft:
#t.cursor.x = max(0, t.cursor.x - 1).cint
t.movecursor(-1, 0)
of KeyRight:
#t.cursor.x = max(t.width, t.cursor.x + 1).cint
t.movecursor(1, 0)
of KeyUp:
t.moveCursor(0, -1)
of KeyDown:
#t.cursor.y = max(t.height, t.cursor.y + 1).cint
t.movecursor(0, 1)
of KeyHome:
t.movecursor(-t.cursor.x, 0)
of KeyEnd:
t.moveCursor(high(t.lines[t.cursor.y])-t.cursor.x, 0)
else:
nil
method setPosition*(t: PTextArea; val: TVector2f) =
t.text_proto.setPosition val
for i in 0..high(t.texts):
updateline(t, i)
t.bg.set_position val
t.bounds = t.bg.get_global_bounds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment