Autocomplete input for ghost buttons

This commit is contained in:
tayyyyyyy
2019-05-22 01:46:09 -07:00
parent c6777cbceb
commit 7a6321ca5f
4 changed files with 155 additions and 45 deletions

View File

@@ -353,12 +353,12 @@
var/middle = L.len / 2 + 1 // Copy is first,second-1 var/middle = L.len / 2 + 1 // Copy is first,second-1
return mergeLists(sortList(L.Copy(0,middle)), sortList(L.Copy(middle))) //second parameter null = to end of list return mergeLists(sortList(L.Copy(0,middle)), sortList(L.Copy(middle))) //second parameter null = to end of list
//Mergsorge: uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead //Mergsorge: uses sortAssoc() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sortNames(var/list/L) /proc/sortNames(var/list/L)
var/list/Q = new() var/list/Q = new()
for(var/atom/x in L) for(var/atom/x in L)
Q[x.name] = x Q[x.name] = x
return sortList(Q) return sortAssoc(Q)
/proc/mergeLists(var/list/L, var/list/R) /proc/mergeLists(var/list/L, var/list/R)
var/Li=1 var/Li=1

View File

@@ -1,71 +1,90 @@
/proc/input_async(mob/user=usr, prompt, list/choices) /proc/input_async(mob/user=usr, prompt, list/choices)
var/datum/async_input/A = new(choices, prompt) var/datum/async_input/A = new(choices, prompt, , user)
A.show(user) A.show()
return A return A
/proc/input_ranked_async(mob/user=usr, prompt="Order by greatest to least preference", list/choices) /proc/input_ranked_async(mob/user=usr, prompt="Order by greatest to least preference", list/choices)
var/datum/async_input/ranked/A = new(choices, prompt) var/datum/async_input/ranked/A = new(choices, prompt, "ranked_input", user)
A.show(user) A.show()
return A
/proc/input_autocomplete_async(mob/user=usr, prompt="Enter text: ", list/choices)
var/datum/async_input/autocomplete/A = new(choices, prompt, "ac_input", user)
A.show()
return A return A
/datum/async_input /datum/async_input
var/datum/browser/popup var/datum/browser/popup
// If associative list, key will be used for display, but the final result will be the value
var/list/choices var/list/choices
var/datum/callback/onCloseCb
var/flash = TRUE var/flash = TRUE
var/immediate_submit = FALSE var/immediate_submit = FALSE
var/prompt var/prompt
var/result = null var/result = null
var/style = "text-align: center;" var/style = "text-align: center;"
var/mob/user
var/window_id var/window_id
var/height = 200 var/height = 200
var/width = 400 var/width = 400
/datum/async_input/New(list/new_choices, new_prompt="Pick an option:", new_window_id="async_input") /datum/async_input/New(list/new_choices, new_prompt="Pick an option:", new_window_id="async_input", mob/new_user=usr)
choices = new_choices choices = new_choices
prompt = new_prompt prompt = new_prompt
window_id = new_window_id window_id = new_window_id
user = new_user
popup = new(user, window_id, , width, height, src)
/datum/async_input/proc/close() /datum/async_input/proc/close()
if(popup) if(popup)
popup.close() popup.close()
if(result && choices[result])
result = choices[result]
if(onCloseCb)
onCloseCb.Invoke(result)
return result return result
/datum/async_input/proc/show(mob/user) // Callback function should take the result as the last argument
var/dat = create_ui(user) /datum/async_input/proc/on_close(var/datum/callback/cb)
popup = new(user, window_id, , width, height, src) onCloseCb = cb
popup.set_content(dat)
/datum/async_input/proc/show()
popup.set_content(create_ui())
if(flash && result == null) if(flash && result == null)
window_flash(user.client) window_flash(user.client)
popup.open() popup.open()
/datum/async_input/proc/create_ui(mob/user) /datum/async_input/proc/create_ui()
var/dat = "<div style=\"[style]\">" var/dat = "<div style=\"[style]\">"
dat += render_prompt(user) dat += render_prompt()
dat += render_choices(user) dat += render_choices()
dat += "<br>" dat += "<br>"
dat += "<br>" dat += "<br>"
dat += button("Submit", "submit=1", , result == null && !immediate_submit) dat += render_buttons()
dat += "</div>" dat += "</div>"
return dat return dat
/datum/async_input/proc/render_prompt(mob/user) /datum/async_input/proc/render_prompt()
return "<h2>[prompt]</h2>" return "<h2>[prompt]</h2>"
/datum/async_input/proc/render_choices(mob/user) /datum/async_input/proc/render_choices()
var/dat = " " var/dat = " "
for(var/choice in choices) for(var/choice in choices)
dat += button(choice, "choice=[choice]", choice == result) dat += button(choice, "choice=[choice]", choice == result)
dat += " " dat += " "
return dat return dat
/datum/async_input/proc/button(label, topic, on=FALSE, disabled=FALSE) /datum/async_input/proc/render_buttons()
return button("Submit", "submit=1", , result == null && !immediate_submit)
/datum/async_input/proc/button(label, topic, on=FALSE, disabled=FALSE, id="")
var/class = "" var/class = ""
if(on) if(on)
class = "linkOn" class = "linkOn"
if(disabled) if(disabled)
class = "linkOff" class = "linkOff"
topic = "" topic = ""
return "<a class=\"[class]\" href='?src=[UID()];[topic]'>[label]</a>" return "<a class=\"[class]\" id='[id]' href='?src=[UID()];[topic]'>[label]</a>"
/datum/async_input/Topic(href, href_list) /datum/async_input/Topic(href, href_list)
if(href_list["submit"] || href_list["close"]) if(href_list["submit"] || href_list["close"])
@@ -74,14 +93,14 @@
if(href_list["choice"]) if(href_list["choice"])
result = href_list["choice"] result = href_list["choice"]
show(usr) show()
return return
/datum/async_input/ranked /datum/async_input/ranked
height = 400 height = 400
immediate_submit = TRUE immediate_submit = TRUE
/datum/async_input/ranked/render_choices(mob/user) /datum/async_input/ranked/render_choices()
var/dat = "<div>" var/dat = "<div>"
dat += "<table style='margin: auto; text-align: left;'>" dat += "<table style='margin: auto; text-align: left;'>"
for(var/i = 1, i <= choices.len, i++) for(var/i = 1, i <= choices.len, i++)
@@ -103,13 +122,47 @@
if(href_list["upvote"]) if(href_list["upvote"])
var/index = text2num(href_list["upvote"]) var/index = text2num(href_list["upvote"])
choices.Swap(index, index - 1) choices.Swap(index, index - 1)
show(usr) show()
return return
if(href_list["downvote"]) if(href_list["downvote"])
var/index = text2num(href_list["downvote"]) var/index = text2num(href_list["downvote"])
choices.Swap(index, index + 1) choices.Swap(index, index + 1)
show(usr) show()
return
..()
/datum/async_input/autocomplete
immediate_submit = TRUE
height = 150
/datum/async_input/autocomplete/New()
..()
popup.add_script("autocomplete.js", 'html/browser/autocomplete.js')
/datum/async_input/autocomplete/render_prompt()
return "<label for='input'>[prompt]</label>"
/datum/async_input/autocomplete/render_choices()
var/dat = "<input list='choices' id='input' name='choices' oninput='updateTopic()' />"
dat += "<datalist id='choices'>"
for(var/choice in choices)
dat += "<option value='[choice]'>"
dat += "</datalist>"
return dat
/datum/async_input/autocomplete/render_buttons()
var/dat = button("Submit", "", , result == null && !immediate_submit, "submit-button")
dat += button("Cancel", "close=1")
return dat
/datum/async_input/autocomplete/Topic(href, href_list)
if(href_list["submit"])
// Entering an invalid choice is the same as canceling
if(href_list["submit"] in choices)
result = href_list["submit"]
close()
return return
..() ..()

View File

@@ -397,9 +397,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(usr, "Not when you're not dead!") to_chat(usr, "Not when you're not dead!")
return return
var/area/A = input("Area to jump to", "BOOYEA") as null|anything in ghostteleportlocs var/datum/async_input/A = input_autocomplete_async(usr, "Area to jump to: ", ghostteleportlocs)
var/area/thearea = ghostteleportlocs[A] A.on_close(CALLBACK(src, .proc/teleport))
/mob/dead/observer/proc/teleport(area/thearea)
if(!thearea) if(!thearea)
return return
@@ -420,9 +421,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set desc = "Follow and orbit a mob." set desc = "Follow and orbit a mob."
var/list/mobs = getpois(skip_mindless=1) var/list/mobs = getpois(skip_mindless=1)
var/input = input("Please, select a mob!", "Haunt", null, null) as null|anything in mobs var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs)
var/mob/target = mobs[input] A.on_close(CALLBACK(src, .proc/ManualFollow))
ManualFollow(target)
// This is the ghost's follow verb with an argument // This is the ghost's follow verb with an argument
/mob/dead/observer/proc/ManualFollow(var/atom/movable/target) /mob/dead/observer/proc/ManualFollow(var/atom/movable/target)
@@ -495,24 +495,20 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set desc = "Teleport to a mob" set desc = "Teleport to a mob"
if(isobserver(usr)) //Make sure they're an observer! if(isobserver(usr)) //Make sure they're an observer!
var/list/dest = list() //List of possible destinations (mobs) var/list/dest = getpois(mobs_only=1) //Fill list, prompt user with list
var/target = null //Chosen target. var/datum/async_input/A = input_autocomplete_async(usr, "Enter a mob name: ", dest)
A.on_close(CALLBACK(src, .proc/jump_to_mob))
dest += getpois(mobs_only=1) //Fill list, prompt user with list /mob/dead/observer/proc/jump_to_mob(mob/M)
target = input("Please, select a mob!", "Jump to Mob", null, null) as null|anything in dest if(!M)
return
if(!target) //Make sure we actually have a target var/mob/A = src //Source mob
return var/turf/T = get_turf(M) //Turf of the destination mob
else
var/mob/M = dest[target] //Destination mob
var/mob/A = src //Source mob
var/turf/T = get_turf(M) //Turf of the destination mob
if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination.
A.forceMove(T)
else
to_chat(A, "This mob is not located in the game world.")
if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination.
A.forceMove(T)
return
to_chat(A, "This mob is not located in the game world.")
/* Now a spell. See spells.dm /* Now a spell. See spells.dm
/mob/dead/observer/verb/boo() /mob/dead/observer/verb/boo()

View File

@@ -0,0 +1,61 @@
var $ = document.querySelector.bind(document);
var input;
var submitButton;
var optionsMap = {};
function updateTopic() {
if (!input || !submitButton) {
return;
}
var hrefList = submitButton.getAttribute('href').split(';');
// Topic must come last in the submit button for this to work
hrefList = hrefList.slice(0, hrefList.length - 1);
hrefList.push(optionsMap[input.value] ? 'submit=' + optionsMap[input.value] : '');
submitButton.setAttribute('href', hrefList.join(';'));
}
function clean(name) {
// \improper shows up as 2 characters outside of ascii range
if (name.charCodeAt(0) > 127) {
return name.slice(2);
}
return name;
}
function setElements() {
input = $('#input');
submitButton = $('#submit-button');
var choices = $('#choices');
if (!input || !submitButton || !choices) {
return;
}
for (var i = 0; i < choices.options.length; i++) {
var name = choices.options[i].value;
var cleaned = clean(name);
optionsMap[cleaned] = name;
choices.options[i].value = cleaned;
}
input.addEventListener('keyup', function(event) {
if (event.key !== 'Enter') {
return;
}
if (Object.keys(optionsMap).indexOf(input.value) === -1) {
// Byond doesn't let you to use enter to select
// so we need to prevent unintended submissions
return
}
submitButton.click();
event.preventDefault();
});
input.focus();
}
window.onload = setElements;