Skip to content

Instantly share code, notes, and snippets.

@flamewing
Last active January 16, 2019 13:31
Show Gist options
  • Save flamewing/5f33a42aa5d1a7e46f412483065a2b43 to your computer and use it in GitHub Desktop.
Save flamewing/5f33a42aa5d1a7e46f412483065a2b43 to your computer and use it in GitHub Desktop.
Moonscript experiments
--------------------------------------------------------------------------------
-- This file is part of the Lua HUD for TASing Sega Genesis Sonic games.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- User interface widgets for Lua in Gens.
-- Written by: Marzo Junior
--------------------------------------------------------------------------------
require "headers/input-handler"
require "headers/ui-icons"
--------------------------------------------------------------------------------
-- Export all names from this module
--------------------------------------------------------------------------------
export *
--------------------------------------------------------------------------------
-- A couple static assertions
--------------------------------------------------------------------------------
assert_widget = (obj) -> assert obj != nil and obj.__class == Widget
assert_function = (fun) ->
ty = type(fun)
assert ty == "function", debug.traceback("Error: Function expected for variable 'fun', got '#{ty}'.")
--------------------------------------------------------------------------------
-- Generic abstract base interface: store position only.
-- Should not be used on its own.
--------------------------------------------------------------------------------
class Widget
add: (child, dx, dy) =>
error debug.traceback("Error: Pure virtual function 'Widget:add' called."), 0
-- Note: calling this from any but derived widgets with an 'add' member will
-- result in an error.
add_status_icon: (dx, dy, image, text, border, fill, txtxoff = 20, txtyoff = 4) =>
icon = Icon_widget 0, 0, image
watch = Text_widget 0, 0, text, border, fill
@add icon, dx, dy
@add watch, dx + txtxoff, dy + txtyoff
-- Move to different location.
move: (x, y) => @x, @y = x, y
-- Create widget and set position.
new: (x, y) => @x, @y = x, y
--------------------------------------------------------------------------------
-- A widget which displays an image.
--------------------------------------------------------------------------------
class Icon_widget extends Widget
-- 'image' can be a gdimage or a drawing function. The latter requires an
-- object to be supplied so that the function knowns what to do.
new: (x, y, image) =>
super x, y
@image = image
switch type(image)
when "function"
@draw = =>
gui.drawimage @x, @y, ui_icons[@\image!]
true
when "string"
@draw = =>
gui.drawimage @x, @y, ui_icons[@image]
true
else
@draw = => false
--------------------------------------------------------------------------------
-- A widget which displays text.
--------------------------------------------------------------------------------
class Text_widget extends Widget
-- 'image' can be a raw string or a function. The latter requires an
-- object to be supplied so that the function knowns what to do.
new: (x, y, text, border = {0, 0, 0, 0}, fill = {255, 255, 255, 255}) =>
super x, y
@text = text
@border = border
@fill = fill
switch type(text)
when "function"
@draw = =>
gui.text @x, @y, @\text!, @fill, @border
true
when "string"
@draw = =>
gui.text @x, @y, @text, @fill, @border
true
else
@draw = => false
--------------------------------------------------------------------------------
-- A container widget which draws a rectangular box.
--------------------------------------------------------------------------------
class Frame_widget extends Widget
-- Children are added relative.
add: (child, dx, dy) =>
assert_widget child
child\move @x + dx, @y + dy
table.insert @children, child
-- Moves all children too.
move: (x, y) =>
super x, y
dx = x - @x
dy = y - @y
for _,m in pairs @children
m\move m.x + dx, m.y + dy
-- Also draws the contained widgets.
draw: =>
gui.box @x, @y, @x + @w, @y + @h, @fill, @border
for _,m in pairs @children
m\draw!
true
new: (x, y, w, h, border = {0, 0, 127, 255}, fill = {0, 0, 0, 192}) =>
super x, y
@w = w
@h = h
@border = border
@fill = fill
@children = {}
--------------------------------------------------------------------------------
-- A clickable version of a frame widget.
--------------------------------------------------------------------------------
class Clickable_widget extends Frame_widget
-- Check if mouse is on the widget's area.
is_hot: =>
@hot = (mouse.x >= @x) and (mouse.y >= @y) and (mouse.x <= @x + @w) and (mouse.y <= @y + @h)
-- Draw widget with different colors to indicate 'hot' or being clicked.
draw: =>
fill = @fill
border = @border
if @\is_hot!
border = {255, 255, 255, 255}
if mouse.click
fill = {127, 0, 0, 255}
@.on_click @udata
else
fill = {0, 0, 127, 255}
gui.box @x, @y, @x + @w, @y + @h, fill, border
for _,m in pairs @children
m\draw!
true
new: (callback, udata, text, w, h, border = {0, 0, 255, 255}, fill = {0, 0, 127, 255}) =>
assert_function callback
super x, y, w, h, border, fill
@hot = false
@on_click = callback
@udata = udata
make_button: (callback, udata, text, w, h, border, fill) ->
btn = Clickable_widget 0, 0, w, h, callback, udata, border, fill
btn\add Text_widget(0, 0, text), 1 + math.floor((w + 1 - 4 * #text)/2), 1
btn
--------------------------------------------------------------------------------
-- Clickable widget with 'on' and 'off' states. This widget has a separate
-- list of children that are drawn if the widget is 'off'.
--------------------------------------------------------------------------------
class Toggle_widget extends Clickable_widget
get_children: =>
if @active
@children
else
@off_children
add: (child, dx, dy, active) =>
assert_widget child
child\move @x + dx, @y + dy
table.insert @\get_children!, child
move: (x, y) =>
@x, @y = x, y
dx = x - @x
dy = y - @y
for _,m in pairs @children
m\move m.x + dx, m.y + dy
for _,m in pairs @off_children
m\move m.x + dx, m.y + dy
draw: =>
fill = @fill
border = @border
if @\is_hot!
border = {255, 255, 255, 255}
if mouse.click
fill = {127, 0, 0, 255}
@.on_click @udata
else
fill = {0, 0, 127, 255}
gui.box @x, @y, @x + @w, @y + @h, fill, border
for _,m in pairs @\get_children!
m\draw!
true
-- 'callback' is a function to be called when the container is 'toggled'.
-- 'udata' is an object which is passed to 'callback'.
new: (x, y, w, h, callback, udata, active, border = {0, 0, 255, 255}, fill = {0, 0, 127, 255}) =>
super x, y, w, h, callback, udata, border, fill
@active = active
@off_children = {}
make_toggle: (dim, horiz, callback, udata, active) ->
local w, h
if horiz
w, h = dim, 3
else
w, h = 3, dim
Toggle_widget 0, 0, w, h, callback, udata, active
--------------------------------------------------------------------------------
-- Container widget with a separate toggle widget. The toggle widget is used
-- to toggle the display of the container's children on or off.
--------------------------------------------------------------------------------
class Container_widget extends Widget
toggled: => @active = not @active
set_state: (flag) =>
@active = flag
if @toggle
@toggle.active = flag
add: (child, dx, dy) =>
assert_widget child
child\move @x + dx, @y + dy
table.insert @\get_children!, child
add_toggle: (child, dx, dy) =>
assert_widget child
child\move @x + dx, @y + dy
@toggle = child
move: (x, y) =>
@x, @y = x, y
dx = x - @x
dy = y - @y
if @toggle
@toggle\move @toggle.x + dx, @toggle.y + dy
for _,m in pairs @children
m\move m.x + dx, m.y + dy
draw: =>
if @active
for _,m in pairs @children
m\draw!
if @toggle
@toggle\draw!
@active
new: (x, y, active = false) =>
super x, y
@active = active
@children = {}
@toggle = nil
--------------------------------------------------------------------------------
-- Container which only shows if certain definable conditions hold.
--------------------------------------------------------------------------------
class Conditional_widget extends Container_widget
add_toggle: (child, dx, dy) =>
assert_widget child
error debug.traceback("Error: Container_widget cannot have a toggle."), 0
move: (x, y) =>
@x, @y = x, y
dx = x - @x
dy = y - @y
for _,m in pairs @children
m\move m.x + dx, m.y + dy
draw: =>
@active = @.is_active @obj
if @active
for _,m in pairs @children
m\draw!
@active
-- 'is_active' is a function that defines the conditions under which the
-- widget appears. 'obj' is an object which is passed to 'is_active'.
new: (x, y, active = false, is_active = (=> false), obj = nil) =>
assert_function is_active
super x, y, active
@is_active = is_active
@children = {}
@obj = obj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment