TGUI input box conversions 2 (#63395)

More text inputs converted to tgui, TGUI text and number input now more sanely handles ENTER key being pressed, you can now press anywhere in the window to enter the input. TGUI text input now considers placeholder text for the default valid state. IE, if there is default text you can press enter immediately without having to rewrite it just to recheck validity. Fixes: useSharedState => useLocalState. not only was sharedstate not needed but it opened up the ui to vulnerabilities
This commit is contained in:
Jeremiah
2021-12-15 14:12:04 -08:00
committed by GitHub
parent 3e6607bb0e
commit d17a60fb53
36 changed files with 143 additions and 149 deletions
+2 -5
View File
@@ -74,11 +74,8 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/engineerin
var/area/newA
var/area/oldA = get_area(get_turf(creator))
if(!isarea(area_choice))
var/str = stripped_input(creator,"New area name:", "Blueprint Editing", "", MAX_NAME_LEN)
if(!str || !length(str)) //cancel
return
if(length(str) > 50)
to_chat(creator, span_warning("The given name is too long. The area remains undefined."))
var/str = tgui_input_text(creator, "New area name", "Blueprint Editing", max_length = MAX_NAME_LEN)
if(!str)
return
newA = new area_choice
newA.setup(str)
+1 -1
View File
@@ -268,7 +268,7 @@
return FALSE
if(initial(rune_to_scribe.req_keyword))
chosen_keyword = stripped_input(cultist, "Enter a keyword for the new rune.", "Words of Power")
chosen_keyword = tgui_input_text(cultist, "Keyword for the new rune", "Words of Power", max_length = MAX_NAME_LEN)
if(!chosen_keyword)
drawing_a_rune = FALSE
start_scribe_rune(tool, cultist)
+1 -1
View File
@@ -149,7 +149,7 @@ GLOBAL_LIST_EMPTY(GPS_list)
switch(action)
if("rename")
var/atom/parentasatom = parent
var/a = stripped_input(usr, "Please enter desired tag.", parentasatom.name, gpstag, 20)
var/a = tgui_input_text(usr, "Enter the desired tag", "GPS Tag", gpstag, 20)
if (!a)
return
+1 -1
View File
@@ -74,7 +74,7 @@
bound_spirit.copy_languages(awakener, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the awakener.
bound_spirit.update_atom_languages()
bound_spirit.grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue
var/input = sanitize_name(stripped_input(bound_spirit, "What are you named?", ,"", MAX_NAME_LEN))
var/input = sanitize_name(tgui_input_text(bound_spirit, "What are you named?", "Spectral Nomenclature", max_length = MAX_NAME_LEN))
if(parent && input)
parent = input
bound_spirit.fully_replace_character_name(null, "The spirit of [input]")
@@ -219,7 +219,7 @@
return FALSE
tool.play_tool_sound(src)
var/input = stripped_input(user, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")
var/input = tgui_input_text(user, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret", "Set Network", "SS13", multiline = TRUE)
if(!input)
to_chat(user, span_warning("No input found, please hang up and try your call again!"))
return
@@ -123,7 +123,7 @@
to_chat(usr, span_danger("Unauthorized access."))
else if(href_list["warn"])
var/warning = stripped_input(usr, "Message:", "Enter your message here!", "", MAX_MESSAGE_LEN)
var/warning = tgui_input_text(usr, "Enter your message here", "Messaging")
if(!warning)
return
var/obj/item/implant/I = locate(href_list["warn"]) in GLOB.tracked_implants
+2 -2
View File
@@ -246,7 +246,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/requests_console, 30)
if(href_list["write"])
to_department = ckey(reject_bad_text(href_list["write"])) //write contains the string of the receiving department's name
var/new_message = (to_department in GLOB.req_console_ckey_departments) && stripped_input(usr, "Write your message:", "Awaiting Input", "", MAX_MESSAGE_LEN)
var/new_message = (to_department in GLOB.req_console_ckey_departments) && tgui_input_text(usr, "Write your message", "Awaiting Input")
if(new_message)
to_department = GLOB.req_console_ckey_departments[to_department]
message = new_message
@@ -254,7 +254,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/requests_console, 30)
priority = clamp(text2num(href_list["priority"]), REQ_NORMAL_MESSAGE_PRIORITY, REQ_EXTREME_MESSAGE_PRIORITY)
if(href_list["writeAnnouncement"])
var/new_message = reject_bad_text(stripped_input(usr, "Write your message:", "Awaiting Input", "", MAX_MESSAGE_LEN))
var/new_message = reject_bad_text(tgui_input_text(usr, "Write your message", "Awaiting Input"))
if(new_message)
message = new_message
priority = clamp(text2num(href_list["priority"]) || REQ_NORMAL_MESSAGE_PRIORITY, REQ_NORMAL_MESSAGE_PRIORITY, REQ_EXTREME_MESSAGE_PRIORITY)
+1 -1
View File
@@ -171,7 +171,7 @@
else
var/obj/item/card/id/new_card = W
if(new_card.registered_account)
var/msg = stripped_input(user, "Name of your roulette wheel:", "Roulette Naming", "Roulette Machine")
var/msg = tgui_input_text(user, "Name of your roulette wheel", "Roulette Naming", "Roulette Machine", MAX_NAME_LEN)
if(!msg)
return
name = msg
+6 -6
View File
@@ -169,7 +169,7 @@ AI MODULES
lawpos = 4
/obj/item/ai_module/supplied/safeguard/attack_self(mob/user)
var/targName = stripped_input(user, "Please enter the subject to safeguard.", "Safeguard who?", user.name,MAX_NAME_LEN)
var/targName = tgui_input_text(user, "Subject to safeguard.", "Safeguard", user.name, MAX_NAME_LEN)
if(!targName)
return
targetName = targName
@@ -195,7 +195,7 @@ AI MODULES
laws = list("Only SUBJECT is human.")
/obj/item/ai_module/zeroth/onehuman/attack_self(mob/user)
var/targName = stripped_input(user, "Please enter the subject who is the only human.", "Who?", user.real_name,MAX_NAME_LEN)
var/targName = tgui_input_text(user, "Enter the subject who is the only human.", "One Human", user.real_name, MAX_NAME_LEN)
if(!targName)
return
targetName = targName
@@ -255,7 +255,7 @@ AI MODULES
return
newpos = 15
lawpos = min(newpos, 50)
var/targName = stripped_input(user, "Please enter a new law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len))
var/targName = tgui_input_text(user, "Enter a new law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE)
if(!targName)
return
if(is_ic_filtered(targName))
@@ -386,7 +386,7 @@ AI MODULES
var/subject = "human being"
/obj/item/ai_module/core/full/asimov/attack_self(mob/user as mob)
var/targName = stripped_input(user, "Please enter a new subject that asimov is concerned with.", "Asimov to whom?", subject, MAX_NAME_LEN)
var/targName = tgui_input_text(user, "Enter a new subject that Asimov is concerned with.", "Asimov", subject, MAX_NAME_LEN)
if(!targName)
return
subject = targName
@@ -468,7 +468,7 @@ AI MODULES
laws = list("")
/obj/item/ai_module/core/freeformcore/attack_self(mob/user)
var/targName = stripped_input(user, "Please enter a new core law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len))
var/targName = tgui_input_text(user, "Enter a new core law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE)
if(!targName)
return
if(is_ic_filtered(targName))
@@ -496,7 +496,7 @@ AI MODULES
laws = list("")
/obj/item/ai_module/syndicate/attack_self(mob/user)
var/targName = stripped_input(user, "Please enter a new law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len))
var/targName = tgui_input_text(user, "Enter a new law for the AI", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE)
if(!targName)
return
if(is_ic_filtered(targName)) // not even the syndicate can uwu
+1 -1
View File
@@ -190,7 +190,7 @@
/obj/item/areaeditor/proc/edit_area()
var/area/A = get_area(usr)
var/prevname = "[A.name]"
var/str = stripped_input(usr,"New area name:", "Area Creation", "", MAX_NAME_LEN)
var/str = tgui_input_text(usr, "New area name", "Area Creation", max_length = MAX_NAME_LEN)
if(!str || !length(str) || str==prevname) //cancel
return
if(length(str) > 50)
+2 -2
View File
@@ -649,7 +649,7 @@ GLOBAL_LIST_EMPTY(PDAs)
if("Clear")//Clears messages
tnote = null
if("Ringtone")
var/t = stripped_input(U, "Please enter new ringtone", name, ttone, 20)
var/t = tgui_input_text(U, "Enter a new ringtone", "PDA Ringtone", ttone, 20)
if(in_range(src, U) && loc == U && t)
if(SEND_SIGNAL(src, COMSIG_PDA_CHANGE_RINGTONE, U, t) & COMPONENT_STOP_RINGTONE_CHANGE)
U << browse(null, "window=pda")
@@ -767,7 +767,7 @@ GLOBAL_LIST_EMPTY(PDAs)
/obj/item/pda/proc/msg_input(mob/living/U = usr, rigged = FALSE)
var/t = stripped_input(U, "Please enter message", name)
var/t = tgui_input_text(U, "Enter a message", "PDA Messaging")
if (!t || toff)
return
if(!U.canUseTopic(src, BE_CLOSE))
@@ -77,10 +77,10 @@
return
var/original_host = host_pda
var/fakename = sanitize_name(stripped_input(user, "Enter a name for the rigged message.", "Forge Message", null, MAX_NAME_LEN), allow_numbers = TRUE)
var/fakename = sanitize_name(tgui_input_text(user, "Enter a name for the rigged message.", "Forge Message", max_length = MAX_NAME_LEN), allow_numbers = TRUE)
if(!fakename || host_pda != original_host || !user.canUseTopic(host_pda, BE_CLOSE))
return
var/fakejob = sanitize_name(stripped_input(user, "Enter a job for the rigged message.", "Forge Message", null, MAX_NAME_LEN), allow_numbers = TRUE)
var/fakejob = sanitize_name(tgui_input_text(user, "Enter a job for the rigged message.", "Forge Message", max_length = MAX_NAME_LEN), allow_numbers = TRUE)
if(!fakejob || host_pda != original_host || !user.canUseTopic(host_pda, BE_CLOSE))
return
if(charges > 0 && host_pda.send_message(user, list(target), rigged = REF(user), fakename = fakename, fakejob = fakejob))
+1 -1
View File
@@ -33,7 +33,7 @@
/obj/item/beacon/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/pen)) // needed for things that use custom names like the locator
var/new_name = stripped_input(user, "What would you like the name to be?")
var/new_name = tgui_input_text(user, "What would you like the name to be?", "Beacon", max_length = MAX_NAME_LEN)
if(!user.canUseTopic(src, BE_CLOSE))
return
if(new_name)
@@ -28,7 +28,7 @@
if(!user.is_literate())
to_chat(user, span_notice("You scribble illegibly on the side of [src]!"))
return
var/new_name = stripped_input(user, "What would you like the label to be?", name, null)
var/new_name = tgui_input_text(user, "What would you like the label to be?", name, max_length = MAX_NAME_LEN)
if((user.get_active_held_item() != used_item) || !user.canUseTopic(src, BE_CLOSE))
return
if(new_name)
@@ -187,13 +187,13 @@
var/objective = "Obey the law. Praise Nanotrasen."
var/custom = FALSE
/obj/machinery/implantchair/brainwash/implant_action(mob/living/C,mob/user)
/obj/machinery/implantchair/brainwash/implant_action(mob/living/C, mob/user)
if(!istype(C) || !C.mind) // I don't know how this makes any sense for silicons but laws trump objectives anyway.
return FALSE
if(custom)
if(!user || !user.Adjacent(src))
return FALSE
objective = stripped_input(usr,"What order do you want to imprint on [C]?","Enter the order","",120)
objective = tgui_input_text(user, "What order do you want to imprint on [C]?", "Brainwashing", max_length = 120)
message_admins("[ADMIN_LOOKUPFLW(user)] set brainwash machine objective to '[objective]'.")
log_game("[key_name(user)] set brainwash machine objective to '[objective]'.")
if(HAS_TRAIT(C, TRAIT_MINDSHIELD))
@@ -52,7 +52,7 @@
to_chat(user, span_notice("You prod at [src] with [I]!"))
return
var/new_name = stripped_input(user, "What would you like the label to be?", name, null)
var/new_name = tgui_input_text(user, "What would you like the label to be?", name, max_length = MAX_NAME_LEN)
if(user.get_active_held_item() != I)
return
if(!user.canUseTopic(src, BE_CLOSE))
@@ -40,7 +40,7 @@
one_use = TRUE
/obj/item/borg/upgrade/rename/attack_self(mob/user)
heldname = sanitize_name(stripped_input(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN), allow_numbers = TRUE)
heldname = sanitize_name(tgui_input_text(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN), allow_numbers = TRUE)
log_game("[key_name(user)] have set \"[heldname]\" as a name in a cyborg reclassification board at [loc_name(user)]")
/obj/item/borg/upgrade/rename/action(mob/living/silicon/robot/R, user = usr)
+1 -1
View File
@@ -22,7 +22,7 @@
if(!user.is_literate())
to_chat(user, span_notice("You scribble illegibly on [src]!"))
return
var/txt = stripped_input(user, "What would you like to write on the sign?", "Sign Label", null , 30)
var/txt = tgui_input_text(user, "What would you like to write on the sign?", "Sign Label", max_length = 30)
if(txt && user.canUseTopic(src, BE_CLOSE))
label = txt
name = "[label] sign"
+1 -1
View File
@@ -122,7 +122,7 @@
if(user.canUseTopic(src, BE_CLOSE))
..()
if(istype(user) && loc == user)
var/input = stripped_input(user,"What do you want your war cry to be? You will shout it when you hit someone in melee.", ,"", 50)
var/input = tgui_input_text(user, "What do you want your war cry to be? You will shout it when you hit someone in melee.", "War Cry", max_length = 50)
if(input)
src.war_cry = input
+1 -1
View File
@@ -1467,7 +1467,7 @@
//Add changing looks when i feel suicidal about making 20 inhands for these.
/obj/item/toy/dummy/attack_self(mob/user)
var/new_name = stripped_input(usr,"What would you like to name the dummy?","Input a name",doll_name,MAX_NAME_LEN)
var/new_name = tgui_input_text(usr, "What would you like to name the dummy?", "Doll Name", doll_name, MAX_NAME_LEN)
if(!new_name)
return
doll_name = new_name
+1 -1
View File
@@ -27,7 +27,7 @@
return
to_chat(joe, span_notice("As you burn the picture, a nickname comes to mind..."))
var/nickname = stripped_input(joe, "Pick a nickname", "Mafioso Nicknames", null, NICKNAME_CAP, TRUE)
var/nickname = tgui_input_text(joe, "Pick a nickname", "Mafioso Nicknames", max_length = NICKNAME_CAP)
nickname = reject_bad_name(nickname, allow_numbers = FALSE, max_length = NICKNAME_CAP, ascii_only = TRUE)
if(!nickname)
return
+1 -1
View File
@@ -353,7 +353,7 @@
trophy_message = W.desc //default value
var/chosen_plaque = stripped_input(user, "What would you like the plaque to say? Default value is item's description.", "Trophy Plaque")
var/chosen_plaque = tgui_input_text(user, "What would you like the plaque to say? Default value is item's description.", "Trophy Plaque", trophy_message)
if(chosen_plaque)
if(user.Adjacent(src))
trophy_message = chosen_plaque
@@ -51,7 +51,7 @@
/obj/structure/door_assembly/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/pen))
var/t = stripped_input(user, "Enter the name for the door.", name, created_name,MAX_NAME_LEN)
var/t = tgui_input_text(user, "Enter the name for the door.", name, created_name, MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
+1 -1
View File
@@ -139,7 +139,7 @@
message_admins("[key_name_admin(user)] disabled scrambled codes on [ADMIN_LOOKUPFLW(borg)].")
log_silicon("[key_name(user)] disabled scrambled codes on [key_name(borg)].")
if ("rename")
var/new_name = sanitize_name(stripped_input(user,"What would you like to name this cyborg?","Input a name",borg.real_name,MAX_NAME_LEN), allow_numbers = TRUE)
var/new_name = sanitize_name(tgui_input_text(user, "What would you like to name this cyborg?", "Cyborg Reclassification", borg.real_name, MAX_NAME_LEN), allow_numbers = TRUE)
if(!new_name)
return
message_admins("[key_name_admin(user)] renamed [ADMIN_LOOKUPFLW(borg)] to [new_name].")
@@ -332,8 +332,8 @@
to_chat(user, span_warning("Your target is already under a mind-controlling influence!"))
return
var/command = stripped_input(user, "Enter the command for your target to follow.\
Uses Left: [G.mind_control_uses], Duration: [DisplayTimeText(G.mind_control_duration)]","Enter command")
var/command = tgui_input_text(user, "Enter the command for your target to follow.\
Uses Left: [G.mind_control_uses], Duration: [DisplayTimeText(G.mind_control_duration)]", "Enter command")
if(!command)
return
@@ -357,7 +357,7 @@
if(L.stat == DEAD)
to_chat(user, span_warning("Your target is dead!"))
return
var/message = stripped_input(user, "Write a message to send to your target's brain.","Enter message")
var/message = tgui_input_text(user, "Message to send to your target's brain", "Enter message")
if(!message)
return
if(QDELETED(L) || L.stat == DEAD)
@@ -16,7 +16,7 @@
to_chat(user, span_notice("We return our vocal glands to their original position."))
return
var/mimic_voice = sanitize_name(stripped_input(user, "Enter a name to mimic.", "Mimic Voice", null, MAX_NAME_LEN))
var/mimic_voice = sanitize_name(tgui_input_text(user, "Enter a name to mimic", "Mimic Voice", max_length = MAX_NAME_LEN))
if(!mimic_voice)
return
..()
@@ -39,7 +39,7 @@
if(turf_check(T))
var/chosen_keyword
if(initial(rune_type.req_keyword))
chosen_keyword = stripped_input(owner, "Enter a keyword for the new rune.", "Words of Power")
chosen_keyword = tgui_input_text(owner, "Enter a keyword for the new rune.", "Words of Power", max_length = MAX_NAME_LEN)
if(!chosen_keyword)
return
//the outer ring is always the same across all runes
@@ -172,7 +172,7 @@ the new instance inside the host to be updated to the template's stats.
taken_names[initial(D.name)] = TRUE
var/set_name
while(!set_name)
var/input = sanitize_name(stripped_input(src, "Select a name for your disease", "Select Name", "", MAX_NAME_LEN))
var/input = sanitize_name(tgui_input_text(src, "Select a name for your disease", "Select Name", max_length = MAX_NAME_LEN))
if(!input)
set_name = "Sentient Virus"
break
@@ -44,7 +44,7 @@ GLOBAL_LIST_EMPTY(jam_on_wardec)
if(custom_threat == "Yes")
declaring_war = TRUE
war_declaration = stripped_input(user, "Insert your custom declaration", "Declaration")
war_declaration = tgui_input_text(user, "Insert your custom declaration", "Declaration", multiline = TRUE)
declaring_war = FALSE
if(!check_allowed(user) || !war_declaration)
@@ -80,7 +80,7 @@ GLOBAL_LIST_EMPTY(jam_on_wardec)
var/custom_threat = tgui_alert(usr, "Do you want to customize the declaration?", "Customize?", list("Yes", "No"))
if(custom_threat == "Yes")
war_declaration = stripped_input(usr, "Insert your custom declaration", "Declaration")
war_declaration = tgui_input_text(usr, "Insert your custom declaration", "Declaration", multiline = TRUE)
if(!war_declaration)
to_chat(usr, span_warning("Invalid war declaration."))
+1 -1
View File
@@ -178,7 +178,7 @@
var/wizard_name_second = pick(GLOB.wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/mob/living/wiz_mob = owner.current
var/newname = sanitize_name(reject_bad_text(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN)))
var/newname = sanitize_name(reject_bad_text(tgui_input_text(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN)))
if (!newname)
newname = randomname
+30 -30
View File
@@ -6,13 +6,13 @@
* validate the input inside the UI and ui_act.
*
* Arguments:
* * user - The user to show the numbox to.
* * message - The content of the numbox, shown in the body of the TGUI window.
* * title - The title of the numbox modal, shown on the top of the TGUI window.
* * user - The user to show the number input to.
* * message - The content of the number input, shown in the body of the TGUI window.
* * title - The title of the number input modal, shown on the top of the TGUI window.
* * default - The default (or current) value, shown as a placeholder. Users can press refresh with this.
* * max_value - Specifies a maximum value. If none is set, any number can be entered. Pressing "max" defaults to 1000.
* * min_value - Specifies a minimum value. Often 0.
* * timeout - The timeout of the numbox, after which the modal will close and qdel itself. Set to zero for no timeout.
* * timeout - The timeout of the number input, after which the modal will close and qdel itself. Set to zero for no timeout.
*/
/proc/tgui_input_number(mob/user, message = null, title = "Number Input", default = null, max_value = null, min_value = 0, timeout = 0)
if (!user)
@@ -26,29 +26,29 @@
/// Client does NOT have tgui_input on: Returns regular input
if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input))
return input(user, message, title, default) as null|num
var/datum/tgui_input_number/numbox = new(user, message, title, default, max_value, min_value, timeout)
numbox.ui_interact(user)
numbox.wait()
if (numbox)
. = numbox.entry
qdel(numbox)
var/datum/tgui_input_number/number_input = new(user, message, title, default, max_value, min_value, timeout)
number_input.ui_interact(user)
number_input.wait()
if (number_input)
. = number_input.entry
qdel(number_input)
/**
* Creates an asynchronous TGUI number input window with an associated callback.
*
* This proc should be used to create numboxes that invoke a callback with the user's entry.
* This proc should be used to create number inputs that invoke a callback with the user's entry.
*
* Arguments:
* * user - The user to show the numbox to.
* * message - The content of the numbox, shown in the body of the TGUI window.
* * title - The title of the numbox modal, shown on the top of the TGUI window.
* * user - The user to show the number input to.
* * message - The content of the number input, shown in the body of the TGUI window.
* * title - The title of the number input modal, shown on the top of the TGUI window.
* * default - The default (or current) value, shown as a placeholder. Users can press refresh with this.
* * max_value - Specifies a maximum value. If none is set, any number can be entered. Pressing "max" defaults to 1000.
* * min_value - Specifies a minimum value. Often 0.
* * callback - The callback to be invoked when a choice is made.
* * timeout - The timeout of the numbox, after which the modal will close and qdel itself. Disabled by default, can be set to seconds otherwise.
* * timeout - The timeout of the number input, after which the modal will close and qdel itself. Set to zero for no timeout.
*/
/proc/tgui_input_number_async(mob/user, message = null, title = "Number Input", default = null, max_value = null, min_value = 0, datum/callback/callback, timeout = 0)
/proc/tgui_input_number_async(mob/user, message = null, title = "Number Input", default = null, max_value = null, min_value = 0, datum/callback/callback, timeout = 60 SECONDS)
if (!user)
user = usr
if (!istype(user))
@@ -57,14 +57,14 @@
user = client.mob
else
return
var/datum/tgui_input_number/async/numbox = new(user, message, title, default, max_value, min_value, callback, timeout)
numbox.ui_interact(user)
var/datum/tgui_input_number/async/number_input = new(user, message, title, default, max_value, min_value, callback, timeout)
number_input.ui_interact(user)
/**
* # tgui_input_number
*
* Datum used for instantiating and using a TGUI-controlled numbox that prompts the user with
* a message and has an input for text entry.
* Datum used for instantiating and using a TGUI-controlled number input that prompts the user with
* a message and has an input for number entry.
*/
/datum/tgui_input_number
/// Boolean field describing if the tgui_input_number was closed by the user.
@@ -79,9 +79,9 @@
var/message
/// The minimum value that can be entered.
var/min_value
/// The time at which the tgui_modal was created, for displaying timeout progress.
/// The time at which the number input was created, for displaying timeout progress.
var/start_time
/// The lifespan of the tgui_input_number, after which the window will close and delete itself.
/// The lifespan of the number input, after which the window will close and delete itself.
var/timeout
/// The title of the TGUI window
var/title
@@ -114,6 +114,7 @@
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "NumberInputModal")
ui.set_autoupdate(FALSE)
ui.open()
/datum/tgui_input_number/ui_close(mob/user)
@@ -125,19 +126,18 @@
/datum/tgui_input_number/ui_static_data(mob/user)
. = list(
"preferences" = list()
"max_value" = max_value,
"message" = message,
"min_value" = min_value,
"placeholder" = default, /// You cannot use default as a const
"preferences" = list(),
"title" = title
)
.["preferences"]["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
.["preferences"]["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
/datum/tgui_input_number/ui_data(mob/user)
. = list(
"max_value" = max_value,
"message" = message,
"min_value" = min_value,
"placeholder" = default, /// You cannot use default as a const
"title" = title,
)
. = list()
if(timeout)
.["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
+27 -28
View File
@@ -6,9 +6,9 @@
* stripped_multiline_input.
*
* Arguments:
* * user - The user to show the textbox to.
* * message - The content of the textbox, shown in the body of the TGUI window.
* * title - The title of the textbox modal, shown on the top of the TGUI window.
* * user - The user to show the text input to.
* * message - The content of the text input, shown in the body of the TGUI window.
* * title - The title of the text input modal, shown on the top of the TGUI window.
* * default - The default (or current) value, shown as a placeholder.
* * max_length - Specifies a max length for input. MAX_MESSAGE_LEN is default (1024)
* * multiline - Bool that determines if the input box is much larger. Good for large messages, laws, etc.
@@ -33,29 +33,28 @@
return stripped_input(user, message, title, default, max_length)
else
return input(user, message, title, default)
var/datum/tgui_input_text/textbox = new(user, message, title, default, max_length, multiline, encode, timeout)
textbox.ui_interact(user)
textbox.wait()
if (textbox)
. = textbox.entry
qdel(textbox)
var/datum/tgui_input_text/text_input = new(user, message, title, default, max_length, multiline, encode, timeout)
text_input.ui_interact(user)
text_input.wait()
if (text_input)
. = text_input.entry
qdel(text_input)
/**
* Creates an asynchronous TGUI text input window with an associated callback.
*
* This proc should be used to create textboxes that invoke a callback with the user's entry.
* This proc should be used to create text inputs that invoke a callback with the user's entry.
* Arguments:
* * user - The user to show the textbox to.
* * message - The content of the textbox, shown in the body of the TGUI window.
* * title - The title of the textbox modal, shown on the top of the TGUI window.
* * user - The user to show the text input to.
* * message - The content of the text input, shown in the body of the TGUI window.
* * title - The title of the text input modal, shown on the top of the TGUI window.
* * default - The default (or current) value, shown as a placeholder.
* * max_length - Specifies a max length for input.
* * multiline - Bool that determines if the input box is much larger. Good for large messages, laws, etc.
* * encode - If toggled, input is filtered via html_encode. Setting this to FALSE gives raw input.
* * callback - The callback to be invoked when a choice is made.
* * timeout - The timeout of the textbox, after which the modal will close and qdel itself. Disabled by default, can be set to seconds otherwise.
*/
/proc/tgui_input_text_async(mob/user, message = null, title = "Text Input", default = null, max_length = null, multiline = FALSE, encode = TRUE, datum/callback/callback, timeout = 0)
/proc/tgui_input_text_async(mob/user, message = null, title = "Text Input", default = null, max_length = null, multiline = FALSE, encode = TRUE, datum/callback/callback, timeout = 60 SECONDS)
if (!user)
user = usr
if (!istype(user))
@@ -64,13 +63,13 @@
user = client.mob
else
return
var/datum/tgui_input_text/async/textbox = new(user, message, title, default, max_length, multiline, encode, callback, timeout)
textbox.ui_interact(user)
var/datum/tgui_input_text/async/text_input = new(user, message, title, default, max_length, multiline, encode, callback, timeout)
text_input.ui_interact(user)
/**
* # tgui_input_text
*
* Datum used for instantiating and using a TGUI-controlled textbox that prompts the user with
* Datum used for instantiating and using a TGUI-controlled text input that prompts the user with
* a message and has an input for text entry.
*/
/datum/tgui_input_text
@@ -88,9 +87,9 @@
var/message
/// Multiline input for larger input boxes.
var/multiline
/// The time at which the tgui_modal was created, for displaying timeout progress.
/// The time at which the text input was created, for displaying timeout progress.
var/start_time
/// The lifespan of the tgui_input_text, after which the window will close and delete itself.
/// The lifespan of the text input, after which the window will close and delete itself.
var/timeout
/// The title of the TGUI window
var/title
@@ -124,6 +123,7 @@
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "TextInputModal")
ui.set_autoupdate(FALSE)
ui.open()
/datum/tgui_input_text/ui_close(mob/user)
@@ -135,19 +135,18 @@
/datum/tgui_input_text/ui_static_data(mob/user)
. = list(
"preferences" = list()
"max_length" = max_length,
"message" = message,
"multiline" = multiline,
"placeholder" = default, /// You cannot use default as a const
"preferences" = list(),
"title" = title
)
.["preferences"]["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large)
.["preferences"]["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped)
/datum/tgui_input_text/ui_data(mob/user)
. = list(
"max_length" = max_length,
"message" = message,
"multiline" = multiline,
"placeholder" = default, /// You cannot use default as a const
"title" = title,
)
. = list()
if(timeout)
.["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
+3 -3
View File
@@ -1214,9 +1214,9 @@ GLOBAL_LIST_EMPTY(vending_products)
if(compartmentLoadAccessCheck(user))
if(istype(I, /obj/item/pen))
name = stripped_input(user,"Set name","Name", name, 20)
desc = stripped_input(user,"Set description","Description", desc, 60)
slogan_list += stripped_input(user,"Set slogan","Slogan","Epic", 60)
name = tgui_input_text(user, "Set name", "Name", name, 20)
desc = tgui_input_text(user, "Set description", "Description", desc, 60)
slogan_list += tgui_input_text(user, "Set slogan", "Slogan", "Epic", 60)
last_slogan = world.time + rand(0, slogan_delay)
return
@@ -1,7 +1,7 @@
import { Loader } from './common/Loader';
import { InputButtons, Preferences } from './common/InputButtons';
import { KEY_ENTER } from 'common/keycodes';
import { useBackend, useSharedState } from '../backend';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, NumberInput, Section, Stack } from '../components';
import { Window } from '../layouts';
@@ -16,10 +16,10 @@ type NumberInputData = {
};
export const NumberInputModal = (_, context) => {
const { data } = useBackend<NumberInputData>(context);
const { act, data } = useBackend<NumberInputData>(context);
const { message, placeholder, preferences, timeout, title } = data;
const { large_buttons } = preferences;
const [input, setInput] = useSharedState(context, 'input', placeholder);
const [input, setInput] = useLocalState(context, 'input', placeholder);
const onChange = (value: number) => {
setInput(value);
};
@@ -35,7 +35,13 @@ export const NumberInputModal = (_, context) => {
return (
<Window title={title} width={270} height={windowHeight}>
{timeout && <Loader value={timeout} />}
<Window.Content>
<Window.Content
onKeyDown={(event) => {
const keyCode = window.event ? event.which : event.keyCode;
if (keyCode === KEY_ENTER) {
act('submit', { entry: input });
}
}}>
<Section fill>
<Stack fill vertical>
<Stack.Item>
@@ -77,12 +83,6 @@ const InputArea = (props, context) => {
maxValue={max_value}
onChange={(_, value) => onChange(value)}
onDrag={(_, value) => onChange(value)}
onKeyDown={(event) => {
const keyCode = window.event ? event.which : event.keyCode;
if (keyCode === KEY_ENTER && input) {
act('submit', { entry: input });
}
}}
value={input || placeholder || 0}
/>
</Stack.Item>
@@ -1,4 +1,4 @@
import { useBackend, useSharedState } from '../backend';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, LabeledList, Icon, NoticeBox, ProgressBar, Section, Stack, Table, Tabs, Tooltip } from '../components';
import { Window } from '../layouts';
@@ -104,7 +104,7 @@ const ICON_MAP = {
};
export const PaiInterface = (_, context) => {
const [tab, setTab] = useSharedState(context, 'tab', 1);
const [tab, setTab] = useLocalState(context, 'tab', 1);
const setTabHandler = (tab: number) => {
setTab(tab);
};
@@ -299,7 +299,7 @@ const DirectiveDisplay = (_, context) => {
* software info.
*/
const InstalledDisplay = (_, context) => {
const [installSelected, setInstallSelected] = useSharedState(
const [installSelected, setInstallSelected] = useLocalState(
context,
'software',
''
@@ -1,6 +1,6 @@
import { Loader } from './common/Loader';
import { InputButtons, Preferences, Validator } from './common/InputButtons';
import { useBackend, useSharedState } from '../backend';
import { useBackend, useLocalState } from '../backend';
import { KEY_ENTER } from 'common/keycodes';
import { Box, Input, Section, Stack, TextArea } from '../components';
import { Window } from '../layouts';
@@ -16,7 +16,7 @@ type TextInputData = {
};
export const TextInputModal = (_, context) => {
const { data } = useBackend<TextInputData>(context);
const { act, data } = useBackend<TextInputData>(context);
const {
max_length,
message,
@@ -27,26 +27,37 @@ export const TextInputModal = (_, context) => {
title,
} = data;
const { large_buttons } = preferences;
const [input, setInput] = useSharedState(context, 'input', placeholder);
const [inputIsValid, setInputIsValid] = useSharedState<Validator>(
const [input, setInput] = useLocalState<string>(
context,
'input',
placeholder || ''
);
const [inputIsValid, setInputIsValid] = useLocalState<Validator>(
context,
'inputIsValid',
{ isValid: false, error: null }
{ isValid: !!placeholder, error: null }
);
const onType = (event) => {
event.preventDefault();
const target = event.target;
setInputIsValid(validateInput(target.value, max_length));
setInput(target.value);
const onType = (value: string) => {
setInputIsValid(validateInput(value, max_length));
setInput(value);
};
// Dynamically changes the window height based on the message.
const windowHeight
= 125 + Math.ceil(message?.length / 3) + (multiline ? 75 : 0);
= 125
+ Math.ceil(message?.length / 3)
+ (multiline ? 75 : 0)
+ (large_buttons ? 5 : 0);
return (
<Window title={title} width={325} height={windowHeight}>
{timeout && <Loader value={timeout} />}
<Window.Content>
<Window.Content
onKeyDown={(event) => {
const keyCode = window.event ? event.which : event.keyCode;
if (keyCode === KEY_ENTER && inputIsValid.isValid) {
act('submit', { entry: input });
}
}}>
<Section fill>
<Stack fill vertical>
<Stack.Item>
@@ -69,9 +80,9 @@ export const TextInputModal = (_, context) => {
/** Gets the user input and invalidates if there's a constraint. */
const InputArea = (props, context) => {
const { act, data } = useBackend<TextInputData>(context);
const { data } = useBackend<TextInputData>(context);
const { multiline } = data;
const { input, inputIsValid, onType } = props;
const { input, onType } = props;
if (!multiline) {
return (
@@ -79,13 +90,7 @@ const InputArea = (props, context) => {
<Input
autoFocus
fluid
onInput={(event) => onType(event)}
onKeyDown={(event) => {
const keyCode = window.event ? event.which : event.keyCode;
if (keyCode === KEY_ENTER && inputIsValid) {
act('submit', { entry: input });
}
}}
onInput={(_, value) => onType(value)}
placeholder="Type something..."
value={input}
/>
@@ -97,14 +102,7 @@ const InputArea = (props, context) => {
<TextArea
autoFocus
height="100%"
onInput={(event) => onType(event)}
onKeyDown={(event) => {
const keyCode = window.event ? event.which : event.keyCode;
if (keyCode === KEY_ENTER && inputIsValid) {
act('submit', { entry: input });
}
}}
onInput={(_, value) => onType(value)}
placeholder="Type something..."
value={input}
/>