The One Where I Port Modals

This commit is contained in:
Chompstation Bot
2021-06-30 19:29:34 +00:00
parent 2457a62edd
commit b6e1989a01
468 changed files with 10519 additions and 1808 deletions

View File

@@ -162,13 +162,13 @@
set name = "Fix TGUI"
set category = "OOC"
if(alert(src, "Only use this verb if you have a white TGUI window stuck on your screen.", "Fix TGUI", "Continue", "Nevermind") != "Continue")
if(alert(src, "Only use this verb if you have a white TGUI window stuck on your screen.", "Fix TGUI", "Continue", "Nevermind") != "Continue") // Not tgui_alert since we're fixing tgui
return
SStgui.close_user_uis(mob)
if(alert(src, "Did that fix the problem?", "Fix TGUI", "Yes", "No") == "No")
if(alert(src, "Did that fix the problem?", "Fix TGUI", "Yes", "No") == "No") // Not tgui_alert since we're fixing tgui
SStgui.force_close_all_windows(mob)
alert(src, "UIs should be fixed now. If not, please cry to your nearest coder.", "Fix TGUI")
alert(src, "UIs should be fixed now. If not, please cry to your nearest coder.", "Fix TGUI") // Not tgui_alert since we're fixing tgui
/**
* verb

View File

@@ -69,7 +69,7 @@
var/datum/shuttle/S = locate(params["ref"])
if(istype(S, /datum/shuttle/autodock/multi))
var/datum/shuttle/autodock/multi/shuttle = S
var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations()
var/dest_key = tgui_input_list(usr, "Choose shuttle destination", "Shuttle Destination", shuttle.get_destinations())
if(dest_key)
shuttle.set_destination(dest_key, usr)
shuttle.launch(src)
@@ -80,13 +80,13 @@
if(!LAZYLEN(possible_d))
to_chat(usr, "<span class='warning'>There are no possible destinations for [shuttle] ([shuttle.type])</span>")
return FALSE
D = input("Choose shuttle destination", "Shuttle Destination") as null|anything in possible_d
D = tgui_input_list(usr, "Choose shuttle destination", "Shuttle Destination", possible_d)
if(D)
shuttle.set_destination(possible_d[D])
shuttle.launch()
else if(istype(S, /datum/shuttle/autodock))
var/datum/shuttle/autodock/shuttle = S
if(alert(usr, "Are you sure you want to launch [shuttle]?", "Launching Shuttle", "Yes", "No") == "Yes")
if(tgui_alert(usr, "Are you sure you want to launch [shuttle]?", "Launching Shuttle", list("Yes", "No")) == "Yes")
shuttle.launch(src)
else
to_chat(usr, "<span class='notice'>The shuttle control panel isn't quite sure how to move [S] ([S?.type]).</span>")

View File

@@ -54,7 +54,7 @@
to_chat(usr, "<span class='notice'>Age has been set to '[S.age]'.</span>")
. = TRUE
if("appearance")
var/datum/card_state/choice = input(usr, "Select the appearance for this card.", "Agent Card Appearance") as null|anything in id_card_states()
var/datum/card_state/choice = tgui_input_list(usr, "Select the appearance for this card.", "Agent Card Appearance", id_card_states())
if(choice && tgui_status(usr, state) == STATUS_INTERACTIVE)
S.icon_state = choice.icon_state
S.item_state = choice.item_state
@@ -118,7 +118,7 @@
to_chat(usr, "<span class='notice'>Sex changed to '[new_sex]'.</span>")
. = TRUE
if("factoryreset")
if(alert("This will factory reset the card, including access and owner. Continue?", "Factory Reset", "No", "Yes") == "Yes" && tgui_status(usr, state) == STATUS_INTERACTIVE)
if(tgui_alert(usr, "This will factory reset the card, including access and owner. Continue?", "Factory Reset", list("No", "Yes")) == "Yes" && tgui_status(usr, state) == STATUS_INTERACTIVE)
S.age = initial(S.age)
S.access = syndicate_access.Copy()
S.assignment = initial(S.assignment)

View File

@@ -134,7 +134,7 @@
return 1
if("hair_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select hair color.", "Hair Color", rgb(target.r_hair, target.g_hair, target.b_hair)) as color|null
var/new_hair = input(usr, "Please select hair color.", "Hair Color", rgb(target.r_hair, target.g_hair, target.b_hair)) as color|null
if(new_hair && can_still_topic(usr, state))
var/r_hair = hex2num(copytext(new_hair, 2, 4))
var/g_hair = hex2num(copytext(new_hair, 4, 6))
@@ -151,7 +151,7 @@
return 1
if("facial_hair_color")
if(can_change(APPEARANCE_FACIAL_HAIR_COLOR))
var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(target.r_facial, target.g_facial, target.b_facial)) as color|null
var/new_facial = input(usr, "Please select facial hair color.", "Facial Hair Color", rgb(target.r_facial, target.g_facial, target.b_facial)) as color|null
if(new_facial && can_still_topic(usr, state))
var/r_facial = hex2num(copytext(new_facial, 2, 4))
var/g_facial = hex2num(copytext(new_facial, 4, 6))
@@ -162,7 +162,7 @@
return 1
if("eye_color")
if(can_change(APPEARANCE_EYE_COLOR))
var/new_eyes = input("Please select eye color.", "Eye Color", rgb(target.r_eyes, target.g_eyes, target.b_eyes)) as color|null
var/new_eyes = input(usr, "Please select eye color.", "Eye Color", rgb(target.r_eyes, target.g_eyes, target.b_eyes)) as color|null
if(new_eyes && can_still_topic(usr, state))
var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
@@ -186,7 +186,7 @@
return TRUE
if("ears_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select ear color.", "Ear Color", rgb(target.r_ears, target.g_ears, target.b_ears)) as color|null
var/new_hair = input(usr, "Please select ear color.", "Ear Color", rgb(target.r_ears, target.g_ears, target.b_ears)) as color|null
if(new_hair && can_still_topic(usr, state))
target.r_ears = hex2num(copytext(new_hair, 2, 4))
target.g_ears = hex2num(copytext(new_hair, 4, 6))
@@ -197,7 +197,7 @@
return 1
if("ears2_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select secondary ear color.", "2nd Ear Color", rgb(target.r_ears2, target.g_ears2, target.b_ears2)) as color|null
var/new_hair = input(usr, "Please select secondary ear color.", "2nd Ear Color", rgb(target.r_ears2, target.g_ears2, target.b_ears2)) as color|null
if(new_hair && can_still_topic(usr, state))
target.r_ears2 = hex2num(copytext(new_hair, 2, 4))
target.g_ears2 = hex2num(copytext(new_hair, 4, 6))
@@ -220,7 +220,7 @@
return TRUE
if("tail_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select tail color.", "Tail Color", rgb(target.r_tail, target.g_tail, target.b_tail)) as color|null
var/new_hair = input(usr, "Please select tail color.", "Tail Color", rgb(target.r_tail, target.g_tail, target.b_tail)) as color|null
if(new_hair && can_still_topic(usr, state))
target.r_tail = hex2num(copytext(new_hair, 2, 4))
target.g_tail = hex2num(copytext(new_hair, 4, 6))
@@ -231,7 +231,7 @@
return 1
if("tail2_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select secondary tail color.", "2nd Tail Color", rgb(target.r_tail2, target.g_tail2, target.b_tail2)) as color|null
var/new_hair = input(usr, "Please select secondary tail color.", "2nd Tail Color", rgb(target.r_tail2, target.g_tail2, target.b_tail2)) as color|null
if(new_hair && can_still_topic(usr, state))
target.r_tail2 = hex2num(copytext(new_hair, 2, 4))
target.g_tail2 = hex2num(copytext(new_hair, 4, 6))
@@ -254,7 +254,7 @@
return TRUE
if("wing_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select wing color.", "Wing Color", rgb(target.r_wing, target.g_wing, target.b_wing)) as color|null
var/new_hair = input(usr, "Please select wing color.", "Wing Color", rgb(target.r_wing, target.g_wing, target.b_wing)) as color|null
if(new_hair && can_still_topic(usr, state))
target.r_wing = hex2num(copytext(new_hair, 2, 4))
target.g_wing = hex2num(copytext(new_hair, 4, 6))
@@ -265,7 +265,7 @@
return 1
if("wing2_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select secondary wing color.", "2nd Wing Color", rgb(target.r_wing2, target.g_wing2, target.b_wing2)) as color|null
var/new_hair = input(usr, "Please select secondary wing color.", "2nd Wing Color", rgb(target.r_wing2, target.g_wing2, target.b_wing2)) as color|null
if(new_hair && can_still_topic(usr, state))
target.r_wing2 = hex2num(copytext(new_hair, 2, 4))
target.g_wing2 = hex2num(copytext(new_hair, 4, 6))

View File

@@ -282,7 +282,7 @@
if(isAI(usr) || isrobot(usr))
to_chat(usr, "<span class='warning'>Firewalls prevent you from recalling the shuttle.</span>")
return
var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No")
var/response = tgui_alert(usr, "Are you sure you wish to recall the shuttle?", "Confirm", list("Yes", "No"))
if(response == "Yes")
cancel_call_proc(usr)
setMenuState(usr, COMM_SCREEN_MAIN)
@@ -301,7 +301,7 @@
var/datum/comm_message_listener/l = obtain_message_listener()
if(params["msgid"])
setCurrentMessage(usr, text2num(params["msgid"]))
var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No")
var/response = tgui_alert(usr, "Are you sure you wish to delete this message?", "Confirm", list("Yes", "No"))
if(response == "Yes")
if(current_viewing_message)
if(l != global_message_listener)
@@ -324,11 +324,11 @@
post_status(src, params["statdisp"], user = usr)
if("setmsg1")
stat_msg1 = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", stat_msg1) as text|null, 40), 40)
stat_msg1 = reject_bad_text(sanitize(input(usr, "Line 1", "Enter Message Text", stat_msg1) as text|null, 40), 40)
setMenuState(usr, COMM_SCREEN_STAT)
if("setmsg2")
stat_msg2 = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", stat_msg2) as text|null, 40), 40)
stat_msg2 = reject_bad_text(sanitize(input(usr, "Line 2", "Enter Message Text", stat_msg2) as text|null, 40), 40)
setMenuState(usr, COMM_SCREEN_STAT)
// OMG CENTCOMM LETTERHEAD
@@ -337,7 +337,7 @@
if(centcomm_message_cooldown > world.time)
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
return
var/input = sanitize(input("Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. \
var/input = sanitize(input(usr, "Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. \
Please be aware that this process is very expensive, and abuse will lead to... termination. \
Transmission does not guarantee a response. \
There is a 30 second delay before you may send another message, be clear, full and concise.", "Central Command Quantum Messaging") as null|message)

View File

@@ -54,7 +54,7 @@
data["isAI"] = isAI(user)
var/z = get_z(user)
var/list/map_levels = uniquelist(using_map.get_map_levels(z, TRUE, om_range = DEFAULT_OVERMAP_RANGE))
var/list/map_levels = uniqueList(using_map.get_map_levels(z, TRUE, om_range = DEFAULT_OVERMAP_RANGE))
data["map_levels"] = map_levels
var/list/crewmembers = list()

View File

@@ -18,7 +18,7 @@
switch(action)
if("set_tag")
var/new_ident = sanitize_text(input("Enter a new ident tag.", "Gyrotron Control", gyro_tag) as null|text)
var/new_ident = sanitize_text(input(usr, "Enter a new ident tag.", "Gyrotron Control", gyro_tag) as null|text)
if(new_ident)
gyro_tag = new_ident
return TRUE

View File

@@ -193,7 +193,7 @@
if(computer && program.can_run(usr, 1) && id_card)
var/t1 = params["assign_target"]
if(t1 == "Custom")
var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment", id_card.assignment), 45)
var/temp_t = sanitize(input(usr, "Enter a custom job assignment.","Assignment", id_card.assignment), 45)
//let custom jobs function as an impromptu alt title, mainly for sechuds
if(temp_t)
id_card.assignment = temp_t

View File

@@ -427,7 +427,7 @@
if(CF.unsendable)
continue
filenames.Add(CF.filename)
var/picked_file = input(user, "Please pick a file to send as attachment (max 32GQ)") as null|anything in filenames
var/picked_file = tgui_input_list(user, "Please pick a file to send as attachment (max 32GQ)", "Select Attachment", filenames)
if(!picked_file)
return 1

View File

@@ -307,7 +307,7 @@
/* HELM */
if("add")
var/datum/computer_file/data/waypoint/R = new()
var/sec_name = input("Input navigation entry name", "New navigation entry", "Sector #[known_sectors.len]") as text
var/sec_name = input(usr, "Input navigation entry name", "New navigation entry", "Sector #[known_sectors.len]") as text
if(!sec_name)
sec_name = "Sector #[known_sectors.len]"
R.fields["name"] = sec_name
@@ -319,8 +319,8 @@
R.fields["x"] = linked.x
R.fields["y"] = linked.y
if("new")
var/newx = input("Input new entry x coordinate", "Coordinate input", linked.x) as num
var/newy = input("Input new entry y coordinate", "Coordinate input", linked.y) as num
var/newx = input(usr, "Input new entry x coordinate", "Coordinate input", linked.x) as num
var/newy = input(usr, "Input new entry y coordinate", "Coordinate input", linked.y) as num
R.fields["x"] = CLAMP(newx, 1, world.maxx)
R.fields["y"] = CLAMP(newy, 1, world.maxy)
known_sectors[sec_name] = R
@@ -335,12 +335,12 @@
if("setcoord")
if(params["setx"])
var/newx = input("Input new destiniation x coordinate", "Coordinate input", dx) as num|null
var/newx = input(usr, "Input new destiniation x coordinate", "Coordinate input", dx) as num|null
if(newx)
dx = CLAMP(newx, 1, world.maxx)
if(params["sety"])
var/newy = input("Input new destiniation y coordinate", "Coordinate input", dy) as num|null
var/newy = input(usr, "Input new destiniation y coordinate", "Coordinate input", dy) as num|null
if(newy)
dy = CLAMP(newy, 1, world.maxy)
. = TRUE
@@ -356,13 +356,13 @@
. = TRUE
if("speedlimit")
var/newlimit = input("Input new speed limit for autopilot (0 to brake)", "Autopilot speed limit", speedlimit*1000) as num|null
var/newlimit = input(usr, "Input new speed limit for autopilot (0 to brake)", "Autopilot speed limit", speedlimit*1000) as num|null
if(newlimit)
speedlimit = CLAMP(newlimit/1000, 0, 100)
. = TRUE
if("accellimit")
var/newlimit = input("Input new acceleration limit", "Acceleration limit", accellimit*1000) as num|null
var/newlimit = input(usr, "Input new acceleration limit", "Acceleration limit", accellimit*1000) as num|null
if(newlimit)
accellimit = max(newlimit/1000, 0)
. = TRUE
@@ -402,7 +402,7 @@
. = TRUE
if("set_global_limit")
var/newlim = input("Input new thrust limit (0..100%)", "Thrust limit", linked.thrust_limit*100) as num
var/newlim = input(usr, "Input new thrust limit (0..100%)", "Thrust limit", linked.thrust_limit*100) as num
linked.thrust_limit = clamp(newlim/100, 0, 1)
for(var/datum/ship_engine/E in linked.engines)
E.set_thrust_limit(linked.thrust_limit)
@@ -416,7 +416,7 @@
if("set_limit")
var/datum/ship_engine/E = locate(params["engine"])
var/newlim = input("Input new thrust limit (0..100)", "Thrust limit", E.get_thrust_limit()) as num
var/newlim = input(usr, "Input new thrust limit (0..100)", "Thrust limit", E.get_thrust_limit()) as num
var/limit = clamp(newlim/100, 0, 1)
if(istype(E))
E.set_thrust_limit(limit)
@@ -437,7 +437,7 @@
/* END ENGINES */
/* SENSORS */
if("range")
var/nrange = input("Set new sensors range", "Sensor range", sensors.range) as num|null
var/nrange = input(usr, "Set new sensors range", "Sensor range", sensors.range) as num|null
if(nrange)
sensors.set_range(CLAMP(nrange, 1, world.view))
. = TRUE

View File

@@ -25,7 +25,7 @@
return TRUE
if("set_tag")
var/new_ident = sanitize_text(input("Enter a new ident tag.", "Core Control", core_tag) as null|text)
var/new_ident = sanitize_text(input(usr, "Enter a new ident tag.", "Core Control", core_tag) as null|text)
if(new_ident)
core_tag = new_ident
return TRUE

View File

@@ -22,7 +22,7 @@
return TRUE
if("set_tag")
var/new_ident = sanitize_text(input("Enter a new ident tag.", "Gyrotron Control", fuel_tag) as null|text)
var/new_ident = sanitize_text(input(usr, "Enter a new ident tag.", "Gyrotron Control", fuel_tag) as null|text)
if(new_ident)
fuel_tag = new_ident

View File

@@ -59,7 +59,7 @@
areaindex[tmpname] = 1
L[tmpname] = I
var/desc = input("Please select a location to lock in.", "Locking Menu") in L|null
var/desc = tgui_input_list(usr, "Please select a location to lock in.", "Locking Menu", L)
if(!desc)
return FALSE
if(tgui_status(usr, state) != STATUS_INTERACTIVE)

View File

@@ -0,0 +1,169 @@
/**
* Creates a TGUI alert window and returns the user's response.
*
* This proc should be used to create alerts that the caller will wait for a response from.
* Arguments:
* * user - The user to show the alert to.
* * message - The content of the alert, shown in the body of the TGUI window.
* * title - The of the alert modal, shown on the top of the TGUI window.
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
* * timeout - The timeout of the alert, after which the modal will close and qdel itself. Set to zero for no timeout.
*/
/proc/tgui_alert(mob/user, message = null, title = null, list/buttons = list("Ok"), timeout = 0)
if (istext(buttons))
stack_trace("tgui_alert() received text for buttons instead of list")
return
if (istext(user))
stack_trace("tgui_alert() received text for user instead of list")
return
if (!user)
user = usr
if (!istype(user))
if (istype(user, /client))
var/client/client = user
user = client.mob
else
return
var/datum/tgui_alert/alert = new(user, message, title, buttons, timeout)
alert.tgui_interact(user)
alert.wait()
if (alert)
. = alert.choice
qdel(alert)
/**
* Creates an asynchronous TGUI alert window with an associated callback.
*
* This proc should be used to create alerts that invoke a callback with the user's chosen option.
* Arguments:
* * user - The user to show the alert to.
* * message - The content of the alert, shown in the body of the TGUI window.
* * title - The of the alert modal, shown on the top of the TGUI window.
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
* * callback - The callback to be invoked when a choice is made.
* * timeout - The timeout of the alert, after which the modal will close and qdel itself. Disabled by default, can be set to seconds otherwise.
*/
/proc/tgui_alert_async(mob/user, message = null, title = null, list/buttons = list("Ok"), datum/callback/callback, timeout = 0)
if (istext(buttons))
stack_trace("tgui_alert() received text for buttons instead of list")
return
if (istext(user))
stack_trace("tgui_alert() received text for user instead of list")
return
if (!user)
user = usr
if (!istype(user))
if (istype(user, /client))
var/client/client = user
user = client.mob
else
return
var/datum/tgui_alert/async/alert = new(user, message, title, buttons, callback, timeout)
alert.tgui_interact(user)
/**
* # tgui_modal
*
* Datum used for instantiating and using a TGUI-controlled modal that prompts the user with
* a message and has buttons for responses.
*/
/datum/tgui_alert
/// The title of the TGUI window
var/title
/// The textual body of the TGUI window
var/message
/// The list of buttons (responses) provided on the TGUI window
var/list/buttons
/// The button that the user has pressed, null if no selection has been made
var/choice
/// The time at which the tgui_modal was created, for displaying timeout progress.
var/start_time
/// The lifespan of the tgui_modal, after which the window will close and delete itself.
var/timeout
/// Boolean field describing if the tgui_modal was closed by the user.
var/closed
/datum/tgui_alert/New(mob/user, message, title, list/buttons, timeout)
src.title = title
src.message = message
src.buttons = buttons.Copy()
if (timeout)
src.timeout = timeout
start_time = world.time
QDEL_IN(src, timeout)
/datum/tgui_alert/Destroy(force, ...)
SStgui.close_uis(src)
QDEL_NULL(buttons)
. = ..()
/**
* Waits for a user's response to the tgui_modal's prompt before returning. Returns early if
* the window was closed by the user.
*/
/datum/tgui_alert/proc/wait()
while (!choice && !closed && !QDELETED(src))
stoplag(1)
/datum/tgui_alert/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "AlertModal")
ui.open()
/datum/tgui_alert/tgui_close(mob/user)
. = ..()
closed = TRUE
/datum/tgui_alert/tgui_state(mob/user)
return GLOB.tgui_always_state
/datum/tgui_alert/tgui_data(mob/user)
. = list(
"title" = title,
"message" = message,
"buttons" = buttons
)
if(timeout)
.["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
/datum/tgui_alert/tgui_act(action, list/params)
. = ..()
if (.)
return
switch(action)
if("choose")
if (!(params["choice"] in buttons))
return
set_choice(params["choice"])
SStgui.close_uis(src)
return TRUE
/datum/tgui_alert/proc/set_choice(choice)
src.choice = choice
/**
* # async tgui_modal
*
* An asynchronous version of tgui_modal to be used with callbacks instead of waiting on user responses.
*/
/datum/tgui_alert/async
/// The callback to be invoked by the tgui_modal upon having a choice made.
var/datum/callback/callback
/datum/tgui_alert/async/New(mob/user, message, title, list/buttons, callback, timeout)
..(user, message, title, buttons, timeout)
src.callback = callback
/datum/tgui_alert/async/Destroy(force, ...)
QDEL_NULL(callback)
. = ..()
/datum/tgui_alert/async/set_choice(choice)
. = ..()
if(!isnull(src.choice))
callback?.InvokeAsync(src.choice)
/datum/tgui_alert/async/wait()
return

View File

@@ -0,0 +1,195 @@
/**
* Creates a TGUI input list window and returns the user's response.
*
* This proc should be used to create alerts that the caller will wait for a response from.
* Arguments:
* * user - The user to show the input box to.
* * message - The content of the input box, shown in the body of the TGUI window.
* * title - The title of the input box, shown on the top of the TGUI window.
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
* * timeout - The timeout of the input box, after which the input box will close and qdel itself. Set to zero for no timeout.
*/
/proc/tgui_input_list(mob/user, message, title, list/buttons, default, timeout = 0)
if (istext(user))
stack_trace("tgui_alert() received text for user instead of list")
return
if (!user)
user = usr
if(!length(buttons))
return
if (!istype(user))
if (istype(user, /client))
var/client/client = user
user = client.mob
else
return
var/datum/tgui_list_input/input = new(user, message, title, buttons, default, timeout)
input.tgui_interact(user)
input.wait()
if (input)
. = input.choice
qdel(input)
/**
* Creates an asynchronous TGUI input list window with an associated callback.
*
* This proc should be used to create inputs that invoke a callback with the user's chosen option.
* Arguments:
* * user - The user to show the input box to.
* * message - The content of the input box, shown in the body of the TGUI window.
* * title - The title of the input box, shown on the top of the TGUI window.
* * buttons - The options that can be chosen by the user, each string is assigned a button on the UI.
* * callback - The callback to be invoked when a choice is made.
* * timeout - The timeout of the input box, after which the menu will close and qdel itself. Set to zero for no timeout.
*/
/proc/tgui_input_list_async(mob/user, message, title, list/buttons, default, datum/callback/callback, timeout = 60 SECONDS)
if (istext(user))
stack_trace("tgui_alert() received text for user instead of list")
return
if (!user)
user = usr
if(!length(buttons))
return
if (!istype(user))
if (istype(user, /client))
var/client/client = user
user = client.mob
else
return
var/datum/tgui_list_input/async/input = new(user, message, title, buttons, default, callback, timeout)
input.tgui_interact(user)
/**
* # tgui_list_input
*
* Datum used for instantiating and using a TGUI-controlled list input that prompts the user with
* a message and shows a list of selectable options
*/
/datum/tgui_list_input
/// The title of the TGUI window
var/title
/// The textual body of the TGUI window
var/message
/// The list of buttons (responses) provided on the TGUI window
var/list/buttons
/// Buttons (strings specifically) mapped to the actual value (e.g. a mob or a verb)
var/list/buttons_map
/// The button that the user has pressed, null if no selection has been made
var/choice
/// The time at which the tgui_list_input was created, for displaying timeout progress.
var/start_time
/// The lifespan of the tgui_list_input, after which the window will close and delete itself.
var/timeout
/// Boolean field describing if the tgui_list_input was closed by the user.
var/closed
/datum/tgui_list_input/New(mob/user, message, title, list/buttons, default, timeout)
src.title = title
src.message = message
src.buttons = list()
src.buttons_map = list()
var/list/repeat_buttons = list()
// Gets rid of illegal characters
var/static/regex/whitelistedWords = regex(@{"([^\u0020-\u8000]+)"})
for(var/i in buttons)
var/string_key = whitelistedWords.Replace("[i]", "")
//avoids duplicated keys E.g: when areas have the same name
string_key = avoid_assoc_duplicate_keys(string_key, repeat_buttons)
src.buttons += string_key
src.buttons_map[string_key] = i
if (timeout)
src.timeout = timeout
start_time = world.time
QDEL_IN(src, timeout)
/datum/tgui_list_input/Destroy(force, ...)
SStgui.close_uis(src)
QDEL_NULL(buttons)
. = ..()
/**
* Waits for a user's response to the tgui_list_input's prompt before returning. Returns early if
* the window was closed by the user.
*/
/datum/tgui_list_input/proc/wait()
while (!choice && !closed)
stoplag(1)
/datum/tgui_list_input/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ListInput")
ui.open()
/datum/tgui_list_input/tgui_close(mob/user)
. = ..()
closed = TRUE
/datum/tgui_list_input/tgui_state(mob/user)
return GLOB.tgui_always_state
/datum/tgui_list_input/tgui_static_data(mob/user)
. = list(
"title" = title,
"message" = message,
"buttons" = buttons
)
/datum/tgui_list_input/tgui_data(mob/user)
. = list()
if(timeout)
.["timeout"] = clamp((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS), 0, 1)
/datum/tgui_list_input/tgui_act(action, list/params)
. = ..()
if (.)
return
switch(action)
if("choose")
if (!(params["choice"] in buttons))
return
set_choice(buttons_map[params["choice"]])
SStgui.close_uis(src)
return TRUE
if("cancel")
SStgui.close_uis(src)
closed = TRUE
return TRUE
/datum/tgui_list_input/proc/set_choice(choice)
src.choice = choice
/**
* # async tgui_list_input
*
* An asynchronous version of tgui_list_input to be used with callbacks instead of waiting on user responses.
*/
/datum/tgui_list_input/async
/// The callback to be invoked by the tgui_list_input upon having a choice made.
var/datum/callback/callback
/datum/tgui_list_input/async/New(mob/user, message, title, list/buttons, default, callback, timeout)
..(user, title, message, buttons, default, timeout)
src.callback = callback
/datum/tgui_list_input/async/Destroy(force, ...)
QDEL_NULL(callback)
. = ..()
/datum/tgui_list_input/async/tgui_close(mob/user)
. = ..()
qdel(src)
/datum/tgui_list_input/async/set_choice(choice)
. = ..()
if(!isnull(src.choice))
callback?.InvokeAsync(src.choice)
/datum/tgui_list_input/async/wait()
return