ID:1672353
 
(See the best response by Sir Lazarus.)
I need something like:
Variable_Edit(atom/A in world, edit as text)
var/list/variables = A.vars
if(!variables.Find(edit)) return
var/change = input("Change [edit] on [A] to what?") as text
//A.[edit] = change

Disregarding everything other than text and num for now cause I wouldn't know how to go that far.
Why not just look up an Admin demo which should have this command in already?

For example:
Edit Nero's Admin System
My HTML-style create & edit system so you can search for the variable you want to edit. (</shameless_plug>)
Best response
// Tested code. Is meant to be dumped into a random project and should still work.
// TAKE CARE: Add the appropriate lines to your /client/Click and /client/Topic. There should only be one of these in your project.

/proc/editableLists()
return world.contents

/client/verb/vEdit(var/datum/object as anything in editableLists())
set name = "edit"
var/html = "<!DOCTYPE html><html lang=\"en\"><head><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" /><title>Edit \"["\proper[object]"]\"</title>\
<style type=\"text/css\">*{box-sizing:border-box}input{width:100%;border:1px solid #DCDCDC;padding: 4px}::-ms-clear{display:none}::-ms-reveal{display:none;}body{font-family:Arial;font-size:12px;overflow-y:scroll}body{margin:0px}table{width:100%;border-spacing:0px;border-collapse:collapse;table-layout:fixed}td,th{padding:4px 8px;vertical-align:top}th{text-align:left;}thead th{border:1px solid #000;border-left-width:0px;border-right-width:0px;}td>div{overflow:hidden;text-overflow:ellipsis;}span,a{cursor:pointer;color:#007FDC}th.a>span,th.b>span,a:visited{color:#DC00CA}th.b>span{text-decoration:underline;}</style>\
<script type=\"text/javascript\">function sortTable(table, col, reverse){var tb=table.tBodies\[0],tr=Array.prototype.slice.call(tb.rows,0),i;reverse=-((+reverse)||-1);tr=tr.sort(function(a,b){return reverse*(a.cells\[col\].textContent.trim().localeCompare(b.cells\[col\].textContent.trim()));});for(i=0;i<tr.length;++i){tb.appendChild(tr\[i])}var th=table.tHead;th&&(th=th.rows\[0\])&&(th=th.cells);for(i=th.length-1;i>=0;i--){th\[i\].className=(i==col?(reverse==1?\"b\":\"a\"):\"\")}}function makeSortable(table){var th=table.tHead,i,dir,currentcol;th&&(th=th.rows\[0])&&(th=th.cells);if(th){i=th.length}else{return}while(--i>=0)(function(i){th\[i\].innerHTML=\"<span>\"+th\[i\].innerHTML+\"</span>\";th\[i\].childNodes\[0\].addEventListener(\"click\", function(){dir=(currentcol==i?1-dir:0);currentcol=i;sortTable(table, i, dir)})}(i));th\[0\].childNodes\[0\].click()}function makeAllSortable(parent){parent=parent||document.body;var t=parent.getElementsByTagName(\"table\"),i=t.length;while(--i>=0){makeSortable(t\[i])}}\
var filter1,filter2; function filter(){ var rows = document.getElementById(\"t\").tBodies\[0\].rows; for (var i = 0; i < rows.length; i++){ rows\[i\].style.display=((!filter1||rows\[i\].childNodes\[0\].innerText.indexOf(filter1)>-1)&&(!filter2||rows\[i\].childNodes\[1\].innerText.indexOf(filter2)>-1||rows\[i\].childNodes\[2\].innerText.indexOf(filter2)>-1)?\"table-row\":\"none\"); } }\
</script>"
+ "</head><body onload=\"makeAllSortable()\">"

html = html + "<div style=\"background:#FFF;position:fixed;top:0px;left:0px;right:0px;padding-top:1px;\"><div style=\"display:inline-block;width:250px;margin-left:8px;\"><input type=\"text\" onkeyup=\"filter1=this.value;filter();\" placeholder=\"Filter on variable\"/></div><div style=\"display:inline-block;width:calc(100% - 250px - 24px - 100px);\"><input type=\"text\" id=\"filter\" onkeyup=\"filter2=this.value;filter();\" placeholder=\"Filter on value\" style=\"border-left: 0;\"/></div> <a href=\"byond://?src=\ref[object]&var_edit=select&select=true\">[src._vEdit_selected == object ? "deselect" : "select"]</a></div><table id=\"t\" style=\"margin-top: 24px;\"><colgroup><col width=\"250px\"/><col width=\"*\"/><col width=\"*\"/><col width=\"24px\"/></colgroup><thead onselectstart=\"return false\"><tr><th>Variable</th><th>Value</th><th r=\"true\">Default</th><th>S</th></tr></thead><tbody>"

var/value
var/default
var/const/ONMOUSEUP = "try{if(event.target.tagName!='A'){document.body.focus()}}catch(ex){}"
var/isClient = istype(object, /client)

for (var/variable in object.vars)
if (variable != "vars" && (!isClient || variable != "_vEdit_selected"))
value = _vEdit_getValue(object.vars[variable])
default = _vEdit_getValue(initial(object.vars[variable]))

html = html + "<tr><td><div onmouseup=\"[ONMOUSEUP]\"><a href=\"byond://?src=\ref[object]&var_edit=[url_encode(variable, 0)]\">[html_encode(variable)]</a></div></td><td><div onmouseup=\"[ONMOUSEUP]\">[value]</div></td><td><div onmouseup=\"[ONMOUSEUP]\">[default]</div></td><td>[issaved(object.vars[variable])?"T":"F"]</td></tr>"

html = html + "</tbody></table>"

html = html + "</body></html>"

src << browse(html, "window=var_edit_\ref[object];size=640x480")

/client/var/datum/_vEdit_selected

/client/proc/_vEdit_getType(value)
if (istext(value))
if (length(value) == 7 && copytext(value, 1, 2) == "#")
return "color"
else
return "text"
else if (isnum(value))
return "number"
else if (isicon(value))
return "icon"
else if (isfile(value))
return "file"
else if (ispath(value))
return "type path"
else if (istype(value, /list))
return "list"
else if (istype(value, /datum))
return "reference"
else if (isnull(value))
return "null"
else
return ""

/client/proc/_vEdit_getValue(value, include_type = 1)
var/datum/d = value
var/type = istype(value, /datum) ? d.type : _vEdit_getType(value)

if (istext(value))
if (length(value) == 7 && copytext(value, 1, 2) == "#")
. = "<font color=\"[value]\">["\proper[html_encode(value)]"]</font>"
else
. = "["\proper[html_encode(value)]"]"
else if (isnum(value))
. = "[value]"
else if (isicon(value))
. = "[value]"
else if (isfile(value))
. = "[value]"
else if (ispath(value))
. = "[value]"
else if (istype(value, /list))
. = "\["

var/first = TRUE
for (var/element in value)
if (!first) . = . + ", "
. = . + _vEdit_getValue(element, 0)
first = FALSE

. = . + "\]"
else if (istype(value, /datum) || istype(value, /client))
. = "<a href=\"byond://?src=\ref[value]&var_edit=edit&edit=true\">["\proper[html_encode(value)]"]</a>[include_type ? " <em>[d.type]</em>" : ""]"
include_type = FALSE
else if (isnull(value))
. = . + "&lt;null&gt;"
include_type = FALSE
else
. = . + "&lt;unknown&gt;"
include_type = FALSE

if (include_type) . = . + " <em>([type ? type : "unknown"])</em>"

return .

/client/proc/_vEdit_edit(variable, datum/object)
var/old_value = object.vars[variable]
var/new_value = _vEdit_prompt(variable, object.vars[variable], _vEdit_getType(object.vars[variable]), object)

if (new_value != "_<VEDIT_NO_VALUE>_")
object.vars[variable] = new_value

if (old_value != new_value)
src.vEdit(object)

/client/proc/_vEdit_prompt(variable, current_value, current_type, datum/object)
var/list/types = list("text", "number", "empty list", "file", "icon", "color", "type path", "reference", "set to initial value", "null")

if (!object) types.Remove("set to initial value")
if (src._vEdit_selected) types.Add("set to selected item (\"["\proper[src._vEdit_selected]"]\")")

var/type = input(src, "Specify desired type.", "Edit \"[variable]\"", current_type) as null|anything in types

if (type != null)
var/value

if (type == "set to initial value")
value = initial(object.vars[variable])
else if (type == "null")
value = null
else if (type == "empty list")
value = list()
else if (type == "set to selected item (\"["\proper[src._vEdit_selected]"]\")")
value = src._vEdit_selected
else
switch (type)
if ("text")
value = input(src, "Specify new value.", "Edit \"[variable]\"", current_value) as null|text
if ("number")
value = input(src, "Specify new value.", "Edit \"[variable]\"", current_value) as null|num
if ("file")
value = input(src, "Specify new value.", "Edit \"[variable]\"", current_value) as null|file
if ("icon")
value = input(src, "Specify new value.", "Edit \"[variable]\"", current_value) as null|icon
if ("color")
value = input(src, "Specify new value.", "Edit \"[variable]\"", current_value) as null|color
if ("path")
value = input(src, "Specify new value.", "Edit \"[variable]\"", current_value) as null|anything in typesof(/datum)
if ("reference")
value = input(src, "Specify new value.", "Edit \"[variable]\"", current_value) as null|anything in editableLists()

if (value == null) return "_<VEDIT_NO_VALUE>_"

return value

return "_<VEDIT_NO_VALUE>_"

/client/proc/_vEdit_topic(href_list[], hsrc)
var
datum/object = hsrc
variable = href_list["var_edit"]

if (variable == "select" && "select" in href_list)
src._vEdit_selected = src._vEdit_selected == object ? null : object
src.vEdit(object)
else if (variable == "edit" && "edit" in href_list)
src.vEdit(object)
else
if (istype(object.vars[variable], /list))
var/action = input(src, "Specify desired action.", "Edit \"[variable]\"") as null|anything in list("Add item to list", "Remove item from list", "Change variable type")
var/list/l = object.vars[variable]

switch (action)
if ("Add item to list")
var/value = _vEdit_prompt(variable, "", "", null)

if (value != "_<VEDIT_NO_VALUE>_")
l.Add(value)
src.vEdit(object)

if ("Remove item from list")
action = alert(src, "Specify desired method.", "Edit \"[variable]\"", "Select value from list", "Specify value to remove", "Cancel")

if (action != null && action != "Cancel")
var/value

if (action == "Select value from list")
value = input(src, "Specify item to remove.", "Edit \"[variable]\"") as null|anything in l
if (value == null) value = "_<VEDIT_NO_VALUE>_"
else
value = _vEdit_prompt(variable, "", "", null)

if (value != "_<VEDIT_NO_VALUE>_")
l.Remove(value)
src.vEdit(object)

if ("Change variable type")
_vEdit_edit(variable, object)
else
_vEdit_edit(variable, object)

#warn Move this to your own /client/Click proc.
/client/Click(atom/object, location, control, params)
params = params2list(params)
if (params["right"] && params["ctrl"] && params["alt"])
src.vEdit(object)
else
return ..()

#warn Move this to your own /client/Topic proc.
/client/Topic(href, href_list[], hsrc)
if (hsrc && "var_edit" in href_list) src._vEdit_topic(href_list, hsrc)
else return ..()
In response to GhostAnime
GhostAnime wrote:
My HTML-style create & edit system so you can search for the variable you want to edit. (</shameless_plug>)

I'm sorry for stealing your thunder, but I was already working on this before you posted.

Why not just look up an Admin demo which should have this command in already?

In my case I prefer to have something I can paste in any BYOND project which would work the same regardless of other people's code. Taking care of /client/Topic and /client/Click should be all that is needed to make this functionality work in a(ny) project.
In response to Sir Lazarus
Sir Lazarus wrote:
GhostAnime wrote:
My HTML-style create & edit system so you can search for the variable you want to edit. (</shameless_plug>)

I'm sorry for stealing your thunder, but I was already working on this before you posted.

Meh, I don't really care about the spotlight, especially seeing that I made the original demo of this system 6 years ago. (Man, time flies by fast).

I do like your system & style, nice and simple :) Though the S column is pretty vague. I assume that the column refers if the value is TRUE or FALSE? (Not too sure what S stands for though, is it string?, since IMO "B" would be more appropriate for the TRUE/FALSE as they are Boolean values)

Why not just look up an Admin demo which should have this command in already?

In my case I prefer to have something I can paste in any BYOND project which would work the same regardless of other people's code. Taking care of /client/Topic and /client/Click should be all that is needed to make this functionality work in a(ny) project.

Hm, I should have stated demo/library in that case. For the edit system, that can definitely be a library by itself but when you branch out to some other fields (like where the user can specify what variables cannot be edited, or specific objects on the create list), it would (kind of) fall under the demo category based on the wording of this pinned post. But that is just semantics *shrug*.

But my point is that the OP could have searched for an admin system and would have seen how the verb works and could have copied it in to their system.

If appropriately designed, I do not see an admin system in the demo/library section differing from how one would expect it to work. The process and visual style in the way it works may differ but the intended effect should happen with very little to no tweaking in any project included. (Again, the keyword here is appropriately designed).
In response to GhostAnime
I have a code like that. I think I used either nero's or your demo a while ago. I don't want an overall variable analysis and search. I want to be able to type in a variable, look at it and make changes.
In response to Mav472
If you want to see the variable list all in one go, refer back to Sir Lazarus' post above.

The difference between mine and his system is that he shows the variables immediately whereas my current version is initially hidden until you click the link above (ex: clicking A will show all vars that starts with A) or typing in a portion of the name to search for the variable (ex: gr[enter] => group, up[enter]=>group). [The next version displays it right away but I don't see myself releasing that version anytime soon]

They both work the same, just different execution/way to go around it. It doesn't really matter to us which one you chose since it'll be a behind-the-scene work.

In the end, I would recommend Sir Lazarus since his version appears to be more user friendly than mine.
That's what i'm saying. I don't want a list of variables (variable analysis) I want a more simplified version of edit. "Which variable would you like to edit? "
In response to Mav472
Mav472 wrote:
That's what i'm saying. I don't want a list of variables (variable analysis) I want a more simplified version of edit. "Which variable would you like to edit? "

... So, uh, how do you want to do that exactly? Because that's what the search field in both mine & SL's does.

In SL's system, above the Variable link, there is a field that states Filter On Variable. You click that, start typing in the variable of interest and the body will narrow down to variables with those names. (Ex: when you type 'ic', it'll immediate filter down to icon & icon_state)

Then you click that variable name to edit.
I don't want a list. Just a verb. Something like this Variable_Edit(atom/A in world, edit as text)
var/list/variables = A.vars
if(!variables.Find(edit)) return
var/change = input("Change [edit] on [A] to what?") as text
//A.[edit] = change

Where I can just type in a variable to change without a list or searching.
So you would rather type out the whole variable name in an input to edit than have a dynamic searching field which can narrow down long variable names with a few characters?

... Whatever floats your boat.

This snippet (small test done) will make you choose if a variable is a text or number if a number is entered. The variable you want to edit must be entered exactly.

This means this is case sensitive and the value will either be text or numeric (the latter asks which form you want via alert but you can always comment out that line to make it numeric by default).
mob/verb/Variable_Edit(atom/A in world, edit as text)
var/list/variables = A.vars - list("ckey", "contents", "loc", "locs", "key","parent_type","type") // I'm subtracting out any forbidden variables as changing these itself directly can have VERY serious consequences

if(!variables.Find(edit)){ alert("Variable [edit] does not exist."); return}
else if(istype(A.vars[edit], /list)){ alert("Cannot edit [edit] as it is a /list"); return}

var/change = input("Change [edit] on [A] to what?", "Edit [edit]", A.vars[edit]) as text

if(isnum(text2num(change)))
// Don't want the numeric alert? Remove the following line (but be sure to have the text2num() portion)
if("Number" == alert("The value '[change]' appears to be a number. Which variable type is this?","Variable Type", "Number", "Text"))
change = text2num(change)

A.vars[edit] = change
alert({"Changed [edit] to [A.vars[edit]] ([isnum(A.vars[edit])?"Numeric":"Text"])"}, "Edit [edit]")



Also, for the future, please put your code in the DM tags like <DM>[CODE]</DM>
In response to GhostAnime
GhostAnime wrote:
Though the S column is pretty vague. I assume that the column refers if the value is TRUE or FALSE? (Not too sure what S stands for though, is it string?, since IMO "B" would be more appropriate for the TRUE/FALSE as they are Boolean values)

The "S" column shows the result of issaved() on the variable. If "T" then the variable is saved in a savefile. If "F" then the variable is tmp or const.

Sorting is possible by clicking a header. Filtering is possible through the use of the text fields. Filters are always a "contains".

Hm, I should have stated demo/library in that case. For the edit system, that can definitely be a library by itself but when you branch out to some other fields (like where the user can specify what variables cannot be edited, or specific objects on the create list), it would (kind of) fall under the demo category based on the wording of this pinned post. But that is just semantics *shrug*.

I think we're on the same page... My approach requires both /client/Click and /client/Topic to be modified, which is a no-no in my design model. Built-in functions should call developer-operated functions, not be overridden by anyone else.

What may be interesting to develop is an event model which developers can use to patch into procs such as /client/Click. This would allow an ambiguous case like this one to work seamlessly, has the additional benefit that developers can use the hooks themselves, and may help us when people come asking for support on the forum.

Thank you for that idea. I'll look into it.