--[[ docs: https://github.com/minetest/minetest/blob/master/doc/lua_api.txt#L1683 ]]-- -- this stuff will be seperate from the rest when the seperate files are put into one local data = { -- window size width = 15, height = 10, } local form_esc = minetest.formspec_escape -- shorten the function local modstorage = core.get_mod_storage() local function create_tabs(selected) return "tabheader[0,0;_option_tabs_;" .. " LUA EDITOR ,FORMSPEC EDITOR, LUA CONSOLE , FILES , STARTUP , FUNCTIONS , HELP ;"..selected..";;]" end local function copy_table(table) local new = {} for i, v in pairs(table) do if type(v) == "table" then v = copy_table(v) end new[i] = v end return new end ---------- ---------- -- FORMSPEC EDITOR START -- ---------- ---------- local widg_list = {"Button", "DropDown", "CheckBox", "Slider", "Tabs", "TextList", "Table", "Field", "TextArea", "InvList", "Label", "Image", "Box", "Tooltip", "Container"} -- all widget options local widgets = nil -- stores all widget data for the current file local selected_widget = 1 -- the widget/tab currently being edited local new_widg_tab = false -- so the new widget tab can be displayed without moving the selection local main_ui_form -- make this function global to the rest of the program local current_ui_file = modstorage:get_string("_GUI_editor_selected_file") -- file name of last edited file if current_ui_file == "" then -- for first ever load current_ui_file = "new" modstorage:set_string("_GUI_editor_selected_file", current_ui_file) modstorage:set_string("_GUI_editor_file_"..current_ui_file, dump({{type="Display", name="", width=5, height=5, width_param=false, height_param=false, left=0.5, top=0.5, position=false, background=false, colour="#000000aa", fullscreen=false, colour_tab=false, col={col=false, bg_normal="#f0fa", bg_hover="#f0fa", set_border=false, border="#f0fa", set_tool=false, tool_bg="#f0fa", tool_font="#f0fa"}}})) end local function reload_ui() -- update the display, and save the file modstorage:set_string("_GUI_editor_file_"..current_ui_file, dump(widgets)) minetest.show_formspec("ui_editor:main", main_ui_form()) end local function load_UI(name) -- open/create a ui file current_ui_file = name modstorage:set_string("_GUI_editor_selected_file", current_ui_file) _, widgets = pcall(loadstring("return "..modstorage:get_string("_GUI_editor_file_"..current_ui_file))) if widgets == nil then widgets = {{type="Display", name="", width=5, height=5, width_param=false, height_param=false, left=0.5, top=0.5, position=false, background=false, colour="#000000aa", fullscreen=false, colour_tab=false, col={col=false, bg_normal="#f0fa", bg_hover="#f0fa", set_border=false, border="#f0fa", set_tool=false, tool_bg="#f0fa", tool_font="#f0fa"}}} end end load_UI(current_ui_file) --widgets = {{type="Display", name="", width=5, height=5, width_param=false, height_param=false, left=0.5, top=0.5, position=false, background=false, colour="#000000aa", fullscreen=false, colour_tab=false, col={col=false, bg_normal="#f0fa", bg_hover="#f0fa", set_border=false, border="#f0fa", set_tool=false, tool_bg="#f0fa", tool_font="#f0fa"}} } ---------- -- UI DISPLAY ---------- -- generates the preview of the UI being edited local function generate_ui() local width = data.width-5 -- the size that is needed for the final formspec size, so large formspecs can be previewed local height = data.height -- data for calculating positions local left = {0.1} local top = {0.1} local fwidth = {1} local fheight = {1} local form = "" local boxes = "" -- because I can't add to form in get_rect() function local depth = 1 -- container depth -- calculates the positions of widgets, and creates the position syntax from a widget def local function get_rect(widget, real, full) local wleft = 0 -- widget top (etc) if widget.left_type == "R-" then -- right is value from right side wleft = left[depth]+fwidth[depth]-widget.left elseif widget.left_type == "W/" then -- right is width/value from left side wleft = left[depth]+fwidth[depth]/widget.left else -- right is value from left side wleft = left[depth]+widget.left end if full then -- container only takes whole numbers as positions. wleft = math.floor(wleft-left[depth])+left[depth] end local wtop = 0 if widget.top_type == "B-" then -- value from bottom wtop = top[depth]+fheight[depth]-widget.top elseif widget.top_type == "H/" then -- height/value from top wtop = top[depth]+(fheight[deformpth]/widget.top) else -- value from top wtop = top[depth]+widget.top end if full then wtop = math.floor(wtop-top[depth])+top[depth] end if widget.right == nil then -- for widgets with no size option return wleft..","..wtop..";" else local wright = 0 if widget.right_type == "R-" then wright = left[depth]+fwidth[depth]-widget.right-wleft elseif widget.right_type == "W/" then wright = left[depth]+fwidth[depth]/widget.right-wleft elseif widget.right_type == "R" then -- relative to left wright = widget.right else wright = left[depth]+widget.right-wleft end local wbottom = 0 if widget.bottom_type == "B-" then wbottom = top[depth]+fheight[depth]-widget.bottom-wtop elseif widget.bottom_type == "H/" then wbottom = top[depth]+fheight[depth]/widget.bottom-wtop elseif widget.bottom_type == "R" then wbottom = widget.bottom else wbottom = top[depth]+widget.bottom-wtop end if wleft+wright > width then -- stops widgets covering the controlls boxes = boxes.."box["..width ..","..wtop ..";"..wleft+wright-width..","..wbottom..";#ff0000]" -- shows where it would go wright = width-wleft end if real then return {left=wleft, top=wtop, width=wright, height=wbottom} -- table uses the calculated values for other things end return wleft..","..wtop..";"..wright..","..wbottom..";" end end -- iterate all widgets for i, v in pairs(widgets) do if v.type == "Display" then -- defines the size if v.width < data.width-5.2 then left = {math.floor(((data.width-5)/2 - v.width/2)*10)/10} -- place the form in the center else width = math.floor((v.width+0.2)*10)/10 -- resize for large forms end if v.height < data.height-0.2 then -- ^ top = {data.height/2 - v.height/2} else height = v.height+0.2 end fwidth = {v.width} fheight = {v.height} form = form .. -- "box["..left[1]..","..top[1]..";"..v.width..","..v.height..";#000000]" -- this adds it to the form "box["..left[1]-0.28 ..","..top[1]-0.3 ..";"..v.width+0.38 ..",".. 0.32 ..";#000000]" .. "box["..left[1]-0.28 ..","..top[1]+v.height..";"..v.width+0.38 ..",".. 0.4 ..";#000000]" .. "box["..left[1]-0.28 ..","..top[1]..";".. 0.3 ..","..v.height..";#000000]" .. "box["..left[1]+v.width..","..top[1]..";".. 0.1 ..","..v.height..";#000000]" if v.background then form = form .. "bgcolor["..v.colour..";"..tostring(v.fullscreen).."]" end if v.col.col then form = form.."listcolors["..v.col.bg_normal..";"..v.col.bg_hover if v.col.set_border then form = form..";"..v.col.border if v.col.set_tool then form = form..";"..v.col.tool_bg..";"..v.col.tool_font end end form = form.."]" end elseif v.type == "Button" then if v.image then -- image option if v.item and not v.exit then form = form .. "item_image_button["..get_rect(v)..form_esc(v.texture)..";"..i.."_none;"..form_esc(v.label).."]" else form = form .. "image_button["..get_rect(v)..form_esc(v.texture)..";"..i.."_none;"..form_esc(v.label).."]" end else form = form .. "button["..get_rect(v)..i.."_none;"..form_esc(v.label).."]" end elseif v.type == "Field" then if v.password then -- password option form = form .. "pwdfield["..get_rect(v)..i.."_none;"..form_esc(v.label).."]" else form = form .. "field["..get_rect(v)..i.."_none;"..form_esc(v.label)..";"..form_esc(v.default).."]" end form = form .. "field_close_on_enter["..i.."_none;false]" elseif v.type == "TextArea" then form = form .. "textarea["..get_rect(v)..i.."_none;"..form_esc(v.label)..";"..form_esc(v.default).."]" elseif v.type == "Label" then if v.vertical then -- vertical option form = form .. "vertlabel["..get_rect(v)..form_esc(v.label).."]" else form = form .. "label["..get_rect(v)..form_esc(v.label).."]" end elseif v.type == "TextList" then local item_str = "" -- convert the list to a sting for i, item in pairs(v.items) do item_str = item_str .. form_esc(item).."," end if v.transparent then -- transparent option form = form .. "textlist["..get_rect(v)..i.."_none;"..item_str..";1;True]" else form = form .. "textlist["..get_rect(v)..i.."_none;"..item_str.."]" end elseif v.type == "DropDown" then local item_str = "" -- convert the list to a string for i, item in pairs(v.items) do item_str = item_str .. form_esc(item).."," end form = form .. "dropdown["..get_rect(v)..i.."_none;"..item_str..";"..v.select_id.."]" elseif v.type == "CheckBox" then form = form .. "checkbox["..get_rect(v)..i.."_none;"..v.label..";"..tostring(v.checked).."]" elseif v.type == "Box" then -- a coloured square form = form .. "box["..get_rect(v)..form_esc(v.colour).."]" elseif v.type == "Image" then if v.item then form = form .. "item_image["..get_rect(v)..form_esc(v.image).."]" elseif v.background then if v.fill then form = form .. "background["..left[1]-0.18 ..","..top[1]-0.22 ..";"..fwidth[1]+0.37 ..","..fheight[1]+0.7 ..";"..form_esc(v.image).."]" else form = form .. "background["..get_rect(v)..form_esc(v.image).."]" end else form = form .. "image["..get_rect(v)..form_esc(v.image).."]" end elseif v.type == "Slider" then orientation = "horizontal" if v.vertical then orientation = "vertical" end form = form .. "scrollbar["..get_rect(v)..orientation..";"..i.."_none;"..v.value.."]" elseif v.type == "InvList" then local extras = {["player:"]=1, ["nodemeta:"]=1, ["detached:"]=1} -- locations that need extra info (v.data) if extras[v.location] then form = form .. "list["..v.location..form_esc(v.data)..";"..form_esc(v.name)..";"..get_rect(v)..v.start.."]" if v.ring then -- items can be shift clicked between ring items form = form .. "listring["..v.location..form_esc(v.data)..";"..form_esc(v.name).."]" end else form = form .. "list["..v.location..";"..form_esc(v.name)..";"..get_rect(v)..v.start.."]" if v.ring then form = form .. "listring["..v.location..";"..form_esc(v.name).."]" end end elseif v.type == "Table" then local cell_str = "" local column_str = "" local most_items = 0 -- the amount of rows needed = the most items in a column for i, c in pairs(v.columns) do if #c.items > most_items then -- get max length most_items = #c.items end if i > 1 then column_str = column_str..";" end column_str = column_str .. c.type -- add column types if c.type == "image" then -- add list of available images for n, t in pairs(c.images) do column_str = column_str..","..n .."="..form_esc(t) end elseif c.type == "color" and c.distance ~= "infinite" then -- add distance that coloures affect column_str = column_str..",span="..c.distance end end for i=1, most_items do -- create a list from all column's lists for n, c in pairs(v.columns) do -- create a row from column's items if n > 1 or i > 1 then cell_str = cell_str.."," end local item = c.items[i] if item == nil then -- blank item if this column doesn't exend as far item = "" end cell_str = cell_str..form_esc(item) end end if column_str:len() > 0 then form = form .. "tablecolumns["..column_str.."]" end form = form .. "table["..get_rect(v)..i.."_none;"..cell_str..";-1]" elseif v.type == "Tooltip" then for n, w in pairs(widgets) do if w.name == v.name and w.type ~= "Tooltip" then if v.colours then form = form .. "tooltip["..n.."_none;"..v.text..";"..v.bg..";"..v.fg.."]" else form = form .. "tooltip["..n.."_none;"..v.text.."]" end end end elseif v.type == "Container - Start" then local rect = get_rect(v, true, true) left[depth+1] = rect.left -- register the new area top[depth+1] = rect.top fwidth[depth+1] = rect.width fheight[depth+1] = rect.height depth = depth+1 elseif v.type == "Container - End" then depth = depth-1 -- go back to the parent area elseif v.type == "Tabs" then local capt_str = "" for i, capt in pairs(v.captions) do if i > 1 then capt_str = capt_str.."," end capt_str = capt_str .. form_esc(capt) end form = form .. "tabheader["..get_rect(v)..i.."_none;"..capt_str..";"..v.tab..";"..tostring(v.transparent)..";"..tostring(v.border).."]" end end return form..boxes, width+5, height end ---------- -- Compiling ---------- -- generates a function to create the UI, with parameters local function generate_function() local form_esc = function(str) -- escape symbols need to be escaped return string.gsub(minetest.formspec_escape(str), "\\", "\\\\") -- which have to be escaped... end local parameters = {} -- these store info which will be put together at the end local before_str = "" local display = {} local form = "" local table_items = false local function name(v) -- converts the name into something that can be used in parameters n = v.name if v.type == "InvList" then -- the name of inv lists is used differently n = v.location.."_"..v.name end local new = "" chars = "abcdefghijklmnopqrstuvwxyz" chars = chars..string.upper(chars) for i=1, #n do local c = n:sub(i,i) if string.find(chars, c, 1, true) or (string.find("1234567890", c, 1, true) and i ~= 1) then -- numbers only allowed after first char new = new..c else new = new.."_" end end return new end local width = {widgets[1].width} if widgets[1].width_param then -- if size defined from parameters width = {"width"} end local height = {widgets[1].height} if widgets[1].height_param then height = {"height"} end local dep = 1 -- depth into containers -- returns a string containing the position and size of a widget, or (hopefully) the most efficient calculation local function get_rect(widget, real, l, t) local fwidth = width[dep] local fheight = height[dep] local wleft = "0" if type(fwidth) == "string" or l then -- if the area width (window or continer) will be changed with a parameter local l_ = l -- if the left of the widget comes from a parameter if l_ == nil then l_ = widget.left end if widget.left_type == "R-" then -- different position types wleft = fwidth..'- '..l_ elseif widget.left_type == "W/" then wleft = fwidth..'/'..l_ else wleft = l_ end if type(wleft) == "string" and not real then wleft = '"..'..wleft..' .."' end else if widget.left_type == "R-" then -- calculation made now if nothing comes from a parameter wleft = fwidth-widget.left elseif widget.left_type == "W/" then wleft = fwidth/widget.left else wleft = widget.left end end local wtop = "0" --top if type(fheight) == "string" or t then local t_ = t if t_ == nil then t_ = widget.top end if widget.top_type == "B-" then wtop = fheight..'- '..t_ elseif widget.left_type == "H/" then wtop = fheight..'/'..t_ else wtop = t_ end if type(wtop) == "string" and not real then wtop = '"..'..wtop..' .."' end else if widget.top_type == "B-" then wtop = fheight-widget.top elseif widget.left_type == "H/" then wtop = fheight/widget.top else wtop = widget.top end end if widget.right == nil then -- for widgets with no size option return wleft..","..wtop else local wright = 0 if type(fwidth) == "string" then -- if the width is changed by a parameter local l_ = l if l_ == nil then l_ = widget.left end -- goes through all right types, and for eacg, goes through all left types to get the best calculation. if widget.right_type == "R-" then -- (I know there is a better way of doing this) if widget.left_type == "R-" then wright = fwidth..'- '..widget.right..'-('..fwidth..'- '..l_..')' elseif widget.left_type == "W/" then wright = fwidth..'- '..widget.right..'-('..fwidth..'/'..l_..')' elseif type(l_) == "string" then wright = fwidth..'- '..widget.right.."- "..l_ else wright = fwidth..'- '..widget.right+l_ end elseif widget.right_type == "W/" then if widget.left_type == "R-" then wright = fwidth..'/'..widget.right..'-('..fwidth..'- '..l_..')' elseif widget.left_type == "W/" then wright = fwidth..'/'..widget.right..'-('..fwidth..'/'..l_..')' else wright = fwidth..'/'..widget.right.."- "..l_ end elseif widget.right_type == "R" then wright = widget.right else if widget.left_type == "R-" then wright = widget.right..'-('..fwidth..'- '..l_..')' elseif widget.left_type == "W/" then wright = widget.right..'-('..fwidth..'/'..l_..')' elseif type(l) == "string" then wright = widget.right.."- "..l_ else wright = widget.right-l_ end end if type(wright) == "string" and not real then wright = '"..'..wright..' .."' end elseif l then -- if there is a parameter for the left, but not the width if widget.right_type == "R-" then if widget.left_type == "R-" then wright = fwidth-widget.right..'-('..fwidth..'- '..l..')' elseif widget.left_type == "W/" then wright = fwidth-widget.right..'-('..fwidth..'/'..l..')' else wright = fwidth-widget.right.."- "..l end elseif widget.right_type == "W/" then if widget.left_type == "R-" then wright = fwidth..'/'..widget.right..'-('..fwidth..'- '..l..')' elseif widget.left_type == "W/" then wright = fwidth..'/'..widget.right..'-('..fwidth..'/'..l..')' else wright = fwidth..'/'..widget.right.."- "..l end elseif widget.right_type == "R" then wright = widget.right else if widget.left_type == "R-" then wright = widget.right..'-('..fwidth..'- '..l..')' elseif widget.left_type == "W/" then wright = widget.right..'-('..fwidth..'/'..l..')' else wright = widget.right.."- "..l end end if type(wright) == "string" and not real then wright = '"..'..wright..' .."' end else -- if all values are known now if widget.right_type == "R-" then wright = fwidth-widget.right-wleft elseif widget.right_type == "W/" then wright = fwidth/widget.right-wleft elseif widget.right_type == "R" then wright = widget.right else wright = widget.right-wleft end end local wbottom = 0 -- similar for bottom if type(fheight) == "string" then local t_ = t if t_ == nil then -- if widget's top comes from a parameter (container) t_ = widget.top end if widget.bottom_type == "B-" then if widget.top_type == "B-" then wbottom = fheight..'- '..widget.bottom..'-('..fheight..'- '..t_..')' elseif widget.left_type == "W/" then wbottom = fheight..'- '..widget.bottom..'-('..fheight..'/'..t_..')' elseif type(t_) == "string" then wbottom = fheight..'- '..widget.bottom.."- "..t_ else wbottom = fheight..'- '..widget.bottom+t_ end elseif widget.bottom_type == "H/" then if widget.top_type == "B-" then wbottom = fheight..'/'..widget.bottom..'-('..fheight..'- '..t_..')' elseif widget.left_type == "W/" then wbottom = fheight..'/'..widget.bottom..'-('..fheight..'/'..t_..')' else wbottom = fheight..'/'..widget.bottom.."- "..t_ end elseif widget.bottom_type == "R" then wbottom = widget.bottom else if widget.top_type == "B-" then wbottom = widget.bottom..'-('..fheight..'- '..t_..')' elseif widget.left_type == "W/" then wbottom = widget.bottom..'-('..fheight..'/'..t_..')' elseif type(t_) == "string" then wbottom = widget.bottom.."- "..t_ else wbottom = widget.bottom-t_ end end if type(wbottom) == "string" and not real then wbottom = '"..'..wbottom..' .."' end elseif t then if widget.bottom_type == "B-" then if widget.top_type == "B-" then wbottom = fheight-widget.bottom-fheight..'- '..t elseif widget.left_type == "W/" then wbottom = fheight-widget.bottom-fheight..'/'..t else wbottom = fheight-widget.bottom.."+"..t end elseif widget.bottom_type == "H/" then if widget.top_type == "B-" then wbottom = fheight/widget.bottom-fheight..'- '..t elseif widget.left_type == "W/" then wbottom = fheight/widget.bottom-fheight..'/'..t else wbottom = fheight/widget.bottom.."- "..t end elseif widget.bottom_type == "R" then wbottom = widget.bottom else if widget.top_type == "B-" then wbottom = widget.bottom-fheight..'- '..t elseif widget.left_type == "W/" then wbottom = widget.bottom-fheight..'/'..t elseif type(t) == "string" then wbottom = widget.bottom.."- "..t else wbottom = widget.bottom-t end end if type(wbottom) == "string" and not real then wbottom = '"..'..wbottom..' .."' end else if widget.bottom_type == "B-" then wbottom = fheight-widget.bottom-wtop elseif widget.bottom_type == "H/" then wbottom = fheight/widget.bottom-wtop elseif widget.bottom_type == "R" then wbottom = widget.bottom else wbottom = widget.bottom-wtop end end if real then return {left=wleft, top=wtop, width=wright, height=wbottom} -- container needs the values seperate end return wleft..","..wtop..";"..wright..","..wbottom end end local w, h = 0, 0 -- go through all the widgets, and add their code for i, v in pairs(widgets) do if v.type == "Display" then local w, h if v.width_param then -- things like this are for parameters table.insert(parameters, "width") w = '"..width.."' else w = tostring(v.width) end if v.height_param then table.insert(parameters, "height") h = '"..height.."' else h = tostring(v.height) end table.insert(display, '"size['..w..','..h..']"') if v.position then -- for non-default position table.insert(display, '"position['..v.left..','..v.top..']"') end if v.background then table.insert(display, '"bgcolor['..v.colour..';'..tostring(v.fullscreen)..']"') end if v.col.col then local cols = '"listcolors['..v.col.bg_normal..";"..v.col.bg_hover if v.col.set_border then cols = cols..";"..v.col.border if v.col.set_tool then cols = cols..";"..v.col.tool_bg..";"..v.col.tool_font end end cols = cols..']"' table.insert(display, cols) end elseif v.type == "Button" then if v.image then local tex = "" if v.image_param then -- image texture param table.insert(parameters, name(v).."_image") tex = '"..'..name(v)..'_image.."' else tex = form_esc(v.texture) end if v.item and not v.exit then -- quit on click table.insert(display, '"item_image_button['..get_rect(v)..';'..tex..';'..form_esc(v.name)..';'..form_esc(v.label)..']"') else if v.exit then -- quit on click - image table.insert(display, '"image_button_exit['..get_rect(v)..';'..tex..';'..form_esc(v.name)..';'..form_esc(v.label)..']"') else -- normal image table.insert(display, '"image_button['..get_rect(v)..';'..tex..';'..form_esc(v.name)..';'..form_esc(v.label)..']"') end end else if v.exit then -- quit on click table.insert(display, '"button_exit['..get_rect(v)..';'..form_esc(v.name)..';'..form_esc(v.label)..']"') else -- basic button table.insert(display, '"button['..get_rect(v)..';'..form_esc(v.name)..';'..form_esc(v.label)..']"') end end elseif v.type == "Field" then if v.password then -- password field table.insert(display, '"pwdfield['..get_rect(v)..';'..form_esc(v.name)..';'..form_esc(v.label)..']"') else local default = "" if v.default_param then -- default param table.insert(parameters, name(v).."_default") default = '"..minetest.formspec_escape('..name(v)..'_default).."' else default = form_esc(v.default) end table.insert(display, '"field['..get_rect(v)..';'..form_esc(v.name)..';'..form_esc(v.label)..';'..default..']"') end if v.enter_close == false then table.insert(display, '"field_close_on_enter['..form_esc(v.name)..';false]"') end elseif v.type == "TextArea" then local default = "" if v.default_param then table.insert(parameters, name(v).."_default") default = '"..minetest.formspec_escape('..name(v)..'_default).."' else default = form_esc(v.default) end table.insert(display, '"textarea['..get_rect(v)..';'..form_esc(v.name)..';'..form_esc(v.label)..';'..form_esc(default)..']"') elseif v.type == "Label" then local label = form_esc(v.label) if v.label_param then table.insert(parameters, name(v).."_label") label = '"..minetest.formspec_escape('..name(v)..'_label).."' end if v.vertical then -- vertical label table.insert(display, '"vertlabel['..get_rect(v)..';'..label..']"') else table.insert(display, '"label['..get_rect(v)..';'..label..']"') end elseif v.type == "TextList" then local items = "" if v.items_param then table.insert(parameters, name(v).."_items") before_str = before_str.. -- add code for converting the list from a parameter to a string ' local '..name(v)..'_item_str = ""\n' .. ' for i, item in pairs('..name(v)..'_items) do\n' .. ' if i ~= 1 then '..name(v)..'_item_str = '..name(v)..'_item_str.."," end\n' .. ' '..name(v)..'_item_str = '..name(v)..'_item_str .. minetest.formspec_escape(item)\n' .. ' end\n\n' items = '"..'..name(v)..'_item_str.."' else items = "" for i, item in pairs(v.items) do if i ~= 1 then items = items.."," end items = items .. form_esc(item) end end if v.item_id_param or v.transparent then if v.item_id_param then -- selected item parameter table.insert(parameters, name(v).."_selected_item") table.insert(display, '"textlist['.. get_rect(v)..';'..form_esc(v.name)..';'..items..';"..'..name(v)..'_selected_item..";'..tostring(v.transparent)..']"') else table.insert(display, '"textlist['..get_rect(v)..';'..form_esc(v.name)..';'..items..';1;'..tostring(v.transparent)..']"') end else table.insert(display, '"textlist['..get_rect(v)..';'..form_esc(v.name)..';'..items..']"') end elseif v.type == "DropDown" then local items = "" if v.items_param then table.insert(parameters, name(v).."_items") before_str = before_str.. -- add code for converting the list from a parameter to a string ' local '..name(v)..'_item_str = ""\n' .. ' for i, item in pairs('..name(v)..'_items) do\n' .. ' if i ~= 1 then '..name(v)..'_item_str = '..name(v)..'_item_str.."," end\n' .. ' '..name(v)..'_item_str = '..name(v)..'_item_str .. minetest.formspec_escape(item)\n' .. ' end\n\n' items = '"..'..name(v)..'_item_str.."' else items = "" for i, item in pairs(v.items) do if i ~= 1 then items = items.."," end items = items .. form_esc(item) end end local item_id = "" if v.item_id_param then -- selected item parameter table.insert(parameters, name(v).."_selected_item") item_id = '"..'..name(v)..'_selected_item.."' else item_id = tostring(v.select_id) end table.insert(display, '"dropdown['..get_rect(v)..';'..form_esc(v.name)..';'..items..';'..item_id..']"') elseif v.type == "CheckBox" then local checked = tostring(v.checked) if v.checked_param then table.insert(parameters, name(v).."_checked") checked = '"..tostring('..name(v)..'_checked).."' end table.insert(display, '"checkbox['..get_rect(v)..';'..form_esc(v.name)..";"..form_esc(v.label)..';'..checked..']"') elseif v.type == "Box" then local colour = form_esc(v.colour) if v.colour_param then table.insert(parameters, name(v).."_colour") colour = '"..'..name(v)..'_colour.."' end table.insert(display, '"box['..get_rect(v)..';'..colour..']"') elseif v.type == "Image" then local image = form_esc(v.image) if v.image_param then -- texture table.insert(parameters, name(v).."_image") image = '"..'..name(v)..'_image.."' end if v.item then table.insert(display, '"item_image['..get_rect(v)..';'..image..']"') elseif v.background then table.insert(display, '"background['..get_rect(v)..';'..image..';'..tostring(v.fill)..']"') else table.insert(display, '"image['..get_rect(v)..';'..image..']"') end elseif v.type == "Slider" then local value = form_esc(v.value) if v.value_param then table.insert(parameters, name(v).."_value") value = '"..'..name(v)..'_value.."' end local orientation = "horizontal" if v.vertical then orientation = "vertical" end table.insert(display, '"scrollbar['..get_rect(v)..';'..orientation..";"..form_esc(v.name)..";"..value..']"') elseif v.type == "InvList" then local extras = {["player:"]=1, ["nodemeta:"]=1, ["detached:"]=1} local data = "" if v.data_param then -- extra location data needed in some locations table.insert(parameters, name(v).."_data") data = '"..minetest.formspec_escape('..name(v)..'_data).."' elseif extras[v.location] then data = form_esc(v.data) end local start = v.start if v.page_param then table.insert(parameters, name(v).."_start_idx") start = '"..'..name(v)..'_start_idx.."' end table.insert(display, '"list['..v.location..data..';'..form_esc(v.name)..';'..get_rect(v)..';'..start..']"') if v.ring then -- shift clicking between item lists table.insert(display, '"listring['..v.location..data..';'..form_esc(v.name)..']"') end elseif v.type == "Table" then local cell_str = "" local column_str = "" local item_param = false local most_items = 0 for i, c in pairs(v.columns) do if #c.items > most_items then -- find how many columns are needed most_items = #c.items end if c.items_param then -- find out if any parameters are needed item_param = true end if i > 1 then -- create a column string column_str = column_str..";" end column_str = column_str .. c.type if c.type == "image" then -- add list of images for n, t in pairs(c.images) do column_str = column_str..","..n .."="..t end elseif c.type == "color" and c.distance ~= "infinite" then -- and distance affected by colour column_str = column_str..",span="..c.distance end end if not item_param then -- calculate item list if none come from parameters for i=1, most_items do for n, c in pairs(v.columns) do if n > 1 or i > 1 then cell_str = cell_str.."," end local item = c.items[i] if item == nil then item = "" end cell_str = cell_str..item end end else -- or add the code to convert the items from the parameters into a string local items = " local "..name(v).."_cells = {" for i, c in pairs(v.columns) do if c.items_param then table.insert(parameters, name(v).."_col_"..i.."_items") items = items.."\n ["..i.."]="..name(v).."_col_"..i.."_items," else local item_str = "{" -- create a string table with the items for n, item in pairs(c.items) do item_str = item_str..'"'..item..'", ' end item_str = item_str.."}" items = items.."\n ["..i.."]="..item_str.."," end end items = items.."\n }\n" table_items = true -- this makes it add the function onto the start of the function before_str = before_str..items .. ' local '..name(v)..'_cell_str = table_item_str('..name(v)..'_cells)\n\n' cell_str = '"..'..name(v)..'_cell_str.."' end if column_str:len() > 0 then -- tablecolumns without columns gives an error table.insert(display, '"tablecolumns['..column_str..']"') end local selected = "" if v.select_param then -- selected item parameter table.insert(parameters, name(v).."_selected_item") selected = '"..'..name(v)..'_selected_item.."' end table.insert(display, '"table['..get_rect(v)..";"..form_esc(v.name)..';'..cell_str..';'..selected..']"') elseif v.type == "Tooltip" then if v.colours then table.insert(display, '"tooltip['..form_esc(v.name)..';'..form_esc(v.text)..';'..form_esc(v.bg)..';'..form_esc(v.fg)..']"') else table.insert(display, '"tooltip['..form_esc(v.name)..';'..form_esc(v.text)..']"') end elseif v.type == "Container - Start" then -- container has 2 sections local l = v.left if v.left_param then -- the only widget which can hve position parameters table.insert(parameters, name(v).."_left") l = name(v)..'_left' end local t = v.top if v.top_param then table.insert(parameters, name(v).."_top") t = name(v)..'_top' end local rect = get_rect(v, true, l, t) -- the area is returned as a table this time dep = dep+1 if type(rect.width) == "string" then -- if it is a calculation, and not the calculated value width[dep] = "("..rect.width..")" else width[dep] = rect.width end if type(rect.height) == "string" then height[dep] = "("..rect.height..")" else height[dep] = rect.height end if type(rect.left) == "string" then rect.left = '"..'..rect.left..' .."' end if type(rect.top) == "string" then rect.top = '"..'..rect.top..' .."' end table.insert(display, '"container['..rect.left..','..rect.top..']"') elseif v.type == "Container - End" then -- only exits the table dep = dep-1 table.insert(display, '"container_end[]"') elseif v.type == "Tabs" then local capt_str = "" for i, capt in pairs(v.captions) do if i > 1 then capt_str = capt_str.."," end capt_str = capt_str .. form_esc(capt) end table.insert(display, '"tabheader['..get_rect(v)..';'..form_esc(v.name)..';'..capt_str..';'..v.tab..';'.. tostring(v.transparent)..';'..tostring(v.border)..']"') end end if table_items then -- the function generating a string from a list of column items before_str = '' .. -- added if a table uses parameters ' local function table_item_str(cells)\n' .. ' local most_items = 0\n' .. ' for i, v in pairs(cells) do\n' .. ' if #v > most_items then\n' .. ' most_items = #v\n' .. ' end\n' .. ' end\n' .. ' local cell_str = ""\n' .. ' for i=1, most_items do\n' .. ' for n=1, #cells do\n' .. ' if n > 1 or i > 1 then ' .. 'cell_str = cell_str.."," ' .. 'end\n' .. ' local item = cells[n][i]\n' .. ' if item == nil then ' .. 'item = "" ' .. 'end\n' .. ' cell_str = cell_str..minetest.formspec_escape(item)\n' .. ' end\n' .. ' end\n' .. ' return cell_str\n' .. ' end\n\n' .. before_str end param_str = "" -- creates the parameter string --> "param1, param2, paramN" for i, v in pairs(parameters) do if i ~= 1 then param_str = param_str .. ", " end param_str = param_str .. v end -- puts the first part of the function together form = form .. "function generate_form("..param_str..")\n" .. before_str .. '\n local form = "" ..\n' for i, v in pairs(display) do -- adds the widget strings form = form .. " "..v.." ..\n" end form = form .. ' ""\n\n return form\nend' -- completes it return form end -- generates a string for a static UI local function generate_string() local form_esc = function(str) -- escape symbols need to be escaped with escaped escape symbols ;p return string.gsub(minetest.formspec_escape(str), "\\", "\\\\") end local fwidth = {0} local fheight = {0} local dep = 1 local function get_rect(widget, real) -- can't be bothered commenting this. see function on line 73, it is basically the same... local wleft = 0 if widget.left_type == "R-" then wleft = fwidth[dep]-widget.left elseif widget.left_type == "W/" then wleft = fwidth[dep]/widget.left else wleft = widget.left end local wtop = 0 if widget.top_type == "B-" then wtop = fheight[dep]-widget.top elseif widget.left_type == "H/" then wtop = fheight[dep]/widget.top else wtop = widget.top end if widget.right == nil then -- for widgets with no size option return wleft..","..wtop..";" else local wright = 0 if widget.right_type == "R-" then wright = fwidth[dep]-widget.right-wleft elseif widget.right_type == "W/" then wright = fwidth[dep]/widget.right-wleft elseif widget.right_type == "R" then wright = widget.right else wright = widget.right-wleft end local wbottom = 0 if widget.bottom_type == "B-" then wbottom = fheight[dep]-widget.bottom-wtop elseif widget.bottom_type == "H/" then wbottom = fheight[dep]/widget.bottom-wtop elseif widget.bottom_type == "R" then wbottom = widget.bottom else wbottom = widget.bottom-wtop end if real then return {left=wleft, top=wtop, width=wright, height=wbottom} end return wleft..","..wtop..";"..wright..","..wbottom..";" end end local output = "" for i, v in pairs(widgets) do -- go through all the widgets and create their strings if v.type == "Display" then fwidth = {v.width} fheight = {v.height} output = output .. "\"size["..v.width..","..v.height.."]\" ..\n" if v.position then output = output .. "\"position["..v.left..","..v.top.."]\" ..\n" end if v.background then output = output .. "\"bgcolor["..v.colour..";"..tostring(v.fullscreen).."]\" ..\n" end if v.col.col then output = output.."\"listcolors["..v.col.bg_normal..";"..v.col.bg_hover if v.col.set_border then output = output..";"..v.col.border if v.col.set_tool then output = output..";"..v.col.tool_bg..";"..v.col.tool_font end end output = output.."]\" ..\n" end elseif v.type == "Button" then if v.image then local ending = get_rect(v)..form_esc(v.texture)..";"..form_esc(v.name)..";"..form_esc(v.label).."]\" ..\n" if v.item and not v.exit then output = output .. "\"item_image_button["..ending else if v.exit then output = output .. "\"image_button_exit["..ending else output = output .. "\"image_button["..ending end end else if v.exit then output = output .. "\"button_exit["..get_rect(v)..form_esc(v.name)..";"..form_esc(v.label).."]\" ..\n" else output = output .. "\"button["..get_rect(v)..form_esc(v.name)..";"..form_esc(v.label).."]\" ..\n" end end elseif v.type == "Field" then if v.password then output = output .. "\"pwdfield["..get_rect(v)..form_esc(v.name)..";"..form_esc(v.label).."]\" ..\n" else output = output .. "\"field["..get_rect(v)..form_esc(v.name)..";"..form_esc(v.label)..";"..form_esc(v.default).."]\" ..\n" end if v.enter_close == false then output = output .. "\"field_close_on_enter["..form_esc(v.name)..";false]\" ..\n" end elseif v.type == "TextArea" then output = output .. "\"textarea["..get_rect(v)..form_esc(v.name)..";"..form_esc(v.label)..";"..form_esc(v.default).."]\" ..\n" elseif v.type == "Label" then if v.vertical then output = output .. "\"vertlabel["..get_rect(v)..form_esc(v.label).."]\" ..\n" else output = output .. "\"label["..get_rect(v)..form_esc(v.label).."]\" ..\n" end elseif v.type == "TextList" then local item_str = "" for i, item in pairs(v.items) do item_str = item_str .. form_esc(item).."," end if not v.transparent then output = output .. "\"textlist["..get_rect(v)..form_esc(v.name)..";"..item_str:sub(0,-2).."]\" ..\n" else output = output .. "\"textlist["..get_rect(v)..form_esc(v.name)..";"..item_str:sub(0,-2)..";1;true]\" ..\n" end elseif v.type == "DropDown" then local item_str = "" for i, item in pairs(v.items) do item_str = item_str .. form_esc(item).."," end output = output .. "\"dropdown["..get_rect(v)..form_esc(v.name)..";"..item_str:sub(0,-2)..";"..v.select_id.."]\" ..\n" elseif v.type == "CheckBox" then output = output .. "\"checkbox["..get_rect(v)..form_esc(v.name)..";"..form_esc(v.label)..";"..tostring(v.checked).."]\" ..\n" elseif v.type == "Box" then output = output .. "\"box["..get_rect(v)..form_esc(v.colour).."]\" ..\n" elseif v.type == "Image" then if v.item then output = output .. "\"item_image["..get_rect(v)..form_esc(v.image).."]\" ..\n" elseif v.background then output = output .. "\"background["..get_rect(v)..form_esc(v.image)..";"..tostring(v.fill).."]\" ..\n" else output = output .. "\"image["..get_rect(v)..form_esc(v.image).."]\" ..\n" end elseif v.type == "Slider" then orientation = "horizontal" if v.vertical then orientation = "vertical" end output = output .. "\"scrollbar["..get_rect(v)..orientation..";"..form_esc(v.name)..";"..v.value.."]\" ..\n" elseif v.type == "InvList" then local extras = {["player:"]=1, ["nodemeta:"]=1, ["detached:"]=1} if extras[v.location] then output = output .. "\"list["..v.location..form_esc(v.data)..";"..form_esc(v.name)..";"..get_rect(v)..v.start.."]\" ..\n" if v.ring then output = output .. "\"listring["..v.location..form_esc(v.data)..";"..form_esc(v.name).."]\" ..\n" end else output = output .. "\"list["..v.location..";"..form_esc(v.name)..";"..get_rect(v)..v.start.."]\" ..\n" if v.ring then output = output .. "\"listring["..v.location..";"..form_esc(v.name).."]\" ..\n" end end elseif v.type == "Table" then local cell_str = "" local column_str = "" -- this converts the column's individual item lists into a string local most_items = 0 for i, c in pairs(v.columns) do if #c.items > most_items then most_items = #c.items -- gets the largest size end if i > 1 then column_str = column_str..";" end column_str = column_str .. c.type -- creates the column list -- adds column parameters \/ if c.type == "image" then for n, t in pairs(c.images) do column_str = column_str..","..n .."="..t end elseif c.type == "color" and c.distance ~= "infinite" then column_str = column_str..",span="..c.distance end end for i=1, most_items do -- adds all the items together for n, c in pairs(v.columns) do if n > 1 or i > 1 then cell_str = cell_str.."," end local item = c.items[i] if item == nil then -- columns with less items get blank items to make them the same length item = "" end cell_str = cell_str..item end end if column_str:len() > 0 then output = output .. "\"tablecolumns["..column_str.."]\" ..\n" end output = output .. "\"table["..get_rect(v)..form_esc(v.name)..";"..cell_str..";]\" ..\n" elseif v.type == "Tooltip" then if v.colours then output = output .. "\"tooltip["..form_esc(v.name)..";"..form_esc(v.text)..";"..form_esc(v.bg)..";"..form_esc(v.fg).."]\" ..\n" else output = output .. "\"tooltip["..form_esc(v.name)..";"..form_esc(v.text).."]\" ..\n" end elseif v.type == "Container - Start" then local rect = get_rect(v, true) fwidth[dep+1] = rect.width -- set the width and height that widgets in the container use fheight[dep+1] = rect.height dep = dep+1 output = output .. "\"container["..rect.left..","..rect.top.."]\" ..\n" elseif v.type == "Container - End" then dep = dep-1 -- close container output = output .. "\"container_end[]\" ..\n" elseif v.type == "Tabs" then local capt_str = "" for i, capt in pairs(v.captions) do if i > 1 then capt_str = capt_str.."," end capt_str = capt_str .. form_esc(capt) end output = output .. "\"tabheader["..get_rect(v)..form_esc(v.name)..";"..capt_str..";"..v.tab..";"..tostring(v.transparent)..";"..tostring(v.border).."]\" ..\n" end end return output .. '""' end ---------- -- UI Editors ---------- -- creates a position chooser with << and >> buttons, text box, and position type (if needed) local function ui_position(name, value, left, top, typ, typ_id) name = form_esc(name) local form = "".. "label["..left+0.1 ..","..top-0.3 ..";"..name.."]" .. "button["..left+0.1 ..","..top..";1,1;"..name.."_size_down;<<]" .. "field["..left+1.3 ..","..top+0.3 ..";1,1;"..name.."_size;;"..form_esc(value).."]" .. "field_close_on_enter["..name.."_size;false]" .. "button["..left+1.9 ..","..top..";1,1;"..name.."_size_up;>>]" local typ_ids = {["L+"]=1, ["T+"]=1, ["R-"]=2, ["B-"]=2, ["W/"]=3, ["H/"]=3, ["R"]=4} if typ == "LEFT" then -- left and right sides use this type if name == "RIGHT" then -- but right has a relative option (I should make it right type, but I decided much later to include it) form = form .."dropdown["..left+3 ..","..top+0.1 ..";1.1,1;"..name.."_type;LEFT +,RIGHT -,WIDTH /,RELATIVE;"..typ_ids[typ_id].."]" else form = form .. "dropdown["..left+3 ..","..top+0.1 ..";1.1,1;"..name.."_type;LEFT +,RIGHT -,WIDTH /;"..typ_ids[typ_id].."]" end elseif typ == "TOP" then if name == "BOTTOM" then form = form.."dropdown["..left+3 ..","..top+0.1 ..";1.1,1;"..name.."_type;TOP +,BOTTOM -,HEIGHT /,RELATIVE;"..typ_ids[typ_id].."]" else form = form .. "dropdown["..left+3 ..","..top+0.1 ..";1.1,1;"..name.."_type;TOP +,BOTTOM -,HEIGHT /;"..typ_ids[typ_id].."]" end end return form end -- handles position ui functionality local function handle_position_changes(id, fields, range) local pos_names = {"width", "height", "left", "top", "right", "bottom", "value"} -- the only names it will check for i, v in pairs(pos_names) do if fields[string.upper(v).."_size_down"] then -- down button if range and range[v] then widgets[id][v] = widgets[id][v] - range[v]/10 -- slider uses a different step else widgets[id][v] = widgets[id][v] - 0.1 end if widgets[id][v] < 0.0001 and widgets[id][v] > -0.0001 then widgets[id][v] = 0 end -- weird number behaviour elseif fields[string.upper(v).."_size_up"] then -- up button if range and range[v] then widgets[id][v] = widgets[id][v] + range[v]/10 else widgets[id][v] = widgets[id][v] + 0.1 end if widgets[id][v] < 0.0001 and widgets[id][v] > -0.0001 then widgets[id][v] = 0 end -- weird number behaviour elseif fields.key_enter_field == string.upper(v).."_size" then -- size edit box/displayer local value = tonumber(fields[string.upper(v).."_size"]) if value ~= nil then widgets[id][v] = value end elseif fields[string.upper(v).."_type"] then -- type selector local typ_trans = {["LEFT +"]="L+", ["RIGHT -"]="R-", ["WIDTH /"]="W/", ["TOP +"]="T+", ["BOTTOM -"]="B-", ["HEIGHT /"]="H/", ["RELATIVE"]="R"} widgets[id][v.."_type"] = typ_trans[fields[string.upper(v).."_type"]] end if range then -- sometimes the number must be within a range if range[v] then if widgets[id][v] < 0 then widgets[id][v] = 0 elseif widgets[id][v] > range[v] then widgets[id][v] = range[v] end end end end end -- creates a field to edit name or other attributes, and a parameter checkbox (if needed) local function ui_field(name, value, left, top, param) name = form_esc(name) local field = "" .. "field["..left+0.2 ..","..top..";2.8,1;"..name.."_input_box;"..name..";"..form_esc(value).."]" .. "field_close_on_enter["..name.."_input_box;false]" if param ~= nil then field = field .. "checkbox["..left+2.8 ..","..top-0.3 ..";"..name.."_param_box;parameter;"..tostring(param).."]" end return field end -- handles field functionality local function handle_field_changes(names, id, fields) for i, v in pairs(names) do -- names are supplied this time, so only nececary ones are checked if fields.key_enter_field == string.upper(v).."_input_box" then widgets[id][v] = fields[string.upper(v).."_input_box"] elseif fields[string.upper(v).."_param_box"] then widgets[id][v.."_param"] = fields[string.upper(v).."_param_box"] == "true" end end end ---------- -- individual widget definitions -- functions for widget's custom editing UIs at the side local widget_editor_uis = { Display = { -- type can be seen here, etc, extra tabs (options, new widget) are at the end ui = function(id, left, top, width) -- function for creating the form local form = "label["..left+1.7 ..","..top ..";- DISPLAY -]" if not widgets[id].colour_tab then form = form .. "button["..left+width-3 ..","..top+6.7 ..";3.1,1;col_page;INVENTORY COLOURS >]" .. ui_position("WIDTH", widgets[id].width, left, top+0.7) .. ui_position("HEIGHT", widgets[id].height, left, top+1.7) .. "checkbox["..left+3 ..","..top+0.7 ..";WIDTH_param_box;parameter;"..tostring(widgets[id].width_param).."]" .. "checkbox["..left+3 ..","..top+1.7 ..";HEIGHT_param_box;parameter;"..tostring(widgets[id].height_param).."]" if widgets[id].position then -- this part only gets displayed if the position checkbox is checked form = form .. ui_position("LEFT", widgets[id].left, left, top+2.7) .. ui_position("TOP", widgets[id].top, left, top+3.7) .. "checkbox["..left+0.1 ..","..top+4.3 ..";pos_box;position;true]" .. "checkbox["..left+2 ..","..top+4.3 ..";back_box;background;"..tostring(widgets[id].background).."]" if widgets[id].background then form = form.. ui_field("COLOUR", widgets[id].colour, left+0.2, top+5.5) .. "checkbox["..left+3 ..","..top+5.2 ..";full;fullscreen;"..tostring(widgets[id].fullscreen).."]" end else form = form .. "checkbox["..left+0.1 ..","..top+2.3 ..";pos_box;position;false]" .. "checkbox["..left+2 ..","..top+2.3 ..";back_box;background;"..tostring(widgets[id].background).."]" if widgets[id].background then form = form.. ui_field("COLOUR", widgets[id].colour, left+0.2, top+3.5) .. "checkbox["..left+3 ..","..top+3.2 ..";full;fullscreen;"..tostring(widgets[id].fullscreen).."]" end end else form = form .. "checkbox["..left+0.1 ..","..top+0.3 ..";do_col;COLOURS;"..tostring(widgets[id].col.col).."]" .. "button["..left+width-1.2 ..","..top+6.7 ..";1.3,1;dat_page;BACK <]" if widgets[id].col.col then form = form .. "field["..left+0.4 ..","..top+1.5 ..";2.8,1;bg_main;BACKGROUND;"..widgets[id].col.bg_normal.."]" .. "field_close_on_enter[bg_main;false]" .. "field["..left+0.4 ..","..top+2.5 ..";2.8,1;bg_hover;HOVER BACKGROUND;"..widgets[id].col.bg_hover.."]" .. "field_close_on_enter[bg_hover;false]" .. "checkbox["..left+0.1 ..","..top+2.8 ..";do_border;BORDER;"..tostring(widgets[id].col.set_border).."]" if widgets[id].col.set_border then form = form .. "field["..left+0.4 ..","..top+4 ..";2.8,1;border;BORDER;"..widgets[id].col.border.."]" .. "field_close_on_enter[border;false]" .. "checkbox["..left+0.1 ..","..top+4.3 ..";do_tool;TOOLTIP;"..tostring(widgets[id].col.set_tool).."]" if widgets[id].col.set_tool then form = form .. "field["..left+0.4 ..","..top+5.5 ..";2.8,1;bg_tool;BACKGROUND;"..widgets[id].col.tool_bg.."]" .. "field_close_on_enter[bg_tool;false]" .. "field["..left+0.4 ..","..top+6.5 ..";2.8,1;tool_text;TEXT;"..widgets[id].col.tool_font.."]" .. "field_close_on_enter[tool_text;false]" end end end end return form end, func = function(id, fields) -- function for handling the form handle_position_changes(id, fields, {left=1, top=1}) handle_field_changes({"colour"}, id, fields) if fields.WIDTH_param_box then widgets[id].width_param = fields.WIDTH_param_box == "true" elseif fields.HEIGHT_param_box then widgets[id].height_param = fields.HEIGHT_param_box == "true" elseif fields.pos_box then widgets[id].position = fields.pos_box == "true" elseif fields.back_box then widgets[id].background = fields.back_box == "true" elseif fields.full then widgets[id].fullscreen = fields.full == "true" elseif fields.col_page then -- colour tab widgets[id].colour_tab = true elseif fields.dat_page then widgets[id].colour_tab = false elseif fields.do_col then widgets[id].col.col = fields.do_col == "true" elseif fields.do_border then widgets[id].col.set_border = fields.do_border == "true" elseif fields.do_tool then widgets[id].col.set_tool = fields.do_tool == "true" elseif fields.key_enter_field == "bg_main" then widgets[id].col.bg_normal = fields.bg_main elseif fields.key_enter_field == "bg_hover" then widgets[id].col.bg_hover = fields.bg_hover elseif fields.key_enter_field == "border" then widgets[id].col.border = fields.border elseif fields.key_enter_field == "bg_tool" then widgets[id].col.tool_bg = fields.bg_tool elseif fields.key_enter_field == "tool_text" then widgets[id].col.tool_font = fields.tool_text end reload_ui() -- refresh the display and save the file end }, Button = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- BUTTON -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+0.7) .. -- all have a name box ui_position("LEFT", widgets[id].left, left, top+1.4, "LEFT", widgets[id].left_type) .. -- and an area or position ui_position("TOP", widgets[id].top, left, top+2.4, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.4, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+4.4, "TOP", widgets[id].bottom_type) .. ui_field("LABEL", widgets[id].label, left+0.2, top+5.7) .. -- then extra things "" if widgets[id].image then form = form .. ui_field("TEXTURE", widgets[id].texture, left+0.2, top+6.7) .. "checkbox["..left+3 ..","..top+6.4 ..";image_param_box;parameter;"..tostring(widgets[id].image_param).."]" .. "checkbox["..left+1.8 ..","..top+7 ..";image_box;image;true]" .. "checkbox["..left+0.1 ..","..top+7 ..";close_box;exit form;"..tostring(widgets[id].exit).."]" if not widgets[id].exit then form = form .. "checkbox["..left+3 ..","..top+7 ..";item_box;item;"..tostring(widgets[id].item).."]" end else form = form .. "checkbox["..left+1.8 ..","..top+6 ..";image_box;image;false]" .. "checkbox["..left+0.1 ..","..top+6 ..";close_box;exit form;"..tostring(widgets[id].exit).."]" end return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name", "label", "texture"}, id, fields) if fields.image_box then widgets[id].image = fields.image_box == "true" elseif fields.image_param_box then widgets[id].image_param = fields.image_param_box == "true" elseif fields.item_box then widgets[id].item = fields.item_box == "true" elseif fields.close_box then widgets[id].exit = fields.close_box == "true" end reload_ui() end }, Field = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- FIELD -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. ui_field("LABEL", widgets[id].label, left+0.2, top+5) .. "" if widgets[id].password then form = form.."checkbox["..left+0.1 ..","..top+5.3 ..";password_box;password;true]" .. "checkbox["..left+0.1 ..","..top+5.7 ..";enter_close_box;close form on enter;"..tostring(widgets[id].enter_close).."]" else form = form.. ui_field("DEFAULT", widgets[id].default, left+0.2, top+6, widgets[id].default_param) .. "checkbox["..left+0.1 ..","..top+6.3 ..";password_box;password;false]" .. "checkbox["..left+0.1 ..","..top+6.7 ..";enter_close_box;close form on enter;"..tostring(widgets[id].enter_close).."]" end return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name", "label", "default"}, id, fields) if fields.password_box then widgets[id].password = fields.password_box == "true" elseif fields.enter_close_box then widgets[id].enter_close = fields.enter_close_box == "true" end reload_ui() end }, TextArea = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- TextArea -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+4.7, "TOP", widgets[id].bottom_type) .. ui_field("LABEL", widgets[id].label, left+0.2, top+6) .. ui_field("DEFAULT", widgets[id].default, left+0.2, top+7, widgets[id].default_param) .. "" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name", "label", "default"}, id, fields) reload_ui() end }, Label = { ui = function(id, left, top, width) local form = "label["..left+2 ..","..top ..";- Label -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_field("LABEL", widgets[id].label, left+0.2, top+4, widgets[id].label_param) .. "checkbox["..left+0.1 ..","..top+4.3 ..";vert_box;vertical;"..tostring(widgets[id].vertical).."]" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name", "label"}, id, fields) if fields.vert_box then widgets[id].vertical = fields.vert_box == "true" end reload_ui() end }, TextList = { ui = function(id, left, top, width) local item_str = "" for i, v in pairs(widgets[id].items) do item_str = item_str .. form_esc(v) .. "," end local form = "label["..left+1.8 ..","..top ..";- TextList -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+4.7, "TOP", widgets[id].bottom_type) .. "label["..left+0.1 ..","..top+5.4 ..";ITEMS]" .. "textlist["..left+0.1 ..","..top+5.75 ..";2.6,0.7;item_list;"..item_str.."]" .. "field["..left+3.3 ..","..top+6 ..";1.8,1;item_input;;]" .. "field_close_on_enter[item_input;false]" .. "checkbox["..left+0.1 ..","..top+6.3 ..";items_param_box;items parameter;"..tostring(widgets[id].items_param).."]" .. "checkbox["..left+0.1 ..","..top+6.7 ..";item_id_param_box;selected item id parameter;"..tostring(widgets[id].item_id_param).."]" .. "checkbox["..left+3 ..","..top+6.7 ..";transparent_box;transparent;"..tostring(widgets[id].transparent).."]" .. "" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name"}, id, fields) if fields.item_list then -- common (lazy) way I make editable lists if string.sub(fields.item_list, 1, 3) == "DCL" then -- remove table.remove(widgets[id].items, tonumber(string.sub(fields.item_list, 5))) end elseif fields.key_enter_field == "item_input" then -- add table.insert(widgets[id].items, fields.item_input) elseif fields.items_param_box then widgets[id].items_param = fields.items_param_box == "true" elseif fields.item_id_param_box then widgets[id].item_id_param = fields.item_id_param_box == "true" elseif fields.transparent_box then widgets[id].transparent = fields.transparent_box == "true" end reload_ui() end }, DropDown = { ui = function(id, left, top, width) local item_str = "" for i, v in pairs(widgets[id].items) do item_str = item_str .. form_esc(v) .. "," end local form = "label["..left+1.8 ..","..top ..";- DropDown -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. "label["..left+0.1 ..","..top+4.4 ..";ITEMS]" .. "label["..left+1.8 ..","..top+4.4 ..";selected: "..widgets[id].select_id.."]" .. "textlist["..left+0.1 ..","..top+4.75 ..";2.6,0.7;item_list;"..item_str.."]" .. "field["..left+3.3 ..","..top+5 ..";1.8,1;item_input;;]" .. "field_close_on_enter[item_input;false]" .. "checkbox["..left+0.1 ..","..top+5.3 ..";items_param_box;items parameter;"..tostring(widgets[id].items_param).."]" .. "checkbox["..left+0.1 ..","..top+5.7 ..";item_id_param_box;selected item id parameter;"..tostring(widgets[id].item_id_param).."]" .. "" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name"}, id, fields) if fields.item_list then if string.sub(fields.item_list, 1, 3) == "DCL" then table.remove(widgets[id].items, tonumber(string.sub(fields.item_list, 5))) else widgets[id].select_id = tonumber(string.sub(fields.item_list, 5)) end elseif fields.key_enter_field == "item_input" then table.insert(widgets[id].items, fields.item_input) elseif fields.items_param_box then widgets[id].items_param = fields.items_param_box == "true" elseif fields.item_id_param_box then widgets[id].item_id_param = fields.item_id_param_box == "true" end reload_ui() end }, CheckBox = { ui = function(id, left, top, width) local form = "label["..left+2 ..","..top ..";- Label -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_field("LABEL", widgets[id].label, left+0.2, top+4) .. "checkbox["..left+0.1 ..","..top+4.3 ..";checked_box;checked;"..tostring(widgets[id].checked).."]" .. "checkbox["..left+0.1 ..","..top+4.7 ..";checked_param_box;checked parameter;"..tostring(widgets[id].checked_param).."]" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name", "label"}, id, fields) if fields.checked_box then widgets[id].checked = fields.checked_box == "true" elseif fields.checked_param_box then widgets[id].checked_param = fields.checked_param_box == "true" end reload_ui() end }, Box = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- Box -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+4.7, "TOP", widgets[id].bottom_type) .. ui_field("COLOUR", widgets[id].colour, left+0.2, top+6, widgets[id].colour_param) .. "" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name", "colour"}, id, fields) reload_ui() end }, Image = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- Image -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+4.7, "TOP", widgets[id].bottom_type) .. ui_field("IMAGE", widgets[id].image, left+0.2, top+6, widgets[id].image_param) .. "checkbox["..left+0.1 ..","..top+6.3 ..";item_box;item;"..tostring(widgets[id].item).."]" .. "" if not widgets[id].item then form = form .. "checkbox["..left+1.5 ..","..top+6.3 ..";back_box;background;"..tostring(widgets[id].background).."]" if widgets[id].background then form = form .. "checkbox["..left+1.5 ..","..top+6.7 ..";fill_box;fill;"..tostring(widgets[id].fill).."]" end end return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name", "image"}, id, fields) if fields.item_box then widgets[id].item = fields.item_box == "true" elseif fields.back_box then widgets[id].background = fields.back_box == "true" elseif fields.fill_box then widgets[id].fill = fields.fill_box == "true" end reload_ui() end }, Slider = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- Slider -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+4.7, "TOP", widgets[id].bottom_type) .. ui_position("VALUE", widgets[id].value, left, top+5.7) .. "checkbox["..left+3 ..","..top+5.7 ..";value_param_box;parameter;"..tostring(widgets[id].value_param).."]" .. "dropdown["..left+0.1 ..","..top+6.7 ..";2,1;orientation;horizontal,vertical;"..(widgets[id].vertical and 2 or 1).."]" .. "" return form end, func = function(id, fields) handle_position_changes(id, fields, {value=1000}) handle_field_changes({"name"}, id, fields) if fields.value_param_box then widgets[id].value_param = fields.value_param_box == "true" elseif fields.orientation then local new = fields.orientation == "vertical" if widgets[id].vertical ~= new then -- swaps the width and height to make it nicer to edit widgets[id].vertical = new widgets[id].right, widgets[id].bottom = widgets[id].bottom, widgets[id].right end end reload_ui() end }, InvList = { ui = function(id, left, top, width) local location_values = {context=1, current_player=2, ["player:"]=3, ["nodemeta:"]=4, ["detached:"]=5} local form = "label["..left+1.4 ..","..top ..";- Inventory List -]" .. ui_position("LEFT", widgets[id].left, left, top+0.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+1.7, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+2.7, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+3.7, "TOP", widgets[id].bottom_type) .. "label["..left+0.1 ..","..top+4.4 ..";LOCATION]" .. "dropdown["..left+0.1 ..","..top+4.75 ..";2.8;location_select;context,current_player,player:,nodemeta:,detached:;" .. location_values[widgets[id].location].."]" .. ui_field("NAME", widgets[id].name, left+0.2, top+6) .. "field["..left+0.4 ..","..top+7 ..";1,1;start;START;"..widgets[id].start.."]" .. "field_close_on_enter[start;false]" .. "checkbox["..left+1.5 ..","..top+6.7 ..";start_box;param;"..tostring(widgets[id].start_param).."]" .. "checkbox["..left+3 ..","..top+5.9 ..";ring_box;ring;"..tostring(widgets[id].ring).."]" local extras = {["player:"]=1, ["nodemeta:"]=1, ["detached:"]=1} -- these locations need extra data if extras[widgets[id].location] then form = form .. "field["..left+3.3 ..","..top+5 ..";1.7,1;data;DATA;"..form_esc(widgets[id].data).."]" .. "field_close_on_enter[data;false]" .. "checkbox["..left+3 ..","..top+5.5 ..";data_box;data param;"..tostring(widgets[id].data_param).."]" end return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name"}, id, fields) if fields.ring_box then widgets[id].ring= fields.ring_box == "true" elseif fields.start_box then widgets[id].start_param = fields.start_box == "true" elseif fields.data_box then widgets[id].data_param = fields.data_box == "true" elseif fields.key_enter_field == "data" then widgets[id].data = fields.data elseif fields.key_enter_field == "start" then widgets[id].start = tonumber(fields.start) if widgets[id].start == nil then widgets[id].start = 0 end elseif fields.location_select then widgets[id].location = fields.location_select end reload_ui() end }, Tooltip = { ui = function(id, left, top, width) local form = "label["..left+1.7 ..","..top ..";- Tooltip -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_field("TEXT", widgets[id].text, left+0.2, top+2) .. "" if widgets[id].colours then form = form .. "field["..left+0.4 ..","..top+3 ..";2.8,1;bg;BACKGROUND;"..form_esc(widgets[id].bg).."]" .. "field_close_on_enter[bg;false]" .. "field["..left+0.4 ..","..top+4 ..";2.8,1;fg;TEXT COLOUR;"..form_esc(widgets[id].fg).."]" .. "field_close_on_enter[fg;false]" .. "checkbox["..left+0.12 ..","..top+4.4 ..";col_box;colours;true]" else form = form .. "checkbox["..left+0.12 ..","..top+2.4 ..";col_box;colours;false]" end return form end, func = function(id, fields) handle_field_changes({"name", "text"}, id, fields) if fields.key_enter_field == "bg" then widgets[id].bg = fields.bg elseif fields.key_enter_field == "fg" then widgets[id].fg = fields.fg elseif fields.col_box then widgets[id].colours = fields.col_box == "true" end reload_ui() end }, Table = { ui = function(id, left, top, width) local column_str = "" for i, v in pairs(widgets[id].columns) do column_str = column_str..","..i..": "..v.type end local form = "label["..left+1.8 ..","..top ..";- Table -]" .. "textlist["..left+0.1 ..","..top+0.4 ..";2.5,1.5;column_select;#ffff00DATA,#ffff00- columns: "..column_str..";"..widgets[id].selected_column+2 ..";]" .. "button["..left+2.7 ..","..top+0.3 ..";0.5,1;column_up;/\\\\]" .. "button["..left+2.7 ..","..top+1.15 ..";0.5,1;column_down;\\\\/]" .. "button["..left+3.1 ..","..top+0.3 ..";0.8,1;column_add;+]" .. "button["..left+3.1 ..","..top+1.15 ..";0.8,1;column_remove;-]" if widgets[id].selected_column == -1 then -- the data tab (size, name, etc) form = form .. ui_field("NAME", widgets[id].name, left+0.2, top+2.5) .. ui_position("LEFT", widgets[id].left, left, top+3.2, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+4.2, "TOP", widgets[id].top_type) .. ui_position("RIGHT", widgets[id].right, left, top+5.2, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+6.2, "TOP", widgets[id].bottom_type) .. "checkbox["..left+0.1 ..","..top+6.9 ..";select_param_box;selected item param;"..tostring(widgets[id].select_param).."]" elseif widgets[id].selected_column > 0 then -- item controller local c = widgets[id].columns[widgets[id].selected_column] typ_convt = {text=1, image=2, color=3, indent=4, tree=5} local items_str = "" for i, v in pairs(c.items) do items_str = items_str..i..": "..v.."," end form = form .. "label["..left+0.1 ..","..top+1.9 ..";TYPE]" .. "dropdown["..left+0.1 ..","..top+2.3 ..";2.7,1;column_type;text,image,color,indent,tree;"..typ_convt[c.type].."]" .. "label["..left+0.1 ..","..top+2.9 ..";ITEMS]" .. "textlist["..left+0.1 ..","..top+3.3 ..";2.5,1.5;item_lst;"..items_str..";".. c.selected_item..";]" .. "button["..left+2.7 ..","..top+3.2 ..";0.5,1;item_up;/\\\\]" .. "button["..left+2.7 ..","..top+4.05 ..";0.5,1;item_down;\\\\/]" .. "button["..left+3.1 ..","..top+3.2 ..";0.8,1;item_add;+]" .. "button["..left+3.1 ..","..top+4.05 ..";0.8,1;item_remove;-]" .. "checkbox["..left+2.7 ..","..top+4.6 ..";item_param_box;items parameter;"..tostring(c.items_param).."]" if #c.items > 0 then -- item editor form = form .. "field["..left+0.4 ..","..top+5.4 ..";2.5,1;item_edit;ITEM;"..c.items[c.selected_item].."]" .. "field_close_on_enter[item_edit;false]" -- some column types need extra stuff if c.type == "image" then -- image local img_str = "" for i, v in pairs(c.images) do img_str = img_str..i ..": "..v.."," end form = form .. "label["..left+0.1 ..","..top+5.8 ..";IMAGES]" .. "textlist["..left+0.1 ..","..top+6.2 ..";2.5,1.4;image_lst;"..img_str.."]" .. "field["..left+3 ..","..top+6.4 ..";2,1;image_add;;]" .. "field_close_on_enter[image_add;false]" elseif c.type == "color" then -- colour form = form .. "field["..left+0.4 ..","..top+6.4 ..";2.5,1;colour_len;DISTANCE;"..c.distance.."]" .. "field_close_on_enter[colour_len;false]" end end end return form end, func = function(id, fields) -- basic stuff handle_position_changes(id, fields) handle_field_changes({"name"}, id, fields) local number_usrs = {indent=1, tree=1, image=1} local c = widgets[id].columns[widgets[id].selected_column] -- column selector and editor if fields.column_select then widgets[id].selected_column = tonumber(string.sub(fields.column_select, 5))-2 elseif fields.column_add then -- column def table.insert(widgets[id].columns, {type="text", items={}, images={}, selected_item=1, items_param=false, distance="infinite"}) widgets[id].selected_column = #widgets[id].columns elseif fields.column_remove and widgets[id].selected_column > 0 then table.remove(widgets[id].columns, widgets[id].selected_column) widgets[id].selected_column = widgets[id].selected_column-1 elseif fields.column_down and widgets[id].selected_column < #widgets[id].columns and widgets[id].selected_column > 0 then table.insert(widgets[id].columns, widgets[id].selected_column+1, table.remove(widgets[id].columns, widgets[id].selected_column)) widgets[id].selected_column = widgets[id].selected_column+1 elseif fields.column_up and widgets[id].selected_column > 1 then table.insert(widgets[id].columns, widgets[id].selected_column-1, table.remove(widgets[id].columns, widgets[id].selected_column)) widgets[id].selected_column = widgets[id].selected_column-1 elseif fields.select_param_box then widgets[id].select_param = fields.select_param_box == "true" -- item editor elseif fields.item_lst then c.selected_item = tonumber(string.sub(fields.item_lst, 5)) if c.selected_item > #c.items then c.selected_item = #c.items end elseif fields.item_add then if number_usrs[c.type] then table.insert(c.items, 0) else table.insert(c.items, "-") end c.selected_item = #c.items elseif fields.item_remove then table.remove(c.items, c.selected_item) if c.selected_item > 1 then c.selected_item = c.selected_item-1 end elseif fields.item_down and c.selected_item < #c.items then table.insert(c.items, c.selected_item+1, table.remove(c.items, c.selected_item)) c.selected_item = c.selected_item+1 elseif fields.item_up and c.selected_item > 1 then table.insert(c.items, c.selected_item-1, table.remove(c.items, c.selected_item)) c.selected_item = c.selected_item-1 elseif fields.item_param_box then c.items_param = fields.item_param_box == "true" elseif fields.key_enter_field == "item_edit" then c.items[c.selected_item] = fields.item_edit if number_usrs[c.type] then c.items[c.selected_item] = tonumber(fields.item_edit) if c.items[c.selected_item] == nil then c.items[c.selected_item] = 0 end c.items[c.selected_item] = math.floor(c.items[c.selected_item]) end -- extra things for column types elseif fields.key_enter_field == "image_add" then table.insert(c.images, fields.image_add) elseif fields.image_lst and string.sub(fields.image_lst, 1, 3) == "DCL" then table.remove(c.images, tonumber(string.sub(fields.image_lst, 5))) elseif fields.colour_len then c.distance = tonumber(fields.colour_len) if c.distance == nil or c.distance <= 0 then c.distance = "infinite" else c.distance = math.floor(c.distance) end elseif fields.column_type then c.type = fields.column_type if number_usrs[c.type] then for i, v in pairs(c.items) do c.items[i] = tonumber(v) if c.items[i] == nil then c.items[i] = 0 end end end end reload_ui() end }, ["Container - Start"] = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- Container -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. "label["..left+3.8 ..","..top+1.4 ..";parameter]" .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. "checkbox["..left+4.2 ..","..top+1.7 ..";left_param_box;;"..tostring(widgets[id].left_param).."]" .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. "checkbox["..left+4.2 ..","..top+2.7 ..";top_param_box;;"..tostring(widgets[id].top_param).."]" .. ui_position("RIGHT", widgets[id].right, left, top+3.7, "LEFT", widgets[id].right_type) .. ui_position("BOTTOM", widgets[id].bottom, left, top+4.7, "TOP", widgets[id].bottom_type) .. "" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name"}, id, fields) if fields.left_param_box then widgets[id].left_param = fields.left_param_box == "true" elseif fields.top_param_box then widgets[id].top_param = fields.top_param_box == "true" end reload_ui() end, del = function(id) table.remove(widgets, id) local depth = 0 while id <= #widgets and depth > -1 do -- find which container end belongs to this container and delete it too if widgets[id].type == "Container - Start" then depth = depth+1 elseif widgets[id].type == "Container - End" then if depth == 0 then table.remove(widgets, id) end depth = depth-1 end id = id+1 end end }, ["Container - End"] = { ui = function(id, left, top, width) local name = "" local depth = 0 local pos = id-1 while pos > 0 and depth > -1 do -- find which container start belongs to this container and display it's name if widgets[pos].type == "Container - Start" then if depth == 0 then name = widgets[pos].name end depth = depth-1 elseif widgets[pos].type == "Container - End" then depth = depth+1 end pos = pos-1 end local form = "label["..left+0.1 ..","..top+1 ..";- End of Container \""..form_esc(name).."\" -]" .. "" return form end, func = function(id, fields) -- ehem? end }, Tabs = { ui =function(id, left, top, width) local item_str = "" for i, v in pairs(widgets[id].captions) do item_str = item_str .. form_esc(v) .. "," end local form = "label["..left+1.9 ..","..top ..";- Tabs -]" .. ui_field("NAME", widgets[id].name, left+0.2, top+1) .. ui_position("LEFT", widgets[id].left, left, top+1.7, "LEFT", widgets[id].left_type) .. ui_position("TOP", widgets[id].top, left, top+2.7, "TOP", widgets[id].top_type) .. "label["..left+0.1 ..","..top+3.4 ..";TABS]" .. "label["..left+1.8 ..","..top+3.4 ..";selected: "..widgets[id].tab.."]" .. "textlist["..left+0.1 ..","..top+3.75 ..";2.6,0.7;item_list;"..item_str.."]" .. "field["..left+3.3 ..","..top+4 ..";1.8,1;item_input;;]" .. "field_close_on_enter[item_input;false]" .. "checkbox["..left+0.1 ..","..top+4.3 ..";transparent_box;transparent;"..tostring(widgets[id].transparent).."]" .. "checkbox["..left+0.1 ..","..top+4.7 ..";border_box;border;"..tostring(widgets[id].border).."]" .. "" return form end, func = function(id, fields) handle_position_changes(id, fields) handle_field_changes({"name"}, id, fields) if fields.item_list then if string.sub(fields.item_list, 1, 3) == "DCL" then table.remove(widgets[id].captions, tonumber(string.sub(fields.item_list, 5))) else widgets[id].tab = tonumber(string.sub(fields.item_list, 5)) end elseif fields.key_enter_field == "item_input" then table.insert(widgets[id].captions, fields.item_input) elseif fields.transparent_box then widgets[id].transparent = fields.transparent_box == "true" elseif fields.border_box then widgets[id].border = fields.border_box == "true" end reload_ui() end }, Options = { ui = function(id, left, top, width) local form = "label["..left+1.8 ..","..top ..";- Options -]" .. "button["..left+0.1 ..","..top+1 ..";2,1;func_create;generate function]" .. "button["..left+2.1 ..","..top+1 ..";2,1;string_create;generate string]" .. "" return form end, func = function(id, fields) if fields.string_create then -- display the formspec to output the generated string (and generate it) minetest.show_formspec("ui_editor:output", "size[10,8]" .. "textarea[1,1;9,7;_;Generated Code;"..form_esc(generate_string()).."]" .. "button[8.8,0;1,1;back;back]") elseif fields.func_create then -- display the (same) formspec to output the generated function (and generate it) minetest.show_formspec("ui_editor:output", "size[10,8]" .. "textarea[1,1;9,7;_;Generated Code;"..form_esc(generate_function()).."]" .. "button[8.8,0;1,1;back;back]") end end }, New = { ui = function(id, left, top, width) local widg_str = "" -- convert the list of widget types to a string for i, v in pairs(widg_list) do widg_str = widg_str..v.."," end local form = "label["..left+1.6 ..","..top ..";- NEW WIDGET -]" .. "textlist["..left+0.1 ..","..top+0.4 ..";"..width-0.2 ..",6;new_widg_selector;"..widg_str.."]" return form end, func = function(id, fields) if fields.new_widg_selector then if string.sub(fields.new_widg_selector, 1, 3) == "DCL" then local name = widg_list[tonumber(string.sub(fields.new_widg_selector, 5))] selected_widget = #widgets +1 -- widget defaults -- -- create widgets with the correct and default data if name == "Button" then table.insert(widgets, {type="Button", name="New Button", label="New", image=false, image_param=false, texture="default_cloud.png", item=false, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=1, bottom_type="R"}) elseif name == "Field" then table.insert(widgets, {type="Field", name="New Field", label="", default="", default_param=false, password=false, enter_close=true, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=1, bottom_type="R"}) elseif name == "TextArea" then table.insert(widgets, {type="TextArea", name="New TextArea", label="", default="", default_param=false, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=2, bottom_type="T+"}) elseif name == "Label" then table.insert(widgets, {type="Label", name="New Label", label="New Label", label_param=false, vertical=false, left=1, left_type="L+", top=1, top_type="T+"}) elseif name == "TextList" then table.insert(widgets, {type="TextList", name="New TextList", items={}, items_param=false, item_id_param=false, transparent=false, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=2, bottom_type="T+"}) elseif name == "DropDown" then table.insert(widgets, {type="DropDown", name="New DropDown", items={}, items_param=false, item_id_param=false, select_id=1, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=1, bottom_type="R"}) elseif name == "CheckBox" then table.insert(widgets, {type="CheckBox", name="New CheckBox", label="New CheckBox", checked=false, checked_param=false, left=1, left_type="L+", top=1, top_type="T+"}) elseif name == "Box" then table.insert(widgets, {type="Box", name="New Box", colour="#ffffff", colour_param=false, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=2, bottom_type="T+"}) elseif name == "Image" then table.insert(widgets, {type="Image", name="New Image", image="default_cloud.png", image_param=false, item=false, background=false, fill=false, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=2, bottom_type="T+"}) elseif name == "Slider" then table.insert(widgets, {type="Slider", name="New Slider", vertical=false, value=0, value_param=false, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="R", bottom=0.3, bottom_type="R"}) elseif name == "Table" then table.insert(widgets, {selected_column=-1, type="Table", name="New Table", selected_param=false, columns = {}, select_param=false, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=2, bottom_type="T+"}) elseif name == "InvList" then table.insert(widgets, {type="InvList", name="main", location="current_player", start_param=false, data="", data_param=false, ring=false, colour_tab=false, start=0, left=1, left_type="L+", top=1, top_type="T+", right=2, right_type="L+", bottom=2, bottom_type="T+"}) elseif name == "Tooltip" then table.insert(widgets, {type="Tooltip", name="widget", text="New Tooltip", colours=false, bg="#00cc00", fg="#000000"}) elseif name == "Container" then table.insert(widgets, {type="Container - Start", name="New container", left_param=false, top_param=false, left=1, left_type="L+", top=1, top_type="T+", right=4, right_type="L+", bottom=4, bottom_type="T+"}) table.insert(widgets, {type="Container - End", name=""}) elseif name == "Tabs" then table.insert(widgets, {type="Tabs", name="New Tabs", captions={}, tab=1, transparent=false, border=false, left=0, left_type="L+", top=0, top_type="T+"}) end new_widg_tab = false reload_ui() end end end }, } ---------- -- GENERAL ---------- -- handles formspec input, or sends to correct places minetest.register_on_formspec_input(function(formname, fields) if formname == "ui_editor:main" then if fields.widg_select then -- select a widget selected_widget = tonumber(string.sub(fields.widg_select, 5))-4 new_widg_tab = false minetest.show_formspec("ui_editor:main", main_ui_form()) elseif fields.widg_mov_up then -- move a widget up if selected_widget > 2 then if widgets[selected_widget].type == "Container - End" and widgets[selected_widget-1].type == "Container - Start" then local pos = selected_widget-2 -- containers must always make sence. each start must have an end after it, local count = 0 -- -- so they can't move past eachother in some cases while pos > 0 do if widgets[pos].type == "Container - End" then count = count-1 elseif widgets[pos].type == "Container - Start" then count = count+1 end pos = pos-1 end if count <= 0 then return true end end table.insert(widgets, selected_widget-1, table.remove(widgets, selected_widget)) -- move it selected_widget = selected_widget-1 new_widg_tab = false reload_ui() end elseif fields.widg_mov_dwn then --move a widget down if selected_widget < #widgets and selected_widget > 1 then if widgets[selected_widget].type == "Container - Start" and widgets[selected_widget+1].type == "Container - End" then local pos = selected_widget+2 -- containers must have an end after them (and can't share an end) local count = 0 while pos <= #widgets do if widgets[pos].type == "Container - End" then count = count+1 elseif widgets[pos].type == "Container - Start" then count = count-1 end pos = pos+1 end if count <= 0 then return true end end table.insert(widgets, selected_widget+1, table.remove(widgets, selected_widget)) selected_widget = selected_widget+1 new_widg_tab = false reload_ui() end elseif fields.widg_duplicate then -- duplicate a widget table.insert(widgets, copy_table(widgets[selected_widget])) new_widg_tab = false reload_ui() elseif fields.widg_new then -- switch to the NEW WIDGET tab new_widg_tab = not new_widg_tab reload_ui() elseif fields.widg_delete then -- delete a widget if widgets[selected_widget].type == "Container - Start" then widget_editor_uis["Container - Start"].del(selected_widget) else table.remove(widgets, selected_widget) end selected_widget = selected_widget-1 new_widg_tab = false reload_ui() elseif fields.quit == nil then -- send update to widget editors if selected_widget == -2 or new_widg_tab then widget_editor_uis["New"].func(selected_widget, fields) elseif selected_widget > 0 then widget_editor_uis[widgets[selected_widget].type].func(selected_widget, fields) new_widg_tab = false elseif selected_widget == -3 then widget_editor_uis["Options"].func(selected_widget, fields) new_widg_tab = false end end elseif formname == "ui_editor:output" then -- the display for outputting generated code if fields.back then reload_ui() end end end) -- loads the correct widget editor local function widget_editor(left, height) local form = "box["..left+0.1 ..",2.2;4.8,"..height-2.3 ..";#000000]" if selected_widget == -1 or selected_widget == 0 or (selected_widget > 1 and widgets[selected_widget] == nil) then selected_widget = -2 -- blank items in the list can be used for adding new widgets end if selected_widget == -2 or new_widg_tab then -- the new widget tab can be displayed without moving the selection form = form .. widget_editor_uis["New"].ui(selected_widget, left+0.1, 2.2, 4.8) elseif selected_widget > 0 then -- load correct editor for current selected widget form = form .. widget_editor_uis[widgets[selected_widget].type].ui(selected_widget, left+0.1, 2.2, 4.8) elseif selected_widget == -3 then form = form .. widget_editor_uis["Options"].ui(selected_widget, left+0.1, 2.2, 4.8) end return form end -- creates the widget selector local function widget_chooser(left) local widget_str = "OPTIONS,NEW WIDGET,,.....," -- options at the top of the list local depth = 0 for i, v in pairs(widgets) do if v.type == "Container - End" then -- the order of end and start are because they do not need indenting depth = depth-1 end widget_str = widget_str .. string.rep("- ", depth) .. form_esc(v.type .. ": " .. v.name) .. "," if v.type == "Container - Start" then -- container contents gets indented depth = depth+1 end end local form = "".. "textlist["..left+0.1 ..",0.1;3.4,2;widg_select;"..widget_str..";"..selected_widget+4 .."]" .. "button["..left+3.6 ..",0.1;0.5,1;widg_mov_up;"..form_esc("/\\").."]" .. "button["..left+3.6 ..",1.2;0.5,1;widg_mov_dwn;"..form_esc("\\/").."]" if selected_widget > 1 and selected_widget <= #widgets and widgets[selected_widget].type ~= "Container - End" then form = form .. "button["..left+4 ..",0;1,1;widg_duplicate;DUPLICATE]" .. "button["..left+4 ..",0.7;1,1;widg_delete;DELETE]" .. "button["..left+4 ..",1.4;1,1;widg_new;NEW]" end return form end -- puts the whole formspec together main_ui_form = function () local ui, width, height = generate_ui() -- the preview local w_selector = widget_chooser(width-5) -- the widget selector local w_editor = widget_editor(width-5, height) -- the widget editor local form = "".. -- add everything together "size["..width..","..height.."]" .. "box["..width-5 ..",0;5,"..height..";#ffffff]" .. ui .. w_selector .. w_editor .. create_tabs(2) -- add the global tabs return form end ---------- ---------- -- END FORM EDITOR -- ---------- ---------- -- register the chat command minetest.register_chatcommand("gui", { description = core.gettext("UI editor"), func = function() minetest.show_formspec("ui_editor:main", main_ui_form()) end, })