|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839 |
- os.loadAPI("/lib/util")
-
- function prettyPrintSize(tokens)
- if type(tokens) == "number" then
- return tostring(tokens)
- end
-
- local str = "( "
- for key, token in pairs(tokens) do
- if token.tokentype == "list" then
- str = str .. prettyPrintSize(token)
- elseif token.tokentype == "num" then
- str = str .. token.val .. " "
- elseif token.tokentype == "percent" then
- str = str .. token.val .. "% "
- elseif token.tokentype == "selfpercent" then
- str = str .. token.val .. "$ "
- elseif token.tokentype == "auto" then
- str = str .. "& "
- elseif token.tokentype == "add" then
- str = str .. "+ "
- elseif token.tokentype == "sub" then
- str = str .. "- "
- elseif token.tokentype == "multiply" then
- str = str .. "* "
- elseif token.tokentype == "divide" then
- str = str .. "/ "
- end
- end
- return str .. ")"
- end
-
- function parseSize(size)
- if type(size) == "number" then
- return size
- end
-
- size = size .. " "
- size = string.gsub(size, "center", "50%% - 50$")
- print(size)
-
- local res = {
- tokentype = "list"
- }
- local current = ""
- local parsing = true
- local i = 1
- while i <= #size do
- local c = string.sub(size, i, i)
-
- if tonumber(c) ~= nil then
- current = current .. c
- elseif current ~= "" then
- local cur = tonumber(current)
- if cur == nil or current == "+" or current == "-" then
- -- Don't do anything because there's no number
- elseif c == "%" then
- table.insert(res, {
- tokentype = "percent";
- val = cur;
- })
- elseif c == "$" then
- table.insert(res, {
- tokentype = "selfpercent";
- val = cur;
- })
- else
- table.insert(res, {
- tokentype = "num";
- val = cur;
- })
- end
- current = ""
- end
-
- if c == "(" then
- local substr = ""
- local depth = 1
-
- while depth > 0 do
- i = i + 1
- local cc = string.sub(size, i, i)
- if cc == "(" then
- depth = depth + 1
- elseif cc == ")" then
- depth = depth - 1
- end
-
- if depth > 0 then
- substr = substr .. cc
- end
- end
- table.insert(res, parseSize(substr))
- elseif c == "&" then
- table.insert(res, {
- tokentype = "auto"
- })
- elseif c == "+" then
- table.insert(res, {
- tokentype = "add"
- })
- elseif c == "-" then
- table.insert(res, {
- tokentype = "sub"
- })
- elseif c == "*" then
- table.insert(res, {
- tokentype = "multiply"
- })
- elseif c == "/" then
- table.insert(res, {
- tokentype = "divide"
- })
- end
-
- i = i + 1
- end
-
- return res
- end
-
- function calcSize(tokens, parentSizeArg, selfSizeArg, autoSizeArg)
- if type(tokens) == "number" then
- return tokens
- elseif tokens == nil then
- return nil
- end
-
- local parentSize = 0
- if type(parentSizeArg) == "function" then
- parentSize = parentSizeArg()
- elseif type(parentSizeArg) == "number" then
- parentSize = parentSizeArg
- end
- local selfSize = 0
- if type(selfSizeArg) == "function" then
- selfSize = selfSizeArg()
- elseif type(selfSizeArg) == "number" then
- selfSize = selfSizeArg
- end
- local autoSize = 0
- if type(autoSizeArg) == "function" then
- autoSize = autoSizeArg()
- elseif type(autoSizeArg) == "number" then
- autoSize = autoSizeArg
- end
-
- function getVal(token)
- if token.tokentype == "num" then
- return token.val
- elseif token.tokentype == "percent" then
- return util.percentOf(token.val, parentSize)
- elseif token.tokentype == "selfpercent" then
- return util.percentOf(token.val, selfSize) - 1
- elseif token.tokentype == "auto" then
- return autoSize
- elseif token.tokentype == "list" then
- return calcSize(token, parentSize, selfSize)
- else
- return 0
- end
- end
-
- local sum = 0
- local opr = "add"
-
- local i = 1
- while i <= #tokens do
- local num = getVal(tokens[i])
- if opr == "add" then
- sum = sum + num
- elseif opr == "sub" then
- sum = sum - num
- elseif opr == "multiply" then
- sum = sum * num
- elseif opr == "divide" then
- sum = sum / num
- else
- error("Unexpected operator: "..opr)
- return 0
- end
-
- local oprToken = tokens[i + 1]
- if oprToken ~= nil then
- opr = oprToken.tokentype
- else
- opr = ""
- end
-
- i = i + 2
- end
-
- return math.floor(sum)
- end
-
- function Widget()
- local self = util.newObject()
- util.makeEventListener(self)
-
- local prog = nil
- local focused = false
- local children = {}
- local parent = nil
- local top = 1
- local bottom = nil
- local left = 1
- local right = nil
- local width = parseSize("&")
- local height = parseSize("&")
- local background = "black"
- local foreground = "white"
-
- local x = 1
- local y = 1
-
- self.getset("prog",
- function() return prog end,
- function(val) prog = val end)
-
- self.getset("focused",
- function() return focused end)
- self.getset("children",
- function() return children end)
-
- self.getset("parent",
- function() return parent end)
- self.getset("_parent",
- function() return parent end,
- function(val) parent = val end)
-
- self.getset("width",
- function() return calcSize(width, parent.width, 0, self.autoWidth) end,
- function(val) width = parseSize(val) end)
- self.getset("height",
- function() return calcSize(height, parent.height, 0, self.autoHeight) end,
- function(val) height = parseSize(val) end)
-
- self.getset("background",
- function() return background end,
- function(val) background = val end)
- self.getset("foreground",
- function() return foreground end,
- function(val) foreground = val end)
-
- self.getset("top",
- function() return calcSize(top, parent.height, self.height, 0) end,
- function(val) top = parseSize(val) end)
- self.getset("bottom",
- function() return calcSize(bottom, parent.height, self.height, 0) end,
- function(val) bottom = parseSize(val) end)
- self.getset("left",
- function() return calcSize(left, parent.width, self.width, 0) end,
- function(val) left = parseSize(val) end)
- self.getset("right",
- function() return calcSize(right, parent.width, self.width, 0) end,
- function(val) right = parseSize(val) end)
-
- self.getset("x",
- function() return x end,
- function(val) x = val end)
- self.getset("y",
- function() return y end,
- function(val) y = val end)
-
- self.focus = function()
- focused = true
- self.emit("focus", self)
- end
- self.unfocus = function()
- focused = false
- self.emit("unfocus", self)
- end
-
- self.onclick = function(x, y)
- for key, child in pairs(self.children()) do
- local cx = child.x()
- local cy = child.y()
- local cw = child.width()
- local ch = child.height()
-
- if x >= cx and x < cx + cw
- and y >= cy and y < cy + ch then
- child.onclick(x, y)
- end
- end
- end
-
- self.onchar = function(char) end
- self.onkey = function(key) end
-
- self.setColors = function(screen)
- screen.setTextColor(colors[foreground])
- screen.setBackgroundColor(colors[background])
-
- local w = self.width()
- local h = self.height()
-
- local str = ""
- for i = 1, w do
- str = str .. " "
- end
-
- local x = self.x()
- local y = self.y()
- for i = 1, h do
- screen.setCursorPos(x, y + i - 1)
- screen.write(str)
- end
- screen.setCursorPos(x, y)
- end
-
- self.addChild = function(widget)
- if widget.parent() ~= nil then
- error("Widget already has a parent")
- return
- end
- table.insert(children, widget)
- widget._parent(self)
- widget.prog(prog)
-
- widget.on("mustreflow", function()
- self.emit("mustreflow")
- end)
-
- widget.on("mustredraw", function(elem)
- self.emit("mustredraw", elem)
- end)
-
- widget.on("focus", function(elem)
- self.emit("focus", elem)
- end)
-
- widget.on("unfocus", function(elem)
- self.emit("unfocus", elem)
- end)
-
- self.emit("mustreflow")
- end
-
- self.removeChild = function(child)
- local removed = false
- for key, val in pairs(children) do
- if val == child then
- removed = true
- child.unfocus()
- table.remove(children, key)
- end
- end
- if removed then
- self.emit("mustreflow")
- end
- end
-
- self.reflow = util.abstract
-
- self.draw = util.abstract
-
- return self
- end
-
- function LinearLayout()
- local self = Widget()
-
- local orientation = "vertical"
- local reversed = false
-
- self.getset("orientation",
- function() return orientation end,
- function(val) orientation = val end)
- self.getset("reversed",
- function() return reversed end,
- function(val) reversed = val end)
-
- self.draw = function(screen)
- self.setColors(screen)
- for key, child in pairs(self.children()) do
- child.draw(screen)
- end
- end
-
- self.reflow = function(screen, x, y)
- self.x(x)
- self.y(y)
-
- local selfw = self.width()
- local selfh = self.height()
- local offset = 0
- for key, child in pairs(self.children()) do
- local cx = x
- local cy = y
- local height = child.height()
- local width = child.width()
- local top = child.top()
- local bottom = child.bottom()
- local left = child.left()
- local right = child.right()
-
- if right ~= nil then
- cx = cx +
- selfw -
- width -
- right + 1
- else
- cx = cx + top - 1
- end
- if bottom ~= nil then
- cy = cy +
- selfh -
- height -
- bottom + 1
- else
- cy = cy + top - 1
- end
-
- if orientation == "vertical" then
- cy = y + offset + top - 1
- offset = offset +
- top - 1 +
- height +
- (bottom or 1) - 1
-
- if reversed then
- cy = selfh - cy - height + 2
- end
- elseif orientation == "horizontal" then
- cx = x + offset + left - 1
- offset = offset +
- left - 1 +
- width +
- (right or 1) - 1
-
- if reversed then
- cx = selfw - cx - width + 2
- end
- end
-
- child.reflow(screen, cx, cy)
- end
- end
-
- return self
- end
-
- function FloatingLayout()
- local self = Widget()
-
- self.draw = function(screen)
- self.setColors(screen)
- for key, child in pairs(self.children()) do
- child.draw(screen)
- end
- end
-
- self.reflow = function(screen, x, y)
- self.x(x)
- self.y(y)
-
- for key, child in pairs(self.children()) do
- local cx = x
- local cy = y
- if child.right() ~= nil then
- cx = cx +
- self.width() -
- child.width() -
- child.right() + 1
- else
- cx = cx + child.left() - 1
- end
- if child.bottom() ~= nil then
- cy = cy +
- self.height() -
- child.height() -
- child.bottom() + 1
- else
- cy = cy + child.top() - 1
- end
-
- child.reflow(screen, cx, cy)
- end
- end
-
- self.autoWidth = function()
- return self.parent().width()
- end
- self.autoHeight = function()
- return self.parent().height()
- end
-
- return self
- end
-
- function TextView()
- local self = Widget()
-
- local text = ""
-
- self.getset("text",
- function() return text end,
- function(val)
- local pre = text
- text = tostring(val)
- if #pre == #text then
- self.emit("mustredraw", self)
- else
- self.emit("mustreflow")
- end
- end)
-
- self.reflow = function(screen, x, y)
- self.x(x)
- self.y(y)
- end
-
- self.draw = function(screen)
- self.setColors(screen)
-
- screen.setCursorPos(self.x(), self.y())
- local w = self.width()
- local h = self.height()
-
- local offy = math.floor(h / 2)
- local offx = math.floor(w - #text - ((w - #text) / 2))
- screen.setCursorPos(self.x() + offx, self.y() + offy)
- screen.write(text)
- end
-
- self.autoWidth = function()
- return #text
- end
- self.autoHeight = function()
- return 1
- end
-
- self.onclick = function(x, y)
- -- TextViews don't have any interesting onclick actions
- end
-
- return self
- end
-
- function TextInput()
- local self = Widget()
-
- local backgroundInactive = "blue"
- local backgroundActive = "red"
- local foregroundInactive = "white"
- local foregroundActive = "white"
-
- local key_backspace = 14
- local key_left = 203
- local key_right = 205
- local key_delete = 211
-
- local text = ""
- local cursor = 0
-
- self.getset("text",
- function() return text end,
- function(val)
- local pre = text
- text = tostring(val)
- if #pre == #text then
- self.emit("mustredraw", self)
- else
- self.emit("mustreflow")
- end
- end)
-
- self.getset("backgroundInactive",
- function() return backgroundInactive end,
- function(val) backgroundInactive = val end)
- self.getset("backgroundActive",
- function() return backgroundActive end,
- function(val) backgroundActive = val end)
- self.getset("foregroundInactive",
- function() return foregroundInactive end,
- function(val) foregroundInactive = val end)
- self.getset("foregroundActive",
- function() return foregroundActive end,
- function(val) foregroundActive = val end)
-
- self.reflow = function(screen, x, y)
- self.x(x)
- self.y(y)
- end
-
- self.draw = function(screen)
- if self.focused() then
- self.background(backgroundActive)
- self.foreground(foregroundActive)
- else
- self.background(backgroundInactive)
- self.foreground(foregroundInactive)
- end
-
- self.setColors(screen)
-
- screen.setCursorPos(self.x(), self.y())
- screen.write("|")
- screen.setCursorPos(self.x() + self.width() - 1, self.y())
- screen.write("|")
-
- local len = self.width() - 2
- screen.setCursorPos(self.x() + 1, self.y())
- screen.write(string.sub(text, 1, len))
-
- if self.focused() then
- screen.setCursorPos(self.x() + 1 + cursor, self.y())
- screen.setBackgroundColor(colors.white)
- screen.write(" ")
- end
- end
-
- self.autoWidth = function()
- return #text + 2
- end
- self.autoHeight = function()
- return 1
- end
-
- self.onclick = function(x, y)
- local w = self.width()
- if x == self.x() or x == w then
- cursor = #text
- else
- cursor = math.min(x - self.x() - 1, #text)
- end
- self.focus()
- end
-
- self.onchar = function(char)
- local pre = string.sub(text, 1, cursor)
- local post = string.sub(text, cursor + 1, #text)
- text = pre .. char .. post
- cursor = cursor + 1
- self.emit("mustreflow")
- end
-
- self.onkey = function(key)
- if key == key_backspace then
- local pre = string.sub(text, 1, cursor - 1)
- local post = string.sub(text, cursor + 1, #text)
- text = pre .. post
- if cursor > 0 then
- cursor = cursor - 1
- end
- self.emit("mustreflow")
- elseif key == key_delete then
- local pre = string.sub(text, 1, cursor)
- local post = string.sub(text, cursor + 2, #text)
- text = pre .. post
- self.emit("mustreflow")
- elseif key == key_left then
- if cursor > 0 then
- cursor = cursor - 1
- end
- self.emit("mustredraw", self)
- elseif key == key_right then
- if cursor < #text then
- cursor = cursor + 1
- end
- self.emit("mustredraw", self)
- end
- end
-
- self.on("focus", function() self.emit("mustredraw", self) end)
- self.on("unfocus", function() self.emit("mustredraw", self) end)
-
- return self
- end
-
- function Button()
- local self = TextView()
-
- local backgroundInactive = "blue"
- local backgroundActive = "red"
- local foregroundInactive = "white"
- local foregroundActive = "white"
-
- self.getset("backgroundInactive",
- function() return backgroundInactive end,
- function(val) backgroundInactive = val end)
- self.getset("backgroundActive",
- function() return backgroundActive end,
- function(val) backgroundActive = val end)
- self.getset("foregroundInactive",
- function() return foregroundInactive end,
- function(val) foregroundInactive = val end)
- self.getset("foregroundActive",
- function() return foregroundActive end,
- function(val) foregroundActive = val end)
-
- self.background(backgroundInactive)
- self.foreground(foregroundInactive)
-
- local resetTimeout
-
- self.onclick = function(x, y)
- if resetTimeout ~= nil then
- self.prog().cancelTimeout(resetTimeout)
- end
- self.background(backgroundActive)
- self.foreground(foregroundActive)
- self.emit("mustredraw", self)
-
- self.emit("focus", self)
- self.emit("click")
-
- resetTimeout = self.prog().setTimeout(0.3, function()
- self.background(backgroundInactive)
- self.foreground(foregroundInactive)
- self.emit("mustredraw", self)
- end)
- end
-
- return self
- end
-
- function DummyScreen()
- local self = {}
-
- self.getSize = function()
- return term.getSize()
- end
- self.clear = function() end
- self.write = function() end
- self.setCursorPos = function() end
- self.setTextColor = function() end
- self.setBackgroundColor = function() end
-
- return self
- end
-
- function Gui(prog, screen)
- local self = util.newObject()
-
- if screen == nil then
- screen = term
- end
-
- local focusedWidget = nil
- local rootWidget = nil
-
- self.getset("width",
- function()
- local x, y = screen.getSize()
- return x
- end)
- self.getset("height",
- function()
- local x, y = screen.getSize();
- return y
- end)
-
- self.getset("screen",
- function() return screen end,
- function(val) screen = val end)
-
- self.getset("focused",
- function() return focusedWidget end)
-
- -- Handle focusing and unfocusing of widgets
- local function onRootChildFocus(widget)
- if focusedWidget ~= nil then
- focusedWidget.unfocus()
- end
- focusedWidget = widget
- end
-
- local function onRootChildUnfocus(widget)
- if widget == focusedWidget then
- focusedWidget = nil
- end
- end
-
- local function onRootChildRedraw(widget)
- widget.draw(screen)
- end
-
- self.setRoot = function(root)
- if rootWidget ~= nil then
- rootWidget.removeListener("focus", onRootChildFocus)
- rootWidget.removeListener("unfocus", onRootChildUnfocus)
- rootWidget.removeListener("mustreflow", self.reflow)
- rootWidget.removeListener("mustredraw", onRootChildRedraw)
- end
- rootWidget = root
- root._parent(self)
- root.on("focus", onRootChildFocus)
- root.on("unfocus", onRootChildUnfocus)
- root.on("mustreflow", self.reflow)
- root.on("mustredraw", onRootChildRedraw)
- root.prog(prog)
- end
-
- self.reflow = function()
- if rootWidget == nil then
- return
- end
- screen.clear()
- screen.setCursorPos(1, 1)
- rootWidget.reflow(screen, 1, 1)
- rootWidget.draw(screen)
- end
-
- self.exit = function()
- prog.exit()
- end
-
- prog.on("init", self.draw)
-
- prog.on("exit", function()
- screen.setTextColor(colors.white)
- screen.setBackgroundColor(colors.black)
- screen.clear()
- screen.setCursorPos(1, 1)
- end)
-
- prog.on("mouse_click", function(button, x, y)
- if focusedWidget ~= nil then
- focusedWidget.unfocus()
- end
- if rootWidget ~= nil then
- rootWidget.onclick(x, y)
- end
- end)
- prog.on("char", function(char)
- if focusedWidget ~= nil then
- focusedWidget.onchar(char)
- end
- end)
- prog.on("key", function(key)
- if focusedWidget ~= nil then
- focusedWidget.onkey(key)
- end
- end)
-
- return self
- end
|